pyzo-4.4.3/0000777000000000000000000000000013166630337010665 5ustar 00000000000000pyzo-4.4.3/doc/0000777000000000000000000000000013166630334011427 5ustar 00000000000000pyzo-4.4.3/doc/.requirements0000666000000000000000000000003012660452173014145 0ustar 00000000000000pyzolib pygments PySide pyzo-4.4.3/doc/codeeditor/0000777000000000000000000000000013166630335013551 5ustar 00000000000000pyzo-4.4.3/doc/codeeditor/index.rst0000666000000000000000000000045612660452173015417 0ustar 00000000000000.. _codeeditor: Codeeditor - The core editing component ======================================= This subpackage is independent of any other components of IEP and only has Qt (PySide or PyQt4) as a dependency. It works on Python version 2.7 and up (including 3.x). Content and API will come here. pyzo-4.4.3/doc/conf.py0000666000000000000000000001742613123036340012726 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Pyzo documentation build configuration file, created by # sphinx-quickstart on Sun Jan 26 00:33:41 2014. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # noqa # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Pyzo' copyright = '2016, Pyzo contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '3.4' # The full version, including alpha/beta/rc tags. release = '3.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Pyzodoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pyzo.tex', 'Pyzo Documentation', 'Pyzo contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'pyzo', 'Pyzo Documentation', ['Pyzo contributors'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Pyzo', 'Pyzo Documentation', 'Pyzo contributors', 'Pyzo', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False pyzo-4.4.3/doc/index.rst0000666000000000000000000000130412660452173013267 0ustar 00000000000000.. IEP documentation master file, created by sphinx-quickstart on Sun Jan 26 00:33:41 2014. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to IEP's documentation! =============================== API docs in progress. IEP is organized in several subpackages, some of which can be used completely independent from the rest. Although IEP the IDE requires Qt and Python 3, some of its subpackages can safely be imported without these dependencies... Contents: .. toctree:: :maxdepth: 2 codeeditor/index yoton/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pyzo-4.4.3/doc/make.bat0000777000000000000000000001505112662716362013047 0ustar 00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyzo.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyzo.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end pyzo-4.4.3/doc/Makefile0000666000000000000000000001514212662716362013100 0ustar 00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyzo.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyzo.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pyzo" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyzo" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." pyzo-4.4.3/doc/yoton/0000777000000000000000000000000013166630335012600 5ustar 00000000000000pyzo-4.4.3/doc/yoton/channels.rst0000666000000000000000000005325512660452173015137 0ustar 00000000000000Channels ======== The channel classes represent the mechanism for the user to send messages into the network and receive messages from it. A channel needs a context to function; the context represents a node in the network. Slots ----- To be able to route messages to the right channel, channels are associated with a slot (a string name). This slot consists of a user-defined base name and an extension to tell the message type and messaging pattern. Messages send from a channel with slot X, are only received by channels with the same slot X. Slots are case insensitive. Messaging patterns ------------------ Yoton supports three base messaging patterns. For each messaging pattern there are specific channel classes. All channels derive from :ref:`yoton.BaseChannel`. **publish/subscribe** The :ref:`yoton.PubChannel` class is used for sending messages into the network, and the :ref:`yoton.SubChannel` class is used to receiving these messages. Multiple PubChannels and SubChannels can exist in the same network at the same slot; the SubChannels simply collect the messages send by all PubChannels. **request/reply** The :ref:`yoton.ReqChannel` class is used to do requests, and the :ref:`yoton.RepChannel` class is used to reply to requests. If multiple ReqChannels are present at the same slot, simple load balancing is performed. **state** The :ref:`yoton.StateChannel` class is used to communicate state to other state channels. Each :ref:`yoton.StateChannel` can set and get the state. Message types ------------- Messages are of a specific type (text, binary, ...), the default being Unicode text. The third (optional) argument to a Channel's initializer is a MessageType object that specifies how messages should be converted to bytes and the other way around. This way, the channels classes themself can be agnostic about the message type, while the user can implement its own MessageType class to send whatever messages he/she likes. .. insertdocs start:: yoton.BaseChannel .. insertdocs :members: .. _insertdocs-yoton-BaseChannel: .. py:class:: yoton.BaseChannel(context, slot_base, message_type=yoton.TEXT) *Inherits from object* Abstract class for all channels. **Parameters** context : :ref:`yoton.Context` instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to enable communicating any type of message they want. **Details** Messages send via a channel are delivered asynchronically to the corresponding channels. All channels are associated with a context and can be used to send messages to other channels in the network. Each channel is also associated with a slot, which is a string that represents a kind of address. A message send by a channel at slot X can only be received by a channel with slot X. Note that the channel appends an extension to the user-supplied slot name, that represents the message type and messaging pattern of the channel. In this way, it is prevented that for example a PubChannel can communicate with a RepChannel. *PROPERTIES* .. _insertdocs-yoton-BaseChannel-closed: .. py:attribute:: yoton.BaseChannel.closed Get whether the channel is closed. .. _insertdocs-yoton-BaseChannel-pending: .. py:attribute:: yoton.BaseChannel.pending Get the number of pending incoming messages. .. _insertdocs-yoton-BaseChannel-received: .. py:attribute:: yoton.BaseChannel.received Signal that is emitted when new data is received. Multiple arrived messages may result in a single call to this method. There is no guarantee that recv() has not been called in the mean time. The signal is emitted with the channel instance as argument. .. _insertdocs-yoton-BaseChannel-slot_incoming: .. py:attribute:: yoton.BaseChannel.slot_incoming Get the incoming slot name. .. _insertdocs-yoton-BaseChannel-slot_outgoing: .. py:attribute:: yoton.BaseChannel.slot_outgoing Get the outgoing slot name. *METHODS* .. _insertdocs-yoton-BaseChannel-close: .. py:method:: yoton.BaseChannel.close() Close the channel, i.e. unregisters this channel at the context. A closed channel cannot be reused. Future attempt to send() messages will result in an IOError being raised. Messages currently in the channel's queue can still be recv()'ed, but no new messages will be delivered at this channel. .. insertdocs end:: .. insertdocs start:: yoton.PubChannel .. insertdocs :members: .. _insertdocs-yoton-PubChannel: .. py:class:: yoton.PubChannel(context, slot_base, message_type=yoton.TEXT) *Inherits from BaseChannel* The publish part of the publish/subscribe messaging pattern. Sent messages are received by all :ref:`yoton.SubChannel` instances with the same slot. There are no limitations for this channel if events are not processed. **Parameters** context : :ref:`yoton.Context` instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to let channels any type of message they want. *METHODS* .. _insertdocs-yoton-PubChannel-send: .. py:method:: yoton.PubChannel.send(message) Send a message over the channel. What is send as one message will also be received as one message. The message is queued and delivered to all corresponding SubChannels (i.e. with the same slot) in the network. .. insertdocs end:: .. insertdocs start:: yoton.SubChannel .. insertdocs :members: .. _insertdocs-yoton-SubChannel: .. py:class:: yoton.SubChannel(context, slot_base, message_type=yoton.TEXT) *Inherits from BaseChannel* The subscribe part of the publish/subscribe messaging pattern. Received messages were sent by a :ref:`yoton.PubChannel` instance at the same slot. This channel can be used as an iterator, which yields all pending messages. The function :ref:`yoton.select_sub_channel` can be used to synchronize multiple SubChannel instances. If no events being processed this channel works as normal, except that the received signal will not be emitted, and sync mode will not work. **Parameters** context : :ref:`yoton.Context` instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to let channels any type of message they want. *METHODS* .. _insertdocs-yoton-SubChannel-next: .. py:method:: yoton.SubChannel.next() Return the next message, or raises StopIteration if non available. .. _insertdocs-yoton-SubChannel-recv: .. py:method:: yoton.SubChannel.recv(block=True) Receive a message from the channel. What was send as one message is also received as one message. If block is False, returns empty message if no data is available. If block is True, waits forever until data is available. If block is an int or float, waits that many seconds. If the channel is closed, returns empty message. .. _insertdocs-yoton-SubChannel-recv_all: .. py:method:: yoton.SubChannel.recv_all() Receive a list of all pending messages. The list can be empty. .. _insertdocs-yoton-SubChannel-recv_selected: .. py:method:: yoton.SubChannel.recv_selected() Receive a list of messages. Use only after calling :ref:`yoton.select_sub_channel` with this channel as one of the arguments. The returned messages are all received before the first pending message in the other SUB-channels given to select_sub_channel. The combination of this method and the function select_sub_channel enables users to combine multiple SUB-channels in a way that preserves the original order of the messages. .. _insertdocs-yoton-SubChannel-set_sync_mode: .. py:method:: yoton.SubChannel.set_sync_mode(value) Set or unset the SubChannel in sync mode. When in sync mode, all channels that send messages to this channel are blocked if the queue for this SubChannel reaches a certain size. This feature can be used to limit the rate of senders if the consumer (i.e. the one that calls recv()) cannot keep up with processing the data. This feature requires the yoton event loop to run at the side of the SubChannel (not necessary for the :ref:`yoton.PubChannel` side). .. insertdocs end:: .. insertdocs start:: yoton.select_sub_channel .. _insertdocs-yoton-select_sub_channel: .. py:function:: yoton.select_sub_channel(channel1, channel2, ...) Returns the channel that has the oldest pending message of all given yoton.SubCannel instances. Returns None if there are no pending messages. This function can be used to read from SubCannels instances in the order that the messages were send. After calling this function, use channel.recv_selected() to obtain all messages that are older than any pending messages in the other given channels. .. insertdocs end:: .. insertdocs start:: yoton.ReqChannel .. insertdocs :members: .. _insertdocs-yoton-ReqChannel: .. py:class:: yoton.ReqChannel(context, slot_base) *Inherits from BaseChannel* The request part of the request/reply messaging pattern. A ReqChannel instance sends request and receive the corresponding replies. The requests are replied by a :ref:`yoton.RepChannel` instance. This class adopts req/rep in a remote procedure call (RPC) scheme. The handling of the result is done using a :ref:`yoton.Future` object, which follows the approach specified in PEP 3148. Note that for the use of callbacks, the yoton event loop must run. Basic load balancing is performed by first asking all potential repliers whether they can handle a request. The actual request is then send to the first replier to respond. **Parameters** context : :ref:`yoton.Context` instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network **Usage** One performs a call on a virtual method of this object. The actual method is executed by the :ref:`yoton.RepChannel` instance. The method can be called with normal and keyword arguments, which can be (a combination of): None, bool, int, float, string, list, tuple, dict. **Example** .. code-block:: python # Fast, but process is idling when waiting for the response. reply = req.add(3,4).result(2.0) # Wait two seconds # Asynchronous processing, but no waiting. def reply_handler(future): ... # Handle reply future = req.add(3,4) future.add_done_callback(reply_handler) .. insertdocs end:: .. insertdocs start:: yoton.RepChannel .. insertdocs :members: .. _insertdocs-yoton-RepChannel: .. py:class:: yoton.RepChannel(context, slot_base) *Inherits from BaseChannel* The reply part of the request/reply messaging pattern. A RepChannel instance receives request and sends the corresponding replies. The requests are send from a :ref:`yoton.ReqChannel` instance. This class adopts req/rep in a remote procedure call (RPC) scheme. To use a RepChannel, subclass this class and implement the methods that need to be available. The reply should be (a combination of) None, bool, int, float, string, list, tuple, dict. This channel needs to be set to event or thread mode to function (in the first case yoton events need to be processed too). To stop handling events again, use set_mode('off'). **Parameters** context : :ref:`yoton.Context` instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network *METHODS* .. _insertdocs-yoton-RepChannel-echo: .. py:method:: yoton.RepChannel.echo(arg1, sleep=0.0) Default procedure that can be used for testing. It returns a tuple (first_arg, context_id) .. _insertdocs-yoton-RepChannel-set_mode: .. py:method:: yoton.RepChannel.set_mode(mode) Set the replier to its operating mode, or turn it off. Modes: * 0 or 'off': do not process requests * 1 or 'event': use the yoton event loop to process requests * 2 or 'thread': process requests in a separate thread .. insertdocs end:: .. insertdocs start:: yoton.Future .. insertdocs :members: .. _insertdocs-yoton-Future: .. py:class:: yoton.Future(req_channel, req, request_id) *Inherits from object* The Future object represents the future result of a request done at a :ref:`yoton.ReqChannel`. It enables: * checking whether the request is done. * getting the result or the exception raised during handling the request. * canceling the request (if it is not yet running) * registering callbacks to handle the result when it is available *METHODS* .. _insertdocs-yoton-Future-add_done_callback: .. py:method:: yoton.Future.add_done_callback(fn) Attaches the callable fn to the future. fn will be called, with the future as its only argument, when the future is cancelled or finishes running. Added callables are called in the order that they were added. If the callable raises a Exception subclass, it will be logged and ignored. If the callable raises a BaseException subclass, the behavior is undefined. If the future has already completed or been cancelled, fn will be called immediately. .. _insertdocs-yoton-Future-cancel: .. py:method:: yoton.Future.cancel() Attempt to cancel the call. If the call is currently being executed and cannot be cancelled then the method will return False, otherwise the call will be cancelled and the method will return True. .. _insertdocs-yoton-Future-cancelled: .. py:method:: yoton.Future.cancelled() Return True if the call was successfully cancelled. .. _insertdocs-yoton-Future-done: .. py:method:: yoton.Future.done() Return True if the call was successfully cancelled or finished running. .. _insertdocs-yoton-Future-exception: .. py:method:: yoton.Future.exception(timeout) Return the exception raised by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then a TimeoutError will be raised. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time. If the future is cancelled before completing then CancelledError will be raised. If the call completed without raising, None is returned. .. _insertdocs-yoton-Future-result: .. py:method:: yoton.Future.result(timeout=None) Return the value returned by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then a TimeoutError will be raised. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time. If the future is cancelled before completing then CancelledError will be raised. If the call raised, this method will raise the same exception. .. _insertdocs-yoton-Future-result_or_cancel: .. py:method:: yoton.Future.result_or_cancel(timeout=1.0) Return the value returned by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then the call is cancelled and the method will return None. .. _insertdocs-yoton-Future-running: .. py:method:: yoton.Future.running() Return True if the call is currently being executed and cannot be cancelled. .. _insertdocs-yoton-Future-set_auto_cancel_timeout: .. py:method:: yoton.Future.set_auto_cancel_timeout(timeout) Set the timeout after which the call is automatically cancelled if it is not done yet. By default, this value is 10 seconds. If timeout is None, there is no limit to the wait time. .. _insertdocs-yoton-Future-set_exception: .. py:method:: yoton.Future.set_exception(exception) Sets the result of the work associated with the Future to the Exception exception. This method should only be used by Executor implementations and unit tests. .. _insertdocs-yoton-Future-set_result: .. py:method:: yoton.Future.set_result(result) Sets the result of the work associated with the Future to result. This method should only be used by Executor implementations and unit tests. .. _insertdocs-yoton-Future-set_running_or_notify_cancel: .. py:method:: yoton.Future.set_running_or_notify_cancel() This method should only be called by Executor implementations before executing the work associated with the Future and by unit tests. If the method returns False then the Future was cancelled, i.e. Future.cancel() was called and returned True. If the method returns True then the Future was not cancelled and has been put in the running state, i.e. calls to Future.running() will return True. This method can only be called once and cannot be called after Future.set_result() or Future.set_exception() have been called. .. insertdocs end:: .. insertdocs start:: yoton.StateChannel .. insertdocs :members: .. _insertdocs-yoton-StateChannel: .. py:class:: yoton.StateChannel(context, slot_base, message_type=yoton.TEXT) *Inherits from BaseChannel* Channel class for the state messaging pattern. A state is synchronized over all state channels of the same slot. Each channel can send (i.e. set) the state and recv (i.e. get) the current state. Note however, that if two StateChannel instances set the state around the same time, due to the network delay, it is undefined which one sets the state the last. The context will automatically call this channel's send_last() method when a new context enters the network. The recv() call is always non-blocking and always returns the last received message: i.e. the current state. There are no limitations for this channel if events are not processed, except that the received signal is not emitted. **Parameters** context : :ref:`yoton.Context` instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to let channels any type of message they want. *METHODS* .. _insertdocs-yoton-StateChannel-recv: .. py:method:: yoton.StateChannel.recv(block=False) Get the state of the channel. Always non-blocking. Returns the most up to date state. .. _insertdocs-yoton-StateChannel-send: .. py:method:: yoton.StateChannel.send(message) Set the state of this channel. The state-message is queued and send over the socket by the IO-thread. Zero-length messages are ignored. .. _insertdocs-yoton-StateChannel-send_last: .. py:method:: yoton.StateChannel.send_last() Resend the last message. .. insertdocs end:: pyzo-4.4.3/doc/yoton/clientserver.rst0000666000000000000000000001035412660452173016042 0ustar 00000000000000:mod:`clientserver` -- Request-reply pattern using a client-server model ========================================================================= .. insertdocs start:: yoton.clientserver .. _insertdocs-yoton-clientserver: .. py:module:: yoton.clientserver yoton.clientserver.py Yoton comes with a small framework to setup a request-reply pattern using a client-server model (over a non-persistent connection), similar to telnet. This allows one process to easily ask small pieces of information from another process. To create a server, create a class that inherits from :ref:`yoton.RequestServer` and implement its handle_request() method. A client process can simply use the :ref:`yoton.do_request` function. Example: ``yoton.do_request('www.google.com:80', 'GET http/1.1\r\n')`` The client server model is implemented using one function and one class: :ref:`yoton.do_request` and :ref:`yoton.RequestServer`. **Details** The server implements a request/reply pattern by listening at a socket. Similar to telnet, each request is handled using a connection and the socket is closed after the response is send. The request server can setup to run in the main thread, or can be started using its own thread. In the latter case, one can easily create multiple servers in a single process, that listen on different ports. .. insertdocs end:: Implementation -------------- The client server model is implemented using one function and one class: :ref:`yoton.do_request` and :ref:`yoton.RequestServer`. .. insertdocs start:: yoton.do_request .. _insertdocs-yoton-do_request: .. py:function:: yoton.do_request(address, request, timeout=-1) Do a request at the server at the specified address. The server can be a :ref:`yoton.RequestServer`, or any other server listening on a socket and following a REQ/REP pattern, such as html or telnet. For example: ``html = do_request('www.google.com:80', 'GET http/1.1\r\n')`` **Parameters** address : str Should be of the shape hostname:port. request : string The request to make. timeout : float If larger than 0, will wait that many seconds for the respons, and return None if timed out. **Notes on hostname** The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connections is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also connect to 'localhost'. * 'publichost': the connection is visible by other computers on the same network. .. insertdocs end:: .. insertdocs start:: yoton.RequestServer .. _insertdocs-yoton-RequestServer: .. py:class:: yoton.RequestServer(address, async=False, verbose=0) *Inherits from Thread* Setup a simple server that handles requests similar to a telnet server, or asyncore. Starting the server using run() will run the server in the calling thread. Starting the server using start() will run the server in a separate thread. To create a server, subclass this class and re-implement the handle_request method. It accepts a request and should return a reply. This server assumes utf-8 encoded messages. **Parameters** address : str Should be of the shape hostname:port. async : bool If True, handles each incoming connection in a separate thread. This might be advantageous if a the handle_request() method takes a long time to execute. verbose : bool If True, print a message each time a connection is accepted. **Notes on hostname** The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connections is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also connect to 'localhost'. * 'publichost': the connection is visible by other computers on the same network. .. insertdocs end:: pyzo-4.4.3/doc/yoton/connection.rst0000666000000000000000000001437312660452173015501 0ustar 00000000000000Connection ========== The connection classes represent the connection between two context. There is one base class (yoton.Connection) and currently there is one implementation: the :ref:`yoton.TcpConnection`. In the future other connections might be added that use other methods than TCP/IP. .. insertdocs start:: yoton.Connection .. insertdocs :members: .. _insertdocs-yoton-Connection: .. py:class:: yoton.Connection(context, name='') *Inherits from object* Abstract base class for a connection between two Context objects. This base class defines the full interface; subclasses only need to implement a few private methods. The connection classes are intended as a simple interface for the user, for example to query port number, and be notified of timeouts and closing of the connection. All connection instances are intended for one-time use. To make a new connection, instantiate a new Connection object. After instantiation, either _bind() or _connect() should be called. *PROPERTIES* .. _insertdocs-yoton-Connection-closed: .. py:attribute:: yoton.Connection.closed Signal emitted when the connection closes. The first argument is the ContextConnection instance, the second argument is the reason for the disconnection (as a string). .. _insertdocs-yoton-Connection-hostname1: .. py:attribute:: yoton.Connection.hostname1 Get the hostname corresponding to this end of the connection. .. _insertdocs-yoton-Connection-hostname2: .. py:attribute:: yoton.Connection.hostname2 Get the hostname for the other end of this connection. Is empty string if not connected. .. _insertdocs-yoton-Connection-id1: .. py:attribute:: yoton.Connection.id1 The id of the context on this side of the connection. .. _insertdocs-yoton-Connection-id2: .. py:attribute:: yoton.Connection.id2 The id of the context on the other side of the connection. .. _insertdocs-yoton-Connection-is_alive: .. py:attribute:: yoton.Connection.is_alive Get whether this connection instance is alive (i.e. either waiting or connected, and not in the process of closing). .. _insertdocs-yoton-Connection-is_connected: .. py:attribute:: yoton.Connection.is_connected Get whether this connection instance is connected. .. _insertdocs-yoton-Connection-is_waiting: .. py:attribute:: yoton.Connection.is_waiting Get whether this connection instance is waiting for a connection. This is the state after using bind() and before another context connects to it. .. _insertdocs-yoton-Connection-name: .. py:attribute:: yoton.Connection.name Set/get the name that this connection is known by. This name can be used to obtain the instance using the Context.connections property. The name can be used in networks in which each context has a particular role, to easier distinguish between the different connections. Other than that, the name has no function. .. _insertdocs-yoton-Connection-pid1: .. py:attribute:: yoton.Connection.pid1 The pid of the context on this side of the connection. (hint: os.getpid()) .. _insertdocs-yoton-Connection-pid2: .. py:attribute:: yoton.Connection.pid2 The pid of the context on the other side of the connection. .. _insertdocs-yoton-Connection-port1: .. py:attribute:: yoton.Connection.port1 Get the port number corresponding to this end of the connection. When binding, use this port to connect the other context. .. _insertdocs-yoton-Connection-port2: .. py:attribute:: yoton.Connection.port2 Get the port number for the other end of the connection. Is zero when not connected. .. _insertdocs-yoton-Connection-timedout: .. py:attribute:: yoton.Connection.timedout This signal is emitted when no data has been received for over 'timeout' seconds. This can mean that the connection is unstable, or that the other end is running extension code. Handlers are called with two arguments: the ContextConnection instance, and a boolean. The latter is True when the connection times out, and False when data is received again. .. _insertdocs-yoton-Connection-timeout: .. py:attribute:: yoton.Connection.timeout Set/get the amount of seconds that no data is received from the other side after which the timedout signal is emitted. *METHODS* .. _insertdocs-yoton-Connection-close: .. py:method:: yoton.Connection.close(reason=None) Close the connection, disconnecting the two contexts and stopping all trafic. If the connection was waiting for a connection, it stops waiting. Optionally, a reason for closing can be specified. A closed connection cannot be reused. .. _insertdocs-yoton-Connection-close_on_problem: .. py:method:: yoton.Connection.close_on_problem(reason=None) Disconnect the connection, stopping all trafic. If it was waiting for a connection, we stop waiting. Optionally, a reason for stopping can be specified. This is highly recommended in case the connection is closed due to a problem. In contrast to the normal close() method, this method does not try to notify the other end of the closing. .. _insertdocs-yoton-Connection-flush: .. py:method:: yoton.Connection.flush(timeout=3.0) Wait until all pending packages are send. An error is raised when the timeout passes while doing so. .. insertdocs end:: .. insertdocs start:: yoton.TcpConnection .. insertdocs :members: .. _insertdocs-yoton-TcpConnection: .. py:class:: yoton.TcpConnection(context, name='') *Inherits from Connection* The TcpConnection class implements a connection between two contexts that are in differenr processes or on different machines connected via the internet. This class handles the low-level communication for the context. A ContextConnection instance wraps a single BSD socket for its communication, and uses TCP/IP as the underlying communication protocol. A persisten connection is used (the BSD sockets stay connected). This allows to better distinguish between connection problems and timeouts caused by the other side being busy. .. insertdocs end:: pyzo-4.4.3/doc/yoton/context.rst0000666000000000000000000001730412660452173015023 0ustar 00000000000000Context ======= .. insertdocs start:: yoton.Context .. insertdocs :members: .. _insertdocs-yoton-Context: .. py:class:: yoton.Context(verbose=0, queue_params=None) *Inherits from object* A context represents a node in the network. It can connect to multiple other contexts (using a :ref:`yoton.Connection`. These other contexts can be in another process on the same machine, or on another machine connected via a network or the internet. This class represents a context that can be used by channel instances to communicate to other channels in the network. (Thus the name.) The context is the entity that queue routes the packages produced by the channels to the other context in the network, where the packages are distributed to the right channels. A context queues packages while it is not connected to any other context. If messages are send on a channel registered at this context while the context is not connected, the messages are stored by the context and will be send to the first connecting context. **Example 1** .. code-block:: python # Create context and bind to a port on localhost context = yoton.Context() context.bind('localhost:11111') # Create a channel and send a message pub = yoton.PubChannel(context, 'test') pub.send('Hello world!') **Example 2** .. code-block:: python # Create context and connect to the port on localhost context = yoton.Context() context.connect('localhost:11111') # Create a channel and receive a message sub = yoton.SubChannel(context, 'test') print(sub.recv() # Will print 'Hello world!' **Queue params** The queue_params parameter allows one to specify the package queues used in the system. It is recommended to use the same parameters for every context in the network. The value of queue_params should be a 2-element tuple specifying queue size and discard mode. The latter can be 'old' (default) or 'new', meaning that if the queue is full, either the oldest or newest messages are discarted. *PROPERTIES* .. _insertdocs-yoton-Context-connection_count: .. py:attribute:: yoton.Context.connection_count Get the number of connected contexts. Can be used as a boolean to check if the context is connected to any other context. .. _insertdocs-yoton-Context-connections: .. py:attribute:: yoton.Context.connections Get a list of the Connection instances currently active for this context. In addition to normal list indexing, the connections objects can be queried from this list using their name. .. _insertdocs-yoton-Context-connections_all: .. py:attribute:: yoton.Context.connections_all Get a list of all Connection instances currently associated with this context, including pending connections (connections waiting for another end to connect). In addition to normal list indexing, the connections objects can be queried from this list using their name. .. _insertdocs-yoton-Context-id: .. py:attribute:: yoton.Context.id The 8-byte UID of this context. *METHODS* .. _insertdocs-yoton-Context-bind: .. py:method:: yoton.Context.bind(address, max_tries=1, name='') Setup a connection with another Context, by being the host. This method starts a thread that waits for incoming connections. Error messages are printed when an attemped connect fails. the thread keeps trying until a successful connection is made, or until the connection is closed. Returns a Connection instance that represents the connection to the other context. These connection objects can also be obtained via the Context.connections property. **Parameters** address : str Should be of the shape hostname:port. The port should be an integer number between 1024 and 2**16. If port does not represent a number, a valid port number is created using a hash function. max_tries : int The number of ports to try; starting from the given port, subsequent ports are tried until a free port is available. The final port can be obtained using the 'port' property of the returned Connection instance. name : string The name for the created Connection instance. It can be used as a key in the connections property. **Notes on hostname** The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connections is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also connect to 'localhost'. * 'publichost': the connection is visible by other computers on the same network. Optionally an integer index can be appended if the machine has multiple IP addresses (see socket.gethostbyname_ex). .. _insertdocs-yoton-Context-close: .. py:method:: yoton.Context.close() Close the context in a nice way, by closing all connections and all channels. Closing a connection means disconnecting two contexts. Closing a channel means disasociating a channel from its context. Unlike connections and channels, a Context instance can be reused after closing (although this might not always the best strategy). .. _insertdocs-yoton-Context-close_channels: .. py:method:: yoton.Context.close_channels() Close all channels associated with this context. This does not close the connections. See also close(). .. _insertdocs-yoton-Context-connect: .. py:method:: yoton.Context.connect(self, address, timeout=1.0, name='') Setup a connection with another context, by connection to a hosting context. An error is raised when the connection could not be made. Returns a Connection instance that represents the connection to the other context. These connection objects can also be obtained via the Context.connections property. **Parameters** address : str Should be of the shape hostname:port. The port should be an integer number between 1024 and 2**16. If port does not represent a number, a valid port number is created using a hash function. max_tries : int The number of ports to try; starting from the given port, subsequent ports are tried until a free port is available. The final port can be obtained using the 'port' property of the returned Connection instance. name : string The name for the created Connection instance. It can be used as a key in the connections property. **Notes on hostname** The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connection is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also host as 'localhost'. * 'publichost': the connection is visible by other computers on the same network. Optionally an integer index can be appended if the machine has multiple IP addresses (see socket.gethostbyname_ex). .. _insertdocs-yoton-Context-flush: .. py:method:: yoton.Context.flush(timeout=5.0) Wait until all pending messages are send. This will flush all messages posted from the calling thread. However, it is not guaranteed that no new messages are posted from another thread. Raises an error when the flushing times out. .. insertdocs end:: pyzo-4.4.3/doc/yoton/events.rst0000666000000000000000000001263212660452173014642 0ustar 00000000000000Event system ============ .. insertdocs start:: yoton.events .. _insertdocs-yoton-events: .. py:module:: yoton.events Module :ref:`yoton.events` Yoton comes with a simple event system to enable event-driven applications. All channels are capable of running without the event system, but some channels have limitations. See the documentation of the channels for more information. Note that signals only work if events are processed. .. insertdocs end:: .. insertdocs start:: yoton.Signal .. insertdocs :members: .. _insertdocs-yoton-Signal: .. py:class:: yoton.Signal() *Inherits from object* The purpose of a signal is to provide an interface to bind/unbind to events and to fire them. One can bind() or unbind() a callable to the signal. When emitted, an event is created for each bound handler. Therefore, the event loop must run for signals to work. Some signals call the handlers using additional arguments to specify specific information. *PROPERTIES* .. _insertdocs-yoton-Signal-type: .. py:attribute:: yoton.Signal.type The type (__class__) of this event. *METHODS* .. _insertdocs-yoton-Signal-bind: .. py:method:: yoton.Signal.bind(func) Add an eventhandler to this event. The callback/handler (func) must be a callable. It is called with one argument: the event instance, which can contain additional information about the event. .. _insertdocs-yoton-Signal-emit: .. py:method:: yoton.Signal.emit(*args, **kwargs) Emit the signal, calling all bound callbacks with *args and **kwargs. An event is queues for each callback registered to this signal. Therefore it is safe to call this method from another thread. .. _insertdocs-yoton-Signal-emit_now: .. py:method:: yoton.Signal.emit_now(*args, **kwargs) Emit the signal *now*. All handlers are called from the calling thread. Beware, this should only be done from the same thread that runs the event loop. .. _insertdocs-yoton-Signal-unbind: .. py:method:: yoton.Signal.unbind(func=None) Unsubscribe a handler, If func is None, remove all handlers. .. insertdocs end:: .. insertdocs start:: yoton.Timer .. insertdocs :members: .. _insertdocs-yoton-Timer: .. py:class:: yoton.Timer(interval=1.0, oneshot=True) *Inherits from Signal* Timer class. You can bind callbacks to the timer. The timer is fired when it runs out of time. **Parameters** interval : number The interval of the timer in seconds. oneshot : bool Whether the timer should do a single shot, or run continuously. *PROPERTIES* .. _insertdocs-yoton-Timer-interval: .. py:attribute:: yoton.Timer.interval Set/get the timer's interval in seconds. .. _insertdocs-yoton-Timer-oneshot: .. py:attribute:: yoton.Timer.oneshot Set/get whether this is a oneshot timer. If not is runs continuously. .. _insertdocs-yoton-Timer-running: .. py:attribute:: yoton.Timer.running Get whether the timer is running. *METHODS* .. _insertdocs-yoton-Timer-start: .. py:method:: yoton.Timer.start(interval=None, oneshot=None) Start the timer. If interval or oneshot are not given, their current values are used. .. _insertdocs-yoton-Timer-stop: .. py:method:: yoton.Timer.stop() Stop the timer from running. .. insertdocs end:: .. insertdocs start:: yoton.call_later .. _insertdocs-yoton-call_later: .. py:function:: yoton.call_later(func, timeout=0.0, *args, **kwargs) Call the given function after the specified timeout. **Parameters** func : callable The function to call. timeout : number The time to wait in seconds. If zero, the event is put on the event queue. If negative, the event will be put at the front of the event queue, so that it's processed asap. args : arguments The arguments to call func with. kwargs: keyword arguments. The keyword arguments to call func with. .. insertdocs end:: .. insertdocs start:: yoton.process_events .. _insertdocs-yoton-process_events: .. py:function:: yoton.process_events(block=False) Process all yoton events currently in the queue. This function should be called periodically in order to keep the yoton event system running. block can be False (no blocking), True (block), or a float blocking for maximally 'block' seconds. .. insertdocs end:: .. insertdocs start:: yoton.start_event_loop .. _insertdocs-yoton-start_event_loop: .. py:function:: yoton.start_event_loop() Enter an event loop that keeps calling yoton.process_events(). The event loop can be stopped using stop_event_loop(). .. insertdocs end:: .. insertdocs start:: yoton.stop_event_loop .. _insertdocs-yoton-stop_event_loop: .. py:function:: yoton.stop_event_loop() Stops the event loop if it is running. .. insertdocs end:: .. insertdocs start:: yoton.embed_event_loop .. _insertdocs-yoton-embed_event_loop: .. py:function:: yoton.embed_event_loop(callback) Embed the yoton event loop in another event loop. The given callback is called whenever a new yoton event is created. The callback should create an event in the other event-loop, which should lead to a call to the process_events() method. The given callback should be thread safe. Use None as an argument to disable the embedding. .. insertdocs end:: pyzo-4.4.3/doc/yoton/examples.rst0000666000000000000000000000513112660452173015150 0ustar 00000000000000Examples ========= Abstract example ---------------- This example shows four connected contexts. In principal, all contexts are the same (a context is neither a client nor a server). Yet, different contexts can play different roles in the network, by using different channels: * The upper left context publishes a stream of messages. * The upper right context employs a reply-channel, thereby taking on a server role. * The bottom left context takes on a client-like role by subscribing to the "stream" channel and by having a request-channel on "data". * The bottom right context has no channels and thus only serves as a means of connecting the different contexts. .. image:: images/yoton_abstract.png Simple client server ---------------------- Two contexts. One takes a server role by having a publish-channel and a reply-channel. The other takes on a client role by deploying the corresponding subscribe-channel and request-channel. .. image:: images/yoton_simple.png Multiple request/reply ----------------------- This network contains only two types of context: requesters and repliers. Yoton performs a simple load balancing scheme: when the user posts a request, the req-channel first asks all repliers whether they can want to handle it. Eventually all repliers will answer that they do, but the actual request is only send to the first to respond. Requesters that are handling a previous request are unable to respond quickly so that the request will be handled by a "free" replier automatically. If all requesters are busy, the first to "come back" will handle the request. .. image:: /images/yoton_multiple_reqrep.png Chat ------ This network consists of context which all take the same role; they all send chat messages and receive chat messages of the other nodes. One big chat room! .. image:: images/yoton_chat.png IDE / kernel ------------- This network illustrates a simplified version of what yoton was initially designed for: client-kernel communication. `IEP `_ uses yoton for its kernel-client communications, see `here `_ which channels IEP uses for that. The network consists of one kernel and two clients which are connected via a broker. Both clients can control the kernel via an stdin stream and receive output on stdout. The kernel also has a reply-channel so that the IDE's can obtain introspection information (think auto-completion). The broker also publishes some status messages. The bottom kernel is apparently only interested in kernel control. .. image:: images/yoton_kernel.png pyzo-4.4.3/doc/yoton/experiments.rst0000666000000000000000000000542712660452173015705 0ustar 00000000000000Experiments with sockets ======================== I performed a series on tests on bot Windows and Linux. Testing sockets on localhost and publichost (what you get with gethostname()), for killing one of the processes, and removing the connection (unplugging the cable). I wanted to answer the following questions: * When and how can we detect that the other process dropped (killed or terminated)? * When and how can we detect connection problems? * Can we still send data if the other end stops receiving data? And if not, can we still detect a connection drop? On Windows, same box ----------------------- * If hosting on network level, and killing the network device, the disconnection is immediately detected (socket.error is raised when calling send() or recv()). * Killing either process will immediately result in an error being raised. * This is true for hosting local or public. * -> Therefore, when hosting on localhost (the connection cannot be lost) we do not need a heartbeat. On Linux, same box --------------------- * I cannot kill the connection, only the process. When I do, the other side will be able to receive, but receives EOF (empty bytes), which looks like a nice exit. Note that this behavior is different than on Windows. * When the other side does not receive, I can still send huge amounts of data. * This applies when hosting as localhost or as gethostname. On Linux & Windows, different boxes ------------------------------------ * When I kill the connection (remove network cable), it takes a while for either end to see that the connection is dead. I can even put the cable back in and go on communicating. This is a feature of TCP to be robust against network problems. See http://www.unixguide.net/network/socketfaq/2.8.shtml * When I keep the connection, but kill the process on either end, this is detected immediately (in the same way as described above, depending on the OS); there's still low-level communication between the two boxes, and the port is detected to be unavailable. * On Linux I can keep sending huge amounts of data even if the other end does not receive. On Windows I can't. In both cases they can detect the other process dropping. Conclusions -------------- * On local machine (broker-kernel), we use localhost so that we will not have network problems. We can detect if a process drops, which is essential. * Between boxes, we use a heartbeat signal to be able to determine whether the connection is still there. If we set that timeout low enough (<30 sec or so) we can even distinguish a network problem from a process crash. This should not be necessary, however, because we can assume (I guess) that the broker and client close nicely. pyzo-4.4.3/doc/yoton/images/0000777000000000000000000000000013166630335014045 5ustar 00000000000000pyzo-4.4.3/doc/yoton/images/yoton_abstract.png0000666000000000000000000004734512660452173017623 0ustar 00000000000000PNG  IHDRX _ IDATxh$Wv}5wZDLx^4K! I6v 3t,qP#OBJdaJד1̺,Xt!Xd%ȫ*fi ٕ ?NwuM?XUnt׷Ώ{N!E>w}h( cLUD"ĥP(88rUUUU4MUd2 u 0ٶxmZ><۶mۖRXMcRL .A?5CUUclllA#aE;"&8;D_Qsnf,kȼ iqI^P>wmpv۫xQu4DTХi8ccLVO]v \'P(8a`m8 b=!2L=KLs۶ۚ]]=V f0m?cOpivA`ޔ$z3_5b : !^LOO[UƺAW=Ul%d tٶ]AW64`]im0B9'q54c`g?Fu!mEpMǹmY;|Fj0^3usο(b 0aYDEmyo:3 n <w >5Mm5/4h=ݹ9f &zAvU pނ>3 c||#P(`n@R`@]X78iZPhp6!u]oSW}uKvM)w#yC}SR88G{FR'Í83٦8'88oߌ+= 0>BLNN6:<+tƺA~I6GAA-}1y^NM3.kߧ{۲T]_bSʻR,f8y+qsXH,.2)#]kځw+붽S_2  cU>U)FFMVubu!Tu_Zq⓱a3 ,Mh!0qY HAiߥRJ ![ӣ((` ݊@IP59FMopOUq{ ihlE-0nu#0TQu)h`wZ¦2s2J-..B V7Yah/BCylh)ыCA4|'BBaq_k_Z2W-5M1Λo}{饗 Ϗ ٶ]7:􀻋EEaD_h x~_Ynv r{z*WCLhL ̅K:̬Bgh5~)_9+E`meYm;WO.UU9 " ~L~cu ~@"%\4vY{"g8Tt ݻW1Υk` 5\U"/drnn ל$z;F /SJIBHt_\;gUϭBhY8La ^ǾmnׅXfu]*jKTP렟%b^y5BPnsi2QJ? &UVAQ9-M$|F&?hW$3 a(g!uP)]J$FSva2^n;㚫'(xDB Uf-ȹP/ !)oop~nj6xhA=s&ۇh:VO NE"$L}@,(l98-vOUC9l;NE׼0[Qw>6C"A'.Ħ[|w,}|~ ߕrq$Ч?^H+zCM}ܳ$i ]׽i}jsma8}׭k }r]]]\A599p޽lWWWX^٧cOrGEqW] |4`)喔UbqV-kSʃzW@#!$h_ƢmDE:5QJzl>UN&=ebh9rÊ˔.sێ8A3-tuU$˼b*&Z QP`]C`m}6X1;-ȅPD!tw/^"- s4*qV-޲,϶97Y\?/=ڽ8;)%*7VVp;ǔ=_0aWhׯ_/Ʒ;whs&''4L9s\kv&x|1+.>Z 6\H(Ay#leGGgyG]( )/pP> nB߉nEw׆FI r8+G, BDnAzclWOut$h`b*~U(F$ʿRIg`WQrƝ>4R)EQ|߇Sڰtgi(N*#`q pm a(=p;&LXܴ:yMv1Vlo+7htNG龳B,/ӯLL,U)L\ 4 )YyS'^vs m|_? DmdDžC~ ,˚hld6Q([:S`}*8<<\CHv VoTNET|͇p΋#[m ZŇ'~5 ₤RbIp1Գ@BTD(,}c AEQvvN^Υ+Vp^nHj20DHaxrgs$6d[۶yYGUU+v ⬤Rmo=%kf.1DHM,+`zVΦG0T*o5O9i^~}rrL NixD m PUՈT& a HRo\b'Mfff|A]3!__LMMmllضt:Ͷ`QE"(zN.׭܄[HAVABL,ռ=` N sc׎X,222wwkkkᾎީTjnn}]@$ڇL&Sv%X4SUA[FP`-džAOHxP l4Mծ|>/B V*IJ4B?19ş[2(=lVNCBJUXI&i..45pB˲JdMP(pΟ77׈H4|>or+VG?/ah l!\${A5⬐\.JMDk  D 7|^~WJQk !H¶xafT"g` h5*<!K |I`0[,pi#?qWVP-ˊ6"6"H͕L.SyMbh2LLLH)ug`KMmaQtR6혘p]ۃ%.AGmǠcPUUJ977Vwd j5yJ\-Wz `KvNQp߁p뺌t:AMu A*}_u4_>99y^kgA:,< ;oSs 7  RJM 's"Dh+P(0ʆ(AWr2`ĺd*8N={8 A!AFQ}u` my(o+_fLDiriU0 7[,LrΫsG_g?N څA0Ɗ>d䝺]LJx^uǏ^~e!)+E !$bEjUU\*~kTa]ihvKr@BHm) \._Ϟ=?8ٽ{luTh5>JeEVABHN P(QQ|A:fLOOY| 3[=!*BBHA(ʯ[:JٳgϾ?څ|1V|כ#z]BpiRD~E(kkk?666 +`nlO½GqJ! -'#,R| 7vww|v2v* C0Z; BBH8xݒ$:Tϭ[ֹ1G茔2˵z6Ղ .9BLE(>$h2$ёRw[__/?iCh&$zu]2 fBBHݻwj찶$8n`Ŀe_.A4BTL\XXç~Z~RҘ–"$ѩ$??+^B⋥]{7:E 8+Duo//ᖟ'o(Ec<ҚBMVVVZ=J@BHyeMnEg2ƶ*%S;OG&@BHw[:3RJ!LR q!A\N|QuV $q9 =t A4Bt J.h$$q9! N !A\N҉EH4B JCBHA\iH + !Aq!!$ˎ3 $q9a9'm/J.h$/ AmLP,u]|(aϕ8LX!!$fzz{ݻwW?HӺw" jBBHJ>~׮]n4m~~gXUՖ΃*P :o}߮PA^-> m.HI5MkD+ !At$ipڵ_j6mboTUM& q% !$DJY['K{o6cNuAn_ M :uk/弎$CBHW56v@QL&WB0 f'OviK;Й IDAT9o< !AtΦiEQ,b[y֜wE0΅RD['''TURr]]YYBgώ<ٳg~aX4Vs#c9O$ q:<L1Msjj"$b<(~3Dlֶm1vkq||\JOI)cX'D\-H Y3jVlR16 5\gCx:CdCE yT;?O,S/bmu`lyO@n-"$ZYp.lDo.FPph2$<%eLC*iJkA$Q_Α|ʉ3Тxa$.H 2D!!$:>S2;;[d- ܲ,RA9i[)y.^u`= #\/$B8<%i!LhZ`.cPHv NE9?OIu9Z rh*ҴE#qH X:yzK%D/A;>.X_1۶)(H$Qer~B9l]]=" 7vAnPPEq)o#N) X^M\Vra+U-~lg!-NmsB@QKۡy)q$0 cLUU.q~|>/(5Ͼg_&gVVHxtn>&u.4,r~qV7xԑS8=ҧ< s=Q#c)vzr~D.crڒݾ&󼺏dMCu]yA:8o5.gh1>>^LuZgp4`lxauim27hC'dOO͛7WǏ۷8nnn>}}}joo/>]ZZ^]]Ře__۷ð_|/҄ 5MXQ>UEŊqMVQӬ~5:cÜcM@!kOUqΕ8nEYo0d9.(X T0D0pvG8-4I eyyyyy9uݻwG_<88?|[[[[[;s[[[}lǏGIGGk>g>UM}iYx2_j #yݶ ##ĨiXx;SK^6M)<7Րlf.l ;2VVVyO˲(祝RQoo޼9::zwvvPn޼_lmm-..;q,..[ǏY緷͛7qu4]E}W677+8b״Ɔ돪*Ga@?R=}z۲xyWʻRnۡ;4iTP(` `7Z:Pz@ LNN6᜔١ s'ptpdUUcGppW_}/4x၁{<~"*2g]ȮcSm}ߏ.Ҳ}M%jut}ME`;nEͪ-&*u]qօoL0uuuݻw^r΋3 nk؋:>,+y^Ĺ3kr*aﯬLLLP#C;DKX9_|.gmnnڠRvwwϗ]PuYN|GuY_5mC״?졦'[؃x29j(K,LeoJYaߑ2y~|FךdMOTs˲$ 6QDiT t+J%) >w}bh;ߔQ^Fy@:KK}tD :=8BC߼y6=<83#Tu"GQުj p۲k*;_Ӫ3q(JuΑ)6ѣG$ɨ$! ð,F=+uVGW@,B߿?_qZHV9wޭ~5=FЈD#iQZfx"6qJt]DŽF|y┽L,;E[ s$J0M뺞J^~}w~o2?s竛N+iiD BTU*Xo3NgQU5L QUu}}}kkkggC˛===iV= .‹SeTJ Dmۦi*rY|>j  >گڵk%I\.g9纮7,--Uo|wUUE[ZZZ]]988GQ͛7]=88x!V4举瀊9yAQ X}އI`&Z$s)e5"^~=Ui^ܱTjf;mYmۯZ}>}JY) t[Bt:ݞ:p fox7Wǫi!vvvPκ㭷9lmmmmm]0wA,kTZy?i?Of֠Ȝ2 y^NR%8l,?k1oj,FGGxxMwwǥ]ڱaKH$տ p6Biڵk׎ǿ{G#raZLqtB"Z?*p~( !l< +vuArT8BPV03<0V,eyq!!le((077/ɓ'm!??ɟ ĉ`QYLCsmFD"%\gq\gifCRADPIJ C2S|EtڰhmL&r>#$Ns$+P`|X$ N a3(3Jv9e>^q˚HhRmAsd@B\(igGYcu&tFB NY#/U~pq.Bftfdd$N{Y2 *|x1] Nbgh% Mc-N8ċi Rm9}qI4cMDh?^JУSS-* k c4D&K.pw2`Sqm y(A a_P(ʩ X<- r(s2`Sqwqnʅ+VGm͛&r+<<<}uݳ8 àpX7衛A^ &''OhFҺ qi( a#"W놅٬T*aSQιRJuXPm' 󤔊Q1z"tșU)bXTfggvfYp#"ya7<].s];f )i( yڸ^aYA:-u9܉:;Z ~6]M[/!TUs ya*+(x^9 8 \cUXq"۶9LT ;r9˲P B4QQ b㘦YCI (}!&kn~ zVi!_t֫#f"@kll̶mMihq5M}dm9@ɱmߵ BPqCyܜ{jf^O8xF瀊ŦFFFpmMc8†6,:{ANz-gٮl6OUUł4H`pD"Q;'zo/(@HԃD"Qv[_shu]c[0дm[9*qVE_bըTD[Bi5f2pW1뺾+y7QL18c= ხdž W]]u=ܸllU7DێPtu=ԴҿE5!vgM5!@r EqKzac,v?T*X5GuDA")ݶVTۉ WWc㍞f) 4MHZUqc#i M_ƞ>}0jlmmmmmžKyŕ>/PO._|9~97وVFG+q]ЛH\ N|!|mtuAĭ_/ B9}t~[c DeV_VNs7^U`*`R%8+<nK/B&eNUFI&|8o5DZ 5 "&XMCof AC4˲H&QG^|q#5671٠r*eWvx܈Sg̢AAAR,yθneeT//b %`mi6\2 +Xc˟ j#Ntf}4y}W(qayzu+:J"G6D&3jW$vwmY4meeq{.@/#XJ+?u8 ԊR)MrZ-_&8.P;55n1X..p*( 9E :ccc`{Vϧ`477%,j }ƣA{b!%.vk6l!vXY&Lͅx5] <6.l!+ q[ :0 |ߧZ^bJY m\ ǨJi'&-RJ)mnh qIT `vvvhh{ iܞNf i'j+ڽCmar :TcC>-EQ%.6cB u׮~<4T`Pܷ;W1?{Ai4h4$q|߇`6I8Ӂ˳9Cԑ \TyBP~,X.((o^Q 3¶ah/ dnnnrru]۶$6M}8~8hE *q6 KވQ m#fff楳_0l]m`pQa,.. DH"BJY<0ЀpFC1i&* o* 1.neၶmW,LR}>,}u]ǹiŚ樎aؘf9GE^`o'x{02a0n0 ֗aR hF q$he2,DMDsiy(T<۶766,eYx׍6b4>kT (Ʉw}T2sf>ڶιbia ^&dXlnnNJYNCpcPc]pwO *y|XEAA8A+u]kM$^W@3IDATvf1KLXj8hk E=yꍮ뚦Y9 _A"lSc.C*nJ8xw7Z?DQ0 hrh9(u\.R[jzX)yǕB(kqFu),d9Lfqq3~WA2/x A}}1˲ l68N'8!iG`Kx3J)58$Jao1Vy:lll8cYco, mu>*gMkmPղyuAakNgH$R(]]][z:*5j4 !$(D¶m˲qjB^:fIMx.\q/]uoE=(h< ^߈g9W^}pM.FmB LY0"~>fu“:AԑL\)7!.;$Atge + !Aq!!$ 4$Aĕ ҐAWB JCBHA\iH h# f] }_ .:=ǻJ`Ӧxzz:N7!$A Aܿ__WUؑMځh{,:<<<<X.^ `z,F:===55Axga pn ) RJ)eeByDth9qsBSTPm{q u]Rf,2 #Ą8RL&vy">ۃ ljDD4åLĨm PofggѕHBRz ׳l5Hcag:!AB27~[]&U6 $a躮iJ$DbB&05M!Cs 3Y_*vۑ;rT*(ea8D²,u4͡ UXRׯ>ad(uxxsqX^^^^^Ѻ_(W,3yc =< 'Zq}s8Qy+8jNW%z+oA8Sqy>xw+^"!$4Z[N68BHQ J0˱:AT233)̓F + !Aq!!$ 4$Aĕ ҐAWB JC * KW Ee]65z֑B(JqX2rd55{V+;cES?H+uFBHQ-`?[Mӄx坝~ F6vQ7MRcay)eEϣcci6փa , V6ea?z`~a ?R񫪪eYLFUU]ױjy}8$8#_|G}G}KC}ι뇇h/aah|bYiuH)O3s`3rΣ}~hwBh>E}-я0Ԇa/JTD1B Zb14J}UUEɤa}tc@ Lt:trtp g{A q q8C&''sܽ{lt{p܂ t6B^>i[2ڏmX,zuKQ #ǮL&EQUmDUUkxd-=bCVEa1pyraH)c'& \JYQ+q!eY!o1~V ԪƊѝ}c5 Tu]H"$!LOOwTaG# -$2a2Dkbb"JMMMaCZZW WJA5l`*im},kll LRaWފCLӜI]5^eL81mHC4pg!DI/Fpk䔐EHDC:wF;A{UMQT*Jfff<6X?.'=9'L n Q#8N'T/EQCLӔRqk PmaPרv 8C&{4EHq1iF=J]U}*{>}Զm۶њb8==aB!asi88<<;DBQc85L \׭rBBHqKj*۶===ggg .<$a/ PG!B̑fBF qeYh *j4MH|+!< 8,TF {\׭R8s ?%ET&qGJQ\nN?\`cRD(D"cWWeYK$mF!Ƅy+qy l꺮(ʑ!.å~9SЉZ&`NgUᕝ, {8m)=r ! Ÿ^4pAY^^^^^VOom(0Z9CCk0 xx9\/1uMϧz"5C]eY<_4pֱ:ZpKit\2l],R ´UAryC,o˞e j B&Q參G!p_!P x(pYuK;ڶG\6 öm1҅58t}t4IENDB`pyzo-4.4.3/doc/yoton/images/yoton_chat.png0000666000000000000000000007130012660452173016723 0ustar 00000000000000PNG  IHDRX 9 IDATx_hYzdJH飰Hǫ|>ϻ&*7eOg\JL$lH L\E6Et;u w΅EU\6Wgu\An햺tuԩ#<;:: * 4w{⺮89c4MB(d+Dgض-Bz Bi.A`۶_VOicLu]{Bàyᙡx|0·.5!mۑ̦:|aa!;P<,u{E=gے B.&˙YR"1t 8 '_=pPxq !ivDښ' ǦinfgϞuk kkki߸]HNMzx w`P}Rj&Zr@!l6kt:̈́ZziAy^" 0 CTIq4híi9, V}Tt9|#sOͬQJ+*7>W"PAІ sBJZ#{ަiv>"˽Ĵ< >f:zy\-B@!Mylg.s]?H&᜗$`ζ,<:__4%#?O?Բ:Ns>gYdž=6}ce#Qm!ԍ6 mã!MbT)*-τZ9G-DAk^@1LΚ&L26JȎ̄PӜ{.͆ەrl22!ˮi`X(QB{޾iMT0=MT9!$JuyID ZQ2T%?CEjհ.P0Vb=F/\}MEpPBx8 } aTcC[fݾo`8U '|͛R~mM, l6ZI@[{G8R E(QRt-!`K}ϛdlҡx\mn Qtp=B9wܪnCt}4j6wT畕T iڡERʶm4ne$pK1"9Jw'B = a%_ZXK˚/ǧYbR8iC6JXWY$cy>9!$wX,K]^)C6N'SSwc øbʎ$>i&૮L7&WVVh(!pGidϞ=vsB,*IfbY`\ BlO  !,,,hvp l3r*Ru ȹeӜiYvٶJJ)#U jI)Ƈ݋'R5/;(%rio<qΝf qJi|eпsX+eY^ -BkkkN!D6=SSS'>b 2y< PP(]Cx; w0X;9g*p A!DCEDWrV˖ZKA(Hw:Lw$X!HBteF!D~h 6^QQ;bч]]H W!H_! rZshxyf=Tm@ 2H( i*~!97M {x$%dHR9Ρ{ދB4MR6نpA-Brd2%9׿Z瑮]A \.yP<>/%`-ްm<溭G?gp+ſsPKy`؆NRP2Q ^5A>>vB@NB~|?A BP)֧B+sPKó~ş)ZOmyp7m81˲[M*Ὀn(v])[PS]06Mj\.W|1Xx4M]UPq0JCN=rWV|'5:g A!t]WɅr !aXad2~ F[rbw"un,v7GBlGo}v7ٶ:Y=8ik/{ilF0 |d2ifx<^od2Y 3hvD02(:·GG]TvȋBa|xttö%J5,kc#26؇GG=;};Paͯ?6 8}-(;RΚD*5<꺮充SMRQ P#cX}4|4kظ|,T4(!R8ǂW|JG a9HITaM?JȞݩ DbyyŢk/!Pd@! L϶s.cy"CUB5i:y|j(<1c4by=>`I%hpbiۃ(XABn9O]WtՂWqxNz/^| \J* nܹsc~o64ג[#g@OqUR`59W"]#k8/`eeŲ,X `@ǀM6KSr7 QB P9`\p!<^\\T5LӬapkX+m~Bic4P(88yǟ`iF)+P>#t:mBg*i\u.ClۖR6jdUƘ뺮wRA`۶m3"wBC)=G(MoɟI1?~~u])ST c cau- uE(ak!ɏ60(H7d2_gfV/{q_i'XHN6tCpST͹40 0+2|gMsR %y#++@)} "ݥP(?8!뺦io5> l]=+MLb͛7mK&V>*<~t&ضBtu>ceO?;w6dxćOqh-ې OPJm3#9 9L\^^F!DL&qRiuX,zϸD뺆a ]s6;9{TyJ;@4Բ6c5t\.g۶yxP(ݗT :@pì~!h-p *էm:ّr3a[PJK!]u]UL ^"X.NQHrҒ &F`5m,ݮ xaB\z5޴vvׯ[j'9?B}Bt cXi+\_1pn10j)YqXǴp\.9QҐ>Ծ`Mz[m7k3}8<{1϶0?`llm UH5É3KrM G>,uRrFnZC?)"] @wMkikK Ug /i7'TTc^Tl[,J·4maM [sݰmxjY) 9jNakӜo,-Ve"& BE9K*k `A&BBHްQB`:bl2gY3in 1(!UmJ '+Np^tc2!@lGqt"X TPw#Z M\[[r'ԡQBdzT ۑ2 R*;6S˺GдvzQ΁2HXZZ*}'TPg`!H|8ZU7voߘhJDu R>" DkG0|9-!Cr"Rq+䱑SSUm)sG^օ5VOp t,ϾKsoC)e.;s뺶msU 7hB/*gf[϶=o1%oW Cr-ġWۈ/<c*I Eyx7Ӝ{}QB=oβ./?6u#?P<~7Y raLׂ ,lTN4@lMd2BU*_UKHE%ZuT@35bQFi٬E 8 > 8E, |F}߲&#H٬2lgY6iwfNLs9YFт{H{3XU]HSrFeY"\\u]7|k۶2;T~kIJOiki/W^ V~ O?u[k0 ^c.]}l_ׯ_R./Gx>0'2JE A:C)@ !Qf M\-Ōć]^ͩ~\.G#F:>"0n|*Y-}}D8)BC4]Ue !=ׯ_' T`@Ee見j),DҒʎB xnl,kJ!\.'v#H[ɇ]c=T_y)(] ܶcwiƖd83ec.BDCH'Gl.BD1 E:LE?n⥕GۡiB$0@UX7 啣Q-**@K ҧ"-2㸮˜TB!(ǞQ2dY!z\kaUq"gdgym0Q ;{޾~uOj:B` ,lV $n>9g% EVAzR p]]H W^YZF5`B/?/<h㸮X?cy7օ 'PLEQi栦i{ܧA8S-=o3=~.³wml:>;;{ŋ?A61, \(U6j c1ʞa*?IwΉP(d6{z.^x 0=#0J5SNuT{zx'0 I޺u.p4Za RSi;RW.Rs7HߣPzlpcBM{^5=sNNRLe;j=3!t]WJx B T!-Q-4R-}Ǚk<ĘlgP[ֵ.T؞zuT%d2Y4/Jun?{6JȎ?z5Լ!d^J &IPL4|JD|h a˾m qun|ɓqJq;+n6m#y|+ ( 7ou!HO<w#0u [6ܙĈMӼtۍ7ٛ]|H$*Y=ei}ȕ>h=y_bU{X&+:I9ζϹ#VUhi6MsN:Iͩ.ҴCߋt/-Hgr;ÚFt@0c&>w !`\> h W_5Ç=E-6x\J3T=7N7 n,TjuuqJ.]M06JMR7իss55gY#|β1J4mObq0It}HӢMt:jD QQ4۱] 8vu,o+_ .\裏._|l˗/766.^SS IDATCd2ԺȺ)@Gva,7g߂1b Q#k@eUTp!n9ƪ:,Ϫw^㏿⋗/_CQ4R .XO::4 gs ''[=0`\нY_z駟>x@مbqtt(ҳ$ ˲J& ]^S-^!Ν;m'ldBe 06ԡeYmLmu?!OwĞ|LLT(]aPqvXu䧖58%s!MS#6P9W'|`>B}1Ӝorb*,uIV0Zxܶ3Z(gG/[ x\J9w'ɨ}(O^ɲW0}r#'sXB ޡOt6)= $}9v@d2Yʯ6tsAb CI2Ri_i\JYЮ`tlcؕri}=[dii(Xk ރ q8׶G6zgvRlATR*X1x<ɼ>%J0wT6X2YZR&.+נ߿bβ8 L$Wg{6 m+++^4K:R*"] TP"X"p8NqpSBؑ?=J?qJ=~{q9Fi2 Oz\z10g`ͩiܗ AJBr>Ӊ{E*z RM aYTf8ax~}N0F G yjiVce{R* V_ǃe è zv>ʰo,rnQ[eZ(mf{@>Ab0JD{n`h_B@B\πR6AzB` T >F_I4!D/4qCExd*$2VA(. H$6|b+CgC:*KE:(-mAfvlߢEOUuV B8.! 3vtAeukMLzBLpGBmR41#XOaic5{қd>3bqccㄡ `&5:{AQ!*M4M#b59$Y6& sM,aw .˳,z"ON8Xk}oq˗/_|ىūiarAJ/$0)xYsyV.=QGITU;ܠA@&9::vډW_}?OlBxgmH9g~y VWWmֱ[VaU+d2XP(az 8|E=˗/ٶ3aTR@u4M)*`_;М$'$˩f줩gT mSsmwy~yV Q&ԟ7C% 0@BpҥK.^X(*N*$lB~f+7xȈ뺝YқFF*Z__=$QJIUr[ٜO6LYQ(pE@ҮH<?`5Maٴ3z=sptW^qW_}9hvţD"QyGs*B!0ƶqgffs9(]->o䜡^1_|#(șH|>݅ H=|駟|_|!6TAP3 u{f,˺tҏc>җ/_~/^m21J dofllllկ~eYf# !F\imQ͊E lVy7lT t]?wΛ {ދB|Bضy^2մbYi P*8<9vfq]0ը֏I&EtQҵ @)]XXҲ""2!Tסi ّrf8W6Msl8a kZV_^`l}Ssn Q;m+麮ՙj%c"JD%C}ِ5Msdddzzŋnh. r*"#|ҡ)?uؕr[pf6?y|B|=x܎[DQ*m뺾:SSSlX!mABtWm*0Zѕ/{AHL&~#!˗/O/vg,rr͵9G {Bm˷X,Vm{|~buj n@) QJo_d<|ooo'?Qgo}{d2WW+++ALMMaG_$* X91(*o:&!{SX[MӤ.yR^5%@ӴxHP0MEXkWeYt|kkkB_":;Hu]ιSSƘmFO1VBvߴJ@JYT(®M Bp9X 9ABlF(rfbGGGu],Riw u]X).>rx !y""h"SBp]W'Wi]"r6D"xG ˳`vf DT9D a/r\ `vMjnBR>*" h"QY $B'O*U vR [*Dbyys` h4\0JW==07aR0;wDy@A$BP{ 8&Fn5@lUKi\wD"?X[[3Mxgj$SDz w@ {*)TUMV 9;  H1\NJ)~ҥKws XXXyR0lP|>gSAvg};*!d4??[r~@AZ ֑R6VAiqA !D yCӂB Q D8 AN "H9o;*cLӴd2d2.\MqW^!P vbmK)^2ckvƍ>?ѣG s|RiBt]G:Bf r9۶mnqB Gf-Rb u"Hw1˲Dj!'_So /jqzzz~~:tooO?- ,fMTjQB`-sC϶8 A{B ] iI`u.c8===== _}뺆aǣҥKOLݴor L;B2b k106X_Ap4Ͳ,];)cCmִN޺)!/ 1 1}AL Af)LM|U:wA|H}_BZ;Q*؀ΡPW cj 1o|^&ц-X|[p1"QUhv6gqGʃsZA!Dޢ Du)48@'m%χ!|iiw#$eYLt=ov_B =M t]/E&/wJBtz-8lc{jYۖ[m!T38\ -Љ ԲIɹaj } !4վ`L2ڎ JV8|~4XZR^xދyMt}4ZRaMM^sMs>kC9w5Y³mP$<6 fB/l  L&sL&wRҾ`|rփτ3󞊧}#7l:tzaVe]1IfMS%V Mt}I޵P)յp^,2!iΕQ 8@-(җ4o A.\v/_޾}ի è`mjk~!u.yQBdzT ۑ:5Tʱ壄<{ MSQ(Ǽ@T|@));Xu…cl6+BEwa_Aj|AAh笥V1t CO } !  D]/\p߹s'_?\׭ ޿C*o@b1#m1&PPME!9JuHu@t] ةiyAt}K%~{HݺFt}ɑA!D@0os~.d }CZ?|#|>͆& !1ָ!P(dL0=dF|yj S\q^~lBU{sUcPsn> wc'J8_L&#/^ꝖD#`qX&ɓMcYmۚ-..@*Z__re,:(!y)}GG쿰$cԂִT#c75hj@!Do͌RVmEQ` b /rt2BU۲,JRrb xӳ`&Gֻr t"LORވ6J)^P!7ز4Jl[5VABHX|bXi+aζuIѠHy') Xljj*[>, 1v܀/_>xE'ʗ ,4ͨRsPc׋(m7g"D';3>>/_tO?H~g+]])q@ӴϟG>}B۶#!躾D ! i\1f¥KJnaH;8΃Q93nI#&! _tECz *kjr"H""hBeVDZA/UDWrVʆӰ4A/% 2"T^@iBA\ u 3Ly`*T4 RM ! q 0MS1HۨQA),k`*n ! 1\e) I$T${z4!T]BA0 xmsQ8<^ n}J"jT(v k ۶ !_*vsPx^Pic$`=)N``Zs"妦be U}FղNJBQkAEL$ ! 2...CJ'[EWyV0,7cX uUo/=r3ZB`T<ŋ˗/Bŵ%w00_4XTP( 띹|GM<BL Pī%k׮}(vneRoVL&ח31Z4M1 }4fWJ4kA!DDǯ]`;Syc{^R5 lY]]2K1yls]~%cob7JHm|zL!VF %)@!DDLfF᎑e{!2z?kÚS2{i8qIޓ7І"శdT,5Md>0 CWv{ jK' eUZ%unÉm!BHyӜ'@P) \͛X0 Mӄϟ?_]]~zc-O ÈVlnvb-b} !0z$co/.~25MN26i(}XulcʌSܶ5ᝳYt5r̉wH2>J&Mn>ӈ nAS㺮m۶m;u]cN \p;M._ufjjTeygzO 2r!󌱉T(Eaˁ+ < S|-0qag`47Y45E~bmmͶm)zx<'wQ^r痿eYt:lYV)z zxG~e!1,Úv;H$(@#.hRTnښw}O>9f>.,,n6܊'iJ45sH5j2W]]ң4 (%izk߽΂Wl\t*[ȞU(l*Bj9 WښiNcV܄lީai "mAZߝMb.  hd|Xt"қ"ȩ0g󬬬XUKMnY4TnY=O !4;?n]j$ `or^T ԲE A qzgYV&} ϣA39<~ƞZ:Y%V90u[c# ',9WC5|3 ԲIɹaj !]Kѡ1pnu[`pgt(J4ArL&s%ι* H3|-Y}7?YG7oN06N:Ƽrz=kcJ·4maMM^sݰmxjY)U|R[B9`[džmOs4[%=,ق12k16<iZOOI)u]Jf g#!Tܰmw3No 1^j`4|Z*6u%UP'g8/:1Jaԏ9F)OqעcC` * 8E>94E ?t] !CEPF iX{znH)e,TJp&4])=o>)5 r>6ogƺ!s+,j۶i*뒌 =aXёn/Լxbkk &'''&&:(ZPQ|*qkZJgwb7+<4CsYyFn/.E6͘&R*LR%O*i..x-Hͮ"<Yu(ҢJ~UyQ)nkD KTq>`3,>a'꒜>{{{0;;;;;;\r_D5/aʲВd0n֌/0]0 H; CxyT٥R]EO Z$]RB7޿B*Cr<3zኑiF~yRA zD79T)Q ] qcq?4M'l̈́XKE>4fys]9`upID1Β4T!6Yrȷ~4MLE34Mxcc# Ag[LU?t01'jLqdyqp!>LFRUk.Z{KI?2vBp&c&]B TA|ᇳ|Ν;xyc!'AjzttR[xll,J033zttTV]ݝ1ثWp^{{{rݻvuS@m~0g#]טo{b]5 F,]^Yw\.EǏ?~\K|Ν{}=) nHZ裏 (Bxttg 1$xgg\.><>Z?E-MA\#»pԱ4Öϧ1MqJҎqs/ȷg`F.3 #J5{w^*2 cuu"?/j\.?ш0:::77: ].QR{66ӧˀ]V'''k;881a\@ ʌʯb*w`Rpam(B B"haYqT }P(|ss|sss_|E6e4My5עspp R2'\͞Y?CooorbbB @hKO cfffZ/+ۘQ;s]om 'ahAȲfd2˱u O5Yp*h;|yaï»TU, ,˪=v$I|i;ZJ=w7|*1lAׅ&(B #{O?I c`6n|loo~255Fޞpl`(Dw{{[x;/}k]$}Bfx8{縂Qw7|s޽f߼y3>>N18l ^@)z ̤RT*¶ٳgPV@*Wq0forrrtt֭[1L8c8Ϟ=W>s^Y*ɧ@rڄ9I/KKׅ( cL4۶ ahYTAc:FA<1JՄ'?AZnnn dꂵѣG8rv"zJn-8s*^fIlIo_5v7onךRdF/]evIPyXo4M+fllEmt~pΧDL[E*L|Zv cLd Gk\\. c>@B8>9gf_?x<ŋ["qU( o9@K5mYdqÕj }mHUD҅.799Y>~DM`fE8#]Ԑb f#z!z #KEĹ bEQ2\__B+GR9kX7-zxHRjˡSH#$ymT*;BGE#PB)[P}G )ҰC̓ .Eg%#:bqOKD04%"fzʲL(aut>G8DG X7I' QaYMڻIM 8{}fȷL{"wdn5Fؗj|7 sN!ACl w1M.aH=אJt *]/-Bہ͘3I¦ݚ󬱖[s&}7yBׅPe yo] :HI1SUjљx׶wpRbى8gՓuӺ©R2e\ipߴDg~l5HpX_G+x&W0 w:0:<} D"ApAӮ2B+6g#!O koOUuzʲl>4&盜 ?g֟Va( 0 x+v뺘e/p㒐珏[JtS&!$RajEӉD"HGQtI >K!< CGӘ]ERQb~]_}_lw(yeYeø볆yϹq_ן-.u5Ym{۲|%s+~ gU.XQAAai1۶9j-M_NFАQELq>y¼`(f*۶YhmY:hqr ̞MqҴ E%T( ocXdzUUOlk+GEsOnOƗuQ]}_/ߖJ%iZ2Dʲ,X2\[[7Y5M$ ϕeOueqmm qIJ,UU H&JEpαKPPU+8c DUU4΃W4MUUqLPzt7Xc=\]}1_1ZDn|'o1hg}+Q L&OlW^NۄP//Kxz9$5%ʘOj_ P*t]GCq4M"smض]#$mmma>i㜣K<ܘkMD#DQ, uEk,W8Oǧ#2^.a5RQf c4Oh~bT46S*=Qϙ`Ti!<^:&N+bv.T* K{J _u9, H@aa(UUu3 Gs}4"1 `&^}qa^D=}?{ 4H)0\cNq򟦧'30Lr轼%_*ʈ,mOqmqݤLUq}߶w|2y,cmFRU˅gJ$4͓?6| B_Al 1 Q~ (]ߟu˲z(UFc,b*=y'v+t5:g)MC/:38疅_X9YèO(y08>pP1 Oreh >D;# .V3)$qΗEA(i _EDi*M0\וe9ɜ[JEXib뺈quul'y8sNLa% Ad5]f$(w&hD;HFB;Ni&B+@uEQq1@Q]M4 S)pu2C6yP$IZXXUU= ð ]bFl12t4M]٬m۔TCF$Ibc,..L' +/@eOxvww777`rrr~~B.--4·i|3 wD8CeYXTA"ഒ\ŷJ"˲/3C]H]Ƒ<>YbHŠ5@pagp\.0;;;;[H#>Ciaxy}|QdEg[XW<xWwo!s~A. NE^8N<ÏIw~ )sq$Igeb+J 4ThI9r\rBxx)0J9] ZC"O\qadr.O<,9WX,m \`~ Ge"H =l[m4U "Nk]|qÝ0:'{`i֟\.8Gƒ H7I$2m 9 L  X 8kI*H6 3狾|쀳 NڰŽ MALF4YGju]_ J#k(} aGUUڶK.0jBdQۄ4~5o.nD[LOOOOOb]7jAN+{OwU $A ;-4` QԤ-8EQuhzzX,8sX:^0c TU4M]CJVB2c-N*JШBq[ZZeY-Vu04-LFQۿM˲ o!qamX5 E%ux𠝤~(ʥc (b}6 t#$[HQ;!o8X\rCI~ƼAm8W'.w뺘`Ps,IԭP E)¼B .$IXR`b%܈ !AV!% B b!!$ B b!!$ B b!!$ B bz OݎTUmA9|UuΛ lvoa筬\b6#Y|W!l1mYA ~o~=A Jcj5.~[TDWz<ϕeOm]*,z88cLul1(z*osEaV*h^gQoDžeXxЌ[ >+ `YW?v !A0Ǐw^**4M 1!cs()a[[[J1|4-[e&>F*88S8皦ax||lY*vmr hqdYvG, GzAd9?>><[!s⑚7[Fǵ_7\iA4cxP4q ,!n}K{eO"$߈ w8‚m۪&IqLD__2h!B iV6\XXV5`G4q>/[1 &߲\.иTUnf7kC#U_m۲, @$=Ρ` X7]ׅ\o;4b(6: /q-o!e0 wwBBHDQUuss7o^xQod2)rXS1~0ƄlAM$UU=ϋV*MpS)뚦iYae,soq-T*ꗃ\A|>;wyj?EL`;躮i1j9eYQo[XX@+ |^4UUG뺎($,*LzYmnh4#+b1~VڶEir9.DhxҲ,˲lA]({qGkëxkq988No'O~W=Ugzz~m`eO> ۶o߾-w}7==}ϲ,q~`c"% |'uǏ۶'16==w߅aѣ2?Yq+>tZ,^fhxDBeXDBQYs.|`۷}̿;[> SSSSSS5?M^ 8rz뺺cVq(rfgggggk~JQ l6k$ B TNABHİ3@APCBHA 5$APCBHA 5$APCBHA 5$APCBHAER̽X !AN6M$Db||<ͮE뎏J%<^(1̯P(dvFA v9Ĺ/ܸT*l6+XعW ,o14MlEH 0cl@Es14œ0 qkkkYeYper|>O;( }?.xb0vR4za݂JAN8M]ydR"IR&d2,[0"۶'YXTlsQMW" JQY8؞0ˉS\}_e MIJpqqXeqQJ$Bi|m ! |wObQt]nﮎ躎M-0䜣5,E4C}ߏ۝#Pi-61 D[c \c /d6&4 01 .mcEA&h8X46fZ5EHDGd~b6˲D&Ƙ=u?ca43Ds]7nHٶm&bxuMsEX,bwI9p C$s]WQιx\7p@T*h\[[-Oqq;精 nABHDGlmm z ] KKKad2E6,41֢[EZs5 ;1-: ZraWm@Qo۶bQ4ͥJ&kNvr\.lOi ;7}y+ (A $u &u f@!A1ԐAC !A1ԐAC !A1ԐAC OqLה?%!$H F hȠWAdll %)}JqKRI N]WIENDB`pyzo-4.4.3/doc/yoton/images/yoton_kernel.png0000666000000000000000000007233712660452173017277 0ustar 00000000000000PNG  IHDRXGlX IDATxoh#ٙ3ș}UF&fvڨzpcs/:,_0Yv_i͏%&daIM!26IG\ѥuƂ^BM',/Te[-%χaT:uXַ<B r9u]וRJ)OF)4MD"qC:EAK8B:wR0 ÈbXZK88+&BnFC ! L&cv-:c0H$lJ hˏ֗PJQY(\:ReY'XH0QA|W7n275Mc1ƺ+LRy^8(]\R)HYyBxt]9"rd2e9sl< Ї6wb 4Ͳwc0??8N4b@ӺE:ypgnnAƒcב0c& av< te0 % L3bYҕn/Ad2#*4 t{Tqw ^򩺮z2l :@r+8 ! }0X> xr "a<ϲŎܫ.ڤw٣mߊ ]AfeeERd*,'&hA@~s%(͛iTNnX/"mYg9WӶ8i=4Mw>; HbfE< b M;uU#ÞB1ow>-BAL&S\`9Ͽܤ٤mڷ˭޸{ƶmo1F9QJk27m ӜQJ;fFyםmx:??yU`l@ӄiFu}<0[A~ ƣ`OuyA0SG9',--uyIoCŧ 2H[c J ytc;)ÚFu 倦 RrX wʲ7pFE an_ F7y RqK˲VWWdkf9$`$A c}f汮o1v^$$8YMMB H_q=WmME TpF4s\ﰸ8??ߖTe~(;oeO4дi^xj4 RMs kZKQQRmۥέ%˷ {#y٦jB^T !BWF 2=𼝪Հm۾)?GWOд &a]/H! CE]&vs)beeeᤝrr4(lKQ)~jTc4=OUfQ h:aM˻l/>3SScBpMSS 1H'=))O糜Z dR ԡ\iLT}Bɓ'5TяZz?B9dFwݢU8j1iURZsm#iJ'(ZW]Lhs\<϶#HUpJD"W^unvZiԸnկzM; "ض]SrFmub[2|&Q/2 ޤs%:A V֡jEVHUUFٶ8N=1,//7H)N3!wv{!L&Q] j΄ 0㑟/PJ uC9b*5B]nYa2|BǺ@\Z4M3 YH\r; #+#k̔vs._TQ !~r\QPOcڶÑ"`F4Бn9k DqT$Du҃RQkwpL]w1| 7\\*x әtevsM/z^hbUeXnRQ-5n,"0>RZy':"І;iKƂa"؆ Twݨ5} Q !ՄVs#A*0>@,v2PRYm+wnt\S=-yHA kڞQ]ϻ.F]!nW5 n vm[i*|f)mŃS=1)4a'ns~(倦Iy:?B /d}p As DU&`Y#|݇9֭D|/y`jڙqTF޸iV1,7=!N+G Cc#~\|= +ߘi/,O"Tp$d;@.- aunb4HH+]: r1ؗ'jgv1oUYXՍ_2 p)pA*J)M eYJ<յ3eQJi! e8+H$ e ϴe?!$dǃl> 0X@>+ dI-Gw".86Z, vJ>(\obKK'RS;=፻ww3fJ I,/)9:9t7[vV<fH[V02m;)]Իse?BUBKKm154M+yF1JrEGa9nap+ Z4Y* : `GK7K+6jgfWVTg$[&Zt태t2iY*iS}Jћ k mTtZa(:ک?b?2}N#b1jفu(Q{v Ro˴;}ڷrZ,{v[>6\Fu"HP7SX5MVo`}?J΋ (HN&[ᱮD̞]7kQm7?}ͭbXuŕn A(nTkU HdccJ)409R^Tb*ͷ=^\Qֽ{ٶuJ0V`&Nj.KNW JwA՞MۓXZZi Pipe&(dkPa(ai˪0 d5q>!DTO5f}ffW0Իd0w_tWeRdƘBIu5O^x\Ar9w纮3iGurPH3 g,ŖeYmɾrj[-+ew~\%dVxDfϲؤe="dB< f76F)d쵔*1{b͛ENZ)oee'I۶\+TC1Msj9IRB:E.Sh#WyJ8@40 Ø*伈)%^mDWYY83MUcd~ʹd9fqF)$'QhZABF)=^zOY!<)d0x\[hxR:Ъ9^>m;QckuuնS+F§vt8s%60!9cJ!0B( Aօ] !-|<%`V-ƞFT|& W?IGwY+ضgxF@Gm_̓袪,tXIW`M6S)6MwCAlu(Q{N}Uu;eSwݧ̫XPwX?\&(zrM'ym&4L>m.7 0,,x&86#0= x!ܟXye erbJ] X)E^J?rbdyUXSRŧns+DFR4gHDMK axށ/0S+ %1a]G"<;UJiŽd,j=keeEJ`j$ @~!&@~6faT<0ݻ$idAF2{+> 30ݻJSMĀ! -VH}4#Kn4Z 8t/4  RwbA70J}jyH$ߙL&L6**85)hNP)(ts }hxxui.//Wcelee1>tꔩ8zR)˲.im4ORRʠbffW G8uJw~>V7,-'*=x@ a{j2&93CGp$2B鰮@J] RP9ǟڅ*n%F-0H' ޾aO<QhaO{}u]];&okg(Bh<~qЏ|M^5/Ih8Y# xd\`W,bgĚ;71*p~֫^K R&8vDo'H7X|iX,\qEɡgpӠˠq(KDN%i'y-eӮ -jǚTmXXU8lS@)@69,14Ǥimϟ?o}C4M! aGyCNG$d\n@.ԌTg>pX=RMR}USx^uk|ڰa)<ry=O_v9޷{[`vϹrn$__W|>Uz/J01tz>y{ v¡ MLӴ,]H뺌1Lm#M ="$ZV([-<򮛶BmovT RN61 XJX<>gXv(z^aۖt~>ZvyTɸ !)MsҲ qF>3V|ҁ P{T`(d2Xkq|ַ5555o ̽TN4MS5 mI!r>HR8qFOkm! |1%`esڶMsWiaSO0--l{@v8b)aӶ)LR_?vk9 D=;;B8*@f|g{*ps.1MRz>iF*dT򒗘 jgjfaUݤP5J':vc4Gt4S)O֨?gAʨOEnqJwvp`kr;X "J}g?яNo|cggq\.\׭]U-sbXP}۶u]?qbuuը|ِR:sI!;m4?xSӄ'ƎvUֽ:1HE6<aTSk@$ ~QJ0YǙzꌹ}m.-,,\z.s˲(Dž}e -,PquvJGmۮ&IWdciz^by9L@UVpx&O3 y 9bg`-hF)ΦU?T.i*N '@Gԟӂ8zg1׮]җtA޽[m{ǧl(\`9L0mM aR۶+DuUJw8B:N&?5w愈͵rg9RrMt]!m;[>nO C:NRduX,y766eUwaQJ*:ߠR3"^72gOBF^'s;BU_5X&-"{ ƢyKI(1ٍeI k-qTګ3 v8/z圁QJkNF2H9O[Vi'O?iYaM۶m$$NNv쇜m:Ԍ1]ʇv_TE˦C?.@[|o5/1q PtZ1|&*`$t2tm9R*nճpA2pX"2Xyy<5X Z?si7J.Fzd2//|A1nmmAT*%P_SPN o;/+Kq(IU٪D"w Bib g,ς4-YJWͬ+]O]u 7?=.;|_g̳g&2H cӫ]<4ع`1wu.voa?!zyGA.`@{*;G){<*R)LEzx<W2Mx7o={V,J+&̮MBF1 q1+npy[Ths:0XG·"vms f֛n*9_l6 ?BuZ7o~׉M QH[Vxc0 b*rNzXTMsMS/ÚF9д-`BQJ C:NTקm3JcNZְx^ڲ,2˷JS7e}}aۅrL,Hy;j5U'_:, IDATBTL"ew\y{TT&JuDuȻ]wҲc1ܺwOͣ$j g9b/l[ʡ:n{TAJUeEmO0NܶeMS~N9$$o5 I$B0\.ئPaGg9v*"H}q՛7H \ZQ]f1Q]߶Εru0=UNT(oBb%q5Y!TAB]ʒS=Iza* V.vFyAB71{N4. aegqqTeez,\ɤbcc g,/*b}ffի6i2%oqPhvcšVJ6jkR2hvJ5r(ftn%rAsqN U :w?l#!{\GӔ`)ҫ 4[rɞ=948F T L(AziͲ ҖN~BHjR-O]BB-k/J'B`c#UdwP*@ _?͛'ܓ'Ot4yE(gvccV΋G<yy)7͏Ek`4+$ R HYUǩ{VAٍibl<em2iˊZLA@㨑i"FRM8[}Oʏ9iY#Rx|iiזJq%~Tgb[ڃr!w_|L2e@,[ZZBh"-#NJ{4rִN(r !u/gII C78f[ 48;fuIRN0شmKǙ,쨮x`uw*M0#ABRmL:8-bmJVr@,[^^Vԙ~r99c1fi.+hiaPhy.Q]kk)UƢ9jq Ӷ=iiJ-.$S4e}_ :J]ycGO6͛7]u]sN)<2y};kCws^GbYeLKKKsBaib '䬠9N2aM#q[ߔmRִԋNa#@}ն !;fߓR9Th) G",{Ҳ kZuKƴe g=(iDž Rt;3U*iZ%uwVA-4*0]!>y %1|&9p/wb%ϕv<ѐSPH"itRL[ցxTݧϲJ RvE߿+eTqu+3]tƑ*( "h"mFuj9In4'͇cL%41 H."nltitbaaAJYIϽ{[@>+ " gpSX-IJ<]p9"m$S8G}uV$)MӴ/_6qeY0#@G,b7詤hsDeOc"%d8x<9auSd! X'Oduu1&O#G k4MS"H xiAj:0=žVWKuHRI UI`5h ^6Da%q 9*r,e !>s4RjYC4rNaY۫@a9?`j4M3 "N)}uw%;vm@As!D:A*"w;} "g@'D:&ԷT* s D: a4Rz`A:F ^8pw(m ˩f费XEw6B;D: Zm@~K h &h"HBBZBTKL&A)pw4T*l1Msyyd2,sH7%"bWP Fs(אַ H*=r[2|-%ns è1 T4LcҒ2 !h v-k׮'?S "DRFY!{?H[V:l rA|Vqkonnz+B.Ƽ| N\N!T_ xNp޽W^MOOyU% $1i0@8G!XR_ק LOO׿NR*pPxmB`t V8糳ζތAN  ]wЛ;UW0t  B3>>CtLZsh OȌ b+ƞe g%!w]!*UrJv R Qsf$y]W @j!dqӜcömr4?3Ӷ)m+h,M7UӶ ib4ͩDբr}佤RJ ð,֭[ٳgO>murM ᰮ_eEu)&4Mst)"4uok0XAJ8*[!Z\,zޘi`  O+++]QiY!gϞ}8q淿~zz@z]w\7yڲ^r#[0QΕN&bvcpRp4 r>3DAΟ0Ux۶O|- {A :8_W~u7o8\+ I!,H|Ҳ]!VVJTV2z$mYcOQ~wڶ휔Q4xԦ#r.Ls<tk 9,Vb@G@G_?|>|HGi"ܶ킔cY2Du}t iNZVuq]mY5BiڲF(-z^ڲ[*jfҲ )H9Hp L&cƑ]x 0hgNAwHf٬iG槁_AB?2iߕowo!vU:6B ~oC_=K_Q|~'gO놡CX&_Ηհ0H()㗿~?_k{BDuў{?VT!k//3̻}K+~C01-ar%/8oy׿u̚@:Mδw Q (,ZZZZ\JcWt~6JUys^1J+4G !3X?Nyfss8 ΘcZd!Qf\HӠ"RSǓO\۶wv{5E]ryxi@.6(rys*˖TiBTY# ] v4s'b S6dO EA!DK;(b[2OgA5sa>A./~%[BiP9 C88@B!B.TO@\KU+]6KB.1t{\(raiI`<aYУ?p urx A@.%/z0)=d꺮iBbXfP9 h52b090]fi9OKB έ9 BdL]z+PqmۉD i(%{/EuJk=У% `+P{3᎒d(JbhPRETu]Ƙ"t{]AGG6& - dһAG81‘-ǹw{뚦ɓn/=` \^!#^#d(BSx` 5 |)j;18f~V~rj;UD48\Se !\^*Qmջ*,=Y@ 9 -pzC+7hvd`_'mA!G`MF&F8TJ]* # ư-,@@(9۶]iaa8@Mr8 sЇ+NҽS;ƞAϬY=[Zv.kP"GNAM$ZϬjɓ??2HG?!b\eT aP 盛R5h,tx"}Zҷ?kjޚWz]s^^(mS1)~y^R/s33M{_w8qlzR Zҷ955u\w򕯔^:[S y*E/_~p&HWXE)MRL&sJf9]f}BO2˗/ǏmUW^XЋEYi]ɓ'/_ ^N) Lƕ]c-eu(F۶ l2_vf9y|6S#yuw8W/ R~f |G ߶muf?xQOqڵ??/w(+g{nu;{B[)z^*UI#|9-LZVTׅi5m44m]W)LF)$$j5HHi,rj1B-KeAl۶0M8/%(ҟ!Ot]Ţ`D"rGET-,d9'q)V{x|4 QW $dB㰦3G93E`.H44띎 }5 7oޔbMww+Έbs3HHngVc J]Fipf' hQ5׿c1 b33u}s^BPd2:2V,*E'VKl3Ɠçv!˗#CJ IDATputKoucJyu〦M«WcYeAׯ`wvv^~mv)2yx`oC/ȗ|Ѫ}׭mV=iRQè>9 !04f~3cڲ>!d@&`x3qA.668B $ŝ:կcHĶ]XS7__Ji5r. (JnܽFrF`k)(U"D|txX=m?5O$ p!u^AI\tEϋqԭ=.9o0+4A8UKK臎 ȅ (s]RJ8 }ַ~5y'?pLRK岟,X BJ(#3vb}ffի(;/5q߉/6@=igM[V:,kii h"Ebuuq!ya0 8-y ۶km.E`*{ S9'@&Q V9q߭3"HSVbTr:t!Zx#BnjQ[TG~J)U􍍍wv:O T̬lOաiSAPքibq߼iAV9g,..V:4u=3TP4u#}1/wӠڶ7*(rD~* 쨉G>u&zVncE.^xVk/(rr9%~ld2Y-RA]9d  !tL&9B~a< v~6eqν&v@G}Y b.^Wm=O(vR+BQJ5Mlm8?ĩrGA z6(1"OE3qF\}!DUUWO}_Y'B:wJ*GQåu~ P=%4= !DI DE0JYu44McTɕ TZ7c7ՠ9TTe47RE4fOARE= G"#V#HARxޞLL\^^|߷m۶퓝Q§F{AjcR˲.:?K*n_B.4!J"r~F6{ts˲j%p.V Oh<;(E(8Fn} "KTXt/,+L&cƑ]x 0h-MY~K6D+0H Au>"'JpH{VUap "?۶o޼Yߵud2ݻy&!Dz>ryd}~JխAbWN 0mcyGǝeA@+P F)-ka>rFs&u c Aac60 MӼw^1r܃5M,K5{ 59rL3f绢dT/@$ bQL0d@Ev=\AR "?F !|*rҲ`W-i˚,ww]؀8<8S#w8,烄 9K Ru5ဦܨ}0 ƣjgQ(ιw=54FPUqMT:?9WZ"cY@:Nڲ`OuFN&wy9<(H?yyxDw}M{BJ< f,ģ f 6˥BJwq 933im*˗D  km!`;ٍ w,؂ 8Sڶm7i"L3\{,X@0>y|8 s q`W3ij"80 49DE`B9 88.K 3rҲRk-F;`u<N0~8qc-X= c4L&sK}eeeqqqhh4Ml><<| bqoooooJ_J:؇/_*|tzd}dy+Bb<3M?|ր >|0Nw{-͐vvvaRB\ z r5V={FiAI˺-D4;:H"wt.kׄL On *[ ZuޭJݻwu|i%L{GCg0ﺟưom)7д!bƾ=o4 mYB>:Lmдjd4blʶ'-k}fFEd9u]կ]~9#x{ɂ͡ qQAVČ]ѰG}A/1;F}g)$藐B ͕F\4 ]ŹM(241`<m_x{hFOݕV>Ξ9svpbbg?٫85E~DX[[q"^xNLLG}WWWB8<<|ʕݨG:Ƈ1z-|_b*^eb\QFt-K-,,ĻRnYi$`vdW sQ2M:ܻw wm>p څﯯㅊ"пd6'_ .͎l7v6jYŷmPsCeYoKO^QơT}?oo~b|駟f2YPl˲_yc_yiE~R"JMLClkk qy?+*$oES 6Z ʕz\K۶])(3A {`[AeuVK4EQDUU3{c@e5O;WHG^]]m1FoO$r$sܹxQ t:8a\Ǘ9ȲlvH?7 CqdYfU>iQ]KI.@D"6\YpK`zz3>}ū/@:}s:CezX9L@Nsh <'mZ=p'O~hE|FIbPLx@p<>dllLLť6g7}Bmwq3[L'OnllA@ѡDŶ?0z~:84{&''D"@ ƹNOO߻wVWW/^16ƚ2h<11 glll\9eyRiw_49zjdIkY/ˆ!3g}v%وS뺆a8- "zy&꺮:9J]D"qq*JM ^xRoooD 9A9loo߹s'>&iswxm]~HnT<ṃ[[[~'Oj^x c \.ퟅz? D"9Gor9xիWkb^cW^p8266ϊ/OᜋŜ1 Y}`OO9{f?`ݻw׿^Wl@VVVsix FyUaeam[E7-Mf`2d2-2[_kzz>K~<|ժ!y^6{)~Ri νV(](5I_#ǸWH$x N;$} v|$8evUɯoT9}ҝeqDQL&;z?Yôcc` w vkr…5OD^v ؾlWݧZ&YsOs! udIa(~S༻e;x yڶMZxD =v_T@BxV0f@s0DbG?L4 V/|4m;:m?333{8Qʪ`[oZ<@=6wqgiii7@Tnk')་-46!޲+뚦Lu!lom{&r'(GTl zK0;;[S\-17\1oNz*BG}y6 4t=טi ݳ|>*ܴMuoxeǹap}?vRĠ Zx#N6{z^Gט m=%^^\H9'LUGy=}FG68. K{իWTc w'@ޝ䰩3`D(+=R(N)qx) dT2i--axO]Oq`lyenŒ }}:1!6Ao0Blῧ/8JRK$fٓQH`C[O;]aeeeߠWr+jZvc9X2qT-rg?ݾH <Ŵ }hV5Qe2YqDnu~PBn}tU_U Zs'eu|SYkgݲ:oȰkϟ/ [hً&pZ5M>T3I:&c␜6Leyڻ[zn.-- N1a(~dMd2Wvm9Pάd EB4W9_n5殮?Zi'oxCg5snx#˪Tzwu}TELϾRU<+U9YX*<םQE秦<)fwvvŀT*U&IҞ-Ur*7 !1hDQ}ĥ\.dj&"c8e@Pqr0f cMmUM0T&XYWyOjQ?c16ihou}gFq9Y|SN2icE+Y^^9lQxꄰ`EQ(~8n Ԓe˸|>oY(ay|;u)٩aYIut6{4`3u9ci{P>L]83nj+ʹ.$I*.mXwH {Un_5A 0 SJ%<Knɲ㹲,5QWVV,z886N&Q 7*DZ rE)Jx]Q• Rz:BϻɌRh,,0|q&m1&4L̹E< ^L&wngSA PU8([yjoqyiϞ=ç+㜇a( j[X, ,Bô`M,P84-%skVL&wvvim[ IOA}<C0,kqq1 :@UU0斖ѓ[3F[dif=ִM_=hAzaYnp/o6IC^L.0T9Tz=J_`%P\vM啕mML]bh۶8뢪Jt]4 afJV.Fb(˲8]ӴǏ߿1fYV*4MQR>u},ĚL&M4 u]\Rt(kE](Q=o#Bz`$pOF)PM`a(r׮ ?d%' UUwJ{9m>4ZK:DQއ9NmL&1 L&95,//e6bKڵkb5žiB;a6M3yTJ\B%xC!|]d#[`qCdi7-J`?L&eY:xsG_(1xi X]wRI4ј[XXhvņP'>tz  !R8wwjG#8'?x1Ms|j Su9HC2zoÇaEn9ι,ˊX%2nu\14MSU_uuyEQ, -GJ&Ihv< ) 0TU[shz6ŘP2PMB>=zo_{RCx?__ ,7###^qlmm؅ l术_._ቪo;9SSSn޼_oX&q񩩩ϟ˲f_~e˲y71fhh1&,!t_Rxw͛7}}ƍ(###ׯ_m/|͛73I3pa5g,;QR?|jjjr =CxgAyt <;&Q)U:ۀKe}p(˫H$^=\ 0 9 [vxA#*ຮ?ɰ1IDAT33333Ng#L6 Hd ea]N&wiD`l-RPAqH3 zbr3h&];KT|6F(` T( TUueܖ0|=Q.uu]Q8A>7MY.c-r9Kv,9Ebʠ4Bs/#; tlSօ݄-!Dzr6>-죿mnI!(t]jw] ~a$vh-B(,,//3v}xS|ZSA'$Ae/˴[D}c7@t:($ʠcq$[tX,b}%?nݢq3~D;I&y FO.׮]sgb|^^|%TPe۶I "/d,tRE85?? ')zo'ϧ )A;|E|q0|>~CBHqcYօ OOOָdžab(luP(xgΎyaA)Bwgg0 ]q6 1MS/z $IAEhahYV:6 s^, 0M0 Mľki[,ayeYqկ e1 M4My={9u}aasnF_qT 78$IUUlfR)˲,¾TJQ,qlihq,Mw/eYnS޲,|idY3'.;twqqQeι,ˋZF7 I˨_.u]uEqT*9(顮Sq0ƞ ɋuwAD#>}_|Q̯iڏD"'1,S7}f;;;*f]-a=d":fggǶm(sхE銟%˲ixsK o7G1S/-ɠNĵ: ÈnWoo~mC;;;WW N?r70իW(ǙVOǏq ѱ3333335R A}EqmnImH @Ӵ>$ !ANM"1hP(A1АA !A1АA !A1АA !A1АA %1c+ YEQ@E,V.B!ު1ffo˥b Ed2ix纮8HX(L<?19+(J߲,뺞N (dggg-GMBHĠycL4| @(K (L <{TՂ|>8JQg`/cLӴ,K<ϲƿجJUUv0 qa; ۶#1'.x] hMH 1NQWxaau0 @c+N[e۶hދ,+$ݷPGlNˢ8]aq)mBBHa1TI=asƘid,D{fggtM?2Msaa.͢WR t5Mq쬮˚9S[y^5M÷8$IQagE4F pG|>&-y8y^Cg z1 d2— }iu][nkמ={.1۶j#(PUU4]98a>lja<O$؉0 [m ,+YOL&hEu1f⬚#bshj& ǷBBHDWtqr@!>m1f,ת,,,d20jOT[ Y^^[e04~50 UU\6(RUMf2d@MEQ<A$ϋ;_ffYak׮\~ݶ/Ç7o 1͛|͍7cLQ˗/?|s.aLMM=&VEdbܼyGFFEyoܸ8###ʲ>K Xfׯ_/}m?KR#KKK _mH\.099999YA5UH$W^r}} 8?l݀Ykkkkkk033333S* H&{!`fϫّAAzЋ=385CBHA$Ir=,(# !$ B b!!$ B b!!$ *FloocU8lnnx T*Xb $5JDc1"$1/_n* NSm ba{T8IENDB`pyzo-4.4.3/doc/yoton/images/yoton_multiple_reqrep.png0000666000000000000000000005701612660452173021225 0ustar 00000000000000PNG  IHDRX 9 IDATxOl#}?5 K[>rʡ1([pP@ËS4[)q{ ] /FB^CX]hZ.; "3 CZ9(+Cΐ.՛ tJ%4]eu !"(TCu+a78ca-,J$IhB{"DWykfvwA$EQ2LwCu!B걧R@@"@ ^vm74>-IX"C0Q+J,Jr$]Al5M[XX D}Jv_I-CF)4 KC-(iV?O 6LxdE&c J @#,"Spua7ai Rຮ(B/jVYE)N t-p>>?? c=  ̠ GPa(+JA)̧u]JeY!t6Q8>RAlێ!7 !tV(Ra dP nB!W?YD r w 4߿vB`(ǦΦx3`Q!c%)BсA[C;aNi !ٳga!T] Рq')_hov>5*QϻqFuC44ƼDg"XV(Ja6<̦u]Mn B+BzKu],  VJz9fSOE!`QPvy|mNz(v/JFhK&0r\حA LT: j IiT*2ƪtw,@<ޡief 0l֯NSS0B!!6)t[V,0 A8˧,O"` 00 N @T݀ۚ6(a(ZvLnA; Y2 0u.1l{ ҕ[ЕekuLFEznH&m\E0/ME !8o^aIR)ܶ taENb .>\Z; X޶{0xg#: 91نLikz R[APph?!i#bYVQj8ԆA 0vLsPx9FVjb<3 Qϟ\dZ=pƻcE]3 꽬=ZBGpaFתv4J3_‡!wm{\'TbS{/{t<5ӲۚV1M0cM]S{P eVG19{ѕektҙچ{9B#0ԕ:BD.IJ4Q;a&SFw;{6 !-B'0==YY-n}<0 T={'WJyu]5]]8k*QZ˒}n%B LgӏQ+iB=l;v{Vu03BAILNԤX~7mLBAIAseJ#xEvm{RX9ݽR ;wC-1 N FP'u+ hk׮Uw^y<؛w9jf& 5]i){{$IUNf:jn6ƛ߿SL?Bȳgn BWaSu;ts˲{h̠ 43 !m|'@n^tޅ>T*nsB8Ym7̨eQeLA+BnܸQ펎~Q Bу!yzh( eV BAzTu5w'MiPA˞ !耶fSba9Pt`~JY 5' ;I "'ˠ177g@<; ݢډK4;!t ygp]WQ LL$|^nP`UUM3/h- 2"UWݺuH塡!~ ' C1,,zR6ߘ044okD#7ݾ=E!=bqyy?{/HT?![B}SϠG(ʰks'Nc*!:10`]P|j zV0'Ȝ*NCMӪOy0 Mh ~槠,x.B=,,,0cсݻo_K@Ӵ%>Az!BMMM޳_/ / [#P$4.yOP!=l_xk666?,--5>y(a{01IZ`VmW(^?!. B{J߿-O階hzs^F`p4M4mۮnQtJ (|Az믿~SO>|M c[BdYƓ@X4 1"B)$I$5]'Jo}[oݻwuo@zKc/Z\I)EQEJ)ueivJW($Iz DDz,>ԧ(o~FhR1M4 J/c &1qw{f]0+z>AEQ{!1~ ?4e՗`G%jͲ,UUg2 @ޖ= ATUýDBE&o8XE0Ya). M:+; .SS! B0ѥYE)ψMN;{haRYH D=S]ZPeIj [mdE&"c  E c)Il!xYY(0ѥONi&>hMQta KJ 8 l1u=͆tslK~"S]y(VS0 ٠7U>^h4kE jǭ[Li\, ㅆa.с](r0Q;߿_Mx ;QIAaCYv' V(Z0Q;JRqeX9]PJWVVn E jS}Dn")>q1V(n  Dс)ڔ竧h$ v?TpE jyU? qy0umF DD4^b6NFP4, CAB)y^}Itd;E0uq1w0Q0Q  Z`뺎EaBm]m((sD Q$z*xB! B&i#DDA,KEx \9\W@{oq?`o!,,,O1Q0Q|u-͵/ޅSfu]D&m@4=O/ ku VO"!62R455ԍKOשּׂr5\bQQNA])WB Sksx|D.)xIM1˲u \T=2}~%![p7B$0Q` Nd8v\Z F }p u z~&fLNmQILikA:SuN=?XhGsBpW7uu'Tk-E):"Lَv ~Bm۩T*֜nBvm;&/E&Db|Ǝi BS%) VL_ Qܵ(av .~A'0Qa "MO'K|ZKRBbw gg3u+*S}4== R<inc#VӘ,;brj;݉,ĮQ)]8ӋlvnnT*MOO'v!Q]_״ U]&T2kځ3ͻ(wkZܖ=ӼgblRey\'5mmfVVkigN D)Х{}+mی1O5&k8b4˪ZV]s-ƶe:~e(8㲼ՙRkSv,--ɲo =;;k۶%s]PE:4xԕAAF9A& B0LA״ZSRgST*I)/\wW4݀ |2;7Q$LAeѬڐ6Z.vh㲼(al1d6BҲY(kc%ɿl^HR4,o1ial} V(0a iWmycU º$3++eU uE)e(S6OkC`$GDQʪ 3uhe!Ä)BQ(d>5c6ƛ!K <{, yx` W?yUUsv` 3D Vk6}AÃAڅ)BTTbU=-¶G詹y?~G}4??f 7nTCVf3{]NމG|uf[Ee9װrEM<=wBQqB??yީiܿ_1ϧA!] uxΛ>#]x0{M1qw.`[rQEQ"g_|E7wB$ɳwٚs6@奥[saF8|BJx $r. { upꫯAuw{n|'.fuWO `"pa Q)`:mw r V+I^jhh]QPC `ٔP,˘ (dYLnw@ `DxށLu|Q4Bmەw1앝QKR Bl˱-) ƒAEegSP'Ffo v!0Bju6ha7 ӊ`9S:ˋovi_aR/Z\駟ʲQDRQP`ڜ3(|`[pLxGvfOxNXO^'5MҢHdf:؏r `&N#ROA6B 0ƛ0g?;O>_LAAWRXh -PVCM0+¨)X lm H$^ϟ?:(B=eDVS;@3xq``Ftu|<Z-=/'hg"(T;a7@Y)("c E a$7w"t{g|l#9Dz0†qALh1Jz &" @<a,.. k~~1V]PaU> ކeS0" Y4Q^-:M 8Ac4)08.mX?0 1Уzf<ηas]WQ[d2z]Ȭ{6_` tlAfkZwA3.w=BaqGU:r:QCsUuF(0T/'NVː/tee% taǺ41nDCsԲq(!0CsڸE R0M.!i55wmOATUzahK&zu X_}k_T*\::4 bUhY lDOAeUUqvh ͵kתdrNݜqN'[_O^~#(x`/zp0 \"NKA$I$)7wR>[y=؛w0GA".~k_;u{/۶}WeR:??V[QxixYDQ\[[ ~o{2l{ gϞT*/>hq7\.0L4Mu1@EQEJ)Π#SSiY!ƮQkP1uMsUU?ܸqW4t;j6Ⱥo⚝/^ZP}%W<`s(74M2 E+qT:t]"IIJjڮmO*kZYU'5m϶7u=!{9( Q<9Zᦢv)4-KiY \w.JƅB⯴;` Ix|ee%'?I$D'i B 2u?MOey1AQیIַgkڮmQʃsT5-I雵M֡ m8wT&_߸!TwmɻwkecjNj?4mv  rXs K#[,a&Y-ip At~aw7LR:MO |#ca/!Jr+P]<6 ʪ{{[w-Qbx.a9.d@E4Sru;pUE,ڵkե|B tzhhR|嗺$|>[YY !7i)? UL]V1uMsЂ>X$oj=OW_}w9y{>ܿ AtQGGGBAg0Gb;<ŧo~>ϟᇶmR6!,х y{F H$IR6qFuϺO>?. !<҉7Uu_>W:L{! Ȗ H?c wvv~;Ka9.!qVşuji}eYoBU|tPHa&ׇfki8{[ CCCnw+ MKQ/ ?ϹO)ECDAT*U UVCm˨e^B4M~򍍍AֱS!j 4,2DQN]xɐt*ӏC4 !?Z|G}499ɃPeYq),ځayOF=1(J&|~_|1993Mxv.]!Xv`E GCn`iDQ\[[R*JՄ?z4q]f:X `a9ڄAnݪ^I^32&`fSz9o~\X,$IewFõMQy #a6=e 0z8Nuoɱ@DAr0B,ˢVޅG}VA];6MXaFeY$U x ;@Ǻ݈uWg @D a#{y c' b.A (j&UːoaA\GEx|mmLup:l E1T*p "j `EiBAQթaPnr30w@-CPi`@D'a9Auɲ|0 ]_aؠo4F GvdGGQ0{CXTU>jM%M^`f l 6-L%NU\.P{; D a/)׏Jhf)@YUUA@D =R/AQE型FX ^5XaBc^#RJ)rxgfSp !("k@oXN D ľ  Bϰ@'XeP?Mj n%:+Bt`ػD!B@]X]]X ,QGaE.,{"D hruVUaMXNÊa M R,QGa"IMwx??]~_~acGAй`io'O/^*t`"t1X v_x199yW>⚍?KKK]k:"D-X 1:`||]!B5m;( B%B 1B:認 m( d2l6ꫯ\84/Q'`"q<0c ^RJ)$ pΝ}Ǐ熢P(aF!;;;Q(0^/=4M4uOT{f]pN~1N4 OeP`"+=u]UM<3Nrmn t:Ne)j<0 YeItl0um)6xs h&IF 4 B"" UUT=}Բ(j0N D(ZV zG)m$ҩ׳wA[ŧw D(P 6wΧAnGy-(8eu!BbhY:/&6p_K ՇQcRwk˿,W_?.oL]k}SH|;0Mڵkg6++BzIw c゙ntE߀l$B/DAPO8}INc)yH(?}0̧Ev3.xx =j!Bw333ׯ_?]{klyk߉J rtJ "c ڣ`"?N,Ȳ|ַ?]\\-˪N ^{g|?6\PAP ^{>hq~X\YY7nT^ Bf_1tOG>|P6 mmm9)&qr( BmۯydՇuڱAyR`fP5`"ica6奄A`@P 0[J*J}7QuM D ۶n y-IVWW[_Stkϩؚp(B֭[LŔeU?RL-J~(EnvB:thh@fyyD/ Hﻦz'/AaU@PۜOU~ElxQ7MO?}ڃ۠ T[S(‹/y$1L^5<3PJAЕP*TUfgg]Vx3Zi>:˥eyl`|yr Bjavs.i !BWWϏT (]. UɮAB>]9! |<0 ߁DQtA|<0\|bcb̿߿z}>tqqR(*t* BP_)ssseMOO7nx hvm{feeRʪJ$ݣwҲ6M]Qcw[Ҳⶣ8w6 ӼD.7el׶D._bez BP_QU0 UcqzC$nFWIt0F%]BKӴ #-DeغsBUaTLӸ榮0Q+(ӲAxk!W ðmhFeQ6-:TUUNOczަXr ]m BP_IRT47mݴ :*O*/`BbOm1#b/$ Dg΂]]]hَvػ#鑿DʪӮ%?; ]uMs$m |\p47upU!BW!9{rS.INM]5r5H06(㲼gۿu 7VN[߭@('koF!e/N=!BP(oŏSEǍjuU5[݆nPmO UBbQu2TsL檓$3 0@ipAm/P`"Q y(V;HӠνe;p,B D(Z.,˲< ٻ@郲,/-- ?0 _* Z"= NQ BBsfW? }r iFG !Bn9 b Ac !B8(t~S>J6(c N D,u1f_PޱL/T D(`3_;~, 1nw|{c]LAiSEh?Ţ\{GKC ~): z9y'S #j=|vv6#t B.:?oqqQӴ3Tt[.hp 'Ii~E!Bǝy;VM%M{]]`[mb|(j`LӴm[( \ځ"rfr3٬4>J(WyǫX;Rc(pe;?/w !G B`Bn_֭(/naRy\\z6AGQA3<4MӴ; ΜY{i??ߩ Da5wΧAnGy-avb'BB0#Dz,Jiu^L<l{p2f,/--u/T*OЅ`Fy(VkL;Anj|~:_>Դpa;?BA!3 `3=jO(?D]aTKx {Ԃ'0Bw~a۶?;?Bm l6:@Hg ~ujg葌PV*9cccd x/NP NޅҶ뺲,3ƺ3v3F'T*e9[.P+IIJw"qR`P*߿ BcLe۶r BC0'IR\2i wCMӚS_],&qr/(kkkiqfff&&&rr4Z5Oo}}cH:nq=Mz[[[### ~X,6>>jT*!Xlt4n0,&q!OVfٚ?V}[ߚ9_|ů1EPභ3FL"w^ 4w}Iݼyq\.Ç 9BX,/k+0ՇӑNAAtrׯ_OӪ>xOMpgOX__/MP ONN7_\\!NGFF3Jeuu& Ld2ضX,ƯmRy7o~y%YULӓ)Aq3E^%$ض>hq~#4G ǧ]~4&A\ؘb$ 8<<T*<)aM!=11coll>_‰nS{n߾<σSM:vm{qu]]`wTGӗNN |ϟSJ;$ԃ9%" ~֖OOcb=mnne|<EUR;6ysMXqssixEQ\ZGژvL$R%cAw/|Ϻpggڵk8;a>' ;^_h||@)H !~ȿ1N7MuKeXlzz|7Ml.˶m\ep*uSQmGdQuEXPg;#v,4ɏp?{T zkj#X,ܗhԬWg,˺k X;?ctllW{*wݻws||;Z_J$ṙ !7oKO.xkQQt_^,lB(oῧ3Msuuoo|wL43wA[瓓c|H 5ܐ%;5_t_Wʟrd'/N$M@=4#˪bujfCE#iSJUUo˟$'?j`K(7A :C~ 8jQYH|fLD> Fxc)I:n ]ocPRT3P,l tzfx*&iaI|;%wWwhrDm^IDAT&zmFFϏTJ`&,ˢVͦx2wxϡ踮z s=;y 0Yx`sIڵ 6UueJ뵝Ɩ)-bߐ eJmW?XƵkNoӶnST'DL&'\̻k3++VVU"I{teV'.(}hfe< okZe'(οx񝵵Ba426Mrw۵r>?תF^[\\l]*y3|qb;"EU)lb=' f 4qM \GH#؆1B/]*&$2ޟ/b FE UQQLd2M㚛‚G͎84niy˲TUm)(W|WtE8SPgRQ7#czCXmIX1AػʄiW6<9-STӊ(ME2 cPƘm8N)>2W/,,vgtuvL,PURb|ܡ&ҋ B!\)R;yB~J*7- Bt$UukX,>뻶=F)rh,=wLsS[7^,;avuOvFND\U~ymMzxXF 2'O D-\#ԶFW ›I5^_ٸH# Gٽyxx jR?>mytkJ7wZ^U qZt\=:] s&B 쨴,zjU,XϪe[=wRAt<ꀯBҳT.azຶa@czF#Wǒ,;C#o<gau7!jbV=ϛqk.?8b񢯋EQ 0Mt,OrGWtE!)j,W3gwT-EQ6]=aioY !sss$\&3.t.?u<+x17 ;a7)Z(a_zZDa*eaTmObOɑe9JJuBH*Z\\ r<+FEQt,..ڶC]%Ir'JY禔}9$IyQgggU yqM $Id2>=4M$~M>rA+s/ qROI:N.Jh&xo#^Ptұ>:$%1Ixh)(8t$ɶmJgX,*"I(Smii1(<cMaMkdYmEڶJTUI"5mȯBIUiBt]w]|nt:x7l!dit 4d2jMUOcx\Ӵ?6ECh@$UUk] e0i zGꋢ8!.Ͷ3+MG+QkXi~~^G :zWG .4[<84͊EQOU<4ͦEq6T* pw| ϲ,B`g1vt%(SSSSSSai5i2c8e" AJ8v 3,yq/[Ͷ _erMQQ! (*§y4|N;md44~ {K<쬪R(|yNg٥%B.~R#c z&᧥8==݉in(6\usss9әniu moo///@2{7VC%6V;@avn|8m|ځj_u]+J]eY ;É% Rk}JԆ'w߰J%^r\.D|f yWBONmO3sn?ɡ u0leymmszGGG/^eWaTO'VjR۪c ? )W;keY# 5!_{4̗ maz2}dХ---QJE@ك|ٟ* g)NÊ0Z ѐ|`PmVUeB` 0#' ~vz'R(O0Q'={:5EQcϞ=[XXx5E|rfudŪ}Ƨ)Pq1#j~~;lZk?rLAUA]*p@C3` " H>4>RچZC{' pBmGw4F}ql6;==}ƍPݺukzz:;V#t!8FdYV' L%M{]`[me>^gsrGoccpZ>;(#/OM!?xYC%b2 bg¬_# @t!0ִBFη(CvLs϶,;8jXxeQJv.?"ɘI)^|h %~[9NnQWGKoҲ]}GI:Iyšyflg~#*ʱ b5 f4qױ/YZJ A0uRJuZC4O'd !f:I)n}[[[ڪ<}(4JUv,믷yX$/cO<(&(tsn66u])xRJ!v7]۶;eYx{ȕeY*e p +A[ynEQH)RNKhaāSDלsιyYaLR Bʁ։ϘEeAȋ/~7؍7>ž7|߾O6qlnnzR.O߿9%c osbgL py͛7+Okf p8Dsmmmkk͹\8VJTq|||||ب<(AN*ok$ B T{o:%. B ڡC" b!!$ B b!!$ B b!!$ B b!!$ "Mke@BHIJjjZV[]]mZ&EQǫ!{^DXx2^j9 aVjITn@4M[1cʫk@¦V=CBBH֣i1V8x4| ;3$^tAY_____9$C$I,2Mzl6swvv/," C59 4 1r۶pxxeNQyn (&qgY9(}FQ$}q r 8U)M@^aάT+ A,LXțRʄ *ep]]("sqM3 fYV;RX0Hkw—E9Rm$!:Z#1Zk=>Y0DEC|&a=C+R+GHąhZ㟛WIh/& xI(1-ZOsDq\v0<M 1eCQmfJ @znAͲL)Í$ !8mq3]IdYVvpcJاi i5<(S1fEABHąm(Iz-zqQHX(BVQQ ,h!ވ8de}h;Z {<95WBy۶Ѱ^C\ͦR o|9CmgY6Z;4wpby,9+$AqZDŽayW $uqm^_`ݸ__Ȑ|ߗR*.x]aeY9fmc$QJ7$a7P(wx}u[y49m.jHox!4aRDW(˶m7N`1j.ӊ1XtZ6ժH?[ =$I#F 8,WvAQӌAsG0wz<p6 a 7ߴx0˹9<~xق\#evff'ؔri!:|Nu(Ęi^:ƦtU7Y\\\.K z3ipAvDg9BuonjUzE5Y\ mSf̥|yCֶ---|p10w~c)!.zEє >=~z\RjqfWV ʲFMSYǹ4؇TU;=lA R?4L!aCli?Q;;C$" v-Jn+>I!x!z>U8(r$I,oжm˲EQz.1'͝5hc?:+gD}ZF>Lu^wDx(Z]_[0'nɜ|(՛mJ9Ͷe&i2jTjei]]e>uy5R닊#vlW#UnIo_~ݑAM- kkom{SJ4yqqF)EFa('!v7RWT*/MS9gZ/"hotgchW CLJ;+Mpe&1FkwtmWJ~ܣY ̣(b1>gƞGT:4GL̽C9M;֞=z'7a8PC)H!n+-TF,0/ҥ^[3?'6k.ԲҒf0 Xw~T:/kxр'xRmO'\wT{eP.|Ǔ|qB%f%}޻f q%b8ӮflAuwpq\>[v)}w <.]ԉDoX ʛAE!Y9Gq-"qZXznR#(a?/''YJ)!Dwዋ'O@w]ܽ{h{8>꺮eYaIض݊Y9 8V5۶](EsRmתv9֓$1 j71\ST6ͳT)" `]}ˀ1P6{߶;qyU7MS)ޯ-y̥RfL .: @(B I1v>fJ),Q*A!5Bu>R"|w>R$m*Ä@l3>}*˞ŝ܊FcC9Օ[)xAa܋q , *L6n>zh'I_\__nUqH{O/1݌9ڤߟ~wskmۏ=5W_8 !? 0CK| Fݏ3쫣k2_LICBG 2_ںHRBh^ pOillljjѬ߬P'' V[׏.I#KofI}o>8̂`Kg=7 _9j W`½|췠0vziJ%@ȕ?e1}9Kbs$6ѯmX%;GѪKo8z9zE|w`V1R%>^| V .alI߅)pAmAb7`l&]Ԑ|⃺[@w&5Gߚ9e$wJ%'f:pP` =@mL 9i{bGT w ]^Sܨ;nEA6.^\t"4G!7 \,__H׿~~[t_W~y-;'ں7d#->Y} !wap}hͺ߯xtFjùUc);3y !d$bq4`4zZ8Znn)E>dp `\U)>Hvr6tX)?Xݨvvvϥh$eɟiwݯktϜ8_<s[n^s71CĄ[ ' 2*95+5v_;d?X<{]yݨhJUwcLl6[?e=xϿ}6ۿmgxCf !~磣 IԮ!gx;M~ 1MyAZEJZmqӡ(" ~~\TR#ny$ɦ۩ۍxڂ)~c~ d/^{.~D U/0-PR5mRJ)N,˥딄CGnwRj8Nt`) |N9D GZJ55z򒒃7x_Ӂl6ԣa (QZmwoLm'5KqضaL$ms~ *l)j~<~ ܮ=uh"zZ.;\_ zPRl 좈qyTrl8f+++iO}S}ߐK qXTJMluqS-}P CfY]/ݶ)%`Jj-~;33a`z6Ґ/95M C.ԆsM}/ZoĐJnؓs7|>i(o6KI99.lWd7 Ü8=eO_pP;A0j\J=.pqΟ>QY~!H=kjA08_§Oq)[F|ZGwqƊQ0-ę4w c4'8R޳;1ؐa#=t '8>#8Y.澿IFa`TuPu\1vilJRc9;w]xߤbjm =[J66gs-RnjY 80|/B?Fv ٣Ѽ1/.JX /V0V;ÙQ禹8jJ-rnC>l()O:l@Z9NTe's e&>V 9\/3<{b3d2n Rf/39¦Jq)C21yXF<{V M)Lsja|]z0Lыe&46-o0M b\"ŋPz00+M)_rE7 eݳ8޸>fٶ-`sw`0랄` P3u(Ec"~4t*Ws]ߙRt0N7pH9ZmcLihY`h"fP'<(s?}?wm4չC (zm{4Rб0j۾|}-0~fuZ"~wo!C!D{r)'BߗR 궒X<q-+b)!xƶkAAʴ ,]\G 5/.=oveOnJs)ٷ=9j- cCK8C+5N=sM΁O4j pxL!?ɾa`5&m`YTeu::v| 7h۫BP\w݃8fYʞNa?1a1L|A- C[;A0 Ay,DmJ4:aƦRj406˜naJ8a wzemWJu^E6R;_؏y}q[)J'(E,atݻw8.Q*|mYQ"zк ]BT*ue}/d߶m&eNVs9MIˮest84܋.Nn '?Z+Hu*؉Jĥ4 }/va vg$ + r@;0ͥcQPXyl_53 ӧO]n~~dz# vzd ÐT'Tt+ %!^s$8Rʘ2*0 ˲\ץ?[MFJ) WU]s'G6^X?7 C)EMN^s BgDP2333A em{0B-6 IXOIJz$JPV- F\=zľJ IDATbdvOG-qiJ)_\\Equ5 $^)3,ꪛ1P:"q.=Q C=E]^q}Zz]@*H4N:+l@nS˲}H$B/Rvvx0 !!n$7ƀd@L3f9t߃^g1W$)˶m͖r`~;=ff1qC!!y1X {fIoxu]zժ"ho,7 bTRr30FOPHo* !LC$7 >Fsj 6i1 qVFKtUqΈGb`1gAmI` cRJJ'n.$7$I>~v`vva b o~ /ˍ 闌0 % lmma#8Z߷& #0@FFFF&&&Z4^~5;; >|۫j߿>loo2 ZUh677XyޞV+ a$Aloozף f{{; CPs-jmF]__uÇZt?WlGĽ=BmG[.6~QR A!ݦo׆1###@:נmWiGJ :5:K:8  hEccc &V6v'ܵ/ܹsxפ=齽Vc$A4ɓ^k8yu244A_~IBHq;dp`V֧]066R=iq&$At(RJ  hair-=mB`hh0޹s3M] 1Ul5͛7Ϳ !$#ﺮy\1ʤ81۶lke$߿joFFFvww1Nv}T =D*\ql}P(Am&9*@mYT:7A6/h+$A4I]ܮNt IABHDԕU+jo̜# $u;kw/zIh B v s !$ k r(rmi'^,+wmvA,B 0.ts,=q]fPSবAhжmƘmkkkjZJ= h:#Imu$.(d2zZ...޽{7ܽ{Rg2$Y^^d2LT*a sssxݻjGLL;B٬R 7k=_T7;33$(2 ވwJdRIE!!$I ;Gٶ8'5\β,RZ'M8RrP(ضǏ pGiAYOO|>xfaaq뺅Bq!ضmfmR0 WWW$q]bHCBHDdٓ'{%H0/%em!. %Ba|>onu]T5NXLNqqkkk\.ڶlZUJq0}1Vlq(FHDض}lZpʛx9o{cXBT*08.i\6 tt0gxUcQ.4xRJ yιRjii*dH h˲N b DZ;j0~$(Es%I4s۶uzDK= y6///?{ &%y٬8'x=M{!Tbo4[:uJP0 CזRݿ ,K_PV 8k`aa0 trm_` h>ǼS<)?Q !\וRJ)u % YAҒ `_WuUJ.dTwu]׶m4 PJa(1R:cY̌aAH) ]q;0wfiY8L& Cx0Rr9:EBAէPAO>ֽQy$,@B |evG={BbDQcLH0&P*~Vi~CA<׷`||ɓ'$A&PT:7,94Mݻwnjcq?v ?iBbZ9;h"T0~? !Aaaa!|}a_ x]G}Ϟ={5#(Y q}I MȏΨb@mADq7 iH h'|7Is9VkPAc瑕Ftrf/R&0 q;EHDG( ?~}_J`)7\aY= R(p-^떦ux^H lXWP h"$rR*; H ˲:u Pg !L$#$I VU0s3nB .ˠe\a.,vp7 7R$q-6' x8xlwO(FH%61,TF|q jܜmRʙ,mlێ0 1dEH E2mJ%!D.81fq?q}A$o@\6M1 kaEzd.s'#O\^,nJaZ]8"GL_|ސ|O۠K!AH.Re|YT`twMR1M) C]eWJi&J8RrXu]T& hC`iiICRhq9G#T*983??Be:€ ma~0q0 4M1M|~00 q8XM+·axxxh6ވeWWW$q]ba(X)%Ԟoo @BHDǹؕלu1>}笾 |>onu]Nx^][[rlֶfժRsX3ZfYP9`8KKKlP(p`Nq)eǜ |7Mm; J)k7uZbAt 2Mjub(T*aq\,Ѽйxp::*1t~&"ڥQJi=@/.yԉ a`ahx^dsrsq?O###=,Mt:e$?/2Psn۶nA'$~iA_Rz:uHP0 C뤔ahsce j;̯\=Zr9۶/LП1ObOEBuRJzi, \.y̗Ie}w]A q];-B!AٳgrFGG _u\.Ymk6sJl6k&c1&tDz0 R^O5:n?~g{^Hd0ϗJtr㙹7AiŋX(ca6R11Alvyyu >gff04M$BPT,*XضX,b5yq\MAJҖeYxTRamaʊ nu]=j ښ0Mnײ[Rm|hWRJǢmb(ᜣc,0JIJ0 8v]W"2Jo6E?r8K۶(*mE~0(b ! @ߣC 8/b/kh#hSJihûзq,U0.K!!$bpi".zDF q\sRj&TTk-AP(| / 0M3sHZ @'˜iqj9zmZ>AqawG)μRbO&Iʻw0]u]Lc#sss6b/LD!ʊmۜ),uRA3ǏkkkW7VCjXຮvfgr \0KP0q9c 1}8N)*\>lͶmIu]lS=f)*iV*bGm1<8e4kLD1|>.bZ W=68o1N}\Npr&3fMӬV s*]YYai+4M1v $I:65M!@MۑLk0 llllllӧO{;-ƕJBI)u0>>AWX,q~R7 F n'WLou= !A ]r\ Մ-BQ b!!$ B b!!$ B b!!$ Z>AD#AtZ~IBHD#A ' :IDAT%`hhC ns dzzzooogg!1;; T} p?zu>IENDB`pyzo-4.4.3/doc/yoton/index.rst0000666000000000000000000000135012660452173014440 0ustar 00000000000000.. _yoton: Yoton - Inter process communication =================================== .. insertdocs start:: yoton.__doc__ Yoton is a Python package that provides a simple interface to communicate between two or more processes. Yoton is independent of any other component of IEP and has *no dependencies* except Python itself. It runs on any Python version from 2.4. Yoton is ... * lightweight * written in pure Python * without dependencies (except Python) * available on Python version >= 2.4, including Python 3 * cross-platform * pretty fast .. insertdocs end:: ---- .. toctree:: :maxdepth: 1 overview examples context connection channels events clientserver internals experiments pyzo-4.4.3/doc/yoton/internals.rst0000666000000000000000000002121612660452173015333 0ustar 00000000000000Internals (if you want to know more) ==================================== In yoton, the :ref:`yoton.Context` is the object that represents the a node in the network. The context only handles packages. It gets packages from all its associated channels and from the other nodes in the network. It routes packages to the other nodes, and deposits packages in channel instances if the package slot matches the channel slot. The :ref:`yoton.Connection` represents a one-to-one connection between two contexts. It handles the low level messaging. It breaks packages into pieces and tries to send them as efficiently as possible. It also receives bytes from the other end, and reconstructs it into packages, which are then given to the context to handle (i.e. route). For the :ref:`yoton.TcpConnection` this is all done by dedicated io threads. Packages -------- Packages are simply a bunch of bytes (the encoded message), wrapped in a header. Packages are directed at a certain slot. They also have a source id, source sequence number, and optionally a destination id and destination sequence number (so that packages can be replies to other packages). When a package is received, it also gets assigned a receiving sequence number (in order to synchronize channels). Levels of communication ----------------------- Two :ref:`yoton.Connection` instances also communicate directly with each-other. They do this during the handshaking procedure, obviously, but also during operation they send each-other heart beat messages to detect time-outs. When the connection is closed in a nice way, thet also send a close message to the other end. A package addressed directly at the Connection has no body (consists only of a header). Two contexts can also communicate. They do this to notify each-other of new formed connections, closing of contexts, etc. A package directed at a context uses a special slot. Channel instances can also communicate. Well, that's what yoton is all about... A sending channels packs a message in a package and gives it to the contect. All other contexts will receive the package and deposit it in the channel's queue if the slots match. On recv'ing, the message is extracted/decoded from the package. Persistent connection --------------------- Two :ref:`yoton.TcpConnection` instances are connected using a single BSD-socket (TCP/IP). The socket operates in persistent mode; once the connection is established, the socket remains open until the connection is closed indefinetely. Would we adopt a req/rep approach (setting up the connection for each request), failure could mean either that the kernel is running extension code, or that the connection is broken. It's not possible to differentiate between the two. On initialization of the connection, TcpConnection's perform a small handshake procedue to establish that both are a :ref:`yoton.TcpConnection` objects, and to exchange the context id's. There is one thread dedicated to receive data from the socket, and subsequently have the context route the packages. Another dedicated thread gets data from a queue (of the Connection) and sends the packages over the sockets. The sockets and queues are blocking, but on a timeout (the receiving thread uses a select() call for this). This makes it easy to periodically send heartbeat packages if necessary, and their absence can be detected. In a previous design, there was a single io thread per context that did all the work. It would run through a generator function owned by the connections to send/receive data. This required all queueing and io to be non-blocking. After changing the design the code got *much* smaller, cleaner and easier to read, and is probably more robust. We could also get rid of several classes to buffer data, because with blocking threads the data can sinply be buffered at the queues and sockets. Message framing --------------- To differentiate between messages, there are two common approaches. One can add a small header to each message that indicates how long the message is. Or one can delimit the messages with a specific character. In earlier designs, yoton used the second approach and was limited to sending text which was encoded using utf-8. This meant the bytes 0xff and 0xfe could be used for delimiting. The first approach is more complex and requires more per-message processing. However, because the message size is know, messages can be received with much less copying of data. This significantly improved the performance for larger messages (with the delimiting approach we would get memory errors when Yoton tried to encode/decode the message to/from utf-8). The current design is such that as little data has to be copied (particularly for larger messages). Heart beat signals ------------------ If there is no data to send for a while, small heart beat messages are produced, so that connection problems can be easily detected. For TCP one needs to send data in order to detect connection problem (because no ACK's will be received). However, the TCP timeout is in the order of minutes and is different between OS's. Therefore we check when the last time was that data was received, enabling us to detect connection problems in the order of a few seconds. Note that when two Context's are connected using 'localhost', there is no way for the connection to be lost, as several network layers are bypassed. In such a situation, we can therefore be sure that the reason for the timeout lies not in the connection, but is caused for example by the process running extension code. When the process runs extension code ------------------------------------ With respect to client-kernel comminication: the kernel will not be able to send any data (neither heart beat signals) if its running extension code. In such a case, the client can still send messages; this data is transported by TCP and ends up in the network buffer until the kernel returns from extension code and starts receiving messages again. For this reason, in a client-kernel configuration, the kernel should always be connected to another process via 'localhost', and should use a proxi/broker to connect with clients on another box. In that case, the client can detect that the kernel is running extension code because the kernel stopped sending data (incl heartbeat messages). Congestion prevention --------------------- In any communication system, there is a risk of congestion: one end sends data faster than the other end can process it. This data can be buffered, but as the buffer fills, it consumes more memory. Yoton uses two approaches to solve this problem. The first (and most common) solution is that all queues have a maximum size. When this size is reached and a new messages is added, messages will be discarted. The user can choose whether the oldest or the newest message should be discarted. The second approach is only possible for the PUB/SUB channels. If the :ref:`yoton.SubChannel` is put in sync-mode (using the set_sync_mode method), the :ref:`yoton.SubChannel` will send a message to the corresponding PubChannels if its queue reaches a certain size. This size is relatively small (e.g. 10-100). When a :ref:`yoton.PubChannel` receives the message, its send method will block (for at most 1 second). The SubChannel sends a second message when the queue is below a certain level again. Note that it takes a while for these control messages to be received by the PubChannel. Therefore the actual queue size can easily grow larger than the threshold. In this situation, the first approach (discarting messages is still used as a failsave, but messages are very unlikely to be discarted since the threshold is much much smaller than the maximum queue size. An important aspect for the second approach is that the queue that buffers packages before they are send over the socket remains small. If this is not the case, the PubChannel is able to spam the queue with gigantic amounts of messages before the SubChannel even receives the first message. To keep this queue small, much like the queue of the SubChannel, it has a certain threshold. If this threshold is reached, subsequent pushes on the queue will block for maximally 1 second. The threshold is in the same order of magnitude as the queue for the SubChannel. References ---------- * http://www.unixguide.net/network/socketfaq/2.9.shtml * http://nitoprograms.blogspot.com/2009/04/message-framing.html * http://nitoprograms.blogspot.com/2009/05/detection-of-halfopen-dropped.html pyzo-4.4.3/doc/yoton/overview.rst0000666000000000000000000000457412660452173015212 0ustar 00000000000000Overview ======== How it works: * Multiple contexts can be connected over TCP/IP; the interconnected contexts together form a network. * Messages are send between channel objects (channels are attached to a context). * Channels are bound to a slot (a string name); a message send from a channel with slot X is received by all channels with slot X. * Yoton may be used procedurally, or in an event-driven fashion. Messaging patterns: * Yoton supports the pub/sub pattern, in an N to M configuration. * Yoton supports the req/rep pattern, allowing multiple requesters and repliers to exist in the same network. * Yoton supports exchanging state information. Some features: * Yoton is optimized to handle large messages by reducing data copying. * Yoton has a simple event system that makes asynchronous messaging and event-driven programming easy. * Yoton also has functionality for basic client-server (telnet-like) communication. A brief overview of the most common classes --------------------------------------------- * :ref:`yoton.Context` * Represents a node in the network. * Has a bind() and connect() method to connect to other nodes. * :ref:`yoton.Connection` * Represents a connection to another context. * Wraps a single BSD-socket, using a persistent connection. * Has signals that the user can connect to to be notified of timeouts and closing of the connection. * Channel classes (i.e. :ref:`yoton.BaseChannel` ) * Channels are associated with a context, and send/receive at a particular slot (a string name). * Messages send at a particular slot can only be received by channels associated with the same slot. Example -------- **One end** .. code-block:: python import yoton # Create one context and a pub channel ct1 = yoton.Context(verbose=verbosity) pub = yoton.PubChannel(ct1, 'chat') # Connect ct1.bind('publichost:test') # Send pub.send('hello world') **Other end** .. code-block:: python import yoton # Create another context and a sub channel ct2 = yoton.Context(verbose=verbosity) sub = yoton.SubChannel(ct2, 'chat') # Connect ct2.connect('publichost:test') # Receive print(sub.recv()) pyzo-4.4.3/Info.plist0000666000000000000000000000227713012623062012630 0ustar 00000000000000 CFBundleIconFile pyzo.icns CFBundleDevelopmentRegion English CFBundleExecutable pyzo CFBundleDocumentTypes CFBundleTypeIconFile py.icns CFBundleTypeName Python file CFBundleTypeRole Editor LSTypeIsPackage LSItemContentTypes public.python-script CFBundleTypeName Source file CFBundleTypeRole Editor LSTypeIsPackage LSItemContentTypes public.plain-text public.utf8-plain-text public.source-code public.script NSHighResolutionCapable True pyzo-4.4.3/MANIFEST.in0000666000000000000000000000014313131366225012413 0ustar 00000000000000include *.appdata.xml include *.md include *.plist include pyzolauncher.py recursive-include doc * pyzo-4.4.3/PKG-INFO0000666000000000000000000000461013166630337011763 0ustar 00000000000000Metadata-Version: 1.1 Name: pyzo Version: 4.4.3 Summary: the Python IDE for scientific computing Home-page: http://www.pyzo.org Author: Almar Klein Author-email: almar.klein@gmail.com License: (new) BSD Description: Pyzo is a cross-platform Python IDE focused on interactivity and introspection, which makes it very suitable for scientific computing. Its practical design is aimed at simplicity and efficiency. Pyzo is written in Python 3 and Qt. Binaries are available for Windows, Linux, and Mac. For questions, there is a discussion group. **Two components + tools** Pyzo consists of two main components, the editor and the shell, and uses a set of pluggable tools to help the programmer in various ways. Some example tools are source structure, project manager, interactive help, and workspace. **Some key features** * Powerful *introspection* (autocompletion, calltips, interactive help) * Allows various ways to *run code interactively* or to run a file as a script. * The shells runs in a *subprocess* and can therefore be interrupted or killed. * *Multiple shells* can be used at the same time, and can be of different Python versions (from v2.4 to 3.x, including pypy) * Support for using several *GUI toolkits* interactively: PySide, PyQt4, wx, fltk, GTK, Tk. * Run IPython shell or native shell. * *Full Unicode support* in both editor and shell. * Various handy *tools*, plus the ability to make your own. * Matlab-style *cell notation* to mark code sections (by starting a line with '##'). Keywords: Python interactive IDE Qt science computing Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Education Classifier: Intended Audience :: Developers Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: Python :: 3 Provides: pyzo pyzo-4.4.3/pyzo/0000777000000000000000000000000013166630335011664 5ustar 00000000000000pyzo-4.4.3/pyzo/codeeditor/0000777000000000000000000000000013166630335014005 5ustar 00000000000000pyzo-4.4.3/pyzo/codeeditor/base.py0000666000000000000000000007737513123037260015303 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ The base code editor class. """ """ WRITING EXTENSIONS FOR THE CODE EDITOR The Code Editor extension mechanism works solely based on inheritance. Extensions can override event handlers (e.g. paintEvent, keyPressEvent). Their default behaviour should be to call their super() event handler. This way, events propagate through the extensions following Python's method resolution order (http://www.python.org/download/releases/2.3/mro/). A 'fancy' code editor with extensions is created like: class FancyEditor( Extension1, Extension2, ... CodeEditorBase): pass The order of the extensions does usually matter! If multiple Extensions process the same key press, the first one has the first chance to consume it. OVERRIDING __init__ An extensions' __init__ method (if required) should look like this: class Extension: def __init__(self, *args, extensionParam1 = 1, extensionParam2 = 3, **kwds): super().__init__(*args, **kwds) some_extension_init_stuff() Note the following points: - All parameters have default values - The use of *args passes all non-named arguments to its super(), which will therefore end up at the QPlainTextEdit constructor. As a consequence, the parameters of the exentsion can only be specified as named arguments - The use of **kwds ensures that parametes that are not defined by this extension, are passed to the next extension(s) in line. - The call to super().__init__ is the first thing to do, this ensures that at least the CodeEditorBase and QPlainTextEdit, of which the CodeEditorBase is derived, are initialized when the initialization of the extension is done OVERRIDING keyPressEvent When overriding keyPressEvent, the extension has several options when an event arrives: - Ignore the event In this case, call super().keyPressEvent(event) for other extensions or the CodeEditorBase to process the event - Consume the event In order to prevent other next extensions or the CodeEditorBase to react on the event, return without calling the super().keyPressEvent - Do something based on the event, and do not let the event propagate In this case, do whatever action is defined by the extension, and do not call the super().keyPressEvent - Do something based on the event, and let the event propagate In this case, do whatever action is defined by the extension, and do call the super().keyEvent In any case, the keyPressEvent should not return a value (i.e., return None). Furthermore, an extension may also want to perform some action *after* the event has been processed by the next extensions and the CodeEditorBase. In this case, perform that action after calling super().keyPressEvent OVERRIDING paintEvent Then overriding the paintEvent, the extension may want to paint either behind or in front of the CodeEditorBase text. In order to paint behind the text, first perform the painting, and then call super().paintEvent. In order to paint in front of the text, first call super().paintEvent, then perform the painting. As a result, the total paint order is as follows for the example of the FancyEditor defined above: - First the extensions that draw behind the text (i.e. paint before calling super().paintEvent, in the order Extension1, Extension2, ... - then the CodeEditorBase, with the text - then the extensions that draw in front of the text (i.e. call super().paintEvent before painting), in the order ..., Extension2, Extension1 OVERRIDING OTHER EVENT HANDLERS When overriding other event handlers, be sure to call the super()'s event handler; either before or after your own actions, as appropriate OTHER ISSUES In order to avoid namespace clashes among the extensions, take the following into account: - Private members should start with __ to make ensure no clashes will occur - Public members / methods should have names that clearly indicate which extension they belong to (e.g. not cancel but autocompleteCancel) - Arguments of the __init__ method should also have clearly destictive names """ from .qt import QtGui,QtCore, QtWidgets Qt = QtCore.Qt from .misc import DEFAULT_OPTION_NAME, DEFAULT_OPTION_NONE, ce_option from .misc import callLater, ustr from .manager import Manager from .highlighter import Highlighter from .style import StyleElementDescription class CodeEditorBase(QtWidgets.QPlainTextEdit): """ The base code editor class. Implements some basic features required by the extensions. """ # Style element for default text and editor background _styleElements = [('Editor.text', 'The style of the default text. ' + 'One can set the background color here.', 'fore:#000,back:#fff',)] # Signal emitted after style has changed styleChanged = QtCore.Signal() # Signal emitted after font (or font size) has changed fontChanged = QtCore.Signal() # Signal to indicate a change in breakpoints. Only emitted if the # appropriate extension is in use breakPointsChanged = QtCore.Signal(object) def __init__(self,*args, **kwds): super(CodeEditorBase, self).__init__(*args) # Set font (always monospace) self.__zoom = 0 self.setFont() # Create highlighter class self.__highlighter = Highlighter(self, self.document()) # Set some document options option = self.document().defaultTextOption() option.setFlags( option.flags() | option.IncludeTrailingSpaces | option.AddSpaceForLineAndParagraphSeparators ) self.document().setDefaultTextOption(option) # When the cursor position changes, invoke an update, so that # the hihghlighting etc will work self.cursorPositionChanged.connect(self.viewport().update) # Init styles to default values self.__style = {} for element in self.getStyleElementDescriptions(): self.__style[element.key] = element.defaultFormat # Connext style update self.styleChanged.connect(self.__afterSetStyle) self.__styleChangedPending = False # Init margins self._leftmargins = [] # Init options now. # NOTE TO PEOPLE DEVELOPING EXTENSIONS: # If an extension has an __init__ in which it first calls the # super().__init__, this __initOptions() function will be called, # while the extension's init is not yet finished. self.__initOptions(kwds) # Define colors from Solarized theme # NOTE TO PEOPLE WANTING CUSTOM COLORS: ignore this and check the # commented lines near the bottom of this method. base03 = "#002b36" base02 = "#073642" base01 = "#586e75" base00 = "#657b83" base0 = "#839496" base1 = "#93a1a1" base2 = "#eee8d5" base3 = "#fdf6e3" yellow = "#b58900" orange = "#cb4b16" red = "#dc322f" # noqa magenta = "#d33682" violet = "#6c71c4" blue = "#268bd2" cyan = "#2aa198" green = "#859900" # noqa if True: # Light vs dark #back1, back2, back3 = base3, base2, base1 # real solarised back1, back2, back3 = "#fff", base2, base1 # crispier fore1, fore2, fore3, fore4 = base00, base01, base02, base03 else: back1, back2, back3 = base03, base02, base01 fore1, fore2, fore3, fore4 = base0, base1, base2, base3 # noqa # todo: proper testing of syntax style # Define style using "Solarized" colors S = {} S["Editor.text"] = "back:%s, fore:%s" % (back1, fore1) S['Syntax.identifier'] = "fore:%s, bold:no, italic:no, underline:no" % fore1 S["Syntax.nonidentifier"] = "fore:%s, bold:no, italic:no, underline:no" % fore2 S["Syntax.keyword"] = "fore:%s, bold:yes, italic:no, underline:no" % fore2 # S["Syntax.functionname"] = "fore:%s, bold:yes, italic:no, underline:no" % fore3 S["Syntax.classname"] = "fore:%s, bold:yes, italic:no, underline:no" % orange # S["Syntax.string"] = "fore:%s, bold:no, italic:no, underline:no" % violet S["Syntax.unterminatedstring"] = "fore:%s, bold:no, italic:no, underline:dotted" % violet S["Syntax.python.multilinestring"] = "fore:%s, bold:no, italic:no, underline:no" % blue # S["Syntax.number"] = "fore:%s, bold:no, italic:no, underline:no" % cyan S["Syntax.comment"] ="fore:%s, bold:no, italic:no, underline:no" % yellow S["Syntax.todocomment"] = "fore:%s, bold:no, italic:yes, underline:no" % magenta S["Syntax.python.cellcomment"] = "fore:%s, bold:yes, italic:no, underline:full" % yellow # S["Editor.Long line indicator"] = "linestyle:solid, fore:%s" % back2 S["Editor.Highlight current line"] = "back:%s" % back2 S["Editor.Indentation guides"] = "linestyle:solid, fore:%s" % back2 S["Editor.Line numbers"] = "back:%s, fore:%s" % (back2, back3) # Define style using html color names. All 140 legal HTML colour # names can be used (in addition to HEX codes). A full list of # recognized colour names is available e.g. here # http://www.html-color-names.com/color-chart.php # S = {} # S["Editor.text"] = "back: white, fore: black" # S['Syntax.identifier'] = "fore: black, bold:no, italic:no, underline:no" # S["Syntax.nonidentifier"] = "fore: blue, bold:no, italic:no, underline:no" # S["Syntax.keyword"] = "fore: blue, bold:yes, italic:no, underline:no" # S["Syntax.functionname"] = "fore: black, bold:yes, italic:no, underline:no" # S["Syntax.classname"] = "fore: magenta, bold:yes, italic:no, underline:no" # S["Syntax.string"] = "fore: red, bold:no, italic:no, underline:no" # S["Syntax.unterminatedstring"] = "fore: red, bold:no, italic:no, underline:dotted" # S["Syntax.python.multilinestring"] = "fore: red, bold:no, italic:no, underline:no" # S["Syntax.number"] = "fore: dark orange, bold:no, italic:no, underline:no" # S["Syntax.comment"] ="fore: green, bold:no, italic:yes, underline:no" # S["Syntax.todocomment"] = "fore: magenta, bold:no, italic:yes, underline:no" # S["Syntax.python.cellcomment"] = "fore: green, bold:yes, italic:no, underline:full" # S["Editor.Long line indicator"] = "linestyle:solid, fore: dark grey" # S["Editor.Highlight current line"] = "back: light grey" # S["Editor.Indentation guides"] = "linestyle:solid, fore: light grey" # S["Editor.Line numbers"] = "back: light grey, fore: black" # Apply style self.setStyle(S) def _setHighlighter(self, highlighterClass): self.__highlighter = highlighterClass(self, self.document()) ## Options def __getOptionSetters(self): """ Get a dict that maps (lowercase) option names to the setter methods. """ # Get all names that can be options allNames = set(dir(self)) nativeNames = set(dir(QtWidgets.QPlainTextEdit)) names = allNames.difference(nativeNames) # Init dict of setter members setters = {} for name in names: # Get name without set if name.lower().startswith('set'): name = name[3:] # Get setter and getter name name_set = 'set' + name[0].upper() + name[1:] name_get = name[0].lower() + name[1:] # Check if both present if not (name_set in names and name_get in names): continue # Get members member_set = getattr(self, name_set) member_get = getattr(self, name_get) # Check if option decorator was used and get default value for member in [member_set, member_get]: if hasattr(member, DEFAULT_OPTION_NAME): defaultValue = member.__dict__[DEFAULT_OPTION_NAME] break else: continue # Set default on both member_set.__dict__[DEFAULT_OPTION_NAME] = defaultValue member_get.__dict__[DEFAULT_OPTION_NAME] = defaultValue # Add to list setters[name.lower()] = member_set # Done return setters def __setOptions(self, setters, options): """ Sets the options, given the list-of-tuples methods and an options dict. """ # List of invalid keys invalidKeys = [] # Set options for key1 in options: key2 = key1.lower() # Allow using the setter name if key2.startswith('set'): key2 = key2[3:] # Check if exists. If so, call! if key2 in setters: fun = setters[key2] val = options[key1] fun(val) else: invalidKeys.append(key1) # Check if invalid keys were given if invalidKeys: print("Warning, invalid options given: " + ', '.join(invalidKeys)) def __initOptions(self, options=None): """ Init the options with their default values. Also applies the docstrings of one to the other. """ # Make options an empty dict if not given if not options: options = {} # Get setters setters = self.__getOptionSetters() # Set default value for member_set in setters.values(): defaultVal = member_set.__dict__[DEFAULT_OPTION_NAME] if defaultVal != DEFAULT_OPTION_NONE: try: member_set(defaultVal) except Exception as why: print('Error initing option ', member_set.__name__) # Also set using given opions? if options: self.__setOptions(setters, options) def setOptions(self, options=None, **kwargs): """ setOptions(options=None, **kwargs) Set the code editor options (e.g. highlightCurrentLine) using a dict-like object, or using keyword arguments (options given in the latter overrule opions in the first). The keys in the dict are case insensitive and one can use the option's setter or getter name. """ # Process options if options: D = {} for key in options: D[key] = options[key] D.update(kwargs) else: D = kwargs # Get setters setters = self.__getOptionSetters() # Go self.__setOptions(setters, D) ## Font def setFont(self, font=None): """ setFont(font=None) Set the font for the editor. Should be a monospace font. If not, Qt will select the best matching monospace font. """ defaultFont = Manager.defaultFont() # Get font object if font is None: font = defaultFont elif isinstance(font, QtGui.QFont): pass elif isinstance(font, str): font = QtGui.QFont(font) else: raise ValueError("setFont accepts None, QFont or string.") # Hint Qt that it should be monospace font.setStyleHint(font.TypeWriter, font.PreferDefault) # Get family, fall back to default if qt could not produce monospace fontInfo = QtGui.QFontInfo(font) if fontInfo.fixedPitch(): family = fontInfo.family() else: family = defaultFont.family() # Get size: default size + zoom size = defaultFont.pointSize() + self.__zoom # Create font instance font = QtGui.QFont(family, size) # Set, emit and return QtWidgets.QPlainTextEdit.setFont(self, font) self.fontChanged.emit() return font def setZoom(self, zoom): """ setZoom(zoom) Set the zooming of the document. The font size is always the default font size + the zoom factor. The final zoom is returned, this may not be the same as the given zoom factor if the given factor is too small. """ # Set zoom (limit such that final pointSize >= 1) size = Manager.defaultFont().pointSize() self.__zoom = int(max(1-size,zoom)) # Set font self.setFont(self.fontInfo().family()) # Return zoom return self.__zoom ## Syntax / styling @classmethod def getStyleElementDescriptions(cls): """ getStyleElementDescriptions() This classmethod returns a list of the StyleElementDescription instances used by this class. This includes the descriptions for the syntax highlighting of all parsers. """ # Collect members by walking the class bases elements = [] def collectElements(cls, iter=1): # Valid class? if cls is object or cls is QtWidgets.QPlainTextEdit: return # Check members if hasattr(cls, '_styleElements'): for element in cls._styleElements: elements.append(element) # Recurse for c in cls.__bases__: collectElements(c, iter+1) collectElements(cls) # Make style element descriptions # (Use a dict to ensure there are no duplicate keys) elements2 = {} for element in elements: # Check if isinstance(element, StyleElementDescription): pass elif isinstance(element, tuple): element = StyleElementDescription(*element) else: print('Warning: invalid element: ' + repr(element)) # Store using the name as a key to prevent duplicates elements2[element.key] = element # Done return list(elements2.values()) def getStyleElementFormat(self, name): """ getStyleElementFormat(name) Get the style format for the style element corresponding with the given name. The name is case insensitive and invariant to the use of spaces. """ key = name.replace(' ','').lower() try: return self.__style[key] except KeyError: raise KeyError('Not a known style element name: "%s".' % name) def setStyle(self, style=None, **kwargs): """ setStyle(style=None, **kwargs) Updates the formatting per style element. The style consists of a dictionary that maps style names to style formats. The style names are case insensitive and invariant to the use of spaces. For convenience, keyword arguments may also be used. In this case, underscores are interpreted as dots. This function can also be called without arguments to force the editor to restyle (and rehighlight) itself. Use getStyleElementDescriptions() to get information about the available styles and their default values. Examples -------- # To make the classname in underline, but keep the color and boldness: setStyle(syntax_classname='underline') # To set all values for function names: setStyle(syntax_functionname='#883,bold:no,italic:no') # To set line number and indent guides colors setStyle({ 'editor.LineNumbers':'fore:#000,back:#777', 'editor.indentationGuides':'#f88' }) """ # Combine user input D = {} if style: for key in style: D[key] = style[key] if True: for key in kwargs: key2 = key.replace('_', '.') D[key2] = kwargs[key] # List of given invalid style element names invalidKeys = [] # Set style elements for key in D: normKey = key.replace(' ', '').lower() if normKey in self.__style: #self.__style[normKey] = StyleFormat(D[key]) self.__style[normKey].update(D[key]) else: invalidKeys.append(key) # Give warning for invalid keys if invalidKeys: print("Warning, invalid style names given: " + ','.join(invalidKeys)) # Notify that style changed, adopt a lazy approach to make loading # quicker. if self.isVisible(): callLater(self.styleChanged.emit) self.__styleChangedPending = False else: self.__styleChangedPending = True def showEvent(self, event): super(CodeEditorBase, self).showEvent(event) # Does the style need updating? if self.__styleChangedPending: callLater(self.styleChanged.emit) self.__styleChangedPending = False def __afterSetStyle(self): """ _afterSetStyle() Method to call after the style has been set. """ # Set text style using editor style sheet format = self.getStyleElementFormat('editor.text') ss = 'QPlainTextEdit{ color:%s; background-color:%s; }' % ( format['fore'], format['back']) self.setStyleSheet(ss) # Make sure the style is applied self.viewport().update() # Re-highlight callLater(self.__highlighter.rehighlight) ## Some basic options @ce_option(4) def indentWidth(self): """ Get the width of a tab character, and also the amount of spaces to use for indentation when indentUsingSpaces() is True. """ return self.__indentWidth def setIndentWidth(self, value): value = int(value) if value<=0: raise ValueError("indentWidth must be >0") self.__indentWidth = value self.setTabStopWidth(self.fontMetrics().width('i'*self.__indentWidth)) @ce_option(False) def indentUsingSpaces(self): """Get whether to use spaces (if True) or tabs (if False) to indent when the tab key is pressed """ return self.__indentUsingSpaces def setIndentUsingSpaces(self, value): self.__indentUsingSpaces = bool(value) self.__highlighter.rehighlight() ## Misc def gotoLine(self, lineNumber): """ gotoLine(lineNumber) Move the cursor to the block given by the line number (first line is number 1) and show that line. """ return self.gotoBlock(lineNumber-1) def gotoBlock(self, blockNumber): """ gotoBlock(blockNumber) Move the cursor to the block given by the block number (first block is number 0) and show that line. """ # Two implementatios. I know that the latter works, so lets # just use that. cursor = self.textCursor() #block = self.document().findBlockByNumber( blockNumber ) #cursor.setPosition(block.position()) cursor.movePosition(cursor.Start) # move to begin of the document cursor.movePosition(cursor.NextBlock,n=blockNumber) # n blocks down try: self.setTextCursor(cursor) except Exception: pass # File is smaller then the caller thought # TODO make this user configurable (setting relativeMargin to anything above # 0.5 will cause cursor to center on each move) relativeMargin = 0.2 # 20% margin on both sides of the window margin = self.height() * relativeMargin cursorRect = self.cursorRect(cursor) if cursorRect.top() < margin or cursorRect.bottom() + margin > self.height(): self.centerCursor() def doForSelectedBlocks(self, function): """ doForSelectedBlocks(function) Call the given function(cursor) for all blocks in the current selection A block is considered to be in the current selection if a part of it is in the current selection The supplied cursor will be located at the beginning of each block. This cursor may be modified by the function as required """ #Note: a 'TextCursor' does not represent the actual on-screen cursor, so #movements do not move the on-screen cursor #Note 2: when the text is changed, the cursor and selection start/end #positions of all cursors are updated accordingly, so the screenCursor #stays in place even if characters are inserted at the editCursor screenCursor = self.textCursor() #For maintaining which region is selected editCursor = self.textCursor() #For inserting the comment marks #Use beginEditBlock / endEditBlock to make this one undo/redo operation editCursor.beginEditBlock() try: editCursor.setPosition(screenCursor.selectionStart()) editCursor.movePosition(editCursor.StartOfBlock) # < :if selection end is at beginning of the block, don't include that #one, except when the selectionStart is same as selectionEnd while editCursor.position() self.height(): break #Reached end of the repaint area if not cursor.block().next().isValid(): break #Reached end of the text cursor.movePosition(cursor.NextBlock) def indentBlock(self, cursor, amount=1): """ indentBlock(cursor, amount=1) Indent the block given by cursor. The cursor specified is used to do the indentation; it is positioned at the beginning of the first non-whitespace position after completion May be overridden to customize indentation. """ text = ustr(cursor.block().text()) leadingWhitespace = text[:len(text)-len(text.lstrip())] #Select the leading whitespace cursor.movePosition(cursor.StartOfBlock) cursor.movePosition(cursor.Right,cursor.KeepAnchor,len(leadingWhitespace)) #Compute the new indentation length, expanding any existing tabs indent = len(leadingWhitespace.expandtabs(self.indentWidth())) if self.indentUsingSpaces(): # Determine correction, so we can round to multiples of indentation correction = indent % self.indentWidth() if correction and amount<0: correction = - (self.indentWidth() - correction) # Flip # Add the indentation tabs indent += (self.indentWidth() * amount) - correction cursor.insertText(' '*max(indent,0)) else: # Convert indentation to number of tabs, and add one indent = (indent // self.indentWidth()) + amount cursor.insertText('\t' * max(indent,0)) def dedentBlock(self, cursor): """ dedentBlock(cursor) Dedent the block given by cursor. Calls indentBlock with amount = -1. May be overridden to customize indentation. """ self.indentBlock(cursor, amount = -1) def indentSelection(self): """ indentSelection() Called when the current line/selection is to be indented. Calls indentLine(cursor) for each line in the selection. May be overridden to customize indentation. See also doForSelectedBlocks and indentBlock. """ self.doForSelectedBlocks(self.indentBlock) def dedentSelection(self): """ dedentSelection() Called when the current line/selection is to be dedented. Calls dedentLine(cursor) for each line in the selection. May be overridden to customize indentation. See also doForSelectedBlocks and dedentBlock. """ self.doForSelectedBlocks(self.dedentBlock) def justifyText(self, linewidth=70): """ justifyText(linewidth=70) """ from .textutils import TextReshaper # Get cursor cursor = self.textCursor() # Make selection include whole lines pos1, pos2 = cursor.position(), cursor.anchor() pos1, pos2 = min(pos1, pos2), max(pos1, pos2) cursor.setPosition(pos1, cursor.MoveAnchor) cursor.movePosition(cursor.StartOfBlock, cursor.MoveAnchor) cursor.setPosition(pos2, cursor.KeepAnchor) cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) # Use reshaper to create replacement text reshaper = TextReshaper(linewidth) reshaper.pushText(cursor.selectedText()) newText = reshaper.popText() # Update the selection #self.setTextCursor(cursor) for testing cursor.insertText(newText) def addLeftMargin(self, des, func): """ Add a margin to the left. Specify a description for the margin, and a function to get that margin. For internal use. """ assert des is not None self._leftmargins.append((des, func)) def getLeftMargin(self, des=None): """ Get the left margin, relative to the given description (which should be the same as given to addLeftMargin). If des is omitted or None, the full left margin is returned. """ margin = 0 for d, func in self._leftmargins: if d == des: break margin += func() return margin def updateMargins(self): """ Force the margins to be recalculated and set the viewport accordingly. """ leftmargin = self.getLeftMargin() self.setViewportMargins(leftmargin , 0, 0, 0) pyzo-4.4.3/pyzo/codeeditor/extensions/0000777000000000000000000000000013166630335016204 5ustar 00000000000000pyzo-4.4.3/pyzo/codeeditor/extensions/appearance.py0000666000000000000000000013517413123037260020657 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Code editor extensions that change its appearance """ from ..qt import QtGui,QtCore, QtWidgets Qt = QtCore.Qt from ..misc import ce_option from ..manager import Manager # todo: what about calling all extensions. CE_HighlightCurrentLine, # or EXT_HighlightcurrentLine? from ..parsers.tokens import ParenthesisToken import enum class HighlightMatchingOccurrences(object): # Register style element _styleElements = [ ( 'Editor.Highlight matching occurrences', 'The background color to highlight matching occurrences of the currently selected word.', 'back:#fdfda3', ) ] def highlightMatchingOccurrences(self): """ highlightMatchingOccurrences() Get whether to highlight matching occurrences. """ return self.__highlightMatchingOccurrences @ce_option(True) def setHighlightMatchingOccurrences(self,value): """ setHighlightMatchingOccurrences(value) Set whether to highlight matching occurrences. """ self.__highlightMatchingOccurrences = bool(value) self.viewport().update() def _doHighlight(self, text): # make cursor at the beginning of the first visible block cursor = self.cursorForPosition(QtCore.QPoint(0,0)) doc = self.document() color = self.getStyleElementFormat('editor.highlightMatchingOccurrences').back painter = QtGui.QPainter() painter.begin(self.viewport()) painter.setBrush(color) painter.setPen(color.darker(110)) # find occurrences for i in range(500): cursor = doc.find(text, cursor, doc.FindCaseSensitively | doc.FindWholeWords) if cursor is None or cursor.isNull(): # no more matches break # don't highlight the actual selection if cursor == self.textCursor(): continue endRect = self.cursorRect(cursor) if endRect.bottom() > self.height(): # rest of document is not visible, don't bother highlighting break cursor.setPosition(min(cursor.position(), cursor.anchor())) startRect = self.cursorRect(cursor) width = endRect.left() - startRect.left() painter.drawRect(startRect.left(), startRect.top(), width, startRect.height()) # move to end of word again, otherwise we never advance in the doc cursor.movePosition(cursor.EndOfWord) else: print('Matching selection highlighting did not break') painter.end() def paintEvent(self, event): """ paintEvent(event) If there is a current selection, and the selected text is a valid Python identifier (no whitespace, starts with a letter), then highlight all the matching occurrences of the selected text in the current view. Paints behinds its super(). """ cursor = self.textCursor() if self.__highlightMatchingOccurrences and cursor.hasSelection(): text = cursor.selectedText() if text.isidentifier(): self._doHighlight(text) super(HighlightMatchingOccurrences, self).paintEvent(event) class _ParenNotFound(Exception) : pass class _ParenIterator : """ Iterates in given direction over parentheses in the document. Uses the stored token-list of the blocks. Iteration gives both a parenthesis and its global position.""" def __init__(self, cursor, direction) : self.cur_block = cursor.block() self.cur_tokens = self._getParenTokens() self.direction = direction # We need to know where we start in the current token list k = 0 try : while self.cur_tokens[k].end != cursor.positionInBlock() : k += 1 self.cur_pos = k except IndexError : # If the parenthesis cannot be found, it means that it is not inluded # in any token, ie. it is part of a string or comment raise _ParenNotFound def _getParenTokens(self) : try: return list(filter(lambda x : isinstance(x, ParenthesisToken), self.cur_block.userData().tokens)) except AttributeError: return [] # can be a piece of text that we do not tokenize (e.g. in shell) def __iter__(self) : return self def __next__(self) : self.cur_pos += self.direction while self.cur_pos >= len(self.cur_tokens) or self.cur_pos < 0 : if self.direction == 1 : self.cur_block = self.cur_block.next() else : self.cur_block = self.cur_block.previous() if not self.cur_block.isValid() : raise StopIteration self.cur_tokens = self._getParenTokens() if self.direction == 1 : self.cur_pos = 0 else : self.cur_pos = len(self.cur_tokens) - 1 return self.cur_tokens[self.cur_pos]._style, self.cur_block.position()+self.cur_tokens[self.cur_pos].end class _PlainTextParenIterator : """ Iterates in given direction over parentheses in the document. To be used when there is no parser. Iteration gives both a parenthesis and its global position.""" def __init__(self, cursor, direction) : self.fulltext = cursor.document().toPlainText() self.position = cursor.position() - 1 self.direction = direction def __iter__(self) : return self def __next__(self) : self.position += self.direction try : while self.fulltext[self.position] not in '([{)]}' : self.position += self.direction if self.position < 0 : raise StopIteration except IndexError : raise StopIteration return self.fulltext[self.position], self.position + 1 class _MatchStatus(enum.Enum) : NoMatch = 0 Match = 1 MisMatch = 2 class _MatchResult : def __init__(self, status, corresponding = None, offending = None) : self.status = status self.corresponding = corresponding self.offending = offending class HighlightMatchingBracket(object): # Register style element _styleElements = [ ( 'Editor.Highlight matching bracket', 'The background color to highlight matching brackets.', 'back:#ccc', ), ( 'Editor.Highlight unmatched bracket', 'The background color to highlight unmatched brackets.', 'back:#F7BE81', ), ( 'Editor.Highlight mismatching bracket', 'The background color to highlight mismatching brackets.', 'back:#F7819F', ) ] def highlightMatchingBracket(self): """ highlightMatchingBracket() Get whether to highlight matching brackets. """ return self.__highlightMatchingBracket @ce_option(True) def setHighlightMatchingBracket(self,value): """ setHighlightMatchingBracket(value) Set whether to highlight matching brackets. """ self.__highlightMatchingBracket = bool(value) self.viewport().update() def highlightMisMatchingBracket(self): """ highlightMisMatchingBracket() Get whether to highlight mismatching brackets. """ return self.__highlightMisMatchingBracket @ce_option(True) def setHighlightMisMatchingBracket(self,value): """ setHighlightMisMatchingBracket(value) Set whether to highlight mismatching brackets. """ self.__highlightMisMatchingBracket = bool(value) self.viewport().update() def _highlightSingleChar(self, painter, cursor, width, colorname): """ _highlightSingleChar(painter, cursor, width, colorname) Draws a highlighting rectangle around the single character to the left of the specified cursor. """ cursor_rect = self.cursorRect(cursor) top = cursor_rect.top() left = cursor_rect.left() - width height = cursor_rect.bottom() - top + 1 color = self.getStyleElementFormat(colorname).back painter.setBrush(color) painter.setPen(color.darker(110)) painter.drawRect(QtCore.QRect(left, top, width, height)) _matchingBrackets = {'(':')', '[':']', '{':'}', ')':'(', ']':'[', '}':'{'} def _findMatchingBracket(self, char, cursor): """ _findMatchingBracket(char, cursor) Find a bracket that matches the specified char in the specified document. Return a _MatchResult object indicating whether this succeded and the positions of the parentheses causing this result. """ if char in ')]}': direction = -1 stacking = ')]}' unstacking = '([{' elif char in '([{': direction = 1 stacking = '([{' unstacking = ')]}' else: raise ValueError('invalid bracket character: ' + char) stacked_paren = [(char, cursor.position())] # using a Python list as a stack # stack not empty because the _ParenIterator will not give back # the parenthesis we're matching our_iterator = _ParenIterator if self.parser() is not None and self.parser().name() != "" else _PlainTextParenIterator for (paren, pos) in our_iterator(cursor, direction) : if paren in stacking : stacked_paren.append((paren, pos)) elif paren in unstacking : if self._matchingBrackets[stacked_paren[-1][0]] != paren : return _MatchResult(_MatchStatus.MisMatch, pos, stacked_paren[-1][1]) else : stacked_paren.pop() if len(stacked_paren) == 0 : # we've found our match return _MatchResult(_MatchStatus.Match, pos) return _MatchResult(_MatchStatus.NoMatch) def _cursorAt(self, doc, pos) : new_cursor = QtGui.QTextCursor(doc) new_cursor.setPosition(pos) return new_cursor def paintEvent(self, event): """ paintEvent(event) If the current cursor is positioned to the right of a bracket ()[]{}, look for a matching one, and, if found, draw a highlighting rectangle around both brackets of the pair. Paints behinds its super(). """ if not self.__highlightMatchingBracket: super(HighlightMatchingBracket, self).paintEvent(event) return cursor = QtGui.QTextCursor(self.textCursor()) if cursor.atBlockStart() : cursor.movePosition(cursor.Right) movedRight = True else : movedRight = False text = cursor.block().text() pos = cursor.positionInBlock() - 1 if len(text) > pos and len(text) > 0: # get the character to the left of the cursor char = text[pos] if not movedRight and char not in '()[]{}' and len(text) > pos+1 : # no brace to the left of cursor; try to the right cursor.movePosition(cursor.Right) char = text[pos+1] if char in '()[]{}': doc = cursor.document() try : match_res = self._findMatchingBracket(char, cursor) fm = QtGui.QFontMetrics(doc.defaultFont()) width = fm.width(char) # assumes that both paren have the same width painter = QtGui.QPainter() painter.begin(self.viewport()) if match_res.status == _MatchStatus.NoMatch : self._highlightSingleChar(painter, cursor, width, 'editor.highlightUnmatchedBracket') elif match_res.status == _MatchStatus.Match : self._highlightSingleChar(painter, cursor, width, 'editor.highlightMatchingBracket') self._highlightSingleChar(painter, self._cursorAt(doc, match_res.corresponding), width, 'editor.highlightMatchingBracket') else : # this is a mismatch if cursor.position() != match_res.offending or not self.highlightMisMatchingBracket() : self._highlightSingleChar(painter, cursor, width, 'editor.highlightUnmatchedBracket') if self.highlightMisMatchingBracket() : self._highlightSingleChar(painter, self._cursorAt(doc, match_res.corresponding), width, 'editor.highlightMisMatchingBracket') self._highlightSingleChar(painter, self._cursorAt(doc, match_res.offending), width, 'editor.highlightMisMatchingBracket') painter.end() except _ParenNotFound: # is raised when current parenthesis is not # found in its line token list, meaning it is in a string literal pass super(HighlightMatchingBracket, self).paintEvent(event) class HighlightCurrentLine(object): """ Highlight the current line """ # Register style element _styleElements = [ ( 'Editor.Highlight current line', 'The background color of the current line highlight.', 'back:#ffff99', ) ] def highlightCurrentLine(self): """ highlightCurrentLine() Get whether to highlight the current line. """ return self.__highlightCurrentLine @ce_option(True) def setHighlightCurrentLine(self,value): """ setHighlightCurrentLine(value) Set whether to highlight the current line. """ self.__highlightCurrentLine = bool(value) self.viewport().update() def paintEvent(self,event): """ paintEvent(event) Paints a rectangle spanning the current block (in case of line wrapping, this means multiple lines) Paints behind its super() """ if not self.highlightCurrentLine(): super(HighlightCurrentLine, self).paintEvent(event) return # Get color color = self.getStyleElementFormat('editor.highlightCurrentLine').back #Find the top of the current block, and the height cursor = self.textCursor() cursor.movePosition(cursor.StartOfBlock) top = self.cursorRect(cursor).top() cursor.movePosition(cursor.EndOfBlock) height = self.cursorRect(cursor).bottom() - top + 1 margin = self.document().documentMargin() painter = QtGui.QPainter() painter.begin(self.viewport()) painter.fillRect(QtCore.QRect(margin, top, self.viewport().width() - 2*margin, height), color) painter.end() super(HighlightCurrentLine, self).paintEvent(event) # for debugging paint events #if 'log' not in self.__class__.__name__.lower(): # print(height, event.rect().width()) class IndentationGuides(object): # Register style element _styleElements = [ ( 'Editor.Indentation guides', 'The color and style of the indentation guides.', 'fore:#DDF,linestyle:solid', ) ] def showIndentationGuides(self): """ showIndentationGuides() Get whether to show indentation guides. """ return self.__showIndentationGuides @ce_option(True) def setShowIndentationGuides(self, value): """ setShowIndentationGuides(value) Set whether to show indentation guides. """ self.__showIndentationGuides = bool(value) self.viewport().update() def paintEvent(self,event): """ paintEvent(event) Paint the indentation guides, using the indentation info calculated by the highlighter. """ super(IndentationGuides, self).paintEvent(event) if not self.showIndentationGuides(): return # Get doc and viewport doc = self.document() viewport = self.viewport() # Get multiplication factor and indent width indentWidth = self.indentWidth() if self.indentUsingSpaces(): factor = 1 else: factor = indentWidth # Init painter painter = QtGui.QPainter() painter.begin(viewport) # Prepare pen format = self.getStyleElementFormat('editor.IndentationGuides') pen = QtGui.QPen(format.fore) pen.setStyle(format.linestyle) painter.setPen(pen) offset = doc.documentMargin() + self.contentOffset().x() def paintIndentationGuides(cursor): y3=self.cursorRect(cursor).top() y4=self.cursorRect(cursor).bottom() bd = cursor.block().userData() if bd and hasattr(bd, 'indentation') and bd.indentation: for x in range(indentWidth, bd.indentation * factor, indentWidth): w = self.fontMetrics().width('i'*x) + offset w += 1 # Put it more under the block if w > 0: # if scrolled horizontally it can become < 0 painter.drawLine(QtCore.QLine(w, y3, w, y4)) self.doForVisibleBlocks(paintIndentationGuides) # Done painter.end() class FullUnderlines(object): def paintEvent(self,event): """ paintEvent(event) Paint a horizontal line for the blocks for which there is a syntax format that has underline:full. Whether this is the case is stored at the blocks user data. """ super(FullUnderlines, self).paintEvent(event) painter = QtGui.QPainter() painter.begin(self.viewport()) margin = self.document().documentMargin() w = self.viewport().width() def paintUnderline(cursor): y = self.cursorRect(cursor).bottom() bd = cursor.block().userData() try: fullUnderlineFormat = bd.fullUnderlineFormat except AttributeError: pass # fullUnderlineFormat may not be an attribute else: if fullUnderlineFormat is not None: # Apply pen pen = QtGui.QPen(fullUnderlineFormat.fore) pen.setStyle(fullUnderlineFormat.linestyle) painter.setPen(pen) # Paint painter.drawLine(QtCore.QLine(margin, y, w - 2*margin, y)) self.doForVisibleBlocks(paintUnderline) painter.end() class CodeFolding(object): def paintEvent(self,event): """ paintEvent(event) """ super(CodeFolding, self).paintEvent(event) return # Code folding code is not yet complete painter = QtGui.QPainter() painter.begin(self.viewport()) margin = self.document().documentMargin() def paintCodeFolders(cursor): y = self.cursorRect(cursor).top() h = self.cursorRect(cursor).height() rect = QtCore.QRect(margin, y, h, h) text = cursor.block().text() if text.rstrip().endswith(':'): painter.drawRect(rect) painter.drawText(rect, QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter, "-") # Apply pen # Paint #painter.drawLine(QtCore.QLine(margin, y, w - 2*margin, y)) self.doForVisibleBlocks(paintCodeFolders) painter.end() class LongLineIndicator(object): # Register style element _styleElements = [ ( 'Editor.Long line indicator', 'The color and style of the long line indicator.', 'fore:#BBB,linestyle:solid', ) ] def longLineIndicatorPosition(self): """ longLineIndicatorPosition() Get the position of the long line indicator (aka edge column). A value of 0 or smaller means that no indicator is shown. """ return self.__longLineIndicatorPosition @ce_option(80) def setLongLineIndicatorPosition(self, value): """ setLongLineIndicatorPosition(value) Set the position of the long line indicator (aka edge column). A value of 0 or smaller means that no indicator is shown. """ self.__longLineIndicatorPosition = int(value) self.viewport().update() def paintEvent(self, event): """ paintEvent(event) Paint the long line indicator. Paints behind its super() """ if self.longLineIndicatorPosition()<=0: super(LongLineIndicator, self).paintEvent(event) return # Get doc and viewport doc = self.document() viewport = self.viewport() # Get position of long line fm = self.fontMetrics() # width of ('i'*length) not length * (width of 'i') b/c of # font kerning and rounding x = fm.width('i' * self.longLineIndicatorPosition()) x += doc.documentMargin() + self.contentOffset().x() x += 1 # Move it a little next to the cursor # Prepate painter painter = QtGui.QPainter() painter.begin(viewport) # Prepare pen format = self.getStyleElementFormat('editor.LongLineIndicator') pen = QtGui.QPen(format.fore) pen.setStyle(format.linestyle) painter.setPen(pen) # Draw line and end painter painter.drawLine(QtCore.QLine(x, 0, x, viewport.height()) ) painter.end() # Propagate event super(LongLineIndicator, self).paintEvent(event) class ShowWhitespace(object): def showWhitespace(self): """Show or hide whitespace markers""" option=self.document().defaultTextOption() return bool(option.flags() & option.ShowTabsAndSpaces) @ce_option(False) def setShowWhitespace(self,value): option=self.document().defaultTextOption() if value: option.setFlags(option.flags() | option.ShowTabsAndSpaces) else: option.setFlags(option.flags() & ~option.ShowTabsAndSpaces) self.document().setDefaultTextOption(option) class ShowLineEndings(object): @ce_option(False) def showLineEndings(self): """ Get whether line ending markers are shown. """ option=self.document().defaultTextOption() return bool(option.flags() & option.ShowLineAndParagraphSeparators) def setShowLineEndings(self,value): option=self.document().defaultTextOption() if value: option.setFlags(option.flags() | option.ShowLineAndParagraphSeparators) else: option.setFlags(option.flags() & ~option.ShowLineAndParagraphSeparators) self.document().setDefaultTextOption(option) class LineNumbers(object): # Margin on both side of the line numbers _LineNumberAreaMargin = 3 # Register style element _styleElements = [ ( 'Editor.Line numbers', 'The text- and background-color of the line numbers.', 'fore:#222,back:#DDD', ) ] class __LineNumberArea(QtWidgets.QWidget): """ This is the widget reponsible for drawing the line numbers. """ def __init__(self, codeEditor): QtWidgets.QWidget.__init__(self, codeEditor) self.setCursor(QtCore.Qt.PointingHandCursor) self._pressedY = None self._lineNrChoser = None def _getY(self, pos): tmp = self.mapToGlobal(pos) return self.parent().viewport().mapFromGlobal(tmp).y() def mousePressEvent(self, event): self._pressedY = self._getY(event.pos()) def mouseReleaseEvent(self, event): self._handleWholeBlockSelection( self._getY(event.pos()) ) def mouseMoveEvent(self, event): self._handleWholeBlockSelection( self._getY(event.pos()) ) def _handleWholeBlockSelection(self, y2): # Get y1 and sort (y1, y2) y1 = self._pressedY if y1 is None: y1 = y2 y1, y2 = min(y1, y2), max(y1, y2) # Get cursor and two cursors corresponding to selected blocks editor = self.parent() cursor = editor.textCursor() c1 = editor.cursorForPosition(QtCore.QPoint(0,y1)) c2 = editor.cursorForPosition(QtCore.QPoint(0,y2)) # Make these two cursors select the whole block c1.movePosition(c1.StartOfBlock, c1.MoveAnchor) c2.movePosition(c2.EndOfBlock, c2.MoveAnchor) # Apply selection cursor.setPosition(c1.position(), cursor.MoveAnchor) cursor.setPosition(c2.position(), cursor.KeepAnchor) editor.setTextCursor(cursor) def mouseDoubleClickEvent(self, event): self.showLineNumberChoser() def showLineNumberChoser(self): # Create line number choser if needed if self._lineNrChoser is None: self._lineNrChoser = LineNumbers.LineNumberChoser(self.parent()) # Get editor and cursor editor = self.parent() cursor = editor.textCursor() # Get (x,y) pos and apply x, y = self.width()+4, editor.cursorRect(cursor).y() self._lineNrChoser.move(QtCore.QPoint(x,y)) # Show/reset line number choser self._lineNrChoser.reset(cursor.blockNumber()+1) def paintEvent(self, event): editor = self.parent() if not editor.showLineNumbers(): return # Get doc and viewport viewport = editor.viewport() # Get format and margin format = editor.getStyleElementFormat('editor.LineNumbers') margin = editor._LineNumberAreaMargin # Init painter painter = QtGui.QPainter() painter.begin(self) # Get which part to paint. Just do all to avoid glitches w = editor.getLineNumberAreaWidth() y1, y2 = 0, editor.height() #y1, y2 = event.rect().top()-10, event.rect().bottom()+10 # Get offset tmp = self.mapToGlobal(QtCore.QPoint(0,0)) offset = viewport.mapFromGlobal(tmp).y() #Draw the background painter.fillRect(QtCore.QRect(0, y1, w, y2), format.back) # Get cursor cursor = editor.cursorForPosition(QtCore.QPoint(0,y1)) # Prepare fonts font1 = editor.font() font2 = editor.font() font2.setBold(True) currentBlockNumber = editor.textCursor().block().blockNumber() # Init painter with font and color painter.setFont(font1) painter.setPen(format.fore) #Repainting always starts at the first block in the viewport, #regardless of the event.rect().y(). Just to keep it simple while True: blockNumber = cursor.block().blockNumber() y = editor.cursorRect(cursor).y() # Set font to bold if line number is the current if blockNumber == currentBlockNumber: painter.setFont(font2) painter.drawText(0, y-offset, w-margin, 50, Qt.AlignRight, str(blockNumber+1)) # Set font back if blockNumber == currentBlockNumber: painter.setFont(font1) if y>y2: break #Reached end of the repaint area if not cursor.block().next().isValid(): break #Reached end of the text cursor.movePosition(cursor.NextBlock) # Done painter.end() class LineNumberChoser(QtWidgets.QSpinBox): def __init__(self, parent): QtWidgets.QSpinBox.__init__(self, parent) self._editor = parent ss = "QSpinBox { border: 2px solid #789; border-radius: 3px; padding: 4px; }" self.setStyleSheet(ss) self.setPrefix('Go to line: ') self.setAccelerated(True) self.setButtonSymbols(self.NoButtons) self.setCorrectionMode(self.CorrectToNearestValue) # Signal for when value changes, and flag to disbale it once self._ignoreSignalOnceFlag = False self.valueChanged.connect(self.onValueChanged) def reset(self, currentLineNumber): # Set value to (given) current line number self._ignoreSignalOnceFlag = True self.setRange(1, self._editor.blockCount()) self.setValue(currentLineNumber) # Select text and focus so that the user can simply start typing self.selectAll() self.setFocus() # Make visible self.show() self.raise_() def focusOutEvent(self, event): self.hide() def keyPressEvent(self, event): if event.key() in [QtCore.Qt.Key_Escape, QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]: self._editor.setFocus() # Moves focus away, thus hiding self else: QtWidgets.QSpinBox.keyPressEvent(self, event) def onValueChanged(self, nr): if self._ignoreSignalOnceFlag: self._ignoreSignalOnceFlag = False else: self._editor.gotoLine(nr) def __init__(self, *args, **kwds): self.__lineNumberArea = None super(LineNumbers, self).__init__(*args, **kwds) # Create widget that draws the line numbers self.__lineNumberArea = self.__LineNumberArea(self) # Issue an update when the font or amount of line numbers changes self.blockCountChanged.connect(self.__onBlockCountChanged) self.fontChanged.connect(self.__onBlockCountChanged) self.__onBlockCountChanged() self.addLeftMargin(LineNumbers, self.getLineNumberAreaWidth) def gotoLinePopup(self): """ Popup the little widget to quickly goto a certain line. Can also be achieved by double-clicking the line number area. """ self.__lineNumberArea.showLineNumberChoser() def showLineNumbers(self): return self.__showLineNumbers @ce_option(True) def setShowLineNumbers(self, value): self.__showLineNumbers = bool(value) # Note that this method is called before the __init__ is finished, # so that the __lineNumberArea is not yet created. if self.__lineNumberArea: if self.__showLineNumbers: self.__onBlockCountChanged() self.__lineNumberArea.show() else: self.__lineNumberArea.hide() self.updateMargins() def getLineNumberAreaWidth(self): """ Count the number of lines, compute the length of the longest line number (in pixels) """ if not self.__showLineNumbers: return 0 lastLineNumber = self.blockCount() margin = self._LineNumberAreaMargin return self.fontMetrics().width(str(lastLineNumber)) + 2*margin def __onBlockCountChanged(self,count = None): """ Update the line number area width. This requires to set the viewport margins, so there is space to draw the linenumber area """ if self.__showLineNumbers: self.updateMargins() def resizeEvent(self,event): super(LineNumbers, self).resizeEvent(event) #On resize, resize the lineNumberArea, too rect=self.contentsRect() m = self.getLeftMargin(LineNumbers) w = self.getLineNumberAreaWidth() self.__lineNumberArea.setGeometry( rect.x()+m, rect.y(), w, rect.height()) def paintEvent(self,event): super(LineNumbers, self).paintEvent(event) #On repaint, update the complete line number area w = self.getLineNumberAreaWidth() self.__lineNumberArea.update(0, 0, w, self.height() ) class BreakPoints(object): _breakPointWidth = 11 # With of total bar, actual points are smaller # Register style element _styleElements = [ ( 'Editor.BreakPoints', 'The fore- and background-color of the breakpoints.', 'fore:#F66,back:#dfdfe1', ) ] class __BreakPointArea(QtWidgets.QWidget): """ This is the widget reponsible for drawing the break points. """ def __init__(self, codeEditor): QtWidgets.QWidget.__init__(self, codeEditor) self.setCursor(QtCore.Qt.PointingHandCursor) self.setMouseTracking(True) self._virtualBreakpoint = 0 def _getY(self, pos): tmp = self.mapToGlobal(pos) return self.parent().viewport().mapFromGlobal(tmp).y() def mousePressEvent(self, event): self._toggleBreakPoint( self._getY(event.pos())) def mouseMoveEvent(self, event): y = self._getY(event.pos()) editor = self.parent() c1 = editor.cursorForPosition(QtCore.QPoint(0,y)) self._virtualBreakpoint = c1.blockNumber() + 1 self.update() def leaveEvent(self, event): self._virtualBreakpoint = 0 self.update() def _toggleBreakPoint(self, y): # Get breakpoint corresponding to pressed pos editor = self.parent() c1 = editor.cursorForPosition(QtCore.QPoint(0,y)) linenr = c1.blockNumber() + 1 # Toggle self.parent().toggleBreakpoint(linenr) def paintEvent(self, event): editor = self.parent() if not editor.showBreakPoints(): return # Get format and margin format = editor.getStyleElementFormat('editor.breakpoints') margin = 1 w = editor._breakPointWidth bulletWidth = w - 2*margin # Init painter painter = QtGui.QPainter() painter.begin(self) # Get which part to paint. Just do all to avoid glitches y1, y2 = 0, editor.height() #Draw the background painter.fillRect(QtCore.QRect(0, y1, w, y2), format.back) # Get debug indicator and list of sorted breakpoints debugBlockIndicator = editor._debugLineIndicator-1 virtualBreakpoint = self._virtualBreakpoint-1 blocknumbers = [i-1 for i in sorted(self.parent()._breakPoints)] if not (blocknumbers or editor._debugLineIndicator or editor._debugLineIndicators or virtualBreakpoint > 0): return # Get cursor cursor = editor.cursorForPosition(QtCore.QPoint(0,y1)) # Get start block number and bullet offset in pixels startBlockNumber = cursor.block().blockNumber() bulletOffset = editor.contentOffset().y() + bulletWidth * 0.25 # Prepare painter painter.setPen(QtGui.QColor('#777')) painter.setBrush(format.fore) painter.setRenderHint(painter.Antialiasing) # Draw breakpoints for blockNumber in blocknumbers: if blockNumber < startBlockNumber: continue # Get block block = editor.document().findBlockByNumber(blockNumber) if block.isValid(): y = editor.blockBoundingGeometry(block).y() + bulletOffset painter.drawEllipse(margin, y, bulletWidth, bulletWidth) # Draw *the* debug marker if debugBlockIndicator > 0: painter.setBrush(QtGui.QColor('#6F6')) # Get block block = editor.document().findBlockByNumber(debugBlockIndicator) if block.isValid(): y = editor.blockBoundingGeometry(block).y() + bulletOffset y += 0.25 * bulletWidth painter.drawEllipse(margin, y, bulletWidth, 0.5*bulletWidth) # Draw other debug markers for debugLineIndicator in editor._debugLineIndicators: debugBlockIndicator = debugLineIndicator - 1 painter.setBrush(QtGui.QColor('#DDD')) # Get block block = editor.document().findBlockByNumber(debugBlockIndicator) if block.isValid(): y = editor.blockBoundingGeometry(block).y() + bulletOffset y += 0.25 * bulletWidth painter.drawEllipse(margin, y, bulletWidth, 0.5*bulletWidth) # Draw virtual break point if virtualBreakpoint > 0: painter.setBrush(QtGui.QColor(0,0,0,0)) # Get block block = editor.document().findBlockByNumber(virtualBreakpoint) if block.isValid(): y = editor.blockBoundingGeometry(block).y() + bulletOffset painter.drawEllipse(margin, y, bulletWidth, bulletWidth) # Done painter.end() def __init__(self, *args, **kwds): self.__breakPointArea = None super(BreakPoints, self).__init__(*args, **kwds) # Create widget that draws the breakpoints self.__breakPointArea = self.__BreakPointArea(self) self.addLeftMargin(BreakPoints, self.getBreakPointAreaWidth) self._breakPoints = {} # int -> block self._debugLineIndicator = 0 self._debugLineIndicators = set() self.blockCountChanged.connect(self.__onBlockCountChanged) def __onBlockCountChanged(self): """ Track breakpoints so we can update the number when text is inserted above. """ newBreakPoints = {} for linenr in list(self._breakPoints): block, block_previous, block_next = self._breakPoints[linenr] block_linenr = block.blockNumber() + 1 prev_ok = block.previous().blockNumber() == block_previous.blockNumber() next_ok = block.next().blockNumber() == block_next.blockNumber() if prev_ok or next_ok: if block_linenr == linenr: if prev_ok and next_ok: pass # All is well else: # Update refs self._breakPoints[linenr] = block, block.previous(), block.next() else: # Update linenr - this is the only case where "move" th bp newBreakPoints[block_linenr] = self._breakPoints.pop(linenr) else: if block_linenr == linenr: # Just update refs self._breakPoints[linenr] = block, block.previous(), block.next() else: # Delete breakpoint? Meh, just update refs self._breakPoints[linenr] = block, block.previous(), block.next() if newBreakPoints: self._breakPoints.update(newBreakPoints) self.breakPointsChanged.emit(self) self.__breakPointArea.update() def breakPoints(self): """ A list of breakpoints for this editor. """ return list(sorted(self._breakPoints)) self._breakPoints = {} self.breakPointsChanged.emit(self) self.__breakPointArea.update() def toggleBreakpoint(self, linenr=None): """ Turn breakpoint on/off for given linenr of current line. """ if linenr is None: linenr = self.textCursor().blockNumber() + 1 if linenr in self._breakPoints: self._breakPoints.pop(linenr) else: c = self.textCursor() c.movePosition(c.Start) c.movePosition(c.NextBlock, c.MoveAnchor, linenr - 1) b = c.block() self._breakPoints[linenr] = b, b.previous(), b.next() self.breakPointsChanged.emit(self) self.__breakPointArea.update() def setDebugLineIndicator(self, linenr, active=True): """ Set the debug line indicator to the given line number. If None or 0, the indicator is hidden. """ linenr = int(linenr or 0) if not linenr: # Remove all indicators if self._debugLineIndicator or self._debugLineIndicators: self._debugLineIndicator = 0 self._debugLineIndicators = set() self.__breakPointArea.update() elif active: # Set *the* indicator if linenr != self._debugLineIndicator: self._debugLineIndicators.discard(linenr) self._debugLineIndicator = linenr self.__breakPointArea.update() else: # Add to set of indicators if linenr not in self._debugLineIndicators: self._debugLineIndicators.add(linenr) self.__breakPointArea.update() def getBreakPointAreaWidth(self): if not self.__showBreakPoints: return 0 else: return self._breakPointWidth def showBreakPoints(self): return self.__showBreakPoints @ce_option(True) def setShowBreakPoints(self, value): self.__showBreakPoints = bool(value) # Note that this method is called before the __init__ is finished, # so that the area is not yet created. if self.__breakPointArea: if self.__showBreakPoints: self.__breakPointArea.show() else: self.__breakPointArea.hide() self.clearBreakPoints() self.updateMargins() def resizeEvent(self,event): super(BreakPoints, self).resizeEvent(event) #On resize, resize the breakpointArea, too rect=self.contentsRect() m = self.getLeftMargin(BreakPoints) w = self.getBreakPointAreaWidth() self.__breakPointArea.setGeometry( rect.x()+m, rect.y(), w, rect.height()) def paintEvent(self,event): super(BreakPoints, self).paintEvent(event) #On repaint, update the complete breakPointArea w = self.getBreakPointAreaWidth() self.__breakPointArea.update(0, 0, w, self.height() ) class Wrap(object): def wrap(self): """Enable or disable wrapping""" option=self.document().defaultTextOption() return not bool(option.wrapMode() == option.NoWrap) @ce_option(True) def setWrap(self,value): option=self.document().defaultTextOption() if value: option.setWrapMode(option.WrapAtWordBoundaryOrAnywhere) else: option.setWrapMode(option.NoWrap) self.document().setDefaultTextOption(option) # todo: move this bit to base class? # This functionality embedded in the highlighter and even has a designated # subpackage. I feel that it should be a part of the base editor. # Note: if we do this, remove the hasattr call in the highlighter. class SyntaxHighlighting(object): """ Notes on syntax highlighting. The syntax highlighting/parsing is performed using three "components". The base component are the token instances. Each token simply represents a row of characters in the text the belong to each-other and should be styled in the same way. There is a token class for each particular "thing" in the code, such as comments, strings, keywords, etc. Some tokens are specific to a particular language. There is a function that produces a set of tokens, when given a line of text and a state parameter. There is such a function for each language. These "parsers" are defined in the parsers subpackage. And lastly, there is the Highlighter class, that applies the parser function to obtain the set of tokens and using the names of these tokens applies styling. The styling can be defined by giving a dict that maps token names to style representations. """ # Register all syntax style elements _styleElements = Manager.getStyleElementDescriptionsForAllParsers() def parser(self): """ parser() Get the parser instance currently in use to parse the code for syntax highlighting and source structure. Can be None. """ try: return self.__parser except AttributeError: return None @ce_option(None) def setParser(self, parserName=''): """ setParser(parserName='') Set the current parser by giving the parser name. """ # Set parser self.__parser = Manager.getParserByName(parserName) # Restyle, use setStyle for lazy updating self.setStyle() pyzo-4.4.3/pyzo/codeeditor/extensions/autocompletion.py0000666000000000000000000002624613124444540021625 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Code editor extensions that provides autocompleter functionality """ from ..qt import QtGui,QtCore, QtWidgets Qt = QtCore.Qt import keyword #TODO: use this CompletionListModel to style the completion suggestions (class names, method names, keywords etc) class CompletionListModel(QtCore.QStringListModel): def data(self, index, role): if role == Qt.ForegroundRole: # data = str(QtWidgets.QStringListModel.data(self, index, QtCore.Qt.DisplayRole)) # return QtGui.QBrush(Qt.red) return None else: return QtCore.QStringListModel.data(self, index, role) # todo: use keywords from the parser class AutoCompletion(object): def __init__(self,*args, **kwds): super(AutoCompletion, self).__init__(*args, **kwds) # Autocompleter self.__completerModel = QtCore.QStringListModel(keyword.kwlist) self.__completer = QtWidgets.QCompleter(self) self.__completer.setModel(self.__completerModel) self.__completer.setCaseSensitivity(Qt.CaseInsensitive) self.__completer.setWidget(self) self.__completerNames = [] self.__recentCompletions = [] #List of recently selected completions # geometry self.__popupSize = 300, 100 # Text position corresponding to first charcter of the word being completed self.__autocompleteStart = None self.__autocompleteDebug = False self.__autocompletionAcceptKeys = (Qt.Key_Tab,) #Connect signals self.__highlightedCompletion = None self.__completer.activated.connect(self.onAutoComplete) self.__completer.highlighted.connect(self._setHighlightedCompletion) def _setHighlightedCompletion(self, value): """ Keeping track of the highlighted item allows us to 'manually' perform an autocompletion. """ self.__highlightedCompletion = value ## Properties def recentCompletionsList(self): """ The list of recent auto-completions. This property may be set to a list that is shared among several editors, in order to share the notion of recent auto-completions """ return self.__recentCompletions def setRecentCompletionsList(self,value): self.__recentCompletions = value def completer(self): return self.__completer def setAutoCompletionAcceptKeys(self, *keys): """ Set the keys (Qt enums) that can accept an autocompletion. Like Tab, or Enter. Defaut Tab. """ self.__autocompletionAcceptKeys = keys ## Autocompletion def setAutocompletPopupSize(self, width, height): """ Set the size (width, heigth) of the automcompletion popup window. """ self.__popupSize = width, height def autocompleteShow(self, offset=0, names=None): """ Pop-up the autocompleter (if not already visible) and position it at current cursor position minus offset. If names is given and not None, it is set as the list of possible completions. """ #Pop-up the autocompleteList startcursor=self.textCursor() startcursor.movePosition(startcursor.Left, n=offset) if self.__autocompleteDebug: print('autocompleteShow called') if names is not None: #TODO: a more intelligent implementation that adds new items and removes #old ones if names != self.__completerNames: self.__completerModel.setStringList(names) self.__completerNames = names if not self.autocompleteActive() or \ startcursor.position() != self.__autocompleteStart.position(): self.__autocompleteStart=startcursor self.__autocompleteStart.setKeepPositionOnInsert(True) #Popup the autocompleter. Don't use .complete() since we want to #position the popup manually self.__positionAutocompleter() if self.__updateAutocompleterPrefix(): self.__completer.popup().show() if self.__autocompleteDebug: print('self.__completer.popup().show() called') else: self.__updateAutocompleterPrefix() def autocompleteAccept(self): pass def autocompleteCancel(self): self.__completer.popup().hide() self.__autocompleteStart = None def onAutoComplete(self, text=None): if text is None: text = self.__highlightedCompletion #Select the text from autocompleteStart until the current cursor cursor=self.textCursor() cursor.setPosition(self.__autocompleteStart.position(),cursor.KeepAnchor) #Replace it with the selected text cursor.insertText(text) self.autocompleteCancel() #Reset the completer #Update the recent completions list if text in self.__recentCompletions: self.__recentCompletions.remove(text) self.__recentCompletions.append(text) def autocompleteActive(self): """ Returns whether an autocompletion list is currently shown. """ return self.__autocompleteStart is not None def __positionAutocompleter(self): """Move the autocompleter list to a proper position""" #Find the start of the autocompletion and move the completer popup there cur=QtGui.QTextCursor(self.__autocompleteStart) #Copy __autocompleteStart # Set size geometry = self.__completer.popup().geometry() geometry.setWidth(self.__popupSize[0]) geometry.setHeight(self.__popupSize[1]) self.__completer.popup().setGeometry(geometry) # Initial choice for position of the completer position = self.cursorRect(cur).bottomLeft() + self.viewport().pos() # Check if the completer is going to go off the screen desktop_geometry = QtWidgets.qApp.desktop().geometry() global_position = self.mapToGlobal(position) if global_position.y() + geometry.height() > desktop_geometry.height(): # Move the completer to above the current line position = self.cursorRect(cur).topLeft() + self.viewport().pos() global_position = self.mapToGlobal(position) global_position -= QtCore.QPoint(0, geometry.height()) self.__completer.popup().move(global_position) def __updateAutocompleterPrefix(self): """ Find the autocompletion prefix (the part of the word that has been entered) and send it to the completer. Update the selected completion (out of several possiblilties) which is best suited """ if not self.autocompleteActive(): self.__completer.popup().hide() #TODO: why is this required? return False #Select the text from autocompleteStart until the current cursor cursor=self.textCursor() cursor.setPosition(self.__autocompleteStart.position(),cursor.KeepAnchor) prefix=cursor.selectedText() self.__completer.setCompletionPrefix(prefix) model = self.__completer.completionModel() if model.rowCount(): # Create a list of all possible completions, and select the one # which is best suited. Use the one which is highest in the # __recentCompletions list, but prefer completions with matching # case if they exists # Create a list of (row, value) tuples of all possible completions completions = [ (row, model.data(model.index(row,0),self.__completer.completionRole())) for row in range(model.rowCount()) ] # Define a function to get the position in the __recentCompletions def completionIndex(data): try: return self.__recentCompletions.index(data) except ValueError: return -1 # Sort twice; the last sort has priority over the first # Sort on most recent completions completions.sort(key = lambda c: completionIndex(c[1]), reverse = True) # Sort on matching case (prefer matching case) completions.sort(key = lambda c: c[1].startswith(prefix), reverse = True) # apply the best match bestMatchRow = completions[0][0] self.__completer.popup().setCurrentIndex(model.index(bestMatchRow,0)) return True else: #No match, just hide self.autocompleteCancel() return False def potentiallyAutoComplete(self, event): """ potentiallyAutoComplete(event) Given a keyEvent, check if we should perform an autocompletion. Returns 0 if no autocompletion was performed. Return 1 if autocompletion was performed, but the key event should be processed as normal. Return 2 if the autocompletion was performed, and the key should be consumed. """ if self.autocompleteActive(): if event.key() in self.__autocompletionAcceptKeys: if event.key() <= 128: self.onAutoComplete() # No arg: select last highlighted self.autocompleteCancel() event.ignore() return 1 # Let key have effect as normal elif event.modifiers() == Qt.NoModifier: # The key self.onAutoComplete() # No arg: select last highlighted self.autocompleteCancel() return 2 # Key should be consumed return 0 def keyPressEvent(self, event): key = event.key() modifiers = event.modifiers() if key == Qt.Key_Escape and modifiers == Qt.NoModifier and \ self.autocompleteActive(): self.autocompleteCancel() return #Consume the key if self.potentiallyAutoComplete(event) > 1: return #Consume #Allowed keys that do not close the autocompleteList: # alphanumeric and _ ans shift # Backspace (until start of autocomplete word) if self.autocompleteActive() and \ not event.text().isalnum() and event.text() != '_' and \ key != Qt.Key_Shift and not ( (key==Qt.Key_Backspace) and self.textCursor().position()>self.__autocompleteStart.position()): self.autocompleteCancel() # Apply the key that was pressed super(AutoCompletion, self).keyPressEvent(event) if self.autocompleteActive(): #While we type, the start of the autocompletion may move due to line #wrapping, so reposition after every key stroke self.__positionAutocompleter() self.__updateAutocompleterPrefix() pyzo-4.4.3/pyzo/codeeditor/extensions/behaviour.py0000666000000000000000000004247613123037260020546 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Code editor extensions that change its behaviour (i.e. how it reacts to keys) """ from ..qt import QtGui,QtCore Qt = QtCore.Qt from ..misc import ustr, ce_option from ..parsers.tokens import (CommentToken,UnterminatedStringToken) from ..parsers import BlockState class MoveLinesUpDown(object): def keyPressEvent(self,event): if event.key() in (Qt.Key_Up, Qt.Key_Down) and ( Qt.ControlModifier & event.modifiers() and Qt.ShiftModifier & event.modifiers()): cursor = self.textCursor() cursor.beginEditBlock() try: self._swaplines(cursor, event.key()) finally: cursor.endEditBlock() else: super().keyPressEvent(event) def _swaplines(self, cursor, key): # Get positions of selection start = cursor.selectionStart() end = cursor.selectionEnd() # Get text of selected blocks cursor.setPosition(start, cursor.MoveAnchor) cursor.movePosition(cursor.StartOfBlock, cursor.MoveAnchor) cursor.setPosition(end, cursor.KeepAnchor) cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) text1 = cursor.selectedText() cursor.removeSelectedText() pos1 = cursor.position() # Move up/down other = [cursor.NextBlock, cursor.PreviousBlock][int(bool(key == Qt.Key_Up))] cursor.movePosition(other, cursor.MoveAnchor) # Select text of other block cursor.movePosition(cursor.StartOfBlock, cursor.MoveAnchor) cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) text2 = cursor.selectedText() cursor.removeSelectedText() pos2 = cursor.position() # Insert text cursor.insertText(text1) pos3 = cursor.position() # Move back if key == Qt.Key_Up: cursor.movePosition(cursor.NextBlock, cursor.MoveAnchor) else: cursor.setPosition(pos1, cursor.MoveAnchor) pos2 += len(text2) pos3 += len(text2) # Replace text cursor.insertText(text2) # Leave original lines selected for continued movement cursor.setPosition(pos2, cursor.MoveAnchor) cursor.setPosition(pos3, cursor.KeepAnchor) self.setTextCursor(cursor) class ScrollWithUpDownKeys(object): def keyPressEvent(self,event): if event.key() in (Qt.Key_Up, Qt.Key_Down) and Qt.ControlModifier == event.modifiers(): s = self.verticalScrollBar() # h = self.cursorRect(self.textCursor()).height() if event.key() == Qt.Key_Up: s.setValue(s.value() + 1) else: s.setValue(s.value() - 1) else: super().keyPressEvent(event) class HomeKey(object): def keyPressEvent(self,event): # Home or shift + home if event.key() == Qt.Key_Home and \ event.modifiers() in (Qt.NoModifier, Qt.ShiftModifier): # Prepare cursor = self.textCursor() shiftDown = event.modifiers() == Qt.ShiftModifier moveMode = [cursor.MoveAnchor, cursor.KeepAnchor][shiftDown] # Get leading whitespace text = ustr(cursor.block().text()) leadingWhitespace = text[:len(text)-len(text.lstrip())] # Get current position and move to start of whitespace i = cursor.positionInBlock() cursor.movePosition(cursor.StartOfBlock, moveMode) cursor.movePosition(cursor.Right, moveMode, len(leadingWhitespace)) # If we were alread there, move to start of block if cursor.positionInBlock() == i: cursor.movePosition(cursor.StartOfBlock, moveMode) # Done self.setTextCursor(cursor) else: super().keyPressEvent(event) class EndKey(object): def keyPressEvent(self,event): if event.key() == Qt.Key_End and \ event.modifiers() in (Qt.NoModifier, Qt.ShiftModifier): # Prepare cursor = self.textCursor() shiftDown = event.modifiers() == Qt.ShiftModifier moveMode = [cursor.MoveAnchor, cursor.KeepAnchor][shiftDown] # Get current position and move to end of line i = cursor.positionInBlock() cursor.movePosition(cursor.EndOfLine, moveMode) # If alread at end of line, move to end of block if cursor.positionInBlock() == i: cursor.movePosition(cursor.EndOfBlock, moveMode) # Done self.setTextCursor(cursor) else: super().keyPressEvent(event) class NumpadPeriodKey(object): """ If the numpad decimal separator key is pressed, always insert a period (.) even if due to localization that key is mapped to a comma (,). When editing code, period is the decimal separator independent of localization """ def keyPressEvent(self,event): # Check for numpad comma if event.key() == QtCore.Qt.Key_Comma and \ event.modifiers() & QtCore.Qt.KeypadModifier: # Create a new QKeyEvent to substitute the original one event = QtGui.QKeyEvent(event.type(), QtCore.Qt.Key_Period, event.modifiers(), '.', event.isAutoRepeat(), event.count()) super().keyPressEvent(event) class Indentation(object): def __cursorIsInLeadingWhitespace(self,cursor = None): """ Checks wether the given cursor is in the leading whitespace of a block, i.e. before the first non-whitespace character. The cursor is not modified. If the cursor is not given or is None, the current textCursor is used """ if cursor is None: cursor = self.textCursor() # Get the text of the current block up to the cursor textBeforeCursor = ustr(cursor.block().text())[:cursor.positionInBlock()] return textBeforeCursor.lstrip() == '' #If we trim it and it is empty, it's all whitespace def keyPressEvent(self,event): key = event.key() modifiers = event.modifiers() #Tab key if key == Qt.Key_Tab: if modifiers == Qt.NoModifier: if self.textCursor().hasSelection(): #Tab pressed while some area was selected self.indentSelection() return elif self.__cursorIsInLeadingWhitespace(): #If the cursor is in the leading whitespace, indent and move cursor to end of whitespace cursor = self.textCursor() self.indentBlock(cursor) self.setTextCursor(cursor) return elif self.indentUsingSpaces(): #Insert space-tabs cursor=self.textCursor() w = self.indentWidth() cursor.insertText(' '*(w-((cursor.positionInBlock() + w ) % w))) return #else: default behaviour, insert tab character else: #Some other modifiers + Tab: ignore return # If backspace is pressed in the leading whitespace, (except for at the first # position of the line), and there is no selection # dedent that line and move cursor to end of whitespace if key == Qt.Key_Backspace and modifiers == Qt.NoModifier and \ self.__cursorIsInLeadingWhitespace() and not self.textCursor().atBlockStart() \ and not self.textCursor().hasSelection(): # Create a cursor, dedent the block and move screen cursor to the end of the whitespace cursor = self.textCursor() self.dedentBlock(cursor) self.setTextCursor(cursor) return # todo: Same for delete, I think not (what to do with the cursor?) # Auto-unindent if event.key() == Qt.Key_Delete: cursor = self.textCursor() if not cursor.hasSelection(): cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) if not cursor.hasSelection() and cursor.block().next().isValid(): cursor.beginEditBlock() cursor.movePosition(cursor.NextBlock) self.indentBlock(cursor, -99) # dedent as much as we can cursor.deletePreviousChar() cursor.endEditBlock() return super().keyPressEvent(event) class AutoIndent(object): """ Auto indentation. This extension only adds the autoIndent property, for the actual indentation, the editor should derive from some AutoIndenter object """ def autoIndent(self): """ autoIndent() Get whether auto indentation is enabled. """ return self.__autoIndent @ce_option(True) def setAutoIndent(self,value): """ setAutoIndent(value) Set whether to enable auto indentation. """ self.__autoIndent = bool(value) class PythonAutoIndent(object): def keyPressEvent(self,event): super().keyPressEvent(event) if not self.autoIndent(): return #This extension code is run *after* key is processed by QPlainTextEdit if event.key() in (Qt.Key_Enter,Qt.Key_Return): cursor=self.textCursor() # Prevent in-block newlines (issue #482) if not cursor.atBlockStart() and not cursor.hasSelection(): cursor.deletePreviousChar() cursor.insertBlock() cursor=self.textCursor() previousBlock=cursor.block().previous() if previousBlock.isValid(): line = ustr(previousBlock.text()) indent=line[:len(line)-len(line.lstrip())] if line.endswith(':'): # We only need to add indent if the : is not in a (multiline) # string or comment. Therefore, find out what the syntax # highlighter thinks of the previous line. ppreviousBlock = previousBlock.previous() # the block before previous ppreviousState = ppreviousBlock.userState() if previousBlock.isValid() else 0 lastElementToken = list(self.parser().parseLine(previousBlock.text(),ppreviousState))[-1] # Because there's at least a : on that line, the list is never empty if (not isinstance(lastElementToken, (CommentToken, UnterminatedStringToken, BlockState))): #TODO: check correct identation (no mixed space/tabs) if self.indentUsingSpaces(): indent+=' '*self.indentWidth() else: indent+='\t' cursor.insertText(indent) #This prevents jump to start of line when up key is pressed self.setTextCursor(cursor) class SmartCopyAndPaste(object): """ Smart copy and paste allows copying and pasting blocks """ @staticmethod def __setCursorPositionAndAnchor(cursor, position, anchor): cursor.setPosition(anchor) cursor.setPosition(position, cursor.KeepAnchor) @classmethod def __ensureCursorBeforeAnchor(cls, cursor): """ Given a cursor, modify it such that the cursor.position() is before or at cursor.anchor() and not the other way around. Returns: anchorBeforeCursor, i.e. whether originally the anchor was before the cursor """ start = cursor.selectionStart() end = cursor.selectionEnd() # Remember whether the cursor is before or after the anchor anchorBeforeCursor = cursor.anchor() < cursor.position() cls.__setCursorPositionAndAnchor(cursor, start, end) # Return wheter originally the cursor was before the anchor return anchorBeforeCursor def copy(self): """ Smart copy: if selection is multi-line and in front of the start of the selection there is only whitespace, extend the selection to include only whitespace """ cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() # For our convenience, ensure that position is at start and # anchor is at the end, but remember whether originally the # anchor was before the cursor or the other way around anchorBeforeCursor = self.__ensureCursorBeforeAnchor(cursor) # Check if we have multi-line selection. block = cursor.block() # Use > not >= to ensure we don't count it as multi-line if the cursor # is just at the beginning of the next block (consistent with 'CodeEditor.doForSelectedLines') if end > (block.position() + block.length()): # Now check if there is only whitespace before the start of selection # If so, include this whitespace in the selection and update the # selection of the editor textBeforeSelection = block.text()[:cursor.positionInBlock()] if len(textBeforeSelection.strip()) == 0: start = block.position() # Move start to include leading whitespace # Update the textcursor of our editor. If originally the # anchor was before the cursor, restore that situation if anchorBeforeCursor: self.__setCursorPositionAndAnchor(cursor, end, start) else: self.__setCursorPositionAndAnchor(cursor, start, end) self.setTextCursor(cursor) # Call our supers copy slot to do the actual copying super().copy() def cut(self): """ Cutting with smart-copy: the part that is copies is the same as self.copy(), but the part that is removed is only the original selection see: Qt qtextcontrol.cpp, cut() """ if (self.textInteractionFlags() & QtCore.Qt.TextEditable) and \ self.textCursor().hasSelection(): cursor = self.textCursor() self.copy() # Restore original cursor self.setTextCursor(cursor) cursor.removeSelectedText() def paste(self): """ Smart paste If you paste on a position that has only whitespace in front of it, remove the whitespace before pasting. Combined with smart copy, this ensure indentation of the """ self._paste(keepSelection = False) def pasteAndSelect(self): """ Smart paste Like paste(), but keep the part that was pasted selected. This allows you to change the indentation after pasting using tab / shift-tab """ self._paste(keepSelection = True) def _paste(self, keepSelection): # Create a cursor of the current selection cursor = self.textCursor() # Ensure that position is at start and anchor is at the end. # This is required to ensure that with keepPositiobOnInsert # set, the cursor will equal the pasted text after the pasting self.__ensureCursorBeforeAnchor(cursor) # Change this cursor to let the position() stay at its place upon # inserting the new code; the anchor will move to the end of the insertion cursor.setKeepPositionOnInsert(True) super().paste() block = cursor.block() # Check if the thing to be pasted is multi-line. Use > not >= # to ensure we don't count it as multi-line if the cursor # is just at the beginning of the next block (consistent with # 'CodeEditor.doForSelectedLines') if cursor.selectionEnd() > block.position() + block.length(): # Now, check if in front of the current selection there is only whitespace if len(block.text()[:cursor.positionInBlock()].strip())==0: # Note that this 'smart pasting' will be a separate item on the # undo stack. This is intentional: the user can undo the 'smart paste' # without undoing the paste cursor2 = QtGui.QTextCursor(cursor) cursor2.setPosition(cursor2.position()) # put the anchor where the cursor is # Move cursor2 to beginning of the line (selecting the whitespace) # and remove the selection cursor2.movePosition(cursor2.StartOfBlock, cursor2.KeepAnchor) cursor2.removeSelectedText() # Set the textcursor of this editor to the just-pasted selection if keepSelection: cursor.setKeepPositionOnInsert(False) self.setTextCursor(cursor) pyzo-4.4.3/pyzo/codeeditor/extensions/calltip.py0000666000000000000000000001053313123037260020177 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. from ..qt import QtCore, QtGui, QtWidgets # noqa Qt = QtCore.Qt class Calltip(object): _styleElements = [('Editor.calltip', 'The style of the calltip. ', 'fore:#555, back:#ff9, border:1')] class __CalltipLabel(QtWidgets.QLabel): def __init__(self): QtWidgets.QLabel.__init__(self) # Start hidden self.hide() # Accept rich text self.setTextFormat(QtCore.Qt.RichText) # Show as tooltip self.setIndent(2) self.setWindowFlags(QtCore.Qt.ToolTip) def enterEvent(self, event): # Act a bit like a tooltip self.hide() def __init__(self, *args, **kwds): super(Calltip, self).__init__(*args, **kwds) # Create label for call tips self.__calltipLabel = self.__CalltipLabel() # Be notified of style updates self.styleChanged.connect(self.__afterSetStyle) # Prevents calltips from being shown immediately after pressing # the escape key. self.__noshow = False def __afterSetStyle(self): format = self.getStyleElementFormat('editor.calltip') ss = "QLabel { color:%s; background:%s; border:%ipx solid %s; }" % ( format['fore'], format['back'], int(format['border']), format['fore'] ) self.__calltipLabel.setStyleSheet(ss) def calltipShow(self, offset=0, richText='', highlightFunctionName=False): """ calltipShow(offset=0, richText='', highlightFunctionName=False) Shows the given calltip. Parameters ---------- offset : int The character offset to show the tooltip. richText : str The text to show (may contain basic html for markup). highlightFunctionName : bool If True the text before the first opening brace is made bold. default False. """ # Do not show the calltip if it was deliberately hidden by the # user. if self.__noshow: return # Process calltip text? if highlightFunctionName: i = richText.find('(') if i>0: richText = '{}{}'.format(richText[:i], richText[i:]) # Get a cursor to establish the position to show the calltip startcursor = self.textCursor() startcursor.movePosition(startcursor.Left, n=offset) # Get position in pixel coordinates rect = self.cursorRect(startcursor) pos = rect.topLeft() pos.setY( pos.y() - rect.height() - 1 ) # Move one above line pos.setX( pos.x() - 3) # Correct for border and indent pos = self.viewport().mapToGlobal(pos) # Set text and update font self.__calltipLabel.setText(richText) self.__calltipLabel.setFont(self.font()) # Use a qt tooltip to show the calltip if richText: self.__calltipLabel.move(pos) self.__calltipLabel.show() else: self.__calltipLabel.hide() def calltipCancel(self): """ calltipCancel() Hides the calltip. """ self.__calltipLabel.hide() def calltipActive(self): """ calltipActive() Get whether the calltip is currently active. """ return self.__calltipLabel.isVisible() def focusOutEvent(self, event): super(Calltip, self).focusOutEvent(event) self.__calltipLabel.hide() def keyPressEvent(self,event): # If the user presses Escape and the calltip is active, hide it if event.key() == Qt.Key_Escape and event.modifiers() == Qt.NoModifier \ and self.calltipActive(): self.calltipCancel() self.__noshow = True return if event.key() in [Qt.Key_ParenLeft, Qt.Key_ParenRight]: self.__noshow = False # Proceed processing the keystrike super(Calltip, self).keyPressEvent(event) pyzo-4.4.3/pyzo/codeeditor/extensions/__init__.py0000666000000000000000000000000113123037260020273 0ustar 00000000000000 pyzo-4.4.3/pyzo/codeeditor/highlighter.py0000666000000000000000000001135013131364447016655 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module highlighter Defines the highlighter class for the base code editor class. It will do the styling when syntax highlighting is enabled. If it is not, will only check out indentation. """ from .qt import QtGui, QtCore Qt = QtCore.Qt from . import parsers from .misc import ustr class BlockData(QtGui.QTextBlockUserData): """ Class to represent the data for a block. """ def __init__(self): QtGui.QTextBlockUserData.__init__(self) self.indentation = None self.fullUnderlineFormat = None self.tokens = [] # The highlighter should be part of the base class, because # some extensions rely on them (e.g. the indent guuides). class Highlighter(QtGui.QSyntaxHighlighter): def __init__(self,codeEditor,*args): QtGui.QSyntaxHighlighter.__init__(self,*args) # Store reference to editor self._codeEditor = codeEditor def getCurrentBlockUserData(self): """ getCurrentBlockUserData() Gets the BlockData object. Creates one if necesary. """ bd = self.currentBlockUserData() if not isinstance(bd, BlockData): bd = BlockData() self.setCurrentBlockUserData(bd) return bd def highlightBlock(self, line): """ highlightBlock(line) This method is automatically called when a line must be re-highlighted. If the code editor has an active parser. This method will use it to perform syntax highlighting. If not, it will only check out the indentation. """ # Make sure this is a Unicode Python string line = ustr(line) # Get previous state previousState = self.previousBlockState() # Get parser parser = None if hasattr(self._codeEditor, 'parser'): parser = self._codeEditor.parser() # Get function to get format nameToFormat = self._codeEditor.getStyleElementFormat fullLineFormat = None tokens = [] if parser: self.setCurrentBlockState(0) tokens = list(parser.parseLine(line, previousState)) for token in tokens : # Handle block state if isinstance(token, parsers.BlockState): self.setCurrentBlockState(token.state) else: # Get format try: styleFormat = nameToFormat(token.name) charFormat = styleFormat.textCharFormat except KeyError: #print(repr(nameToFormat(token.name))) continue # Set format self.setFormat(token.start,token.end-token.start,charFormat) # Is this a cell? if (fullLineFormat is None) and styleFormat._parts.get('underline','') == 'full': fullLineFormat = styleFormat # Get user data bd = self.getCurrentBlockUserData() # Store token list for future use (e.g. brace matching) bd.tokens = tokens # Handle underlines bd.fullUnderlineFormat = fullLineFormat # Get the indentation setting of the editors indentUsingSpaces = self._codeEditor.indentUsingSpaces() leadingWhitespace=line[:len(line)-len(line.lstrip())] if '\t' in leadingWhitespace and ' ' in leadingWhitespace: #Mixed whitespace bd.indentation = 0 format=QtGui.QTextCharFormat() format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline) format.setUnderlineColor(QtCore.Qt.red) format.setToolTip('Mixed tabs and spaces') self.setFormat(0,len(leadingWhitespace),format) elif ('\t' in leadingWhitespace and indentUsingSpaces) or \ (' ' in leadingWhitespace and not indentUsingSpaces): #Whitespace differs from document setting bd.indentation = 0 format=QtGui.QTextCharFormat() format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline) format.setUnderlineColor(QtCore.Qt.blue) format.setToolTip('Whitespace differs from document setting') self.setFormat(0,len(leadingWhitespace),format) else: # Store info for indentation guides # amount of tabs or spaces bd.indentation = len(leadingWhitespace) pyzo-4.4.3/pyzo/codeeditor/manager.py0000666000000000000000000002121213123037260015756 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Codeeditor is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module manager This module contains a static class that can be used for some management tasks. """ import os, sys from .qt import QtGui, QtCore, QtWidgets # noqa Qt = QtCore.Qt from . import parsers class Manager: """ Manager Static class to do some management tasks: * It manages the parsers * Getting style element descriptions of all parsers * Linking file extensions to parsers * Font information """ _defaultFontFamily = 'dummy_font_family_name' # Static dict of all parsers _parserInstances = {} _fileExtensions = {} ## Parsers # @classmethod # def collectParsersDynamically(cls): # """ insert the function is this module's namespace. # """ # # # Get the path of this subpackage # path = __file__ # path = os.path.dirname( os.path.abspath(path) ) # # # Determine if we're in a zipfile # i = path.find('.zip') # if i>0: # # get list of files from zipfile # path = path[:i+4] # z = zipfile.ZipFile(path) # files = [os.path.split(i)[-1] for i in z.namelist() # if 'codeeditor' in i and 'parsers' in i] # else: # # get list of files from file system # files = os.listdir(path) # # # Extract all parsers # parserModules = [] # for file in files: # # # Only python files # if file.endswith('.pyc'): # if file[:-1] in files: # continue # Only try import once # elif not file.endswith('.py'): # continue # # Only syntax files # if '_parser.' not in file: # continue # # # Import module # fullfile = os.path.join(path, file) # modname = os.path.splitext(file)[0] # print('modname', modname) # mod = __import__("codeeditor.parsers."+modname, fromlist=[modname]) # parserModules.append(mod) # # print(parserModules) @classmethod def _collectParsers(cls): """ _collectParsers() Collect all parser classes. This function is called on startup. """ # Prepare (use a set to prevent duplicates) foundParsers = set() G = parsers.__dict__ ModuleClass = os.__class__ # Collect parser classes for module_name in G: # Check if it is indeed a module, and if it has the right name if not isinstance(G[module_name], ModuleClass): continue if not module_name.endswith('_parser'): continue # Collect all valid classes from the module moduleDict = G[module_name].__dict__ for name_in_module in moduleDict: ob = moduleDict[name_in_module] if isinstance(ob, type) and issubclass(ob, parsers.Parser): foundParsers.add(ob) # Put in list with the parser names as keys parserInstances = {} for parserClass in foundParsers: name = parserClass.__name__ if name.endswith('Parser') and len(name)>6: # Get parser identifier name name = name[:-6].lower() # Try instantiating the parser try: parserInstances[name] = parserInstance = parserClass() except Exception: # We cannot get the exception object in a Python2/Python3 # compatible way print('Could not instantiate parser "%s".'%name) continue # Register extensions for this parser for ext in parserInstance.filenameExtensions(): cls._fileExtensions[ext] = name # Store cls._parserInstances = parserInstances @classmethod def getParserNames(cls): """ getParserNames() Get a list of all available parsers. """ return list(cls._parserInstances.keys()) @classmethod def getParserByName(cls, parserName): """ getParserByName(parserName) Get the parser object corresponding to the given name. If no parser is known by the given name, a warning message is printed and None is returned. """ if not parserName: return parsers.Parser() #Default dummy parser # Case insensitive parserName = parserName.lower() # Return instantiated parser object. if parserName in cls._parserInstances: return cls._parserInstances[parserName] else: print('Warning: no parser known by the name "%s".'%parserName) print('I know these: ', cls._parserInstances.keys()) return parsers.Parser() #Default dummy parser @classmethod def getStyleElementDescriptionsForAllParsers(cls): """ getStyleElementDescriptionsForAllParsers() Get all style element descriptions corresponding to the tokens of all parsers. This function is used by the code editor to register all syntax element styles to the code editor class. """ descriptions = {} for parser in cls._parserInstances.values(): for token in parser.getUsedTokens(): description = token.description descriptions[description.key] = description return list(descriptions.values()) ## File extensions @classmethod def suggestParserfromFilenameExtension(cls, ext): """ suggestParserfromFilenameExtension(ext) Given a filename extension, rerurns the name of the suggested parser corresponding to the language of the file. See also registerFilenameExtension() """ # Normalize ext ext = '.' + ext.lstrip('.').lower() # Get parser if ext in cls._fileExtensions: return cls._fileExtensions[ext] else: return '' @classmethod def registerFilenameExtension(cls, ext, parser): """ registerFilenameExtension(ext, parser) Registers the given filename extension to the given parser. The parser can be a Parser instance or its name. This function can be used to register extensions to parsers that are not registered by default. """ # Normalize ext ext = '.' + ext.lstrip('.').lower() # Check parser if isinstance(parser, parsers.Parser): parser = parser.name() # Register cls._fileExtensions[ext] = parser ## Fonts @classmethod def fontNames(cls): """ fontNames() Get a list of all monospace fonts available on this system. """ db = QtGui.QFontDatabase() QFont, QFontInfo = QtGui.QFont, QtGui.QFontInfo # fn = font_name (str) return [fn for fn in db.families() if QFontInfo(QFont(fn)).fixedPitch()] @classmethod def setDefaultFontFamily(cls, name): """ setDefaultFontFamily(name) Set the default (monospace) font family name for this system. This should be set only once during startup. """ cls._defaultFontFamily = name @classmethod def defaultFont(cls): """ defaultFont() Get the default (monospace) font for this system. Returns a QFont object. """ # Get font family f = QtGui.QFont(cls._defaultFontFamily) f.setStyleHint(f.TypeWriter, f.PreferDefault) fi = QtGui.QFontInfo(f) family = fi.family() # Get the font size size = 9 if sys.platform.startswith('darwin'): # Account for Qt font size difference # http://qt-project.org/forums/viewthread/27201 # Win/linux use 96 ppi, OS X uses 72 -> 133% ratio size = int(size*1.33333+0.4999) # Done return QtGui.QFont(family, size) # Init try: Manager._collectParsers() except Exception as why: print('Error collecting parsers') print(why) pyzo-4.4.3/pyzo/codeeditor/misc.py0000666000000000000000000000546713123037260015315 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module misc Defined ustr (Unicode string) class and the option property decorator. """ import sys from .qt import QtGui, QtCore, QtWidgets # noqa # Set Python version and get some names PYTHON_VERSION = sys.version_info[0] if PYTHON_VERSION < 3: ustr = unicode # noqa bstr = str from Queue import Queue, Empty else: ustr = str bstr = bytes from queue import Queue, Empty DEFAULT_OPTION_NAME = '_ce_default_value' DEFAULT_OPTION_NONE = '_+_just some absurd value_+_' def ce_option(arg1): """ Decorator for properties of the code editor. It should be used on the setter function, with its default value as an argument. The default value is then stored on the function object. At the end of the initialization, the base codeeditor class will check all members and (by using the default-value-attribute as a flag) select the ones that are options. These are then set to their default values. Similarly this information is used by the setOptions method to know which members are "options". """ # If the decorator is used without arguments, arg1 is the function # being decorated. If arguments are used, arg1 is the argument, and # we should return a callable that is then used as a decorator. # Create decorator function. def decorator_fun(f): f.__dict__[DEFAULT_OPTION_NAME] = default return f # Handle default = DEFAULT_OPTION_NONE if hasattr(arg1, '__call__'): return decorator_fun(arg1) else: default = arg1 return decorator_fun class _CallbackEventHandler(QtCore.QObject): """ Helper class to provide the callLater function. """ def __init__(self): QtCore.QObject.__init__(self) self.queue = Queue() def customEvent(self, event): while True: try: callback, args = self.queue.get_nowait() except Empty: break try: callback(*args) except Exception as why: print('callback failed: {}:\n{}'.format(callback, why)) def postEventWithCallback(self, callback, *args): self.queue.put((callback, args)) QtWidgets.qApp.postEvent(self, QtCore.QEvent(QtCore.QEvent.User)) def callLater(callback, *args): """ callLater(callback, *args) Post a callback to be called in the main thread. """ _callbackEventHandler.postEventWithCallback(callback, *args) # Create callback event handler instance and insert function in Pyzo namespace _callbackEventHandler = _CallbackEventHandler() pyzo-4.4.3/pyzo/codeeditor/parsers/0000777000000000000000000000000013166630335015464 5ustar 00000000000000pyzo-4.4.3/pyzo/codeeditor/parsers/cython_parser.py0000666000000000000000000000365113123037260020712 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. # Import tokens in module namespace from .python_parser import PythonParser, pythonKeywords # Set keywords cythonExtraKeywords = set(['cdef', 'cpdef', 'ctypedef', 'cimport', 'float', 'double', 'int', 'long']) class CythonParser(PythonParser): """ Parser for Cython/Pyrex. """ _extensions = ['pyi', '.pyx' , '.pxd'] _keywords = pythonKeywords | cythonExtraKeywords def _identifierState(self, identifier=None): """ Given an identifier returs the identifier state: 3 means the current identifier can be a function. 4 means the current identifier can be a class. 0 otherwise. This method enables storing the state during the line, and helps the Cython parser to reuse the Python parser's code. This implementation keeps a counter. If the counter is 0, the state is zero. """ if identifier is None: # Explicit get and reset state = 0 try: if self._idsCounter>0: state = self._idsState except Exception: pass self._idsState = 0 self._idsCounter = 0 return state elif identifier in ['def', 'cdef', 'cpdef']: # Set function state self._idsState = 3 self._idsCounter = 2 return 3 elif identifier == 'class': # Set class state self._idsState = 4 self._idsCounter = 1 return 4 elif self._idsCounter>0: self._idsCounter -= 1 return self._idsState else: # This one can be func or class, next one can't return 0 pyzo-4.4.3/pyzo/codeeditor/parsers/c_parser.py0000666000000000000000000001637713123037260017641 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import re from . import Parser, BlockState, text_type from .tokens import ALPHANUM from .tokens import (Token, CommentToken, StringToken, UnterminatedStringToken, IdentifierToken, NonIdentifierToken, KeywordToken, NumberToken) # todo: compiler directives (or how do you call these things starting with #) class MultilineCommentToken(CommentToken): """ Characters representing a multi-line comment. """ defaultStyle = 'fore:#007F00' class CharToken(Token): """ Single-quoted char """ defaultStyle = 'fore:#7F007F' # This regexp is used to find special stuff, such as comments, numbers and # strings. tokenProg = re.compile( '([' + ALPHANUM + '_]+)|' + # Identifiers/numbers (group 1) or '(\/\/)|' + # Single line comment (group 2) '(\/\*)|' + # Comment (group 3) or '(\'\\\\?.\')|' + # char (group 4) '(\")' # string (group 5) ) #For a string, get the RegExp #program that matches the end. (^|[^\\]) means: start of the line #or something that is not \ (since \ is supposed to escape the following #quote) (\\\\)* means: any number of two slashes \\ since each slash will #escape the next one stringEndProg = re.compile(r'(^|[^\\])(\\\\)*"') commentEndProg = re.compile(r'\*/') class CParser(Parser): """ A C parser. """ _extensions = ['.c', '.h', '.cpp', 'cxx', 'hxx'] _keywords = ['int', 'const', 'char', 'void', 'short', 'long', 'case'] def parseLine(self, line, previousState=0): """ parseLine(line, previousState=0) Parses a line of C code, yielding tokens. """ line = text_type(line) pos = 0 # Position following the previous match # identifierState and previousstate values: # 0: nothing special # 1: string # 2: multiline comment /* */ # First determine whether we should look for the end of a string, # or if we should process a token. if previousState == 1: token = StringToken(line, 0, 0) tokens = self._findEndOfString(line, token) # Process tokens for token in tokens: yield token if isinstance(token, BlockState): return pos = token.end elif previousState == 2: token = MultilineCommentToken(line, 0, 0) tokens = self._findEndOfComment(line, token) # Process tokens for token in tokens: yield token if isinstance(token, BlockState): return pos = token.end # Enter the main loop that iterates over the tokens and skips strings while True: # Get next tokens tokens = self._findNextToken(line, pos) if not tokens: return elif isinstance(tokens[-1], StringToken): moreTokens = self._findEndOfString(line, tokens[-1]) tokens = tokens[:-1] + moreTokens elif isinstance(tokens[-1], MultilineCommentToken): moreTokens = self._findEndOfComment(line, tokens[-1]) tokens = tokens[:-1] + moreTokens # Process tokens for token in tokens: yield token if isinstance(token, BlockState): return pos = token.end def _findEndOfComment(self, line, token): """ Find the matching comment end in the rest of the line """ # Do not use the start parameter of search, since ^ does not work then endMatch = commentEndProg.search(line, token.end) if endMatch: # The comment does end on this line token.end = endMatch.end() return [token] else: # The comment does not end on this line token.end = len(line) return [token, BlockState(2)] def _findEndOfString(self, line, token): """ Find the matching string end in the rest of the line """ # todo: distinguish between single and double quote strings # Find the matching end in the rest of the line # Do not use the start parameter of search, since ^ does not work then endMatch = stringEndProg.search(line[token.end:]) if endMatch: # The string does end on this line token.end = token.end + endMatch.end() return [token] else: # The string does not end on this line if line.strip().endswith("\\"): #Multi line string token = StringToken(line, token.start, len(line)) return [token, BlockState(1)] else: return [UnterminatedStringToken(line, token.start, len(line))] def _findNextToken(self, line, pos): """ _findNextToken(line, pos): Returns a token or None if no new tokens can be found. """ # Init tokens, if positing too large, stop now if pos > len(line): return None tokens = [] # Find the start of the next string or comment match = tokenProg.search(line, pos) # Process the Non-Identifier between pos and match.start() # or end of line nonIdentifierEnd = match.start() if match else len(line) # Return the Non-Identifier token if non-null token = NonIdentifierToken(line,pos,nonIdentifierEnd) if token: tokens.append(token) # If no match, we are done processing the line if not match: return tokens # The rest is to establish what identifier we are dealing with # Identifier ("a word or number") Find out whether it is a key word if match.group(1) is not None: identifier = match.group(1) tokenArgs = line, match.start(), match.end() if identifier in self._keywords: tokens.append( KeywordToken(*tokenArgs) ) elif identifier[0] in '0123456789': # identifierState = 0 tokens.append( NumberToken(*tokenArgs) ) else: tokens.append( IdentifierToken(*tokenArgs) ) # Single line comment elif match.group(2) is not None: tokens.append( CommentToken(line,match.start(),len(line)) ) elif match.group(3) is not None: tokens.append( MultilineCommentToken(line,match.start(),match.end()) ) elif match.group(4) is not None: # Char tokens.append( CharToken(line,match.start(),match.end()) ) else: # We have matched a string-start tokens.append( StringToken(line,match.start(),match.end()) ) # Done return tokens if __name__=='__main__': parser = CParser() for token in parser.parseLine('void test(int i=2) /* test '): print ("%s %s" % (token.name, token)) pyzo-4.4.3/pyzo/codeeditor/parsers/python_parser.py0000666000000000000000000003050713123037260020727 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import re from . import Parser, BlockState, text_type from .tokens import ALPHANUM from ..misc import ustr # Import tokens in module namespace from .tokens import (CommentToken, StringToken, UnterminatedStringToken, IdentifierToken, NonIdentifierToken, KeywordToken, NumberToken, FunctionNameToken, ClassNameToken, TodoCommentToken, OpenParenToken, CloseParenToken) # Keywords sets # Source: import keyword; keyword.kwlist (Python 2.6.6) python2Keywords = set(['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield']) # Source: import keyword; keyword.kwlist (Python 3.1.2) python3Keywords = set(['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']) # Merge the two sets to get a general Python keyword list pythonKeywords = python2Keywords | python3Keywords class MultilineStringToken(StringToken): """ Characters representing a multi-line string. """ defaultStyle = 'fore:#7F0000' class CellCommentToken(CommentToken): """ Characters representing a cell separator comment: "##". """ defaultStyle = 'bold:yes, underline:yes' # This regexp is used to find special stuff, such as comments, numbers and # strings. tokenProg = re.compile( '#|' + # Comment or '([' + ALPHANUM + '_]+)|' + # Identifiers/numbers (group 1) or '(' + # Begin of string group (group 2) '([bB]|[uU])?' + # Possibly bytes or unicode (py2.x) '[rR]?' + # Possibly a raw string '("""|\'\'\'|"|\')' + # String start (triple qoutes first, group 4) ')|' + # End of string group '(\(|\[|\{)|' + # Opening parenthesis (gr 5) '(\)|\]|\})' # Closing parenthesis (gr 6) ) #For a given type of string ( ', " , ''' , """ ),get the RegExp #program that matches the end. (^|[^\\]) means: start of the line #or something that is not \ (since \ is supposed to escape the following #quote) (\\\\)* means: any number of two slashes \\ since each slash will #escape the next one endProgs = { "'": re.compile(r"(^|[^\\])(\\\\)*'"), '"': re.compile(r'(^|[^\\])(\\\\)*"'), "'''": re.compile(r"(^|[^\\])(\\\\)*'''"), '"""': re.compile(r'(^|[^\\])(\\\\)*"""') } class PythonParser(Parser): """ Parser for Python in general (2.x or 3.x). """ _extensions = ['.py' , '.pyw'] #The list of keywords is overridden by the Python2/3 specific parsers _keywords = pythonKeywords def _identifierState(self, identifier=None): """ Given an identifier returs the identifier state: 3 means the current identifier can be a function. 4 means the current identifier can be a class. 0 otherwise. This method enables storing the state during the line, and helps the Cython parser to reuse the Python parser's code. """ if identifier is None: # Explicit get/reset try: state = self._idsState except Exception: state = 0 self._idsState = 0 return state elif identifier == 'def': # Set function state self._idsState = 3 return 3 elif identifier == 'class': # Set class state self._idsState = 4 return 4 else: # This one can be func or class, next one can't state = self._idsState self._idsState = 0 return state def parseLine(self, line, previousState=0): """ parseLine(line, previousState=0) Parse a line of Python code, yielding tokens. previousstate is the state of the previous block, and is used to handle line continuation and multiline strings. """ line = text_type(line) # Init pos = 0 # Position following the previous match # identifierState and previousstate values: # 0: nothing special # 1: multiline comment single qoutes # 2: multiline comment double quotes # 3: a def keyword # 4: a class keyword #Handle line continuation after def or class #identifierState is 3 or 4 if the previous identifier was 3 or 4 if previousState == 3 or previousState == 4: self._identifierState({3:'def',4:'class'}[previousState]) else: self._identifierState(None) if previousState in [1,2]: token = MultilineStringToken(line, 0, 0) token._style = ['', "'''", '"""'][previousState] tokens = self._findEndOfString(line, token) # Process tokens for token in tokens: yield token if isinstance(token, BlockState): return pos = token.end # Enter the main loop that iterates over the tokens and skips strings while True: # Get next tokens tokens = self._findNextToken(line, pos) if not tokens: return elif isinstance(tokens[-1], StringToken): moreTokens = self._findEndOfString(line, tokens[-1]) tokens = tokens[:-1] + moreTokens # Process tokens for token in tokens: yield token if isinstance(token, BlockState): return pos = token.end def _findEndOfString(self, line, token): """ _findEndOfString(line, token) Find the end of a string. Returns (token, endToken). The first is the given token or a replacement (UnterminatedStringToken). The latter is None, or the BlockState. If given, the line is finished. """ # Set state self._identifierState(None) # Find the matching end in the rest of the line # Do not use the start parameter of search, since ^ does not work then style = token._style endMatch = endProgs[style].search(line[token.end:]) if endMatch: # The string does end on this line tokenArgs = line, token.start, token.end + endMatch.end() if style in ['"""', "'''"]: token = MultilineStringToken(*tokenArgs) else: token.end = token.end + endMatch.end() return [token] else: # The string does not end on this line tokenArgs = line, token.start, token.end + len(line) if style == "'''": return [MultilineStringToken(*tokenArgs), BlockState(1)] elif style == '"""': return [MultilineStringToken(*tokenArgs), BlockState(2)] else: return [UnterminatedStringToken(*tokenArgs)] def _findNextToken(self, line, pos): """ _findNextToken(line, pos): Returns a token or None if no new tokens can be found. """ # Init tokens, if pos too large, were done if pos > len(line): return None tokens = [] # Find the start of the next string or comment match = tokenProg.search(line, pos) # Process the Non-Identifier between pos and match.start() # or end of line nonIdentifierEnd = match.start() if match else len(line) # Return the Non-Identifier token if non-null # todo: here it goes wrong (allow returning more than one token?) token = NonIdentifierToken(line,pos,nonIdentifierEnd) strippedNonIdentifier = ustr(token).strip() if token: tokens.append(token) # Do checks for line continuation and identifierState # Is the last non-whitespace a line-continuation character? if strippedNonIdentifier.endswith('\\'): lineContinuation = True # If there are non-whitespace characters after def or class, # cancel the identifierState if strippedNonIdentifier != '\\': self._identifierState(None) else: lineContinuation = False # If there are non-whitespace characters after def or class, # cancel the identifierState if strippedNonIdentifier != '': self._identifierState(None) # If no match, we are done processing the line if not match: if lineContinuation: tokens.append( BlockState(self._identifierState()) ) return tokens # The rest is to establish what identifier we are dealing with # Comment if match.group() == '#': matchStart = match.start() if not line[:matchStart].strip() and ( line[matchStart:].startswith('##') or line[matchStart:].startswith('#%%') or line[matchStart:].startswith('# %%')): tokens.append( CellCommentToken(line,matchStart,len(line)) ) elif self._isTodoItem(line[matchStart+1:]): tokens.append( TodoCommentToken(line,matchStart,len(line)) ) else: tokens.append( CommentToken(line,matchStart,len(line)) ) if lineContinuation: tokens.append( BlockState(self._identifierState()) ) return tokens # If there are non-whitespace characters after def or class, # cancel the identifierState (this time, also if there is just a \ # since apparently it was not on the end of a line) if strippedNonIdentifier != '': self._identifierState(None) # Identifier ("a word or number") Find out whether it is a key word if match.group(1) is not None: identifier = match.group(1) tokenArgs = line, match.start(), match.end() # Set identifier state identifierState = self._identifierState(identifier) if identifier in self._keywords: tokens.append( KeywordToken(*tokenArgs) ) elif identifier[0] in '0123456789': self._identifierState(None) tokens.append( NumberToken(*tokenArgs) ) else: if (identifierState==3 and line[match.end():].lstrip().startswith('(') ): tokens.append( FunctionNameToken(*tokenArgs) ) elif identifierState==4: tokens.append( ClassNameToken(*tokenArgs) ) else: tokens.append( IdentifierToken(*tokenArgs) ) elif match.group(2) is not None : # We have matched a string-start # Find the string style ( ' or " or ''' or """) token = StringToken(line, match.start(), match.end()) token._style = match.group(4) # The style is in match group 4 tokens.append( token ) elif match.group(5) is not None : token = OpenParenToken(line, match.start(), match.end()) token._style = match.group(5) tokens.append(token) elif match.group(6) is not None : token = CloseParenToken(line, match.start(), match.end()) token._style = match.group(6) tokens.append(token) # Done return tokens class Python2Parser(PythonParser): """ Parser for Python 2.x code. """ # The application should choose whether to set the Py 2 specific parser _extensions = [] _keywords = python2Keywords class Python3Parser(PythonParser): """ Parser for Python 3.x code. """ # The application should choose whether to set the Py 3 specific parser _extensions = [] _keywords = python3Keywords pyzo-4.4.3/pyzo/codeeditor/parsers/tokens.py0000666000000000000000000001154213123037260017333 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module tokens Defines the base Token class and a few generic tokens. Tokens are used by parsers to identify for groups of characters what they represent. This is in turn used by the highlighter to determine how these characters should be styled. """ # Many parsers need this ALPHANUM = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' from ..style import StyleFormat, StyleElementDescription from ..misc import ustr class Token(object): """ Token(line, start, end) Base token class. A token is a group of characters representing "something". What is represented, is specified by the subclass. Each token class should have a docstring describing the meaning of the characters it is applied to. """ defaultStyle = 'fore:#000, bold:no, underline:no, italic:no' isToken = True # For the BlockState object, which is also returned by the parsers, this is False def __init__(self, line='', start=0, end=0): self.line = ustr(line) self.start = start self.end = end self._name = self._getName() def __str__(self): # on 2.x we use __unicode__ return self.line[self.start:self.end] def __unicode__(self): # for py 2.x return self.line[self.start:self.end] def __repr__(self): return repr('%s:%s' % (self.name, self)) def __len__(self): # Defining a length also gives a Token a boolean value: True if there # are any characters (len!=0) and False if there are none return self.end - self.start def _getName(self): """ Get the name of this token. """ nameParts = ['Syntax'] if '_parser' in self.__module__: language = self.__module__.split('_')[0] language = language.split('.')[-1] nameParts.append( language[0].upper() + language[1:] ) nameParts.append( self.__class__.__name__[:-5].lower() ) return '.'.join(nameParts) def getDefaultStyleFormat(self): elements = [] def collect(cls): if hasattr(cls, 'defaultStyle'): elements.append(cls.defaultStyle) for c in cls.__bases__: collect(c) collect(self.__class__) se = StyleFormat() for e in reversed(elements): se.update(e) return se @property def name(self): """ The name of this token. Used to identify it and attach a style. """ return self._name @property def description(self): """ description() Returns a StyleElementDescription instance that describes the style element that this token represents. """ format = self.getDefaultStyleFormat() des = 'syntax: ' + self.__doc__ return StyleElementDescription(self.name, des, str(format)) class CommentToken(Token): """ Characters representing a comment in the code. """ defaultStyle = 'fore:#007F00' class TodoCommentToken(CommentToken): """ Characters representing a comment in the code. """ defaultStyle = 'fore:#E00,italic' class StringToken(Token): """ Characters representing a textual string in the code. """ defaultStyle = 'fore:#7F007F' class UnterminatedStringToken(StringToken): """ Characters belonging to an unterminated string. """ defaultStyle = 'underline:dotted' # todo: request from user: whitespace token class TextToken(Token): """ Anything that is not a string or comment. """ defaultStyle = 'fore:#000' class IdentifierToken(TextToken): """ Characters representing normal text (i.e. words). """ defaultStyle = '' class NonIdentifierToken(TextToken): """ Not a word (operators, whitespace, etc.). """ defaultStyle = '' class KeywordToken(IdentifierToken): """ A keyword is a word with a special meaning to the language. """ defaultStyle = 'fore:#00007F, bold:yes' class NumberToken(IdentifierToken): """ Characters represening a number. """ defaultStyle = 'fore:#007F7F' class FunctionNameToken(IdentifierToken): """ Characters represening the name of a function. """ defaultStyle = 'fore:#007F7F, bold:yes' class ClassNameToken(IdentifierToken): """ Characters represening the name of a class. """ defaultStyle = 'fore:#0000FF, bold:yes' class ParenthesisToken(Token) : """ Parenthesis (and square and curly brackets). """ defaultStyle = '' class OpenParenToken(ParenthesisToken) : """ Opening parenthesis (and square and curly brackets). """ defaultStyle = '' class CloseParenToken(ParenthesisToken) : """ Closing parenthesis (and square and curly brackets). """ defaultStyle = '' pyzo-4.4.3/pyzo/codeeditor/parsers/__init__.py0000666000000000000000000001324313123037260017567 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Codeeditor is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Subpackage parsers This subpackage contains all the syntax parsers for the different languages. """ """ CREATING PARSERS Making a parser requires these things: * Place a module in the parsers directory, which has a name ending in "_parser.py" * In the module implement one or more classes that inherit from ..parsers.Parser (or a derived class), and implement the parseLine method. * The module should import all the tokens in whiches to use from ..parsers.tokens. New tokens can also be defined by subclassing one of the token classes. * In codeeditor/parsers/__init__.py, add the new module to the list of imported parsers. """ import sys from . import tokens if sys.version_info[0] >= 3: text_type = str else: text_type = unicode # noqa class BlockState(object): """ BlockState(state=0, info=None) The blockstate object should be used by parsers to return the block state of the processed line. This would typically be the last item to be yielded, but this it may also be yielded befor the last yielded token. One can even yield multiple of these items, in which case the last one considered valid. """ isToken = False def __init__(self, state=0, info=None): self._state = int(state) self._info = info @property def state(self): """ The integer value representing the block state. """ return self._state @property def info(self): """ Get the information corresponding to the block. """ return self._info # Base parser class (needs to be defined before importing parser modules) class Parser(object): """ Base parser class. All parsers should inherit from this class. This base class generates a 'TextToken' for each line """ _extensions = [] _keywords = [] def parseLine(self, line, previousState=0): """ parseLine(line, previousState=0) The method that should be implemented by the parser. The previousState argument can be used to determine how the previous block ended (e.g. for multiline comments). It is an integer, the meaning of which is only known to the specific parser. This method should yield token instances. The last token can be a BlockState to specify the previousState for the next block. """ yield tokens.TextToken(line,0,len(line)) def name(self): """ name() Get the name of the parser. """ name = self.__class__.__name__.lower() if name.endswith('parser'): name = name[:-6] return name def __repr__(self): """ String representation of the parser. """ return '' % self.name() def keywords(self): """ keywords() Get a list of keywords valid for this parser. """ return [k for k in self._keywords] def filenameExtensions(self): """ filenameExtensions() Get a list of filename extensions for which this parser is appropriate. """ return ['.'+e.lstrip('.').lower() for e in self._extensions] def getStyleElementDescriptions(cls): """ getStyleElementDescriptions() This method returns a list of the StyleElementDescription instances used by this parser. """ descriptions = {} for token in cls.getUsedTokens(cls): descriptions[token.description.key] = token.description return list(descriptions.values()) def getUsedTokens(self): """ getUsedTokens() Get a a list of token instances used by this parser. """ # Get module object of the parser try: mod = sys.modules[self.__module__] except KeyError: return [] # Get token classes from module tokenClasses = [] for name in mod.__dict__: member = mod.__dict__[name] if isinstance(member, type) and \ issubclass(member, tokens.Token): if member is not tokens.Token: tokenClasses.append(member) # Return as instances return [t() for t in tokenClasses] def _isTodoItem(self, text): """ _isTodoItem(text) Get whether the given text (which should be a comment) represents a todo item. Todo items start with "todo", "2do" or "fixme", optionally with a colon at the end. """ # Get first word word = text.lstrip().split(' ',1)[0].rstrip(':') # Test if word.lower() in ['todo', '2do', 'fixme']: return True else: return False ## Import parsers statically # We could load the parser dynamically from the source files in the # directory, but this takes quite some effort to get righ when apps # are frozen. This is doable (I do it in Visvis) but it requires the # user to specify the parser modules by hand when freezing an app. # # In summary: it takes a lot of trouble, which can be avoided by just # listing all parsers here. from . import ( python_parser, # noqa cython_parser, # noqa c_parser, # noqa ) pyzo-4.4.3/pyzo/codeeditor/qt.py0000666000000000000000000000017213123037260014772 0ustar 00000000000000# This is the one place where codeeditor depends on Pyzo itself from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa pyzo-4.4.3/pyzo/codeeditor/style.py0000666000000000000000000002020013123037260015500 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Modyule style Provides basic functionaliy for styling. Styling is done using a dictionary of StyleFormat instances. Each such instance reprsents a certain element being styled (e.g. keywords, line numbers, indentation guides). All possible style elements are represented using StyleElementDescription instances. These have a name, description and default format, which makes it easy to build a UI to allow the user to change the syle. """ from .qt import QtGui, QtCore Qt = QtCore.Qt class StyleElementDescription: """ StyleElementDescription(name, defaultFormat, description) Describes a style element by its name, default format, and description. A style description is a simple placeholder for something that can be styled. """ def __init__(self, name, description, defaultFormat): self._name = name self._description = description self._defaultFormat = StyleFormat(defaultFormat) def __repr__(self): return '<"%s": "%s">' % (self.name, self.defaultFormat) @property def name(self): return self._name @property def key(self): return self._name.replace(' ', '').lower() @property def description(self): return self._description @property def defaultFormat(self): return self._defaultFormat class StyleFormat: """ StyleFormat(format='') Represents the style format for a specific style element. A "style" is a dictionary that maps names (of style elements) to StyleFormat instances. The given format can be a string or another StyleFormat instance. Style formats can be combined using their update() method. A style format consists of multiple parts, where each "part" consists of a key and a value. The keys can be anything, depending on what kind of thing is being styled. The value can be obtained using the index operator (e.g. styleFomat['fore']) For a few special keys, properties are defined that return the Qt object corresponding to the value. These values are also buffered to enable fast access. These keys are: * fore: (QColor) the foreground color * back: (QColor) the background color * bold: (bool) whether the text should be bold * italic: (bool) whether the text should be in italic * underline: (int) whether an underline should be used (and which one) * linestyle: (int) what line style to use (e.g. for indent guides) * textCharFOrmat: (QTextCharFormat) for the syntax styles The format neglects spaces and case. Parts are separated by commas or semicolons. If only a key is given it's value is interpreted as 'yes'. If only a color is given, its key is interpreted as 'fore' and back. Colors should be given using the '#' hex formatting. An example format string: 'fore:#334, bold, underline:dotLine' By calling str(styleFormatInstance) the string representing of the format can be obtained. By iterating over the instance, a series of key-value pairs is obtained. """ def __init__(self, format=''): self._parts = {} self.update(format) def _resetProperties(self): self._fore = None self._back = None self._bold = None self._italic = None self._underline = None self._linestyle = None self._textCharFormat = None def __str__(self): """ Get a (cleaned up) string representation of this style format. """ parts = [] for key in self._parts: parts.append('%s:%s' % (key, self._parts[key])) return ', '.join(parts) def __repr__(self): return '' % str(self) def __getitem__(self, key): try: return self._parts[key] except KeyError: raise KeyError('Invalid part key for style format.') def __iter__(self): """ Yields a series of tuples (key, val). """ parts = [] for key in self._parts: parts.append( (key, self._parts[key]) ) return parts.__iter__() def update(self, format): """ update(format) Update this style format with the given format. """ # Reset buffered values self._resetProperties() # Make a string, so we update the format with the given one if isinstance(format, StyleFormat): format = str(format) # Split on ',' and ',', ignore spaces styleParts = [p for p in format.replace('=',':').replace(';',',').split(',')] for stylePart in styleParts: # Make sure it consists of identifier and value pair # e.g. fore:#xxx, bold:yes, underline:no if not ':' in stylePart: if stylePart.startswith('#'): stylePart = 'foreandback:' + stylePart else: stylePart += ':yes' # Get key value and strip and make lowecase key, _, val = [i.strip().lower() for i in stylePart.partition(':')] # Store in parts if key == 'foreandback': self._parts['fore'] = val self._parts['back'] = val elif key: self._parts[key] = val ## Properties def _getValueSafe(self, key): try: return self._parts[key] except KeyError: return 'no' @property def fore(self): if self._fore is None: self._fore = QtGui.QColor(self._parts['fore']) return self._fore @property def back(self): if self._back is None: self._back = QtGui.QColor(self._parts['back']) return self._back @property def bold(self): if self._bold is None: if self._getValueSafe('bold') in ['yes', 'true']: self._bold = True else: self._bold = False return self._bold @property def italic(self): if self._italic is None: if self._getValueSafe('italic') in ['yes', 'true']: self._italic = True else: self._italic = False return self._italic @property def underline(self): if self._underline is None: val = self._getValueSafe('underline') if val in ['yes', 'true']: self._underline = QtGui.QTextCharFormat.SingleUnderline elif val in ['dotted', 'dots', 'dotline']: self._underline = QtGui.QTextCharFormat.DotLine elif val in ['wave']: self._underline = QtGui.QTextCharFormat.WaveUnderline else: self._underline = QtGui.QTextCharFormat.NoUnderline return self._underline @property def linestyle(self): if self._linestyle is None: val = self._getValueSafe('linestyle') if val in ['yes', 'true']: self._linestyle = Qt.SolidLine elif val in ['dotted', 'dot', 'dots', 'dotline']: self._linestyle = Qt.DotLine elif val in ['dashed', 'dash', 'dashes', 'dashline']: self._linestyle = Qt.DashLine else: self._linestyle = Qt.SolidLine # default to solid return self._linestyle @property def textCharFormat(self): if self._textCharFormat is None: self._textCharFormat = QtGui.QTextCharFormat() self._textCharFormat.setForeground(self.fore) self._textCharFormat.setUnderlineStyle(self.underline) if self.bold: self._textCharFormat.setFontWeight(QtGui.QFont.Bold) if self.italic: self._textCharFormat.setFontItalic(True) return self._textCharFormat pyzo-4.4.3/pyzo/codeeditor/textutils.py0000666000000000000000000001326613123037260016423 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. class TextReshaper: """ Object to reshape a piece of text, taking indentation, paragraphs, comments and bulletpoints into account. """ def __init__(self, lw, margin=3): self.lw = lw self.margin = margin self._lines1 = [] self._lines2 = [] self._wordBuffer = [] self._charsInBuffer = -1 # First word ads one extra self._pendingPrefix = None # A one-shot prefix self._currentPrefix = None # The prefix used until a new prefix is set @classmethod def reshapeText(cls, text, lw): tr = cls(lw) tr.pushText(text) return tr.popText() def pushLine(self, line): """ Push a single line to the input. """ self._lines1.append(line.rstrip()) def pushText(self, text): """ Push a (multiline) text to the input. """ for line in text.splitlines(): self.pushLine(line) def popLines(self): """ Get all available lines from the output. """ try: while True: self._popLine() except StopIteration: self._flush() return [line for line in self._lines2] def popText(self): """ Get all text from the output (i.e. lines joined with newline). """ return '\n'.join(self.popLines()) def _prefixString(self): if self._pendingPrefix is not None: prefix = self._pendingPrefix self._pendingPrefix = None return prefix else: return self._currentPrefix or '' def _addWordToBuffer(self, word): self._wordBuffer.append(word) self._charsInBuffer += len(word) + 1 # add one for space def _flush(self): if self._wordBuffer: self._lines2.append(self._prefixString() + ' '.join(self._wordBuffer)) self._wordBuffer, self._charsInBuffer = [], -1 def _addNewParagraph(self): # Flush remaining words self._flush() # Create empty line prefix = self._currentPrefix or '' prefix = ' ' * len(prefix) self._lines2.append(prefix) # Allow new prefix self._currentPrefix = None def _popLine(self): """ Pop a line from the input. Examine how it starts and convert it to words. """ # Pop line try: line = self._lines1.pop(0) except IndexError: raise StopIteration() # Strip the line strippedline1 = line.lstrip() strippedline2 = line.lstrip(' \t#*') # Analyze this line (how does it start?) if not strippedline1: self._addNewParagraph() return elif strippedline1.startswith('* '): self._flush() indent = len(line) - len(strippedline1) linePrefix = line[:indent] self._pendingPrefix = linePrefix + '* ' self._currentPrefix = linePrefix + ' ' else: # Hey, an actual line! Determine prefix indent = len(line) - len(strippedline1) linePrefix = line[:indent] # Check comments if strippedline1.startswith('#'): linePrefix += '# ' # What to do now? if linePrefix != self._currentPrefix: self._flush() self._currentPrefix = linePrefix # Process words one by one... for word in strippedline2.split(' '): self._addWordToBuffer(word) currentLineWidth = self._charsInBuffer + len(self._currentPrefix) if currentLineWidth < self.lw: # Not enough words in buffer yet pass elif len(self._wordBuffer) > 1: # Enough words to compose a line marginWith = currentLineWidth - self.lw marginWithout = self.lw - (currentLineWidth - len(word)) if marginWith < marginWithout and marginWith < self.margin: # add all buffered words self._flush() else: # add all buffered words (except last) self._wordBuffer.pop(-1) self._flush() self._addWordToBuffer(word) else: # This single word covers more than one line self._flush() testText = """ # This is a piece # of comment Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. # Indented comments # should work # as well skdb-a-very-long-word-ksdbfksasdvbassdfhjsdfbjdfbvhjdbvhjbdfhjvbdfjbvjdfbvjdfbvjdbfvj A change in indentation makes it a separate line sdckj bsdkjcb sdc sdckj foo bar aap noot mies * Bullet points are preserved * Even if they are very long the should be preserved. I know that brevity is a great virtue but you know, sometimes you just need those extra words to make a point. """ if __name__ == '__main__': print(TextReshaper.reshapeText(testText, 70)) pyzo-4.4.3/pyzo/codeeditor/_test.py0000666000000000000000000000377013123037260015473 0ustar 00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ This script runs a test for the code editor component. """ import os, sys from qt import QtGui, QtCore, QtWidgets Qt = QtCore.Qt ## Go up one directory and then import the codeeditor package os.chdir('..') sys.path.insert(0,'.') from codeeditor import CodeEditor if __name__=='__main__': app = QtWidgets.QApplication([]) # Create editor instance e = CodeEditor(highlightCurrentLine = True, longLineIndicatorPosition = 20, showIndentationGuides = True, showWhitespace = True, showLineEndings = True, wrap = True, showLineNumbers = True) QtWidgets.QShortcut(QtGui.QKeySequence("F1"), e).activated.connect(e.autocompleteShow) QtWidgets.QShortcut(QtGui.QKeySequence("F2"), e).activated.connect(e.autocompleteCancel) QtWidgets.QShortcut(QtGui.QKeySequence("F3"), e).activated.connect(lambda: e.calltipShow(0, 'test(foo, bar)')) QtWidgets.QShortcut(QtGui.QKeySequence("Shift+Tab"), e).activated.connect(e.dedentSelection) # Shift + Tab #TODO: somehow these shortcuts don't work in this test-app, but they do in # pyzo. May have something to do with overriding slots of Qt-native objects? QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+C"), e).activated.connect(e.copy) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+X"), e).activated.connect(e.cut) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+V"), e).activated.connect(e.paste) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Shift+V"), e).activated.connect(e.pasteAndSelect) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Z"), e).activated.connect(e.undo) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Y"), e).activated.connect(e.redo) e.setPlainText("foo(bar)\nfor bar in range(5):\n print bar\n" + "\nclass aap:\n def monkey(self):\n pass\n\n") # Run application e.show() s=QtWidgets.QSplitter() s.addWidget(e) s.addWidget(QtWidgets.QLabel('test')) s.show() app.exec_() pyzo-4.4.3/pyzo/codeeditor/__init__.py0000666000000000000000000000525313123037260016112 0ustar 00000000000000# -*- coding: utf-8 -*- # flake8: noqa # Copyright (C) 2013, the codeeditor development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ CodeEditor A full featured code editor component based on QPlainTextEdit. """ from .manager import Manager from .base import CodeEditorBase from .extensions.appearance import ( HighlightMatchingBracket, HighlightMatchingOccurrences, HighlightCurrentLine, FullUnderlines, IndentationGuides, CodeFolding, LongLineIndicator, ShowWhitespace, ShowLineEndings, Wrap, LineNumbers, SyntaxHighlighting, BreakPoints, ) from .extensions.behaviour import ( Indentation, HomeKey, EndKey, NumpadPeriodKey, AutoIndent, PythonAutoIndent, SmartCopyAndPaste, MoveLinesUpDown, ScrollWithUpDownKeys, ) from .extensions.autocompletion import AutoCompletion from .extensions.calltip import Calltip # Order of superclasses: first the extensions, then CodeEditorBase # The first superclass is the first extension that gets to handle each key and # the first to receive paint events. class CodeEditor( HighlightCurrentLine, HighlightMatchingOccurrences, HighlightMatchingBracket, FullUnderlines, IndentationGuides, CodeFolding, LongLineIndicator, ShowWhitespace, ShowLineEndings, Wrap, BreakPoints, LineNumbers, AutoCompletion, #Escape: first remove autocompletion, Calltip, #then calltip Indentation, MoveLinesUpDown, ScrollWithUpDownKeys, HomeKey, EndKey, NumpadPeriodKey, AutoIndent, PythonAutoIndent, SyntaxHighlighting, SmartCopyAndPaste, # overrides cut(), copy(), paste() CodeEditorBase, #CodeEditorBase must be the last one in the list ): """ CodeEditor with all the extensions """ pass pyzo-4.4.3/pyzo/contributors.txt0000666000000000000000000000057712703204205015160 0ustar 00000000000000### Project coordination Almar Klein ### Development Rob Reilink Ludo Visser Gijs van Oort David Salter Scott Logan Laurent Signac Jan Müller Jason Sexauer Julien Enselme Yann Salmon Ghislain Vaillant (Debian building) ### Translators Hector Mtz-Seara Monné - Spanish and Catalan Laurent Signac - French Diogo Costa - Portuguese Gerd Schmitt - German George Volkov - Russian pyzo-4.4.3/pyzo/core/0000777000000000000000000000000013166630335012614 5ustar 00000000000000pyzo-4.4.3/pyzo/core/about.py0000666000000000000000000001104713123037260014272 0ustar 00000000000000 import os import sys from pyzo.util.qt import QtCore, QtGui, QtWidgets from pyzo.util import qt import pyzo from pyzo.util import paths class AboutDialog(QtWidgets.QDialog): def __init__(self, parent): QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle(pyzo.translate("menu dialog", "About Pyzo")) self.resize(600,500) # Layout layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) # Create image and title im = QtGui.QPixmap( os.path.join(pyzo.pyzoDir, 'resources', 'appicons', 'pyzologo64.png') ) imlabel = QtWidgets.QLabel(self) imlabel.setPixmap(im) textlabel = QtWidgets.QLabel(self) textlabel.setText('

Pyzo: the Interactive Editor for Python

') # titleLayout = QtWidgets.QHBoxLayout() titleLayout.addWidget(imlabel, 0) titleLayout.addWidget(textlabel, 1) # layout.addLayout(titleLayout, 0) # Create tab bar self._tabs = QtWidgets.QTabWidget(self) self._tabs.setDocumentMode(True) layout.addWidget(self._tabs, 1) # Create button box self._butBox = QtWidgets.QDialogButtonBox(self) self._butBox.setOrientation(QtCore.Qt.Horizontal) self._butBox.setStandardButtons(self._butBox.Close) layout.addWidget(self._butBox, 0) # Signals self._butBox.rejected.connect(self.close) # Create tabs self.createGeneralTab() self.createContributorsTab() self.createLicenseTab() def addTab(self, title, text, rich=True): # Create label to show info label = QtWidgets.QTextEdit(self) label.setLineWrapMode(label.WidgetWidth) label.setReadOnly(True) # Set text if rich: label.setHtml(text) else: label.setText(text) # Add to tab bar self._tabs.addTab(label, title) # Done return label def createGeneralTab(self): aboutText = """ {}

Version info
Pyzo version: {}
Platform: {}
Python version: {}
Qt version: {}
{} version: {}

Pyzo directories
Pyzo source directory: {}
Pyzo userdata directory: {}

Acknowledgements
Pyzo is written in Python 3 and uses the Qt widget toolkit. Pyzo uses code and concepts that are inspired by IPython, Pype, and Spyder. Pyzo uses a (modified) subset of the silk icon set, by Mark James (http://www.famfamfam.com/lab/icons/silk/). """ # Determine if this is PyQt4 or Pyside qtWrapper = qt.API_NAME qtVersion = qt.QT_VERSION qtWrapperVersion = qt.PYSIDE_VERSION or qt.PYQT_VERSION # Insert information texts if paths.is_frozen(): versionText = pyzo.__version__ + ' (binary)' else: versionText = pyzo.__version__ + ' (source)' aboutText = aboutText.format('Pyzo - Python to the people!', versionText, sys.platform, sys.version.split(' ')[0], qtVersion, qtWrapper, qtWrapperVersion, pyzo.pyzoDir, pyzo.appDataDir) self.addTab("General", aboutText) def createContributorsTab(self): fname = os.path.join(pyzo.pyzoDir, 'contributors.txt') try: with open(fname, 'rb') as f: text = f.read().decode('utf-8', 'ignore').strip() except Exception as err: text = str(err) label = self.addTab('Contributors', text, False) # Decrease font font = label.font() font.setPointSize(int(font.pointSize()*0.9)) label.setFont(font) def createLicenseTab(self): fname = os.path.join(pyzo.pyzoDir, 'license.txt') try: with open(fname, 'rb') as f: text = f.read().decode('utf-8', 'ignore').strip() except Exception as err: text = str(err) label = self.addTab('BSD license', text, False) # Decrease font font = label.font() font.setPointSize(int(font.pointSize()*0.9)) label.setFont(font) if __name__ == '__main__': #pyzo.license = {'name': 'AK', 'company': ''} m = AboutDialog(None) m.show() pyzo-4.4.3/pyzo/core/assistant.py0000666000000000000000000002155613123037260015177 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # Author: Windel Bouwman # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Tool that can view qt help files via the qthelp engine. Run make_docs.sh from: https://bitbucket.org/windel/qthelpdocs Copy the "docs" directory to the pyzo root! """ from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa from pyzo import getResourceDirs import os help_help = """

Documentation

Welcome to the Pyzo assistant. Pyzo uses the Qt Help system for documentation. This is also used by the Qt Assistant. You can use this viewer to view documentation provided by other projects.

Add documentation

To add documentation to Pyzo, go to the settings tab and select add. Then select a Qt Compressed Help file (*.qch). qch-files can be found in the Qt installation directory (for example in /usr/share/doc/qt under linux). For other projects you can download pre-build qch files from here: https://github.com/windelbouwman/qthelpdocs/releases.

Note When a documentation file is added, it is not copied into the pyzo settings dir, so you have to leave this file in place.

""" tool_name = "Assistant" tool_summary = "Browse qt help documents" class Settings(QtWidgets.QWidget): def __init__(self, engine): super().__init__() self._engine = engine layout = QtWidgets.QVBoxLayout(self) add_button = QtWidgets.QPushButton("Add") del_button = QtWidgets.QPushButton("Delete") self._view = QtWidgets.QListView() layout.addWidget(self._view) layout2 = QtWidgets.QHBoxLayout() layout2.addWidget(add_button) layout2.addWidget(del_button) layout.addLayout(layout2) self._model = QtCore.QStringListModel() self._view.setModel(self._model) self._model.setStringList(self._engine.registeredDocumentations()) add_button.clicked.connect(self.add_doc) del_button.clicked.connect(self.del_doc) def add_doc(self): doc_file = QtWidgets.QFileDialog.getOpenFileName( self, "Select a compressed help file", filter="Qt compressed help files (*.qch)") if isinstance(doc_file, tuple): doc_file = doc_file[0] self.add_doc_do(doc_file) def add_doc_do(self, doc_file): ok = self._engine.registerDocumentation(doc_file) if ok: self._model.setStringList(self._engine.registeredDocumentations()) else: QtWidgets.QMessageBox.critical(self, "Error", "Error loading doc") def del_doc(self): idx = self._view.currentIndex() if idx.isValid(): doc_file = self._model.data(idx, QtCore.Qt.DisplayRole) self.del_doc_do(doc_file) def del_doc_do(self, doc_file): self._engine.unregisterDocumentation(doc_file) self._model.setStringList(self._engine.registeredDocumentations()) class HelpBrowser(QtWidgets.QTextBrowser): """ Override textbrowser to implement load resource """ def __init__(self, engine): super().__init__() self._engine = engine # Override default navigation behavior: self.anchorClicked.connect(self.handle_url) self.setOpenLinks(False) def handle_url(self, url): """ Open external urls not in this viewer """ if url.scheme() in ['http', 'https']: QtGui.QDesktopServices.openUrl(url) else: self.setSource(url) def loadResource(self, typ, url): if url.scheme() == "qthelp": return self._engine.fileData(url) else: return super().loadResource(typ, url) class PyzoAssistant(QtWidgets.QWidget): """ Show help contents and browse qt help files. """ def __init__(self, parent=None, collection_filename=None): """ Initializes an assistance instance. When collection_file is none, it is determined from the appDataDir. """ from pyzo.util.qt import QtHelp super().__init__(parent) self.setWindowTitle('Help') pyzoDir, appDataDir = getResourceDirs() if collection_filename is None: # Collection file is stored in pyzo data dir: collection_filename = os.path.join(appDataDir, 'tools', 'docs.qhc') self._engine = QtHelp.QHelpEngine(collection_filename) # Important, call setup data to load the files: self._engine.setupData() # If no files are loaded, register at least the pyzo docs: if len(self._engine.registeredDocumentations()) == 0: doc_file = os.path.join(pyzoDir, 'resources', 'pyzo.qch') self._engine.registerDocumentation(doc_file) # The main players: self._content = self._engine.contentWidget() self._index = self._engine.indexWidget() self._indexTab = QtWidgets.QWidget() il = QtWidgets.QVBoxLayout(self._indexTab) filter_text = QtWidgets.QLineEdit() il.addWidget(filter_text) il.addWidget(self._index) self._helpBrowser = HelpBrowser(self._engine) self._searchEngine = self._engine.searchEngine() self._settings = Settings(self._engine) self._progress = QtWidgets.QWidget() pl = QtWidgets.QHBoxLayout(self._progress) bar = QtWidgets.QProgressBar() bar.setMaximum(0) pl.addWidget(QtWidgets.QLabel('Indexing')) pl.addWidget(bar) self._searchResultWidget = self._searchEngine.resultWidget() self._searchQueryWidget = self._searchEngine.queryWidget() self._searchTab = QtWidgets.QWidget() search_layout = QtWidgets.QVBoxLayout(self._searchTab) search_layout.addWidget(self._searchQueryWidget) search_layout.addWidget(self._searchResultWidget) tab = QtWidgets.QTabWidget() tab.addTab(self._content, "Contents") tab.addTab(self._indexTab, "Index") tab.addTab(self._searchTab, "Search") tab.addTab(self._settings, "Settings") splitter = QtWidgets.QSplitter(self) splitter.addWidget(tab) splitter.addWidget(self._helpBrowser) layout = QtWidgets.QVBoxLayout(self) layout.addWidget(splitter) layout.addWidget(self._progress) # Connect clicks: self._content.linkActivated.connect(self._helpBrowser.setSource) self._index.linkActivated.connect(self._helpBrowser.setSource) self._searchEngine.searchingFinished.connect(self.onSearchFinish) self._searchEngine.indexingStarted.connect(self.onIndexingStarted) self._searchEngine.indexingFinished.connect(self.onIndexingFinished) filter_text.textChanged.connect(self._index.filterIndices) self._searchResultWidget.requestShowLink.connect(self._helpBrowser.setSource) self._searchQueryWidget.search.connect(self.goSearch) # Always re-index on startup: self._searchEngine.reindexDocumentation() self._search_term = None # Show initial page: # self.showHelpForTerm('welcome to pyzo') self._helpBrowser.setHtml(help_help) def goSearch(self): query = self._searchQueryWidget.query() self._searchEngine.search(query) def onIndexingStarted(self): self._progress.show() def onIndexingFinished(self): self._progress.hide() def find_best_page(self, hits): if self._search_term is None: url, _ = hits[0] return url try: # Try to find max with fuzzy wuzzy: from fuzzywuzzy import fuzz url, title = max(hits, key=lambda hit: fuzz.ratio(hit[1], self._search_term)) return url except ImportError: pass # Find exact page title: for url2, page_title in hits: if page_title == self._search_term: url = url2 return url for url2, page_title in hits: if self._search_term in page_title: url = url2 return url # Pick first hit: url, _ = hits[0] return url def onSearchFinish(self, hits): if hits == 0: return hits = self._searchEngine.hits(0, hits) if not hits: return url = self.find_best_page(hits) self._helpBrowser.setSource(QtCore.QUrl(url)) def showHelpForTerm(self, name): from pyzo.util.qt import QtHelp # Cache for later use: self._search_term = name # Create a query: query = QtHelp.QHelpSearchQuery(QtHelp.QHelpSearchQuery.DEFAULT, [name]) self._searchEngine.search([query]) if __name__ == '__main__': app = QtWidgets.QApplication([]) view = PyzoAssistant() view.show() app.exec() pyzo-4.4.3/pyzo/core/baseTextCtrl.py0000666000000000000000000005346613124444540015603 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module baseTextCtrl Defines the base text control to be inherited by the shell and editor classes. Implements styling, introspection and a bit of other stuff that is common for both shells and editors. """ import pyzo import os, time from pyzo.core.pyzoLogging import print import pyzo.codeeditor.parsers.tokens as Tokens from pyzo.util.qt import QtCore, QtGui, QtWidgets qt = QtGui # Define style stuff subStyleStuff = {} #subStyleStuff = { 'face': Qsci.QsciScintillaBase.SCI_STYLESETFONT , # 'fore': Qsci.QsciScintillaBase.SCI_STYLESETFORE, # 'back': Qsci.QsciScintillaBase.SCI_STYLESETBACK, # 'size': Qsci.QsciScintillaBase.SCI_STYLESETSIZE, # 'bold': Qsci.QsciScintillaBase.SCI_STYLESETBOLD, # 'italic': Qsci.QsciScintillaBase.SCI_STYLESETITALIC, # 'underline': Qsci.QsciScintillaBase.SCI_STYLESETUNDERLINE} def normalizePath(path): """ Normalize the path given. All slashes will be made the same (and doubles removed) The real case as stored on the file system is recovered. Returns None on error. """ # normalize path = os.path.abspath(path) # make sure it is defined from the drive up path = os.path.normpath(path) # If does not exist, return as is. # This also happens if the path's case is incorrect and the # file system is case sensitive. That's ok, because the stuff we # do below is intended to get the path right on case insensitive # file systems. if not os.path.isfile(path): return path # split drive name from the rest drive, rest = os.path.splitdrive(path) fullpath = drive.upper() + os.sep # make lowercase and split in parts parts = rest.lower().split(os.sep) parts = [part for part in parts if part] for part in parts: options = [x for x in os.listdir(fullpath) if x.lower()==part] if len(options) > 1: print("Error normalizing path: Ambiguous path names!") return path elif not options: print("Invalid path (part %s) in %s" % (part, fullpath)) return path fullpath = os.path.join(fullpath, options[0]) # remove last sep return fullpath def parseLine_autocomplete(tokens): """ Given a list of tokens (from start to cursor position) returns a tuple (base, name). autocomp_parse("eat = banan") -> "", "banan" ...("eat = food.fruit.ban") -> "food.fruit", "ban" When no match found, both elements are an empty string. """ if not len(tokens): return "","" if isinstance(tokens[-1],Tokens.NonIdentifierToken) and str(tokens[-1])=='.': name = '' elif isinstance(tokens[-1],(Tokens.IdentifierToken,Tokens.KeywordToken)): name = str(tokens[-1]) else: return '','' needle = '' #Now go through the remaining tokens in reverse order for token in tokens[-2::-1]: if isinstance(token,Tokens.NonIdentifierToken) and str(token)=='.': needle = str(token) + needle elif isinstance(token,(Tokens.IdentifierToken,Tokens.KeywordToken)): needle = str(token) + needle else: break if needle.endswith('.'): needle = needle[:-1] return needle, name def parseLine_signature(tokens): """ Given a list of tokens (from start to cursor position) returns a tuple (name, needle, stats). stats is another tuple: - location of end bracket - amount of kommas till cursor (taking nested brackets into account) """ openBraces = [] #Positions at which braces are opened for token in tokens: if not isinstance(token, (Tokens.NonIdentifierToken, Tokens.OpenParenToken)): continue for i, c in enumerate(str(token)): if c=='(': openBraces.append(token.start + i) elif c==')': if len(openBraces): openBraces.pop() if len(openBraces): i = openBraces[-1] # Now trim the token list up to (but not inculding) position of openBraces tokens = list(filter(lambda token: token.start < i, tokens)) # Trim the last token if len(tokens): tokens[-1].end = i name, needle = parseLine_autocomplete(tokens) return name, needle, (i,0) #TODO: implement stats return "","",(0,0) class KeyEvent: """ A simple class for easier key events. """ def __init__(self, key): self.key = key try: self.char = chr(key) except ValueError: self.char = "" def makeBytes(text): """ Make sure the argument is bytes, converting with UTF-8 encoding if it is a string. """ if isinstance(text, bytes): return text elif isinstance(text, str): return text.encode('utf-8') else: raise ValueError("Expected str or bytes!") _allScintillas = [] def getAllScintillas(): """ Get a list of all the scintialla editing components that derive from BaseTextCtrl. Used mainly by the menu. """ for i in reversed(range(len(_allScintillas))): e = _allScintillas[i]() if e is None: _allScintillas.pop(i) else: yield e pyzo.getAllScintillas = getAllScintillas from pyzo import codeeditor class BaseTextCtrl(codeeditor.CodeEditor): """ The base text control class. Inherited by the shell class and the Pyzo editor. The class implements autocompletion, calltips, and auto-help Inherits from QsciScintilla. I tried to clean up the rather dirty api by using more sensible names. Hereby I apply the following rules: - if you set something, the method starts with "set" - if you get something, the method starts with "get" - a position is the integer position fron the start of the document - a linenr is the number of a line, an index the position on that line - all the above indices apply to the bytes (encoded utf-8) in which the text is stored. If you have unicode text, they do not apply! - the method name mentions explicityly what you get. getBytes() returns the bytes of the document, getString() gets the unicode string that it represents. This applies to the get-methods. the set-methods use the term text, and automatically convert to bytes using UTF-8 encoding when a string is given. """ def __init__(self, *args, **kwds): super().__init__(*args, **kwds) # Set font and zooming self.setFont(pyzo.config.view.fontname) self.setZoom(pyzo.config.view.zoom) # Create timer for autocompletion delay self._delayTimer = QtCore.QTimer(self) self._delayTimer.setSingleShot(True) self._delayTimer.timeout.connect(self._introspectNow) # For buffering autocompletion and calltip info self._callTipBuffer_name = '' self._callTipBuffer_time = 0 self._callTipBuffer_result = '' self._autoCompBuffer_name = '' self._autoCompBuffer_time = 0 self._autoCompBuffer_result = [] self.setAutoCompletionAcceptKeysFromStr(pyzo.config.settings.autoComplete_acceptKeys) self.completer().highlighted.connect(self.updateHelp) self.setIndentUsingSpaces(pyzo.config.settings.defaultIndentUsingSpaces) self.setIndentWidth(pyzo.config.settings.defaultIndentWidth) self.setAutocompletPopupSize(*pyzo.config.view.autoComplete_popupSize) def setAutoCompletionAcceptKeysFromStr(self, keys): """ Set the keys that can accept an autocompletion from a comma delimited string. """ # Set autocomp accept key to default if necessary. # We force it to be string (see issue 134) if not isinstance(keys, str): keys = 'Tab' # Split keys = keys.replace(',', ' ').split(' ') keys = [key for key in keys if key] # Set autocomp accept keys qtKeys = [] for key in keys: if len(key) > 1: key = 'Key_' + key[0].upper() + key[1:].lower() qtkey = getattr(QtCore.Qt, key, None) else: qtkey = ord(key) if qtkey: qtKeys.append(qtkey) if QtCore.Qt.Key_Enter in qtKeys and QtCore.Qt.Key_Return not in qtKeys: qtKeys.append(QtCore.Qt.Key_Return) self.setAutoCompletionAcceptKeys(*qtKeys) def _isValidPython(self): """ _isValidPython() Check if the code at the cursor is valid python: - the active lexer is the python lexer - the style at the cursor is "default" """ #TODO: return True def introspect(self, tryAutoComp=False, delay=True): """ introspect(tryAutoComp=False, delay=True) The starting point for introspection (autocompletion and calltip). It will always try to produce a calltip. If tryAutoComp is True, will also try to produce an autocompletion list (which, on success, will hide the calltip). This method will obtain the line and (re)start a timer that will call _introspectNow() after a short while. This way, if the user types a lot of characters, there is not a stream of useless introspection attempts; the introspection is only really started after he stops typing for, say 0.1 or 0.5 seconds (depending on pyzo.config.autoCompDelay). The method _introspectNow() will parse the line to obtain information required to obtain the autocompletion and signature information. Then it calls processCallTip and processAutoComp which are implemented in the editor and shell classes. """ # Find the tokens up to the cursor cursor = self.textCursor() # In order to find the tokens, we need the userState from the highlighter if cursor.block().previous().isValid(): previousState = cursor.block().previous().userState() else: previousState = 0 text = cursor.block().text()[:cursor.positionInBlock()] tokensUptoCursor = list( filter(lambda token:token.isToken, #filter to remove BlockStates self.parser().parseLine(text, previousState))) # TODO: Only proceed if valid python (no need to check for comments/ # strings, this is done by the processing of the tokens). Check for python style # Is the char valid for auto completion? if tryAutoComp: if not text or not ( text[-1] in (Tokens.ALPHANUM + "._") ): self.autocompleteCancel() tryAutoComp = False # Store line and (re)start timer cursor.setKeepPositionOnInsert(True) self._delayTimer._tokensUptoCursor = tokensUptoCursor self._delayTimer._cursor = cursor self._delayTimer._tryAutoComp = tryAutoComp if delay: self._delayTimer.start(pyzo.config.advanced.autoCompDelay) else: self._delayTimer.start(1) # self._introspectNow() def _introspectNow(self): """ This method is called a short while after introspect() by the timer. It parses the line and calls the specific methods to process the callTip and autoComp. """ tokens = self._delayTimer._tokensUptoCursor if pyzo.config.settings.autoCallTip: # Parse the line, to get the name of the function we should calltip # if the name is empty/None, we should not show a signature name, needle, stats = parseLine_signature(tokens) if needle: # Compose actual name fullName = needle if name: fullName = name + '.' + needle # Process offset = self._delayTimer._cursor.positionInBlock() - stats[0] + len(needle) cto = CallTipObject(self, fullName, offset) self.processCallTip(cto) else: self.calltipCancel() if self._delayTimer._tryAutoComp and pyzo.config.settings.autoComplete: # Parse the line, to see what (partial) name we need to complete name, needle = parseLine_autocomplete(tokens) if name or needle: # Try to do auto completion aco = AutoCompObject(self, name, needle) self.processAutoComp(aco) def processCallTip(self, cto): """ Overridden in derive class """ pass def processAutoComp(self, aco): """ Overridden in derive class """ pass def _onDoubleClick(self): """ When double clicking on a name, autocomplete it. """ self.processHelp() def processHelp(self, name=None, showError=False): """ Show help on the given full object name. - called when going up/down in the autocompletion list. - called when double clicking a name """ # uses parse_autocomplete() to find baseName and objectName # Get help tool hw = pyzo.toolManager.getTool('pyzointeractivehelp') ass = pyzo.toolManager.getTool('pyzoassistant') # Get the shell shell = pyzo.shells.getCurrentShell() # Both should exist if not hw or not shell: return if not name: # Obtain name from current cursor position # Is this valid python? if self._isValidPython(): # Obtain line from text cursor = self.textCursor() line = cursor.block().text() text = line[:cursor.positionInBlock()] # Obtain nameBefore, name = parseLine_autocomplete(text) if nameBefore: name = "%s.%s" % (nameBefore, name) if name: hw.setObjectName(name) if ass: ass.showHelpForTerm(name) ## Callbacks def updateHelp(self,name): """A name has been highlighted, show help on that name""" if self._autoCompBuffer_name: name = self._autoCompBuffer_name + '.' + name elif not self.completer().completionPrefix(): # Dont update help if there is no dot or prefix; # the choice would be arbitrary return # Apply self.processHelp(name,True) def event(self,event): """ event(event) Overload main event handler so we can pass Ctrl-C Ctr-v etc, to the main window. """ if isinstance(event, QtGui.QKeyEvent): # Ignore CTRL+{A-Z} since those keys are handled through the menu if (event.modifiers() & QtCore.Qt.ControlModifier) and \ (event.key()>=QtCore.Qt.Key_A) and (event.key()<=QtCore.Qt.Key_Z): event.ignore() return False # Default behavior codeeditor.CodeEditor.event(self, event) return True def keyPressEvent(self, event): """ Receive qt key event. From here we'l dispatch the event to perform autocompletion or other stuff... """ # Get ordinal key ordKey = -1 if event.text(): ordKey = ord(event.text()[0]) # Cancel any introspection in progress self._delayTimer._line = '' # Invoke autocomplete via tab key? if event.key() == QtCore.Qt.Key_Tab and not self.autocompleteActive(): if pyzo.config.settings.autoComplete: cursor = self.textCursor() if cursor.position() == cursor.anchor(): text = cursor.block().text()[:cursor.positionInBlock()] if text and (text[-1] in (Tokens.ALPHANUM + "._")): self.introspect(True, False) return super().keyPressEvent(event) # Analyse character/key to determine what introspection to fire if ordKey: if (ordKey >= 48 or ordKey in [8, 46]) and pyzo.config.settings.autoComplete == 1: # If a char that allows completion or backspace or dot was pressed self.introspect(True) elif ordKey >= 32: # Printable chars, only calltip self.introspect() elif event.key() in [QtCore.Qt.Key_Left, QtCore.Qt.Key_Right]: self.introspect() class CallTipObject: """ Object to help the process of call tips. An instance of this class is created for each call tip action. """ def __init__(self, textCtrl, name, offset): self.textCtrl = textCtrl self.name = name self.bufferName = name self.offset = offset def tryUsingBuffer(self): """ tryUsingBuffer() Try performing this callTip using the buffer. Returns True on success. """ bufferName = self.textCtrl._callTipBuffer_name t = time.time() - self.textCtrl._callTipBuffer_time if ( self.bufferName == bufferName and t < 0 ): self._finish(self.textCtrl._callTipBuffer_result) return True else: return False def finish(self, callTipText): """ finish(callTipText) Finish the introspection using the given calltipText. Will also automatically call setBuffer. """ self.setBuffer(callTipText) self._finish(callTipText) def setBuffer(self, callTipText, timeout=4): """ setBuffer(callTipText) Sets the buffer with the provided text. """ self.textCtrl._callTipBuffer_name = self.bufferName self.textCtrl._callTipBuffer_time = time.time() + timeout self.textCtrl._callTipBuffer_result = callTipText def _finish(self, callTipText): self.textCtrl.calltipShow(self.offset, callTipText, True) class AutoCompObject: """ Object to help the process of auto completion. An instance of this class is created for each auto completion action. """ def __init__(self, textCtrl, name, needle): self.textCtrl = textCtrl self.bufferName = name # name to identify with self.name = name # object to find attributes of self.needle = needle # partial name to look for self.names = set() # the names (use a set to prevent duplicates) self.importNames = [] self.importLines = {} def addNames(self, names): """ addNames(names) Add a list of names to the collection. Duplicates are removed.""" self.names.update(names) def tryUsingBuffer(self): """ tryUsingBuffer() Try performing this auto-completion using the buffer. Returns True on success. """ bufferName = self.textCtrl._autoCompBuffer_name t = time.time() - self.textCtrl._autoCompBuffer_time if ( self.bufferName == bufferName and t < 0 ): self._finish(self.textCtrl._autoCompBuffer_result) return True else: return False def finish(self): """ finish() Finish the introspection using the collected names. Will automatically call setBuffer. """ # Remember at the object that started this introspection # and get sorted names names = self.setBuffer(self.names) # really finish self._finish(names) def setBuffer(self, names=None, timeout=None): """ setBuffer(names=None) Sets the buffer with the provided names (or the collected names). Also returns a list with the sorted names. """ # Determine timeout # Global namespaces change more often than local one, plus when # typing a xxx.yyy, the autocompletion buffer changes and is thus # automatically refreshed. # I've once encountered a wrong autocomp list on an object, but # haven' been able to reproduce it. It was probably some odity. if timeout is None: if self.bufferName: timeout = 5 else: timeout = 1 # Get names if names is None: names = self.names # Make list and sort names = list(names) names.sort(key=str.upper) # Store self.textCtrl._autoCompBuffer_name = self.bufferName self.textCtrl._autoCompBuffer_time = time.time() + timeout self.textCtrl._autoCompBuffer_result = names # Return sorted list return names def _finish(self, names): # Show completion list if required. self.textCtrl.autocompleteShow(len(self.needle), names) def nameInImportNames(self, importNames): """ nameInImportNames(importNames) Test whether the name, or a base part of it is present in the given list of names. Returns the (part of) the name that's in the list, or None otherwise. """ baseName = self.name while baseName not in importNames: if '.' in baseName: baseName = baseName.rsplit('.',1)[0] else: baseName = None break return baseName if __name__=="__main__": app = QtWidgets.QApplication([]) win = BaseTextCtrl(None) # win.setStyle('.py') tmp = "foo(bar)\nfor bar in range(5):\n print bar\n" tmp += "\nclass aap:\n def monkey(self):\n pass\n\n" tmp += "a\u20acb\n" win.setPlainText(tmp) win.show() app.exec_() pyzo-4.4.3/pyzo/core/codeparser.py0000666000000000000000000006373013123037260015315 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module codeparser Analyses the source code to get the structure of a module/script. This can be used for fictive introspection, and to display the structure of a source file in for example a tree widget. """ #TODO: replace this module, get data from the syntax highlighter in the code editor import time, threading, re import pyzo # Define regular expression patterns classPattern = r'^\s*' # Optional whitespace classPattern += r'(cp?def\s+)?' # Cython preamble + whitespace classPattern += r'class\s+' # The class keyword + whitespace classPattern += r'([a-zA-Z_][a-zA-Z_0-9]*)\s*' # The NAME + optional whitespace classPattern += r'(\(.*?\))?' # The superclass(es) classPattern += r'\s*:' # Optional whitespace and the colon # defPattern = r'^\s*' # Optional whitespace defPattern += r'(cp?)?def\s+' # The Cython preamble, def keyword and whitespace defPattern += r'([a-zA-Z_][\*a-zA-Z_0-9]*\s+)?' # Optional Cython return type defPattern += r'([a-zA-Z_][a-zA-Z_0-9]*)\s*' # The NAME + optional whitespace defPattern += r'\((.*?)\)' # The SIGNATURE #defPattern += r'\s*:' # Optional whitespace and the colon # Leave the colon, easier for cython class Job: """ Simple class to represent a job. """ def __init__(self, text, editorId): self.text = text self.editorId = editorId class Result: """ Simple class to represent a parser result. """ def __init__(self, rootItem, importList, editorId): self.rootItem = rootItem self.importList = importList self.editorId = editorId def isMatch(self, editorId): """ isMatch(editorId): Returns whether the result matches with the given editorId. The editorId can also be an editor instance. """ if isinstance(editorId, int): return self.editorId == editorId else: return self.editorId == id(editorId) class Parser(threading.Thread): """ Parser Parsing sourcecode in a separate thread, this class obtains introspection informarion. This class is also the interface to the parsed information; it has methods that can be used to extract information from the result. """ def __init__(self): threading.Thread.__init__(self) # Reference current job self._job = None # Reference to last result self._result = None # Lock to enable save threading self._lock = threading.RLock() # Set deamon self.daemon = True self._exit = False def stop(self, timeout=1.0): self._exit = True self.join(timeout) def parseThis(self, editor): """ parseThis(editor) Give the parser new text to parse. If the parser is busy parsing text, it will stop doing that and start anew with the most recent version of the text. """ # Get text text = editor.toPlainText() # Make job self._lock.acquire() self._job = Job(text, id(editor)) self._lock.release() def getFictiveNameSpace(self, editor): """ getFictiveNameSpace(editor) Produce the fictive namespace, based on the current position. A list of names is returned. """ # Obtain result result = self._getResult() if result is None or not result.isMatch(editor): return [] # Get linenr and indent. These are used to establish the namespace # based on indentation. cursor = editor.textCursor() linenr = cursor.blockNumber() index = cursor.positionInBlock() # init empty namespace and item list namespace = [] items = result.rootItem.children curIsClass = False # to not add methods (of classes) while items: curitem = None for item in items: # append name if not curIsClass and item.type in ['class', 'def']: namespace.append( item.name ) # check if this is the one only last one remains if (item.type in ['class', 'def'] and item.linenr <= linenr and item.linenr2 > linenr): curitem = item # prepare for next round if curitem and curitem.indent < index: items = curitem.children if curitem.type=='class': curIsClass = True else: curIsClass = False else: items = [] return namespace def getFictiveClass(self, name, editor, handleSelf=False): """ getFictiveClass(name, editor, handleSelf=False) Return the fictive class object of the given name, or None if it does not exist. If handleSelf is True, automatically handles "self." names. """ return self._getFictiveItem(name, 'class', editor, handleSelf) def getFictiveSignature(self, name, editor, handleSelf=False): """ getFictiveSignature(name, editor, handleSelf=False) Get the signature of the fictive function or method of the given name. Returns None if the given name is not a known function or method. If handleSelf is True, automatically handles "self." names. """ # Get item being a function item = self._getFictiveItem(name, 'def', editor, handleSelf) # Get item being a class if not item: item = self._getFictiveItem(name, 'class', editor, handleSelf) if item: for subItem in item.children: if subItem.name == '__init__' and subItem.type == 'def': item = subItem break else: item = None # Process or return None if there was no item if item: nameParts = name.split('.') return '{}({})'.format(nameParts[-1], item.sig) else: return None def getFictiveImports(self, editor): """ getFictiveImports(editor) Get the fictive imports of this source file. tuple: - list of names that are imported, - a dict with the line to import each name """ # Obtain result result = self._getResult() if result is None or not result.isMatch(editor): return [], [] # Extract list of names and dict of lines imports = [] importlines = {} for item in result.importList: imports.append(item.name) importlines[item.name] = item.text return imports, importlines def _getResult(self): """ getResult() Savely Obtain result. """ self._lock.acquire() result = self._result self._lock.release() return result def _getFictiveItem(self, name, type, editor, handleSelf=False): """ _getFictiveItem(name, type, editor, handleSelf=False) Obtain the fictive item of the given name and type. If handleSelf is True, will handle "self." correctly. Intended for internal use. """ # Obtain result result = self._getResult() if result is None or not result.isMatch(editor): return None # Split name in parts nameParts = name.split('.') # Try if the first part represents a class instance if handleSelf: item = self._getFictiveCurrentClass(editor, nameParts[0]) if item: nameParts[0] = item.name # Init name = nameParts.pop(0) items = result.rootItem.children theItem = None # Search for name while items: for item in items: if item.name == name: # Found it if nameParts: # Go down one level name = nameParts.pop(0) items = item.children break else: # This is it, is it what we wanted? if item.type == type: theItem = item items = [] break else: # Did not find it items = [] return theItem def _getFictiveCurrentClass(self, editor, selfname): """ _getFictiveCurrentClass(editor, selfname) Get the fictive object for the class referenced using selfname (usually 'self'). Intendef for internal use. """ # Obtain result result = self._getResult() if result is None: return None # Get linenr and indent cursor = editor.textCursor() linenr = cursor.blockNumber() index = cursor.positionInBlock() # Init items = result.rootItem.children theclass = None while items: curitem = None for item in items: # check if this is the one only last one remains if item.linenr <= linenr: if not item.linenr2 > linenr: continue curitem = item if item.type == 'def' and item.selfname==selfname: theclass = item.parent else: break # prepare for next round if curitem and curitem.indent < index: items = curitem.children else: items = [] # return return theclass def run(self): """ run() This is the main loop. """ time.sleep(0.5) try: while True: time.sleep(0.1) if self._exit: return if self._job: # Savely obtain job self._lock.acquire() job = self._job self._job = None self._lock.release() # Analyse job result = self._analyze(job) # Savely store result self._lock.acquire() self._result = result self._lock.release() # Notify if pyzo.editors is not None: pyzo.editors.parserDone.emit() except AttributeError: pass # when python exits, time can be None... def _analyze(self, job): """ The core function. Analyses the source code. Produces: - a tree of FictiveObject objects. - a (flat) list of the same object - a list of imports """ # Remove multiline strings text = washMultilineStrings(job.text) # Split text in lines lines = text.splitlines() lines.insert(0,"") # so the lines start at 1 # The structure object. It will first only consist of class and defs # the rest will be inserted afterwards. root = FictiveObject("root", 0, -1, 'root') # Also keep a flat list (while running this function) flatList = [] # Cells and imports are inserted in the structure afterwards leafs = [] # Keep a list of imports importList = [] # To know when to make something new when for instance a class is defined # in an if statement, we keep track of the last valid node/object: # Put inside a list, so we can set it from inside a subfuncion lastObject = [root] # Define funcion to put an item in the structure in the right parent def appendToStructure(object): # find position in structure to insert node = lastObject[0] while ( (object.indent <= node.indent) and (node is not root) ): node = node.parent # insert object flatList.append(object) node.children.append(object) object.parent = node lastObject[0] = object # Find objects! # type can be: cell, class, def, import, var for i in range( len(lines) ): # Obtain line line = lines[i] # Should we stop? if self._job or self._exit: break # Remove indentation tmp = line.lstrip() indent = len(line) - len(tmp) line = tmp.rstrip() # Detect cells if line.startswith('##') or line.startswith('#%%') or line.startswith('# %%'): if line.startswith('##'): name = line[2:].lstrip() elif line.startswith('#%%'): name = line[3:].lstrip() else: name = line[4:].lstrip() item = FictiveObject('cell', i, indent, name) leafs.append(item) # Next! (we have to put this before the elif stuff below # because it looks like a comment!) continue # Split in line and comment line, tmp, cmnt = line.partition('#') line, cmnt = line.rstrip(), cmnt.lower().strip() # Detect todos if cmnt and (cmnt.startswith('todo:') or cmnt.startswith('2do:') ): item = FictiveObject('todo', i, indent, cmnt) item.linenr2 = i+1 # a todo is active at one line only leafs.append(item) # Continue of no line left if not line: continue # Find last valid node. As the indent of the root is set to -1, # this will always stop at the root while indent <= lastObject[0].indent: lastObject[0].linenr2 = i # close object lastObject[0] = lastObject[0].parent # Make a lowercase version of the line foundSomething = False # Detect classes if not foundSomething: classResult = re.search(classPattern, line) if classResult: foundSomething = True # Get name name = classResult.group(2) item = FictiveObject('class', i, indent, name) appendToStructure(item) item.supers = [] item.members = [] # Get inheritance supers = classResult.group(3) if supers: supers = supers[1:-1].split(',') supers = [tmp.strip() for tmp in supers] item.supers = [tmp for tmp in supers if tmp] # Detect functions and methods (also multiline) if (not foundSomething) and line.count('def '): # Get a multiline version (for long defs) multiLine = line for ii in range(1,5): if i+ii add method to attr and find selfname if item.parent.type == 'class': item.parent.members.append(name) # Find what is used as "self" i2 = line.find('(') i4 = line.find(",",i2) if i4 < 0: i4 = line.find(")",i2) if i4 < 0: i4 = i2 selfname = line[i2+1:i4].strip() if selfname: item.selfname = selfname elif line.count('import '): if line.startswith("import "): for name in ParseImport(line[7:]): item = FictiveObject('import', i, indent, name) item.text = line item.linenr2 = i+1 # an import is active at one line only leafs.append(item) importList.append(item) elif line.startswith("from "): i1 = line.find(" import ") for name in ParseImport(line[i1+8:]): if not IsValidName(name): continue # we cannot do that! item = FictiveObject('import', i, indent, name) item.text = line item.linenr2 = i+1 # an import is active at one line only leafs.append(item) importList.append(item) elif not indent and line.startswith('if __name__ ==') and '__main__' in line: item = FictiveObject('nameismain', i, indent, '__main__') item.text = line appendToStructure(item) elif line.count('='): if lastObject[0].type=='def' and lastObject[0].selfname: selfname = lastObject[0].selfname + "." line = line.partition("=")[0] if line.count(selfname): # A lot of ifs here. If we got here, the line is part of # a valid method and contains the selfname before the =. # Now we need to establish whether there is a valid # assignment done here... parts = line.split(",") # handle tuples for part in parts: part = part.strip() part2 = part[len(selfname):] if part.startswith(selfname) and IsValidName(part2): # add to the list if not already present defItem = lastObject[0] classItem = lastObject[0].parent # item = FictiveObject('attribute', i, indent, part2) item.parent = defItem defItem.children.append(item) if part2 not in classItem.members: classItem.members.append(part2) ## Post processing def getTwoItems(series, linenr): """ Return the two items just above and below the given linenr. The object always is a class or def. """ # find object after linenr object1, object2 = None, None # if no items at all i = -1 for i in range(len(series)): object = series[i] if object.type not in ['class','def']: continue if object.linenr > linenr: object2 = object break # find object just before linenr for ii in range(i,-1,-1): object = series[ii] if object.type not in ['class','def']: continue if object.linenr < linenr: object1 = object break # return result return object1, object2 # insert the leafs (backwards as the last inserted is at the top) for leaf in reversed(leafs): ob1, ob2 = getTwoItems(flatList, leaf.linenr) if ob1 is None: # also if ob2 is None # insert in root root.children.insert(0,leaf) leaf.parent = root continue if ob2 is None: ob2parent = root else: ob2parent = ob2.parent # get the object IN which to insert it: ob1 sibling = None while 1: canGoDeeper = ob1 is not ob2parent canGoDeeper = canGoDeeper and ob1 is not root shouldGoDeeper = ob1.indent >= leaf.indent shouldGoDeeper = shouldGoDeeper or ob1.linenr2 < leaf.linenr if canGoDeeper and shouldGoDeeper: sibling = ob1 ob1 = ob1.parent else: break # insert into ob1, after sibling (if available) L = ob1.children if sibling: i = L.index(sibling) L.insert(i+1,leaf) else: L.insert(0,leaf) # Return result return Result(root, importList, job.editorId) ## Helper classes and functions class FictiveObject: """ An un-instantiated object. type can be class, def, import, cell, todo extra stuff: class - supers, members def - selfname imports - text cell - todo - attribute - """ def __init__(self, type, linenr, indent, name): self.children = [] self.type = type self.linenr = linenr # at which line this object starts self.linenr2 = 9999999 # at which line it ends self.indent = indent self.name = name self.sig = '' # for functions and methods namechars = 'abcdefghijklmnopqrstuvwxyz_0123456789' def IsValidName(name): """ Given a string, checks whether it is a valid name (dots are not valid!) """ if not name: return False name = name.lower() if name[0] not in namechars[0:-10]: return False tmp = map(lambda x: x not in namechars, name[2:]) return sum(tmp)==0 def ParseImport(names): for part in names.split(","): i1 = part.find(' as ') if i1>0: name = part[i1+3:].strip() else: name = part.strip() yield name def findString(text, s, i): """ findString(text, s) Find s in text, but only if s is not in a string or commented Helper function for washMultilineStrings """ while True: i = _findString(text, s, i) if i<-1: i = -i+1 else: break return i def _findString(text, s, i): """ Helper function of findString, which is called recursively until a match is found, or it is clear there is no match. """ # Find occurrence i2 = text.find(s, i) if i2<0: return -1 # Find newline (if none, we're done) i1 = text.rfind('\n', 0, i2) if i1<0: return i2 # Extract the part on the line up to the match line = text[i1:i2] # Count quotes, we're done if we found none if not line.count('"') and not line.count("'") and not line.count('#'): return i2 # So we found quotes, now really count them ... prev = '' inString = '' # this is a boolean combined with a flag which quote was used isComment = False for c in line: if c == '#': if not inString: isComment = True break elif c in "\"\'": if not inString: inString = c elif prev != '\\': if inString == c: inString = '' # exit string else: pass # the other quote can savely be used inside this string prev = c # If we are in a string, this match is false ... if inString or isComment: return -i2 # indicate failure and where to continue else: return i2 # all's right def washMultilineStrings(text): """ washMultilineStrings(text) Replace all text within multiline strings with dummy chars so that it is not parsed. """ i=0 s1 = "'''" s2 = '"""' while i take all text, unclosed string! if i4==-1: i4 = 2**32 # Leave only the first two quotes of the start of the comment i3 -= 1 i4 += 3 # Replace all non-newline chars tmp = re.sub(r'\S', ' ', text[i3:i4]) text = text[:i3] + tmp + text[i3+len(tmp):] # Prepare for next round i = i4+1 return text """ ## testing skipping of multiline strings def ThisShouldNotBeVisible(): pass class ThisShouldNotBeVisibleEither(): pass """ pyzo-4.4.3/pyzo/core/commandline.py0000666000000000000000000001241013123037260015441 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2014, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module to deal with command line arguments. In specific, this allows doing "pyzo some_file.py" and the file will be opened in an existing pyzo window (if available) or a new pyzo process is started to open the file. This module is used at the very early stages of starting pyzo, and also in main.py to apply any command line args for the current process, and to closse down the server when pyzo is closed. """ import sys import os from yoton.clientserver import RequestServer, do_request import pyzo # Local address to host on. we use yoton's port hash to have an arbitrary port ADDRESS = 'localhost:pyzoserver' class Server(RequestServer): """ Server that listens on a port for commands. The commands can be send by executing the Pyzo executable with command line arguments. """ def handle_request(self, request): """ This is where the requests enter. """ # Get command request = request.strip() command, _, arg = request.partition(' ') # Handle command try: reply = handle_command(command, arg) except Exception as err: msg = 'Error handling request %r:\n%s' % (request, str(err)) pyzo.callLater(print, msg) return msg else: pyzo.callLater(print, 'Request:', request) pyzo.callLater(print, 'Reply:', reply) return reply def handle_command(command, arg): """ Function that handles all pyzo commands. This gets called either from the server, or from the code that processed command line args. """ if not command: return 'empty command?' elif command == 'testerr': return 1/0 elif command == 'stopserver': # For efficiently stopping the server if server: server.stop() return 'Stopped the server' elif command == 'echo': # For testing return 'echo %r' % arg elif command == 'open': # Open a file in the editor if not arg: return 'The open command requires a filename.' pyzo.callLater(pyzo.editors.loadFile, arg) return 'Opened file %r' % arg elif command == 'new': # Open a new (temp) file in the editor pyzo.callLater(pyzo.editors.newFile) return 'Created new file' elif command == 'close': # Close pyzo pyzo.callLater(pyzo.main.close) return 'Closing Pyzo' else: # Assume the user wanted to open a file fname = (command + ' ' + arg).rstrip() if not pyzo.editors: return 'Still warming up ...' else: pyzo.callLater(pyzo.editors.loadFile, fname) return 'Try opening file %r' % fname # We should always return. So if we get here, it is a bug. # Return something so that we can be aware. return 'error ' + command def handle_cmd_args(): """ Handle command line arguments by sending them to the server. Returns a result string if any commands were processed, and None otherwise. """ args = sys.argv[1:] request = ' '.join(args) if 'psn_' in request and not os.path.isfile(request): request = ' '.join(args[1:]) # An OSX thing when clicking app icon request = request.strip() # if not request: return None else: # Always send to server, even if we are the ones that run the server try: return do_request(ADDRESS, request, 0.4).rstrip() except Exception as err: print('Could not process command line args:\n%s' % str(err)) return None def stop_our_server(): """ Stop our server, for shutting down nicely. This is faster than calling server.stop(), because in the latter case the server will need to timeout (0.25 s) before it sees that it needs to stop. """ if is_our_server_running(): try: server.stop() # Post a stop message do_request(ADDRESS, 'stopserver', 0.1) # trigger print('Stopped our command server.') except Exception as err: print('Failed to stop command server:') print(err) def is_our_server_running(): """ Return True if our server is running. If it is, this process is the main Pyzo; the first Pyzo that was started. If the server is not running, this is probably not the first Pyzo, but there might also be problem with starting the server. """ return server and server.isAlive() def is_pyzo_server_running(): """ Test whether the Pyzo server is running *somewhere* (not necesarily in this process). """ try: res = do_request(ADDRESS, 'echo', 0.2) return res.startswith('echo') except Exception: return False # Shold we start the server? _try_start_server = True if sys.platform.startswith('win'): _try_start_server = not is_pyzo_server_running() # Create server server_err = None server = None try: if _try_start_server: server = Server(ADDRESS) server.start() except OSError as err: server_err = err server = None pyzo-4.4.3/pyzo/core/compactTabWidget.py0000666000000000000000000003633613123037260016411 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ compact tab widget class See docs of the tab widget. """ from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa import sys if sys.version_info[0] < 3: str = unicode # noqa ELLIPSIS = unichr(8230) # noqa else: ELLIPSIS = chr(8230) # Constants for the alignments of tabs MIN_NAME_WIDTH = 4 MAX_NAME_WIDTH = 64 ## Define style sheet for the tabs STYLESHEET = """ QTabWidget::pane { /* The tab widget frame */ border-top: 0px solid #A09B90; } QTabWidget::tab-bar { left: 0px; /* move to the right by x px */ } /* Style the tab using the tab sub-control. Note that it reads QTabBar _not_ QTabWidget */ QTabBar::tab { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 rgba(220,220,220,128), stop: 0.4 rgba(200,200,200,128), stop: 1.0 rgba(100,100,100,128) ); border: 1px solid #A09B90; border-bottom-color: #DAD5CC; /* same as the pane color */ border-top-left-radius: 4px; border-top-right-radius: 4px; min-width: 5ex; padding-bottom: PADDING_BOTTOMpx; padding-top: PADDING_TOPpx; padding-left: PADDING_LEFTpx; padding-right: PADDING_RIGHTpx; margin-right: -1px; /* "combine" borders */ } QTabBar::tab:last { margin-right: 0px; } /* Style the selected tab, hoovered tab, and other tabs. */ QTabBar::tab:hover { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 rgba(245,250,255,128), stop: 0.4 rgba(210,210,210,128), stop: 1.0 rgba(200,200,200,128) ); } QTabBar::tab:selected { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 rgba(0,0,128,128), stop: 0.12 rgba(0,0,128,128), stop: 0.120001 rgba(245,250,255,128), stop: 0.4 rgba(210,210,210,128), stop: 1.0 rgba(200,200,200,128) ); } QTabBar::tab:selected { border-width: 1px; border-bottom-width: 0px; border-top-left-radius: 5px; border-top-right-radius: 5px; border-color: #333; } QTabBar::tab:!selected { margin-top: 3px; /* make non-selected tabs look smaller */ } """ ## Define tab widget class class TabData: """ To keep track of real names of the tabs, but also keep supporting tabData. """ def __init__(self, name): self.name = name self.data = None class CompactTabBar(QtWidgets.QTabBar): """ CompactTabBar(parent, *args, padding=(4,4,6,6), preventEqualTexts=True) Tab bar corresponcing to the CompactTabWidget. With the "padding" argument the padding of the tabs can be chosen. It should be an integer, or a 4 element tuple specifying the padding for top, bottom, left, right. When a tab has a button, the padding is the space between button and text. With preventEqualTexts to True, will reduce the amount of eliding if two tabs have (partly) the same name, so that they can always be distinguished. """ # Add signal to be notified of double clicks on tabs tabDoubleClicked = QtCore.Signal(int) barDoubleClicked = QtCore.Signal() def __init__(self, *args, padding=(4,4,6,6), preventEqualTexts=True): QtWidgets.QTabBar.__init__(self, *args) # Put tab widget in document mode self.setDocumentMode(True) # Widget needs to draw its background (otherwise Mac has a dark bg) self.setDrawBase(False) if sys.platform == 'darwin': self.setAutoFillBackground(True) # Set whether we want to prevent eliding for names that start the same. self._preventEqualTexts = preventEqualTexts # Allow moving tabs around self.setMovable(True) # Get padding if isinstance(padding, (int, float)): padding = padding, padding, padding, padding elif isinstance(padding, (tuple, list)): pass else: raise ValueError('Invalid value for padding.') # Set style sheet stylesheet = STYLESHEET stylesheet = stylesheet.replace('PADDING_TOP', str(padding[0])) stylesheet = stylesheet.replace('PADDING_BOTTOM', str(padding[1])) stylesheet = stylesheet.replace('PADDING_LEFT', str(padding[2])) stylesheet = stylesheet.replace('PADDING_RIGHT', str(padding[3])) self.setStyleSheet(stylesheet) # We do our own eliding self.setElideMode(QtCore.Qt.ElideNone) # Make tabs wider if there's plenty space? self.setExpanding(False) # If there's not enough space, use scroll buttons self.setUsesScrollButtons(True) # When a tab is removed, select previous self.setSelectionBehaviorOnRemove(self.SelectPreviousTab) # Init alignment parameters self._alignWidth = MIN_NAME_WIDTH # Width in characters self._alignWidthIsReducing = False # Whether in process of reducing # Create timer for aligning self._alignTimer = QtCore.QTimer(self) self._alignTimer.setInterval(10) self._alignTimer.setSingleShot(True) self._alignTimer.timeout.connect(self._alignRecursive) def _compactTabBarData(self, i): """ _compactTabBarData(i) Get the underlying tab data for tab i. Only for internal use. """ # Get current TabData instance tabData = QtWidgets.QTabBar.tabData(self, i) if (tabData is not None) and hasattr(tabData, 'toPyObject'): tabData = tabData.toPyObject() # Older version of Qt # If none, make it as good as we can if not tabData: name = str(QtWidgets.QTabBar.tabText(self, i)) tabData = TabData( name ) QtWidgets.QTabBar.setTabData(self, i, tabData) # Done return tabData ## Overload a few methods def mouseDoubleClickEvent(self, event): i = self.tabAt(event.pos()) if i == -1: # There was no tab under the cursor self.barDoubleClicked.emit() else: # Tab was double clicked self.tabDoubleClicked.emit(i) def mousePressEvent(self, event): if event.button() == QtCore.Qt.MiddleButton: i = self.tabAt(event.pos()) if i >= 0: self.parent().tabCloseRequested.emit(i) return super().mousePressEvent(event) def setTabData(self, i, data): """ setTabData(i, data) Set the given object at the tab with index 1. """ # Get underlying python instance tabData = self._compactTabBarData(i) # Attach given data tabData.data = data def tabData(self, i): """ tabData(i) Get the tab data at item i. Always returns a Python object. """ # Get underlying python instance tabData = self._compactTabBarData(i) # Return stored data return tabData.data def setTabText(self, i, text): """ setTabText(i, text) Set the text for tab i. """ tabData = self._compactTabBarData(i) if text != tabData.name: tabData.name = text self.alignTabs() def tabText(self, i): """ tabText(i) Get the title of the tab at index i. """ tabData = self._compactTabBarData(i) return tabData.name ## Overload events and protected functions def tabInserted(self, i): QtWidgets.QTabBar.tabInserted(self, i) # Is called when a tab is inserted # Get given name and store name = str(QtWidgets.QTabBar.tabText(self, i)) tabData = TabData(name) QtWidgets.QTabBar.setTabData(self, i, tabData) # Update self.alignTabs() def tabRemoved(self, i): QtWidgets.QTabBar.tabRemoved(self, i) # Update self.alignTabs() def resizeEvent(self, event): QtWidgets.QTabBar.resizeEvent(self, event) self.alignTabs() def showEvent(self, event): QtWidgets.QTabBar.showEvent(self, event) self.alignTabs() ## For aligning def alignTabs(self): """ alignTabs() Align the tab items. Their names are ellided if required so that all tabs fit on the tab bar if possible. When there is too little space, the QTabBar will kick in and draw scroll arrows. """ # Set name widths correct (in case new names were added) self._setMaxWidthOfAllItems() # Start alignment process self._alignWidthIsReducing = False self._alignTimer.start() def _alignRecursive(self): """ _alignRecursive() Recursive alignment of the items. The alignment process should be initiated from alignTabs(). """ # Only if visible if not self.isVisible(): return # Get tab bar and number of items N = self.count() # Get right edge of last tab and left edge of corner widget pos1 = self.tabRect(0).topLeft() pos2 = self.tabRect(N-1).topRight() cornerWidget = self.parent().cornerWidget() if cornerWidget: pos3 = cornerWidget.pos() else: pos3 = QtCore.QPoint(self.width(), 0) x1 = pos1.x() x2 = pos2.x() x3 = pos3.x() alignMargin = x3 - (x2-x1) -3 # Must be positive (has margin) # Are the tabs too wide? if alignMargin < 0: # Tabs extend beyond corner widget # Reduce width then self._alignWidth -= 1 self._alignWidth = max(self._alignWidth, MIN_NAME_WIDTH) # Apply self._setMaxWidthOfAllItems() self._alignWidthIsReducing = True # Try again if there's still room for reduction if self._alignWidth > MIN_NAME_WIDTH: self._alignTimer.start() elif alignMargin > 10 and not self._alignWidthIsReducing: # Gap between tabs and corner widget is a bit large # Increase width then self._alignWidth += 1 self._alignWidth = min(self._alignWidth, MAX_NAME_WIDTH) # Apply itemsElided = self._setMaxWidthOfAllItems() # Try again if there's still room for increment if itemsElided and self._alignWidth < MAX_NAME_WIDTH: self._alignTimer.start() #self._alignTimer.timeout.emit() else: pass # margin is good def _getAllNames(self): """ _getAllNames() Get a list of all (full) tab names. """ return [self._compactTabBarData(i).name for i in range(self.count())] def _setMaxWidthOfAllItems(self): """ _setMaxWidthOfAllItems() Sets the maximum width of all items now, by eliding the names. Returns whether any items were elided. """ # Get whether an item was reduced in size itemReduced = False for i in range(self.count()): # Get width w = self._alignWidth # Get name name = self._compactTabBarData(i).name # If its too long, first make it shorter by stripping dir names if (w+1) < len(name) and '/' in name: name = name.split('/')[-1] # Check if we can reduce the name size, correct w if necessary if ( (w+1) < len(name) ) and self._preventEqualTexts: # Increase w untill there are no names that start the same allNames = self._getAllNames() hasSimilarNames = True diff = 2 w -= 1 while hasSimilarNames and w < len(name): w += 1 w2 = w - (diff-1) shortName = name[:w2] similarnames = [n for n in allNames if n[:w2]==shortName] hasSimilarNames = len(similarnames)>1 # Check again, with corrected w if (w+1) < len(name): name = name[:w] + ELLIPSIS itemReduced = True # Set text now QtWidgets.QTabBar.setTabText(self, i, name) # Done return itemReduced class CompactTabWidget(QtWidgets.QTabWidget): """ CompactTabWidget(parent, *args, **kwargs) Implements a tab widget with a tabbar that is in document mode and has more compact tabs that conventional tab widgets, so more items fit on the same space. Further much care is taken to ellide the names in a smart way: * All items are allowed the same amount of characters instead of that the same amount of characters is removed from all names. * If there are two item with the same beginning, it is made sure that enough characters are shown such that the names can be distinguished. The kwargs are passed to the tab bar constructor. There are a few keywords arguments to influence the appearance of the tabs. See the CompactTabBar class. """ def __init__(self, *args, **kwargs): QtWidgets.QTabWidget.__init__(self, *args) # Set tab bar self.setTabBar(CompactTabBar(self, **kwargs)) # Draw tabs at the top by default self.setTabPosition(QtWidgets.QTabWidget.North) def setTabData(self, i, data): """ setTabData(i, data) Set the given object at the tab with index 1. """ self.tabBar().setTabData(i, data) def tabData(self, i): """ tabData(i) Get the tab data at item i. Always returns a Python object. """ return self.tabBar().tabData(i) def setTabText(self, i, text): """ setTabText(i, text) Set the text for tab i. """ self.tabBar().setTabText(i, text) def tabText(self, i): """ tabText(i) Get the title of the tab at index i. """ return self.tabBar().tabText(i) if __name__ == '__main__': w = CompactTabWidget() w.show() w.addTab(QtWidgets.QWidget(w), 'aapenootjedopje') w.addTab(QtWidgets.QWidget(w), 'aapenootjedropje') w.addTab( QtWidgets.QWidget(w), 'noot en mies') w.addTab( QtWidgets.QWidget(w), 'boom bijv een iep') w.addTab( QtWidgets.QWidget(w), 'roosemarijnus') w.addTab( QtWidgets.QWidget(w), 'vis') w.addTab( QtWidgets.QWidget(w), 'vuurvuurvuur') pyzo-4.4.3/pyzo/core/editor.py0000666000000000000000000006136013123037260014451 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module editor Defines the PyzoEditor class which is used to edit documents. This module/class also implements all the relatively low level file loading/saving /reloading stuff. """ import os, sys import re, codecs from pyzo.util.qt import QtCore, QtGui, QtWidgets qt = QtGui from pyzo.codeeditor import Manager from pyzo.core.menu import EditorContextMenu from pyzo.core.baseTextCtrl import BaseTextCtrl, normalizePath from pyzo.core.pyzoLogging import print # noqa import pyzo # Set default line ending (if not set) if not pyzo.config.settings.defaultLineEndings: if sys.platform.startswith('win'): pyzo.config.settings.defaultLineEndings = 'CRLF' else: pyzo.config.settings.defaultLineEndings = 'LF' def determineEncoding(bb): """ Get the encoding used to encode a file. Accepts the bytes of the file. Returns the codec name. If the codec could not be determined, uses UTF-8. """ # Init firstTwoLines = bb.split(b'\n', 2)[:2] encoding = 'UTF-8' for line in firstTwoLines: # Try to make line a string try: line = line.decode('ASCII').strip() except Exception: continue # Has comment? if line and line[0] == '#': # Matches regular expression given in PEP 0263? expression = "coding[:=]\s*([-\w.]+)" result = re.search(expression, line) if result: # Is it a known encoding? Correct name if it is candidate_encoding = result.group(1) try: c = codecs.lookup(candidate_encoding) candidate_encoding = c.name except Exception: pass else: encoding = candidate_encoding # Done return encoding def determineLineEnding(text): """ Get the line ending style used in the text. \n, \r, \r\n, The EOLmode is determined by counting the occurrences of each line ending... """ # test line ending by counting the occurrence of each c_win = text.count("\r\n") c_mac = text.count("\r") - c_win c_lin = text.count("\n") - c_win # set the appropriate style if c_win > c_mac and c_win > c_lin: mode = '\r\n' elif c_mac > c_win and c_mac > c_lin: mode = '\r' else: mode = '\n' # return return mode def determineIndentation(text): """ Get the indentation used in this document. The text is analyzed to find the most used indentations. The result is -1 if tab indents are most common. A positive result means spaces are used; the amount signifies the amount of spaces per indentation. 0 is returned if the indentation could not be determined. """ # create dictionary of indents, -1 means a tab indents = {} indents[-1] = 0 lines = text.splitlines() lines.insert(0,"") # so the lines start at 1 for i in range( len(lines) ): line = lines[i] # remove indentation tmp = line.lstrip() indent = len(line) - len(tmp) line = tmp.rstrip() if line.startswith('#'): continue else: # remove everything after the # line = line.split("#",1)[0].rstrip() if not line: # continue of no line left continue # a colon means there will be an indent # check the next line (or the one thereafter) # and calculate the indentation difference with THIS line. if line.endswith(":"): if len(lines) > i+2: line2 = lines[i+1] tmp = line2.lstrip() if not tmp: line2 = lines[i+2] tmp = line2.lstrip() if tmp: ind2 = len(line2)-len(tmp) ind3 = ind2 - indent if line2.startswith("\t"): indents[-1] += 1 elif ind3>0: if not ind3 in indents: indents[ind3] = 1 indents[ind3] += 1 # find which was the most common tab width. indent, maxvotes = 0,0 for nspaces in indents: if indents[nspaces] > maxvotes: indent, maxvotes = nspaces, indents[nspaces] #print "found tabwidth %i" % indent return indent # To give each new file a unique name newFileCounter = 0 def createEditor(parent, filename=None): """ Tries to load the file given by the filename and if succesful, creates an editor instance to put it in, which is returned. If filename is None, an new/unsaved/temp file is created. """ if filename is None: # Increase counter global newFileCounter newFileCounter += 1 # Create editor editor = PyzoEditor(parent) editor.document().setModified(True) # Set name editor._name = "".format(newFileCounter) else: # check and normalize if not os.path.isfile(filename): raise IOError("File does not exist '%s'." % filename) # load file (as bytes) with open(filename, 'rb') as f: bb = f.read() f.close() # convert to text, be gentle with files not encoded with utf-8 encoding = determineEncoding(bb) text = bb.decode(encoding,'replace') # process line endings lineEndings = determineLineEnding(text) # if we got here safely ... # create editor and set text editor = PyzoEditor(parent) editor.setPlainText(text) editor.lineEndings = lineEndings editor.encoding = encoding editor.document().setModified(False) # store name and filename editor._filename = filename editor._name = os.path.split(filename)[1] # process indentation indentWidth = determineIndentation(text) if indentWidth == -1: #Tabs editor.setIndentWidth(pyzo.config.settings.defaultIndentWidth) editor.setIndentUsingSpaces(False) elif indentWidth: editor.setIndentWidth(indentWidth) editor.setIndentUsingSpaces(True) if editor._filename: editor._modifyTime = os.path.getmtime(editor._filename) # Set parser if editor._filename: ext = os.path.splitext(editor._filename)[1] parser = Manager.suggestParserfromFilenameExtension(ext) editor.setParser(parser) else: # todo: rename style -> parser editor.setParser(pyzo.config.settings.defaultStyle) # return return editor class PyzoEditor(BaseTextCtrl): # called when dirty changed or filename changed, etc somethingChanged = QtCore.Signal() def __init__(self, parent, **kwds): super().__init__(parent, showLineNumbers = True, **kwds) # Init filename and name self._filename = '' self._name = '' # View settings self.setShowWhitespace(pyzo.config.view.showWhitespace) #TODO: self.setViewWrapSymbols(view.showWrapSymbols) self.setShowLineEndings(pyzo.config.view.showLineEndings) self.setShowIndentationGuides(pyzo.config.view.showIndentationGuides) # self.setWrap(bool(pyzo.config.view.wrap)) self.setHighlightCurrentLine(pyzo.config.view.highlightCurrentLine) self.setLongLineIndicatorPosition(pyzo.config.view.edgeColumn) self.setHighlightMatchingBracket(pyzo.config.view.highlightMatchingBracket) #TODO: self.setFolding( int(view.codeFolding)*5 ) # bracematch is set in baseTextCtrl, since it also applies to shells # dito for zoom and tabWidth # Set line endings to default self.lineEndings = pyzo.config.settings.defaultLineEndings # Set encoding to default self.encoding = 'UTF-8' # Modification time to test file change self._modifyTime = 0 self.modificationChanged.connect(self._onModificationChanged) # To see whether the doc has changed to update the parser. self.textChanged.connect(self._onModified) # This timer is used to hide the marker that shows which code is executed self._showRunCursorTimer = QtCore.QTimer() # Add context menu (the offset is to prevent accidental auto-clicking) self._menu = EditorContextMenu(self) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(lambda p: self._menu.popup(self.mapToGlobal(p)+QtCore.QPoint(0,3))) ## Properties @property def name(self): return self._name @property def filename(self): return self._filename @property def lineEndings(self): """ Line-endings style of this file. Setter accepts machine-readable (e.g. '\r') and human-readable (e.g. 'CR') input """ return self._lineEndings @lineEndings.setter def lineEndings(self,value): if value in ('\r','\n','\r\n'): self._lineEndings = value return try: self._lineEndings = {'CR': '\r', 'LF': '\n', 'CRLF': '\r\n'}[value] except KeyError: raise ValueError('Invalid line endings style %r' % value) @property def lineEndingsHumanReadable(self): """ Current line-endings style, human readable (e.g. 'CR') """ return {'\r': 'CR', '\n': 'LF', '\r\n': 'CRLF'}[self.lineEndings] @property def encoding(self): """ Encoding used to convert the text of this file to bytes. """ return self._encoding @encoding.setter def encoding(self, value): # Test given value, correct name if it exists try: c = codecs.lookup(value) value = c.name except Exception: value = codecs.lookup('UTF-8').name # Store self._encoding = value ## def justifyText(self): """ Overloaded version of justifyText to make it use our configurable justificationwidth. """ super().justifyText(pyzo.config.settings.justificationWidth) def showRunCursor(self, cursor): """ Momentarily highlight a piece of code to show that this is being executed """ extraSelection = QtWidgets.QTextEdit.ExtraSelection() extraSelection.cursor = cursor extraSelection.format.setBackground(QtCore.Qt.gray) self.setExtraSelections([extraSelection]) self._showRunCursorTimer.singleShot(200, lambda: self.setExtraSelections([])) def id(self): """ Get an id of this editor. This is the filename, or for tmp files, the name. """ if self._filename: return self._filename else: return self._name def focusInEvent(self, event): """ Test whether the file has been changed 'behind our back' """ # Act normally to the focus event BaseTextCtrl.focusInEvent(self, event) # Test file change self.testWhetherFileWasChanged() def testWhetherFileWasChanged(self): """ testWhetherFileWasChanged() Test to see whether the file was changed outside our backs, and let the user decide what to do. Returns True if it was changed. """ # get the path path = self._filename if not os.path.isfile(path): # file is deleted from the outside return # test the modification time... mtime = os.path.getmtime(path) if mtime != self._modifyTime: # ask user dlg = QtWidgets.QMessageBox(self) dlg.setWindowTitle('File was changed') dlg.setText("File has been modified outside of the editor:\n"+ self._filename) dlg.setInformativeText("Do you want to reload?") t=dlg.addButton("Reload", QtWidgets.QMessageBox.AcceptRole) #0 dlg.addButton("Keep this version", QtWidgets.QMessageBox.RejectRole) #1 dlg.setDefaultButton(t) # whatever the result, we will reset the modified time self._modifyTime = os.path.getmtime(path) # get result and act result = dlg.exec_() if result == QtWidgets.QMessageBox.AcceptRole: self.reload() else: pass # when cancelled or explicitly said, do nothing # Return that indeed the file was changes return True def _onModificationChanged(self,changed): """Handler for the modificationChanged signal. Emit somethingChanged for the editorStack to update the modification notice.""" self.somethingChanged.emit() def _onModified(self): pyzo.parser.parseThis(self) def dragMoveEvent(self, event): """ Otherwise cursor can get stuck. https://bitbucket.org/iep-project/iep/issue/252 https://qt-project.org/forums/viewthread/3180 """ if event.mimeData().hasUrls(): event.acceptProposedAction() else: BaseTextCtrl.dropEvent(self, event) def dropEvent(self, event): """ Drop files in the list. """ if event.mimeData().hasUrls(): # file: let the editorstack do the work. pyzo.editors.dropEvent(event) else: # text: act normal BaseTextCtrl.dropEvent(self, event) def showEvent(self, event=None): """ Capture show event to change title. """ # Act normally if event: BaseTextCtrl.showEvent(self, event) # Make parser update pyzo.parser.parseThis(self) def setTitleInMainWindow(self): """ set the title text in the main window to show filename. """ # compose title name, path = self._name, self._filename if path: pyzo.main.setMainTitle(path) else: pyzo.main.setMainTitle(name) def save(self, filename=None): """ Save the file. No checking is done. """ # get filename if filename is None: filename = self._filename if not filename: raise ValueError("No filename specified, and no filename known.") # Test whether it was changed without us knowing. If so, dont save now. if self.testWhetherFileWasChanged(): return # Get text and remember where we are text = self.toPlainText() cursor = self.textCursor() linenr = cursor.blockNumber() + 1 index = cursor.positionInBlock() scroll = self.verticalScrollBar().value() # Convert line endings (optionally remove trailing whitespace if pyzo.config.settings.removeTrailingWhitespaceWhenSaving: lines = [line.rstrip() for line in text.split('\n')] if lines[-1]: lines.append('') # Ensure the file ends in an empty line text = self.lineEndings.join(lines) self.setPlainText(text) # Go back to where we were cursor = self.textCursor() cursor.movePosition(cursor.Start) # move to begin of the document cursor.movePosition(cursor.NextBlock,n=linenr-1) # n blocks down index = min(index, cursor.block().length()-1) cursor.movePosition(cursor.Right,n=index) # n chars right self.setTextCursor(cursor) self.verticalScrollBar().setValue(scroll) else: text = text.replace('\n', self.lineEndings) # Make bytes bb = text.encode(self.encoding) # Store f = open(filename, 'wb') try: f.write(bb) finally: f.close() # Update stats self._filename = normalizePath( filename ) self._name = os.path.split(self._filename)[1] self.document().setModified(False) self._modifyTime = os.path.getmtime(self._filename) # update title (in case of a rename) self.setTitleInMainWindow() # allow item to update its texts (no need: onModifiedChanged does this) #self.somethingChanged.emit() def reload(self): """ Reload text using the self._filename. We do not have a load method; we first try to load the file and only when we succeed create an editor to show it in... This method is only for reloading in case the file was changed outside of the editor. """ # We can only load if the filename is known if not self._filename: return filename = self._filename # Remember where we are cursor = self.textCursor() linenr = cursor.blockNumber() + 1 # Load file (as bytes) with open(filename, 'rb') as f: bb = f.read() # Convert to text text = bb.decode('UTF-8') # Process line endings (before setting the text) self.lineEndings= determineLineEnding(text) # Set text self.setPlainText(text) self.document().setModified(False) # Go where we were (approximately) self.gotoLine(linenr) def deleteLines(self): cursor = self.textCursor() # Find start and end of selection start = cursor.selectionStart() end = cursor.selectionEnd() # Expand selection: from start of first block to start of next block cursor.setPosition(start) cursor.movePosition(cursor.StartOfBlock) cursor.setPosition(end, cursor.KeepAnchor) cursor.movePosition(cursor.NextBlock, cursor.KeepAnchor) cursor.removeSelectedText() def duplicateLines(self): cursor = self.textCursor() # Find start and end of selection start = cursor.selectionStart() end = cursor.selectionEnd() # Expand selection: from start of first block to start of next block cursor.setPosition(start) cursor.movePosition(cursor.StartOfBlock) cursor.setPosition(end, cursor.KeepAnchor) cursor.movePosition(cursor.NextBlock, cursor.KeepAnchor) text = cursor.selectedText() cursor.setPosition(start) cursor.movePosition(cursor.StartOfBlock) cursor.insertText(text) def commentCode(self): """ Comment the lines that are currently selected """ indents = [] def getIndent(cursor): text = cursor.block().text().rstrip() if text: indents.append(len(text) - len(text.lstrip())) def commentBlock(cursor): cursor.setPosition(cursor.block().position() + minindent) cursor.insertText('# ') self.doForSelectedBlocks(getIndent) minindent = min(indents) if indents else 0 self.doForSelectedBlocks(commentBlock) def uncommentCode(self): """ Uncomment the lines that are currently selected """ #TODO: this should not be applied to lines that are part of a multi-line string #Define the uncomment function to be applied to all blocks def uncommentBlock(cursor): """ Find the first # on the line; if there is just whitespace before it, remove the # and if it is followed by a space remove the space, too """ text = cursor.block().text() commentStart = text.find('#') if commentStart == -1: return #No comment on this line if text[:commentStart].strip() != '': return #Text before the # #Move the cursor to the beginning of the comment cursor.setPosition(cursor.block().position() + commentStart) cursor.deleteChar() if text[commentStart:].startswith('# '): cursor.deleteChar() #Apply this function to all blocks self.doForSelectedBlocks(uncommentBlock) def gotoDef(self): """ Goto the definition for the word under the cursor """ # Get name of object to go to cursor = self.textCursor() if not cursor.hasSelection(): cursor.select(cursor.WordUnderCursor) word = cursor.selection().toPlainText() # Send the open command to the shell s = pyzo.shells.getCurrentShell() if s is not None: if word and word.isidentifier(): s.executeCommand('open %s\n'%word) else: s.write('Invalid identifier %r\n' % word) ## Introspection processing methods def processCallTip(self, cto): """ Processes a calltip request using a CallTipObject instance. """ # Try using buffer first if cto.tryUsingBuffer(): return # Try obtaining calltip from the source sig = pyzo.parser.getFictiveSignature(cto.name, self, True) if sig: # Done cto.finish(sig) else: # Try the shell shell = pyzo.shells.getCurrentShell() if shell: shell.processCallTip(cto) def processAutoComp(self, aco): """ Processes an autocomp request using an AutoCompObject instance. """ # Try using buffer first if aco.tryUsingBuffer(): return # Init name to poll by remote process (can be changed!) nameForShell = aco.name # Get normal fictive namespace fictiveNS = pyzo.parser.getFictiveNameSpace(self) fictiveNS = set(fictiveNS) # Add names if not aco.name: # "root" names aco.addNames(fictiveNS) # imports importNames, importLines = pyzo.parser.getFictiveImports(self) aco.addNames(importNames) else: # Prepare list of class names to check out classNames = [aco.name] handleSelf = True # Unroll supers while classNames: className = classNames.pop(0) if not className: continue if handleSelf or (className in fictiveNS): # Only the self list (only first iter) fictiveClass = pyzo.parser.getFictiveClass( className, self, handleSelf) handleSelf = False if fictiveClass: aco.addNames( fictiveClass.members ) classNames.extend(fictiveClass.supers) else: nameForShell = className break # If there's a shell, let it finish the autocompletion shell = pyzo.shells.getCurrentShell() if shell: aco.name = nameForShell # might be the same or a base class shell.processAutoComp(aco) else: # Otherwise we finish it ourselves aco.finish() if __name__=="__main__": # Do some stubbing to run this module as a unit separate from pyzo # TODO: untangle pyzo from this module where possible class DummyParser: def parseThis(self, x): pass pyzo.parser = DummyParser() EditorContextMenu = QtWidgets.QMenu # noqa app = QtWidgets.QApplication([]) win = PyzoEditor(None) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+C"), win).activated.connect(win.copy) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+X"), win).activated.connect(win.cut) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+V"), win).activated.connect(win.paste) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Shift+V"), win).activated.connect(win.pasteAndSelect) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Z"), win).activated.connect(win.undo) QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+Y"), win).activated.connect(win.redo) tmp = "foo(bar)\nfor bar in range(5):\n print bar\n" tmp += "\nclass aap:\n def monkey(self):\n pass\n\n" win.setPlainText(tmp) win.show() app.exec_() pyzo-4.4.3/pyzo/core/editorTabs.py0000666000000000000000000015160413123037260015264 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ EditorTabs class Replaces the earlier EditorStack class. The editor tabs class represents the different open files. They can be selected using a tab widget (with tabs placed north of the editor). It also has a find/replace widget that is at the bottom of the editor. """ import os, time, gc from pyzo.util.qt import QtCore, QtGui, QtWidgets import pyzo from pyzo.core.compactTabWidget import CompactTabWidget from pyzo.core.editor import createEditor from pyzo.core.baseTextCtrl import normalizePath from pyzo.core.pyzoLogging import print from pyzo.core.icons import EditorTabToolButton from pyzo import translate # Constants for the alignments of tabs MIN_NAME_WIDTH = 50 MAX_NAME_WIDTH = 200 def simpleDialog(item, action, question, options, defaultOption): """ simpleDialog(editor, action, question, options, defaultOption) Options with special buttons ---------------------------- ok, open, save, cancel, close, discard, apply, reset, restoredefaults, help, saveall, yes, yestoall, no, notoall, abort, retry, ignore. Returns the selected option as a string, or None if canceled. """ # Get filename if isinstance(item, FileItem): filename = item.id else: filename = item.id() # create button map mb = QtWidgets.QMessageBox M = { 'ok':mb.Ok, 'open':mb.Open, 'save':mb.Save, 'cancel':mb.Cancel, 'close':mb.Close, 'discard':mb.Discard, 'apply':mb.Apply, 'reset':mb.Reset, 'restoredefaults':mb.RestoreDefaults, 'help':mb.Help, 'saveall':mb.SaveAll, 'yes':mb.Yes, 'yestoall':mb.YesToAll, 'no':mb.No, 'notoall':mb.NoToAll, 'abort':mb.Abort, 'retry':mb.Retry, 'ignore':mb.Ignore} # setup dialog dlg = QtWidgets.QMessageBox(pyzo.main) dlg.setWindowTitle('Pyzo') dlg.setText(action + " file:\n{}".format(filename)) dlg.setInformativeText(question) # process options buttons = {} for option in options: option_lower = option.lower() # Use standard button? if option_lower in M: button = dlg.addButton(M[option_lower]) else: button = dlg.addButton(option, dlg.AcceptRole) buttons[button] = option # Set as default? if option_lower == defaultOption.lower(): dlg.setDefaultButton(button) # get result dlg.exec_() button = dlg.clickedButton() if button in buttons: return buttons[button] else: return None def get_shortest_unique_filename(filename, filenames): """ Get a representation of filename in a way that makes it look unique compared to the other given filenames. The most unique part of the path is used, and every directory in between that part and the actual filename is represented with a slash. """ # Normalize and avoid having filename itself in filenames filename1 = filename.replace('\\', '/') filenames = [fn.replace('\\', '/') for fn in filenames] filenames = [fn for fn in filenames if fn != filename1] # Prepare for finding uniqueness nameparts1 = filename1.split('/') uniqueness = [len(filenames) for i in nameparts1] # Establish what parts of the filename are not unique when compared to # each entry in filenames. for filename2 in filenames: nameparts2 = filename2.split('/') nonunique_for_this_filename = set() for i in range(len(nameparts1)): if i < len(nameparts2): if nameparts2[i] == nameparts1[i]: nonunique_for_this_filename.add(i) if nameparts2[-1-i] == nameparts1[-1-i]: nonunique_for_this_filename.add(-i-1) for i in nonunique_for_this_filename: uniqueness[i] -= 1 # How unique is the filename? If its not unique at all, use only base name max_uniqueness = max(uniqueness[:-1]) if max_uniqueness == 0: return nameparts1[-1] # Produce display name based on base name and last most-unique part displayname = nameparts1[-1] for i in reversed(range(len(uniqueness)-1)): displayname = '/' + displayname if uniqueness[i] == max_uniqueness: displayname = nameparts1[i] + displayname break return displayname # todo: some management stuff could (should?) go here class FileItem: """ FileItem(editor) A file item represents an open file. It is associated with an editing component and has a filename. """ def __init__(self, editor): # Store editor self._editor = editor # Init pinned state self._pinned = False @property def editor(self): """ Get the editor component corresponding to this item. """ return self._editor @property def id(self): """ Get an id of this editor. This is the filename, or for tmp files, the name. """ if self.filename: return self.filename else: return self.name @property def filename(self): """ Get the full filename corresponding to this item. """ return self._editor.filename @property def name(self): """ Get the name corresponding to this item. """ return self._editor.name @property def dirty(self): """ Get whether the file has been changed since it is changed. """ return self._editor.document().isModified() @property def pinned(self): """ Get whether this item is pinned (i.e. will not be closed when closing all files. """ return self._pinned # todo: when this works with the new editor, put in own module. class FindReplaceWidget(QtWidgets.QFrame): """ A widget to find and replace text. """ def __init__(self, *args): QtWidgets.QFrame.__init__(self, *args) self.setFocusPolicy(QtCore.Qt.ClickFocus) # init layout layout = QtWidgets.QHBoxLayout(self) layout.setSpacing(0) self.setLayout(layout) # Create some widgets first to realize a correct tab order self._hidebut = QtWidgets.QToolButton(self) self._findText = QtWidgets.QLineEdit(self) self._replaceText = QtWidgets.QLineEdit(self) if True: # Create sub layouts vsubLayout = QtWidgets.QVBoxLayout() vsubLayout.setSpacing(0) layout.addLayout(vsubLayout, 0) # Add button self._hidebut.setFont( QtGui.QFont('helvetica',7) ) self._hidebut.setToolTip(translate('search', 'Hide search widget (Escape)')) self._hidebut.setIcon( pyzo.icons.cancel ) self._hidebut.setIconSize(QtCore.QSize(16,16)) vsubLayout.addWidget(self._hidebut, 0) vsubLayout.addStretch(1) layout.addSpacing(10) if True: # Create sub layouts vsubLayout = QtWidgets.QVBoxLayout() hsubLayout = QtWidgets.QHBoxLayout() vsubLayout.setSpacing(0) hsubLayout.setSpacing(0) layout.addLayout(vsubLayout, 0) # Add find text self._findText.setToolTip(translate('search', 'Find pattern')) vsubLayout.addWidget(self._findText, 0) vsubLayout.addLayout(hsubLayout) # Add previous button self._findPrev = QtWidgets.QToolButton(self) t = translate('search', 'Previous ::: Find previous occurrence of the pattern.') self._findPrev.setText(t); self._findPrev.setToolTip(t.tt) hsubLayout.addWidget(self._findPrev, 0) hsubLayout.addStretch(1) # Add next button self._findNext = QtWidgets.QToolButton(self) t = translate('search', 'Next ::: Find next occurrence of the pattern.') self._findNext.setText(t); self._findNext.setToolTip(t.tt) #self._findNext.setDefault(True) # Not possible with tool buttons hsubLayout.addWidget(self._findNext, 0) layout.addSpacing(10) if True: # Create sub layouts vsubLayout = QtWidgets.QVBoxLayout() hsubLayout = QtWidgets.QHBoxLayout() vsubLayout.setSpacing(0) hsubLayout.setSpacing(0) layout.addLayout(vsubLayout, 0) # Add replace text self._replaceText.setToolTip(translate('search', 'Replace pattern')) vsubLayout.addWidget(self._replaceText, 0) vsubLayout.addLayout(hsubLayout) # Add replace-all button self._replaceAll = QtWidgets.QToolButton(self) t = translate('search', 'Repl. all ::: Replace all matches in current document.') self._replaceAll.setText(t); self._replaceAll.setToolTip(t.tt) hsubLayout.addWidget(self._replaceAll, 0) hsubLayout.addStretch(1) # Add replace button self._replace = QtWidgets.QToolButton(self) t = translate('search', 'Replace ::: Replace this match.') self._replace.setText(t); self._replace.setToolTip(t.tt) hsubLayout.addWidget(self._replace, 0) layout.addSpacing(10) if True: # Create sub layouts vsubLayout = QtWidgets.QVBoxLayout() vsubLayout.setSpacing(0) layout.addLayout(vsubLayout, 0) # Add match-case checkbox t = translate('search', 'Match case ::: Find words that match case.') self._caseCheck = QtWidgets.QCheckBox(t, self) self._caseCheck.setToolTip(t.tt) vsubLayout.addWidget(self._caseCheck, 0) # Add regexp checkbox t = translate('search', 'RegExp ::: Find using regular expressions.') self._regExp = QtWidgets.QCheckBox(t, self) self._regExp.setToolTip(t.tt) vsubLayout.addWidget(self._regExp, 0) if True: # Create sub layouts vsubLayout = QtWidgets.QVBoxLayout() vsubLayout.setSpacing(0) layout.addLayout(vsubLayout, 0) # Add whole-word checkbox t = translate('search', 'Whole words ::: Find only whole words.') self._wholeWord = QtWidgets.QCheckBox(t, self) self._wholeWord.setToolTip(t.tt) self._wholeWord.resize(60, 16) vsubLayout.addWidget(self._wholeWord, 0) # Add autohide dropbox t = translate('search', 'Auto hide ::: Hide search/replace when unused for 10 s.') self._autoHide = QtWidgets.QCheckBox(t, self) self._autoHide.setToolTip(t.tt) self._autoHide.resize(60, 16) vsubLayout.addWidget(self._autoHide, 0) layout.addStretch(1) # Set placeholder texts for lineEdit in [self._findText, self._replaceText]: if hasattr(lineEdit, 'setPlaceholderText'): lineEdit.setPlaceholderText(lineEdit.toolTip()) lineEdit.textChanged.connect(self.autoHideTimerReset) # Set focus policy for but in [self._findPrev, self._findNext, self._replaceAll, self._replace, self._caseCheck, self._wholeWord, self._regExp]: #but.setFocusPolicy(QtCore.Qt.ClickFocus) but.clicked.connect(self.autoHideTimerReset) # create timer objects self._timerBeginEnd = QtCore.QTimer(self) self._timerBeginEnd.setSingleShot(True) self._timerBeginEnd.timeout.connect( self.resetAppearance ) # self._timerAutoHide = QtCore.QTimer(self) self._timerAutoHide.setSingleShot(False) self._timerAutoHide.setInterval(500) # ms self._timerAutoHide.timeout.connect( self.autoHideTimerCallback ) self._timerAutoHide_t0 = time.time() self._timerAutoHide.start() # create callbacks self._findText.returnPressed.connect(self.findNext) self._hidebut.clicked.connect(self.hideMe) self._findNext.clicked.connect(self.findNext) self._findPrev.clicked.connect(self.findPrevious) self._replace.clicked.connect(self.replaceOne) self._replaceAll.clicked.connect(self.replaceAll) # self._regExp.stateChanged.connect(self.handleReplacePossible) # init case and regexp self._caseCheck.setChecked( bool(pyzo.config.state.find_matchCase) ) self._regExp.setChecked( bool(pyzo.config.state.find_regExp) ) self._wholeWord.setChecked( bool(pyzo.config.state.find_wholeWord) ) self._autoHide.setChecked( bool(pyzo.config.state.find_autoHide) ) # show or hide? if bool(pyzo.config.state.find_show): self.show() else: self.hide() def autoHideTimerReset(self): self._timerAutoHide_t0 = time.time() def autoHideTimerCallback(self): """ Check whether we should hide the tool. """ timeout = pyzo.config.advanced.find_autoHide_timeout if self._autoHide.isChecked(): if (time.time() - self._timerAutoHide_t0) > timeout: # seconds # Hide if editor has focus es = self.parent() # editor stack editor = es.getCurrentEditor() if editor and editor.hasFocus(): self.hide() def hideMe(self): """ Hide the find/replace widget. """ self.hide() es = self.parent() # editor stack #es._boxLayout.activate() editor = es.getCurrentEditor() if editor: editor.setFocus() def event(self, event): """ Handle tab key and escape key. For the tab key we need to overload event instead of KeyPressEvent. """ if isinstance(event, QtGui.QKeyEvent): if event.key() in (QtCore.Qt.Key_Tab, QtCore.Qt.Key_Backtab): event.accept() # focusNextPrevChild is called by Qt return True elif event.key() == QtCore.Qt.Key_Escape: self.hideMe() event.accept() return True # Otherwise ... handle in default manner return QtWidgets.QFrame.event(self, event) def handleReplacePossible(self, state): """ Disable replacing when using regular expressions. """ for w in [self._replaceText, self._replaceAll, self._replace]: w.setEnabled(not state) def startFind(self,event=None): """ Use this rather than show(). It will check if anything is selected in the current editor, and if so, will set that as the initial search string """ # show self.show() self.autoHideTimerReset() # get needle editor = self.parent().getCurrentEditor() if editor: needle = editor.textCursor().selectedText().replace('\u2029', '\n') if needle: self._findText.setText( needle ) # select the find-text self.selectFindText() def notifyPassBeginEnd(self): self.setStyleSheet("QFrame { background:#f00; }") self._timerBeginEnd.start(300) def resetAppearance(self): self.setStyleSheet("QFrame {}") def selectFindText(self): """ Select the textcontrol for the find needle, and the text in it """ # select text self._findText.selectAll() # focus self._findText.setFocus() def findNext(self, event=None): self.find() #self._findText.setFocus() def findPrevious(self, event=None): self.find(False) # self._findText.setFocus() def findSelection(self, event=None): self.startFind() self.findNext() def findSelectionBw(self, event=None): self.startFind() self.findPrevious() def find(self, forward=True, wrapAround=True): """ The main find method. Returns True if a match was found. """ # Reset timer self.autoHideTimerReset() # get editor editor = self.parent().getCurrentEditor() if not editor: return # find flags flags = QtGui.QTextDocument.FindFlags() if self._caseCheck.isChecked(): flags |= QtGui.QTextDocument.FindCaseSensitively if not forward: flags |= QtGui.QTextDocument.FindBackward #if self._wholeWord.isChecked(): # flags |= QtGui.QTextDocument.FindWholeWords # focus self.selectFindText() # get text to find needle = self._findText.text() if self._regExp.isChecked(): #Make needle a QRegExp; speciffy case-sensitivity here since the #FindCaseSensitively flag is ignored when finding using a QRegExp needle = QtCore.QRegExp(needle, QtCore.Qt.CaseSensitive if self._caseCheck.isChecked() else QtCore.Qt.CaseInsensitive) elif self._wholeWord.isChecked(): # Use regexp, because the default begaviour does not find # whole words correctly, see issue #276 # it should *not* find this in this_word needle = QtCore.QRegExp(r'\b' + needle + r'\b', QtCore.Qt.CaseSensitive if self._caseCheck.isChecked() else QtCore.Qt.CaseInsensitive) # estblish start position cursor = editor.textCursor() result = editor.document().find(needle, cursor, flags) if not result.isNull(): editor.setTextCursor(result) elif wrapAround: self.notifyPassBeginEnd() #Move cursor to start or end of document if forward: cursor.movePosition(cursor.Start) else: cursor.movePosition(cursor.End) #Try again result = editor.document().find(needle, cursor, flags) if not result.isNull(): editor.setTextCursor(result) # done editor.setFocus() return not result.isNull() def replaceOne(self,event=None, wrapAround=True): """ If the currently selected text matches the find string, replaces that text. Then it finds and selects the next match. Returns True if a next match was found. """ # get editor editor = self.parent().getCurrentEditor() if not editor: return #Create a cursor to do the editing cursor = editor.textCursor() # matchCase matchCase = self._caseCheck.isChecked() # get text to find needle = self._findText.text() if not matchCase: needle = needle.lower() # get replacement replacement = self._replaceText.text() # get original text original = cursor.selectedText().replace('\u2029', '\n') if not original: original = '' if not matchCase: original = original.lower() # replace #TODO: < line does not work for regexp-search! if original and original == needle: cursor.insertText( replacement ) # next! return self.find(wrapAround=wrapAround) def replaceAll(self,event=None): #TODO: share a cursor between all replaces, in order to #make this one undo/redo-step # get editor editor = self.parent().getCurrentEditor() if not editor: return # get current position originalPosition = editor.textCursor() # Move to beginning of text and replace all # Make this a single undo operation cursor = editor.textCursor() cursor.beginEditBlock() try: cursor.movePosition(cursor.Start) editor.setTextCursor(cursor) while self.replaceOne(wrapAround=False): pass finally: cursor.endEditBlock() # reset position editor.setTextCursor(originalPosition) class FileTabWidget(CompactTabWidget): """ FileTabWidget(parent) The tab widget that contains the editors and lists all open files. """ def __init__(self, parent): CompactTabWidget.__init__(self, parent, padding=(2,1,0,4)) # Init main file self._mainFile = '' # Init item history self._itemHistory = [] # # Create a corner widget # but = QtWidgets.QToolButton() # but.setIcon( pyzo.icons.cross ) # but.setIconSize(QtCore.QSize(16,16)) # but.clicked.connect(self.onClose) # self.setCornerWidget(but) # Bind signal to update items and keep track of history self.currentChanged.connect(self.updateItems) self.currentChanged.connect(self.trackHistory) self.currentChanged.connect(self.setTitleInMainWindowWhenTabChanged) self.setTitleInMainWindowWhenTabChanged(-1) def setTitleInMainWindowWhenTabChanged(self, index): # Valid index? if index<0 or index>=self.count(): pyzo.main.setMainTitle() # No open file # Remove current item from history currentItem = self.currentItem() if currentItem: currentItem.editor.setTitleInMainWindow() ## Item management def items(self): """ Get the items in the tab widget. These are Item instances, and are in the order in which they are at the tab bar. """ tabBar = self.tabBar() items = [] for i in range(tabBar.count()): item = tabBar.tabData(i) if item is None: continue items.append(item) return items def currentItem(self): """ Get the item corresponding to the currently active tab. """ i = self.currentIndex() if i>=0: return self.tabBar().tabData(i) def getItemAt(self, i): return self.tabBar().tabData(i) def mainItem(self): """ Get the item corresponding to the "main" file. Returns None if there is no main file. """ for item in self.items(): if item.id == self._mainFile: return item else: return None def trackHistory(self, index): """ trackHistory(index) Called when a tab is changed. Puts the current item on top of the history. """ # Valid index? if index<0 or index>=self.count(): return # Remove current item from history currentItem = self.currentItem() while currentItem in self._itemHistory: self._itemHistory.remove(currentItem) # Add current item to history self._itemHistory.insert(0, currentItem) # Limit history size self._itemHistory[10:] = [] def setCurrentItem(self, item): """ _setCurrentItem(self, item) Set a FileItem instance to be the current. If the given item is not in the list, no action is taken. item can be an int, FileItem, or file name. """ if isinstance(item, int): self.setCurrentIndex(item) elif isinstance(item, FileItem): items = self.items() for i in range(self.count()): if item is items[i]: self.setCurrentIndex(i) break elif isinstance(item, str): items = self.items() for i in range(self.count()): if item == items[i].filename: self.setCurrentIndex(i) break else: raise ValueError('item should be int, FileItem or file name.') def selectPreviousItem(self): """ Select the previously selected item. """ # make an old item history if len(self._itemHistory)>1 and self._itemHistory[1] is not None: item = self._itemHistory[1] self.setCurrentItem(item) # just select first one then ... elif self.count(): item = 0 self.setCurrentItem(item) ## Closing, adding and updating def onClose(self): """ onClose() Request to close the current tab. """ self.tabCloseRequested.emit(self.currentIndex()) def removeTab(self, which): """ removeTab(which) Removes the specified tab. which can be an integer, an item, or an editor. """ # Init items = self.items() theIndex = -1 # Find index if isinstance(which, int) and which>=0 and which= 0: # Close tab CompactTabWidget.removeTab(self, theIndex) # Delete editor items[theIndex].editor.destroy() gc.collect() def addItem(self, item, update=True): """ addItem(item, update=True) Add item to the tab widget. Set update to false if you are calling this method many times in a row. Then use updateItemsFull() to update the tab widget. """ # Add tab and widget i = self.addTab(item.editor, item.name) tabBut = EditorTabToolButton(self.tabBar()) self.tabBar().setTabButton(i, QtWidgets.QTabBar.LeftSide, tabBut) # Keep informed about changes item.editor.somethingChanged.connect(self.updateItems) item.editor.blockCountChanged.connect(self.updateItems) item.editor.breakPointsChanged.connect(self.parent().updateBreakPoints) # Store the item at the tab self.tabBar().setTabData(i, item) # Emit the currentChanged again (already emitted on addTab), because # now the itemdata is actually set self.currentChanged.emit(self.currentIndex()) # Update if update: self.updateItems() def updateItemsFull(self): """ updateItemsFull() Update the appearance of the items and also updates names and re-aligns the items. """ self.updateItems() self.tabBar().alignTabs() def updateItems(self): """ updateItems() Update the appearance of the items. """ # Get items and tab bar items = self.items() tabBar = self.tabBar() # Check whether we have name clashes, which we can try to resolve namecounts = {} for i in range(len(items)): item = items[i] if item is None: continue xx = namecounts.setdefault(item.name, []) xx.append(item) for i in range(len(items)): # Get item item = items[i] if item is None: continue # Get display name items_with_this_name = namecounts[item.name] if len(items_with_this_name) <= 1: display_name = item.name else: filenames = [j.filename for j in items_with_this_name] try: display_name = get_shortest_unique_filename(item.filename, filenames) except Exception as err: # Catch this, just in case ... print('could not get unique name for:\n%r' % filenames) print(err) display_name = item.name tabBar.setTabText(i, display_name) # Update name and tooltip if item.dirty: tabBar.setTabToolTip(i, item.filename + ' [modified]') else: tabBar.setTabToolTip(i, item.filename) # Determine text color. Is main file? Is current? if self._mainFile == item.id: tabBar.setTabTextColor(i, QtGui.QColor('#008')) elif i == self.currentIndex(): tabBar.setTabTextColor(i, QtGui.QColor('#000')) else: tabBar.setTabTextColor(i, QtGui.QColor('#444')) # Get number of blocks nBlocks = item.editor.blockCount() if nBlocks == 1 and not item.editor.toPlainText(): nBlocks = 0 # Update appearance of icon but = tabBar.tabButton(i, QtWidgets.QTabBar.LeftSide) but.updateIcon(item.dirty, self._mainFile==item.id, item.pinned, nBlocks) class EditorTabs(QtWidgets.QWidget): """ The EditorTabs instance manages the open files and corresponding editors. It does the saving loading etc. """ # Signal to indicate that a breakpoint has changed, emits dict breakPointsChanged = QtCore.Signal(object) # Signal to notify that a different file was selected currentChanged = QtCore.Signal() # Signal to notify that the parser has parsed the text (emit by parser) parserDone = QtCore.Signal() def __init__(self, parent): QtWidgets.QWidget.__init__(self,parent) # keep a booking of opened directories self._lastpath = '' # keep track of all breakpoints self._breakPoints = {} # create tab widget self._tabs = FileTabWidget(self) self._tabs.tabCloseRequested.connect(self.closeFile) self._tabs.currentChanged.connect(self.onCurrentChanged) # Double clicking a tab saves the file, clicking on the bar opens a new file self._tabs.tabBar().tabDoubleClicked.connect(self.saveFile) self._tabs.tabBar().barDoubleClicked.connect(self.newFile) # Create find/replace widget self._findReplace = FindReplaceWidget(self) # create box layout control and add widgets self._boxLayout = QtWidgets.QVBoxLayout(self) self._boxLayout.addWidget(self._tabs, 1) self._boxLayout.addWidget(self._findReplace, 0) # spacing of widgets self._boxLayout.setSpacing(0) # apply self.setLayout(self._boxLayout) #self.setAttribute(QtCore.Qt.WA_AlwaysShowToolTips,True) # accept drops self.setAcceptDrops(True) # restore state (call later so that the menu module can bind to the # currentChanged signal first, in order to set tab/indentation # checkmarks appropriately) # todo: Resetting the scrolling would work better if set after # the widgets are properly sized. pyzo.callLater(self.restoreEditorState) def addContextMenu(self): """ Adds a context menu to the tab bar """ from pyzo.core.menu import EditorTabContextMenu self._menu = EditorTabContextMenu(self, "EditorTabMenu") self._tabs.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._tabs.customContextMenuRequested.connect(self.contextMenuTriggered) def contextMenuTriggered(self, p): """ Called when context menu is clicked """ # Get index of current tab index = self._tabs.tabBar().tabAt(p) self._menu.setIndex(index) # Show menu if item is available if index >= 0: p = self._tabs.tabBar().tabRect(index).bottomLeft() self._menu.popup(self._tabs.tabBar().mapToGlobal(p)) def onCurrentChanged(self): self.currentChanged.emit() def getCurrentEditor(self): """ Get the currently active editor. """ item = self._tabs.currentItem() if item: return item.editor else: return None def getMainEditor(self): """ Get the editor that represents the main file, or None if there is no main file. """ item = self._tabs.mainItem() if item: return item.editor else: return None def __iter__(self): tmp = [item.editor for item in self._tabs.items()] return tmp.__iter__() def updateBreakPoints(self, editor=None): # Get list of editors to update keypoints for if editor is None: editors = self self._breakPoints = {} # Full reset else: editors = [editor] # Update our keypoints dict for editor in editors: fname = editor._filename or editor._name if not fname: continue linenumbers = editor.breakPoints() if linenumbers: self._breakPoints[fname] = linenumbers else: self._breakPoints.pop(fname, None) # Emit signal so shells can update the kernel self.breakPointsChanged.emit(self._breakPoints) def setDebugLineIndicators(self, *filename_linenr): """ Set the debug line indicator. There is one indicator global to pyzo, corresponding to the last shell for which we received the indicator. """ if len(filename_linenr) and filename_linenr[0] is None: filename_linenr = [] # Normalize case filename_linenr = [(os.path.normcase(i[0]), int(i[1])) for i in filename_linenr] for item in self._tabs.items(): # Prepare editor = item._editor fname = editor._filename or editor._name fname = os.path.normcase(fname) # Reset editor.setDebugLineIndicator(None) # Set for filename, linenr in filename_linenr: if fname == filename: active = (filename, linenr) == filename_linenr[-1] editor.setDebugLineIndicator(linenr, active) ## Loading ad saving files def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() def dropEvent(self, event): """ Drop files in the list. """ for qurl in event.mimeData().urls(): path = str( qurl.toLocalFile() ) if os.path.isfile(path): self.loadFile(path) elif os.path.isdir(path): self.loadDir(path) else: pass def newFile(self): """ Create a new (unsaved) file. """ # create editor editor = createEditor(self, None) editor.document().setModified(False) # Start out as OK # add to list item = FileItem(editor) self._tabs.addItem(item) self._tabs.setCurrentItem(item) # set focus to new file editor.setFocus() return item def openFile(self): """ Create a dialog for the user to select a file. """ # determine start dir # todo: better selection of dir, using project manager editor = self.getCurrentEditor() if editor and editor._filename: startdir = os.path.split(editor._filename)[0] else: startdir = self._lastpath if (not startdir) or (not os.path.isdir(startdir)): startdir = '' # show dialog msg = translate("editorTabs", "Select one or more files to open") filter = "Python (*.py *.pyw);;" filter += "Pyrex (*.pyi *.pyx *.pxd);;" filter += "C (*.c *.h *.cpp *.c++);;" #filter += "Py+Cy+C (*.py *.pyw *.pyi *.pyx *.pxd *.c *.h *.cpp);;" filter += "All (*)" if True: filenames = QtWidgets.QFileDialog.getOpenFileNames(self, msg, startdir, filter) if isinstance(filenames, tuple): # PySide filenames = filenames[0] else: # Example how to preselect files, can be used when the users # opens a file in a project to select all files currently not # loaded. d = QtWidgets.QFileDialog(self, msg, startdir, filter) d.setFileMode(d.ExistingFiles) d.selectFile('"codeparser.py" "editorStack.py"') d.exec_() if d.result(): filenames = d.selectedFiles() else: filenames = [] # were some selected? if not filenames: return # load for filename in filenames: self.loadFile(filename) def openDir(self): """ Create a dialog for the user to select a directory. """ # determine start dir editor = self.getCurrentEditor() if editor and editor._filename: startdir = os.path.split(editor._filename)[0] else: startdir = self._lastpath if not os.path.isdir(startdir): startdir = '' # show dialog msg = "Select a directory to open" dirname = QtWidgets.QFileDialog.getExistingDirectory(self, msg, startdir) # was a dir selected? if not dirname: return # load self.loadDir(dirname) def loadFile(self, filename, updateTabs=True): """ Load the specified file. On success returns the item of the file, also if it was already open.""" # Note that by giving the name of a tempfile, we can select that # temp file. # normalize path if filename[0] != '<': filename = normalizePath(filename) if not filename: return None # if the file is already open... for item in self._tabs.items(): if item.id == filename: # id gets _filename or _name for temp files break else: item = None if item: self._tabs.setCurrentItem(item) print("File already open: '{}'".format(filename)) return item # create editor try: editor = createEditor(self, filename) except Exception as err: # Notify in logger print("Error loading file: ", err) # Make sure the user knows m = QtWidgets.QMessageBox(self) m.setWindowTitle("Error loading file") m.setText(str(err)) m.setIcon(m.Warning) m.exec_() return None # create list item item = FileItem(editor) self._tabs.addItem(item, updateTabs) if updateTabs: self._tabs.setCurrentItem(item) # store the path self._lastpath = os.path.dirname(item.filename) return item def loadDir(self, path): """ Create a project with the dir's name and add all files contained in the directory to it. extensions is a komma separated list of extenstions of files to accept... """ # if the path does not exist, stop path = os.path.abspath(path) if not os.path.isdir(path): print("ERROR loading dir: the specified directory does not exist!") return # get extensions extensions = pyzo.config.advanced.fileExtensionsToLoadFromDir extensions = extensions.replace(',',' ').replace(';',' ') extensions = ["."+a.lstrip(".").strip() for a in extensions.split(" ")] # init item item = None # open all qualified files... self._tabs.setUpdatesEnabled(False) try: filelist = os.listdir(path) for filename in filelist: filename = os.path.join(path, filename) ext = os.path.splitext(filename)[1] if str(ext) in extensions: item = self.loadFile(filename, False) finally: self._tabs.setUpdatesEnabled(True) self._tabs.updateItems() # return lastopened item return item def saveFileAs(self, editor=None): """ Create a dialog for the user to select a file. returns: True if succesfull, False if fails """ # get editor if editor is None: editor = self.getCurrentEditor() if editor is None: return False # get startdir if editor._filename: startdir = os.path.dirname(editor._filename) else: startdir = self._lastpath # Try the file browser or project manager to suggest a path fileBrowser = pyzo.toolManager.getTool('pyzofilebrowser') projectManager = pyzo.toolManager.getTool('pyzoprojectmanager') if fileBrowser: startdir = fileBrowser.getDefaultSavePath() if projectManager and not startdir: startdir = projectManager.getDefaultSavePath() if not os.path.isdir(startdir): startdir = '' # show dialog msg = translate("editorTabs", "Select the file to save to") filter = "Python (*.py *.pyw);;" filter += "Pyrex (*.pyi *.pyx *.pxd);;" filter += "C (*.c *.h *.cpp);;" #filter += "Py+Cy+C (*.py *.pyw *.pyi *.pyx *.pxd *.c *.h *.cpp);;" filter += "All (*.*)" filename = QtWidgets.QFileDialog.getSaveFileName(self, msg, startdir, filter) if isinstance(filename, tuple): # PySide filename = filename[0] # give python extension if it has no extension head, tail = os.path.split(filename) if tail and '.' not in tail: filename += '.py' # proceed or cancel if filename: return self.saveFile(editor, filename) else: return False # Cancel was pressed def saveFile(self, editor=None, filename=None): """ Save the file. returns: True if succesfull, False if fails """ # get editor if editor is None: editor = self.getCurrentEditor() elif isinstance(editor, int): index = editor editor = None if index>=0: item = self._tabs.items()[index] editor = item.editor if editor is None: return False # get filename if filename is None: filename = editor._filename if not filename: return self.saveFileAs(editor) # let the editor do the low level stuff... try: editor.save(filename) except Exception as err: # Notify in logger print("Error saving file:",err) # Make sure the user knows m = QtWidgets.QMessageBox(self) m.setWindowTitle("Error saving file") m.setText(str(err)) m.setIcon(m.Warning) m.exec_() # Return now return False # get actual normalized filename filename = editor._filename # notify # TODO: message concerining line endings print("saved file: {} ({})".format(filename, editor.lineEndingsHumanReadable)) self._tabs.updateItems() # todo: this is where we once detected whether the file being saved was a style file. # Notify done return True def saveAllFiles(self): """ Save all files""" for editor in self: self.saveFile(editor) ## Closing files / closing down def _get_action_texts(self): options = translate("editor", "Close, Discard, Cancel, Save").split(',') options = [i.strip() for i in options] try: close_txt, discard_txt, cancel_txt, save_txt = options except Exception: print('error in translation for close, discard, cancel, save.') close_txt, discard_txt, cancel_txt, save_txt = "Close", "Discard", "Cancel", "Save" return close_txt, discard_txt, cancel_txt, save_txt def askToSaveFileIfDirty(self, editor): """ askToSaveFileIfDirty(editor) If the given file is not saved, pop up a dialog where the user can save the file Returns 1 if file need not be saved. Returns 2 if file was saved. Returns 3 if user discarded changes. Returns 0 if cancelled. """ # should we ask to save the file? if editor.document().isModified(): # Ask user what to do close_txt, discard_txt, cancel_txt, save_txt = self._get_action_texts() result = simpleDialog(editor, translate("editor", "Closing"), translate("editor", "Save modified file?"), [discard_txt, cancel_txt, save_txt], save_txt) # Get result and act if result == save_txt: return 2 if self.saveFile(editor) else 0 elif result == discard_txt: return 3 else: # cancel return 0 return 1 def closeFile(self, editor=None): """ Close the selected (or current) editor. Returns same result as askToSaveFileIfDirty() """ # get editor if editor is None: editor = self.getCurrentEditor() item = self._tabs.currentItem() elif isinstance(editor, int): index = editor editor, item = None, None if index>=0: item = self._tabs.items()[index] editor = item.editor else: item = None for i in self._tabs.items(): if i.editor is editor: item = i if editor is None or item is None: return # Ask if dirty result = self.askToSaveFileIfDirty(editor) # Ask if closing pinned file close_txt, discard_txt, cancel_txt, save_txt = self._get_action_texts() if result and item.pinned: result = simpleDialog(editor, translate("editor", "Closing pinned"), translate("editor", "Are you sure you want to close this pinned file?"), [close_txt, cancel_txt], cancel_txt) result = result == close_txt # ok, close... if result: if editor._name.startswith(" self._last_date: self._last_date = today self._commands.append('# ==== ' + today.strftime('%Y-%m-%d')) self.command_added.emit(self._commands[-1]) # Clear it try: index = self._commands.index(command) except ValueError: pass else: self._commands.pop(index) self.command_removed.emit(index) # Append self._commands.append(command) self.command_added.emit(self._commands[-1]) # Reset? if len(self._commands) > self.max_commands: self._commands[:self.max_commands // 2] = [] self.commands_reset.emit() def pop(self, index): """ Remove a command by index. """ self._commands.pop(index) self.command_removed.emit(index) def find_starting_with(self, firstpart, n=1): """ Find the nth (1-based) command that starts with firstpart, or None. """ count = 0 for c in reversed(self._commands): if c.startswith(firstpart): count += 1 if count >= n: return c return None def find_all(self, needle): """ Find all commands that contain the given text. In order of being used. """ commands = [] for c in reversed(self._commands): if needle in c: commands.append(c) return commands pyzo-4.4.3/pyzo/core/icons.py0000666000000000000000000005217113123037260014276 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Icons module Defines functionality for creating icons by composing different overlays and also by directly drawing into the pixmap. This allows making icons that show information to the user in a very effective, yet subtle manner. """ from pyzo.util.qt import QtCore, QtGui, QtWidgets import pyzo class IconArtist: """ IconArtist(icon=None) Object to draw icons with. Can be instantiated with an existing icon or as a blank icon. Perform operations and then use finish() to obtain the result. """ def __init__(self, icon=None): # Get pixmap from given icon (None creates empty pixmap) self._pm = self._getPixmap(icon) # Instantiate painter for the pixmap self._painter = QtGui.QPainter() self._painter.begin(self._pm) def finish(self, icon=None): """ finish() Finish the drawing and return the resulting icon. """ self._painter.end() return QtGui.QIcon(self._pm) def _getPixmap(self, icon): # Get icon if given by name if isinstance(icon, str): icon = pyzo.icons[icon] # Create pixmap if icon is None: pm = QtGui.QPixmap(16, 16) pm.fill(QtGui.QColor(0,0,0,0)) return pm elif isinstance(icon, tuple): pm = QtGui.QPixmap(icon[0], icon[1]) pm.fill(QtGui.QColor(0,0,0,0)) return pm elif isinstance(icon, QtGui.QPixmap): return icon elif isinstance(icon, QtGui.QIcon): return icon.pixmap(16, 16) else: raise ValueError('Icon for IconArtis should be icon, pixmap or name.') def setPenColor(self, color): """ setPenColor(color) Set the color of the pen. Color can be anything that can be passed to Qcolor(). """ pen = QtGui.QPen() if isinstance(color, tuple): pen.setColor(QtGui.QColor(*color)) else: pen.setColor(QtGui.QColor(color)) self._painter.setPen(pen) def addLayer(self, overlay, x=0, y=0): """ addOverlay(overlay, x=0, y=0) Add an overlay icon to the icon (add the specified position). """ pm = self._getPixmap(overlay) self._painter.drawPixmap(x, y, pm) def addLine(self, x1, y1, x2, y2): """ addLine( x1, y1, x2, y2) Add a line to the icon. """ self._painter.drawLine(x1, y1, x2, y2) def addPoint(self, x, y): """ addPoint( x, y) Add a point to the icon. """ self._painter.drawPoint(x, y) def addMenuArrow(self, strength=100): """ addMenuArrow() Adds a menu arrow to the icon to let the user know the icon is clickable. """ x, y = 0, 12 a1, a2 = int(strength/2), strength # Zeroth line of 3+2 self.setPenColor((0,0,0,a1)) self.addPoint(x+0,y-1); self.addPoint(x+4,y-1) self.setPenColor((0,0,0,a2)) self.addPoint(x+1,y-1); self.addPoint(x+2,y-1); self.addPoint(x+3,y-1) # First line of 3+2 self.setPenColor((0,0,0,a1)) self.addPoint(x+0,y+0); self.addPoint(x+4,y+0) self.setPenColor((0,0,0,a2)) self.addPoint(x+1,y+0); self.addPoint(x+2,y+0); self.addPoint(x+3,y+0) # Second line of 3 self.addPoint(x+1,y+1); self.addPoint(x+2,y+1); self.addPoint(x+3,y+1) # Third line of 1+2 self.addPoint(x+2,y+2) self.setPenColor((0,0,0,a1)) self.addPoint(x+1,y+2); self.addPoint(x+3,y+2) # Fourth line of 1 self.setPenColor((0,0,0,a2)) self.addPoint(x+2,y+3) # todo: not used; remove me? class TabCloseButton(QtWidgets.QToolButton): """ TabCloseButton This class implements a very compact close button to be used in tabs. It allows managing tab (the closing part of it) in a fast and intuitive fashion. """ SIZE = 5,8 def __init__(self): QtWidgets.QToolButton.__init__(self) # Init self.setIconSize(QtCore.QSize(*self.SIZE)) self.setStyleSheet("QToolButton{ border:none; padding:0px; margin:0px; }") self.setIcon(self.getCrossIcon1()) def mousePressEvent(self, event): # Get tabs tabs = self.parent().parent() # Get index from position pos = self.mapTo(tabs, event.pos()) index = tabs.tabBar().tabAt(pos) # Close it tabs.tabCloseRequested.emit(index) def enterEvent(self, event): QtWidgets.QToolButton.enterEvent(self, event) self.setIcon(self.getCrossIcon2()) def leaveEvent(self, event): QtWidgets.QToolButton.leaveEvent(self, event) self.setIcon(self.getCrossIcon1()) def _createCrossPixmap(self, alpha): artist = IconArtist(self.SIZE) # artist.setPenColor((0,0,0,alpha)) # artist.addPoint(0,0); artist.addPoint(1,1) artist.addPoint(2,2); artist.addPoint(3,3) artist.addPoint(4,4) artist.addPoint(0,4); artist.addPoint(1,3) artist.addPoint(3,1); artist.addPoint(4,0) # artist.setPenColor((0,0,0,int(0.5*alpha))) # artist.addPoint(1,0); artist.addPoint(0,1) artist.addPoint(2,1); artist.addPoint(1,2) artist.addPoint(3,2); artist.addPoint(2,3) artist.addPoint(4,3); artist.addPoint(3,4) # artist.addPoint(0,3); artist.addPoint(1,4) artist.addPoint(3,0); artist.addPoint(4,1) # return artist.finish().pixmap(*self.SIZE) def getCrossIcon1(self): if hasattr(self, '_cross1'): pm = self._cross1 else: pm = self._createCrossPixmap(80) return QtGui.QIcon(pm) def getCrossIcon2(self): if hasattr(self, '_cross2'): pm = self._cross2 else: pm = self._createCrossPixmap(240) # Set return QtGui.QIcon(pm) # todo: not used; remove me? class ToolButtonWithMenuIndication(QtWidgets.QToolButton): """ ToolButtonWithMenuIndication Tool button that wraps the icon in a slightly larger icon that contains a small arrow that lights up when hovering over the icon. The button itself is not drawn. If the icon is clicked, the customContextMenuRequested signal of the "grandparent" is emitted. In this way we realize a suble icon that can be clicked on to show a menu. """ SIZE = 21, 16 def __init__(self): QtWidgets.QToolButton.__init__(self) # Init self.setIconSize(QtCore.QSize(*self.SIZE)) self.setStyleSheet("QToolButton{ border: none; }") # Create arrow pixmaps self._menuarrow1 = self._createMenuArrowPixmap(0) self._menuarrow2 = self._createMenuArrowPixmap(70) self._menuarrow = self._menuarrow1 # Variable to keep icon self._icon = None # Variable to keep track of when the mouse was pressed, so that # we can allow dragging as well as clicking the menu. self._menuPressed = False def mousePressEvent(self, event): # Ignore event so that the tabbar will change to that tab event.ignore() self._menuPressed = event.pos() def mouseMoveEvent(self, event): QtWidgets.QToolButton.mouseMoveEvent(self, event) if self._menuPressed: dragDist = QtWidgets.QApplication.startDragDistance() if (event.pos()-self._menuPressed).manhattanLength() >= dragDist: self._menuPressed = False def mouseReleaseEvent(self, event): event.ignore() if self._menuPressed: tabs = self.parent().parent() pos = self.mapTo(tabs, event.pos()) tabs.customContextMenuRequested.emit(pos) def enterEvent(self, event): QtWidgets.QToolButton.enterEvent(self, event) self._menuarrow = self._menuarrow2 self.setIcon() self._menuPressed = False def leaveEvent(self, event): QtWidgets.QToolButton.leaveEvent(self, event) self._menuarrow = self._menuarrow1 self.setIcon() self._menuPressed = False def setIcon(self, icon=None): # Store icon if given, otherwise use buffered version if icon is not None: self._icon = icon # Compose icon by superimposing the menuarrow pixmap artist = IconArtist(self.SIZE) if self._icon: artist.addLayer(self._icon, 5, 0) artist.addLayer(self._menuarrow, 0,0) icon = artist.finish() # Set icon QtWidgets.QToolButton.setIcon(self, icon) def _createMenuArrowPixmap(self, strength): artist = IconArtist() artist.addMenuArrow(strength) return artist.finish().pixmap(16,16) class TabToolButton(QtWidgets.QToolButton): """ TabToolButton Base menu for editor and shell tabs. """ SIZE = 16, 16 def __init__(self, *args): QtWidgets.QToolButton.__init__(self, *args) # Init self.setIconSize(QtCore.QSize(*self.SIZE)) self.setStyleSheet("QToolButton{ border: none; }") def mousePressEvent(self, event): # Ignore event so that the tabbar will change to that tab event.ignore() class TabToolButtonWithCloseButton(TabToolButton): """ TabToolButtonWithCloseButton Tool button that wraps the icon in a slightly larger icon that contains a small cross that can be used to invoke a close request. """ SIZE = 22, 16 CROSS_OFFSET = 0, 2 def __init__(self, *args): TabToolButton.__init__(self, *args) # Variable to keep icon self._icon = None self._cross = self.getCrossPixmap1() # For mouse tracking inside icon self.setMouseTracking(True) self._overCross = False def _isOverCross(self, pos): x1, x2 = self.CROSS_OFFSET[0], self.CROSS_OFFSET[0]+5+1 y1, y2 = self.CROSS_OFFSET[1], self.CROSS_OFFSET[1]+5+1 if pos.x()>=x1 and pos.x()<=x2 and pos.y()>=y1 and pos.y()<=y2: return True else: return False def mousePressEvent(self, event): if self._isOverCross(event.pos()): # Accept event so that the tabbar will NOT change to that tab event.accept() else: event.ignore() def mouseReleaseEvent(self, event): if self._isOverCross(event.pos()): event.accept() # Get tabs tabs = self.parent().parent() # Get index from position pos = self.mapTo(tabs, event.pos()) index = tabs.tabBar().tabAt(pos) # Close it tabs.tabCloseRequested.emit(index) else: event.ignore() def mouseMoveEvent(self, event): QtWidgets.QToolButton.mouseMoveEvent(self, event) new_overCross = self._isOverCross(event.pos()) if new_overCross != self._overCross: self._overCross = new_overCross if new_overCross: self._cross = self.getCrossPixmap2() else: self._cross = self.getCrossPixmap1() self.setIcon() def leaveEvent(self, event): if self._overCross: self._overCross = False self._cross = self.getCrossPixmap1() self.setIcon() def setIcon(self, icon=None): # Store icon if given, otherwise use buffered version if icon is not None: self._icon = icon # Compose icon by superimposing the menuarrow pixmap artist = IconArtist(self.SIZE) if self._icon: if self.CROSS_OFFSET[0] > 8: artist.addLayer(self._icon, 0,0) else: artist.addLayer(self._icon, 6,0) artist.addLayer(self._cross, *self.CROSS_OFFSET) icon = artist.finish() # Set icon QtWidgets.QToolButton.setIcon(self, icon) def _createMenuArrowPixmap(self, strength): artist = IconArtist() artist.addMenuArrow(strength) return artist.finish().pixmap(16,16) def _createCrossPixmap(self, alpha): artist = IconArtist((5,5)) # artist.setPenColor((0,0,0,alpha)) # artist.addPoint(0,0); artist.addPoint(1,1) artist.addPoint(2,2); artist.addPoint(3,3) artist.addPoint(4,4) artist.addPoint(0,4); artist.addPoint(1,3) artist.addPoint(3,1); artist.addPoint(4,0) # artist.setPenColor((0,0,0,int(0.5*alpha))) # artist.addPoint(1,0); artist.addPoint(0,1) artist.addPoint(2,1); artist.addPoint(1,2) artist.addPoint(3,2); artist.addPoint(2,3) artist.addPoint(4,3); artist.addPoint(3,4) # artist.addPoint(0,3); artist.addPoint(1,4) artist.addPoint(3,0); artist.addPoint(4,1) # return artist.finish().pixmap(5,5) def getCrossPixmap1(self): if hasattr(self, '_cross1'): pm = self._cross1 else: pm = self._createCrossPixmap(50) return pm def getCrossPixmap2(self): if hasattr(self, '_cross2'): pm = self._cross2 else: pm = self._createCrossPixmap(240) # Set return pm class EditorTabToolButton(TabToolButtonWithCloseButton): """ Button for the tabs of the editors. This is just a tight wrapper for the icon. """ def updateIcon(self, isDirty, isMain, isPinned, nBlocks=10001): # Init drawing artist = IconArtist() # Create base if isDirty: artist.addLayer('page_white_dirty') artist.setPenColor('#f00') else: artist.addLayer('page_white') artist.setPenColor('#444') # Paint lines if not nBlocks: nLines = 0 elif nBlocks <= 10: nLines = 1 elif nBlocks <= 100: nLines = 2 elif nBlocks <= 1000: nLines = 3 elif nBlocks <= 10000: nLines = 4 else: nLines = 5 # fraction = float(nBlocks) / 10**nLines fraction = min(fraction, 1.0) # for i in range(nLines): y = 4 + 2 * i n = 5 if y>6: n = 8 #if i == nLines-1: # n = int(fraction * n) artist.addLine(4,y,4+n,y) # Overlays if isMain: artist.addLayer('overlay_star') if isPinned: artist.addLayer('overlay_thumbnail') if isDirty: artist.addLayer('overlay_disk') # Apply self.setIcon(artist.finish()) class ShellIconMaker: """ Object that can make an icon for the shells """ POSITION = (6,7) # absolute position of center of wheel. # Relative position for the wheel at two levels. Center is at (3,,3) POSITIONS1 = [(2,2), (3,2), (4,2), (4,3), (4,4), (3,4), (2,4), (2,3)] POSITIONS2 = [ (2,1), (3,1), (4,1), (5,2), (5,3), (5,4), (4,5), (3,5), (2,5), (1,4), (1,3), (1,2) ] # Maps to make transitions between levels more natural MAP1to2 = [1,2, 4,5, 7,8, 10,11] MAP2to1 = [1,2,3, 3,4,5, 5,6,7, 7,0,1] MAX_ITERS_IN_LEVEL_1 = 2 def __init__(self, objectWithIcon): self._objectWithIcon = objectWithIcon # Motion properties self._index = 0 self._level = 0 self._count = 0 # to count number of iters in level 1 # Prepare blob pixmap self._blob = self._createBlobPixmap() self._legs = self._createLegsPixmap() # Create timer self._timer = QtCore.QTimer(None) self._timer.setInterval(150) self._timer.setSingleShot(False) self._timer.timeout.connect(self.onTimer) def setIcon(self, icon): self._objectWithIcon.setIcon(icon) def _createBlobPixmap(self): artist = IconArtist() artist.setPenColor((0,150,0,255)) artist.addPoint(1,1) artist.setPenColor((0,150,0, 200)) artist.addPoint(1,0); artist.addPoint(1,2) artist.addPoint(0,1); artist.addPoint(2,1) artist.setPenColor((0,150,0, 100)) artist.addPoint(0,0); artist.addPoint(2,0) artist.addPoint(0,2); artist.addPoint(2,2) return artist.finish().pixmap(16,16) def _createLegsPixmap(self): artist = IconArtist() x,y = self.POSITION artist.setPenColor((0,50,0,150)) artist.addPoint(x+1,y-1); artist.addPoint(x+1,y-2); artist.addPoint(x+0,y-2) artist.addPoint(x+3,y+1); artist.addPoint(x+4,y+1); artist.addPoint(x+4,y+2) artist.addPoint(x+2,y+3); artist.addPoint(x+2,y+4) artist.addPoint(x+0,y+3); artist.addPoint(x+0,y+4) artist.addPoint(x-1,y+2); artist.addPoint(x-2,y+2) artist.addPoint(x-1,y+0); artist.addPoint(x-2,y+0) return artist.finish().pixmap(16,16) def updateIcon(self, status='Ready'): """ updateIcon(status) Public method to set what state the icon must show. """ # Normalize and store if isinstance(status, str): status = status.lower() self._status = status # Handle if status == 'busy': self._index = 0 if self._level == 2: self._index = self.MAP2to1[self._index] self._level = 1 elif status == 'very busy': self._index = 0 if self._level == 1: self._index = self.MAP1to2[self._index] self._level = 2 else: self._level = 0 # At least one timer iteration self._timer.start() def _nextIndex(self): self._index += 1 if self._level == 1 and self._index >= 8: self._index = 0 elif self._level == 2 and self._index >= 12: self._index = 0 def _index1(self): return self._index def _index2(self): n = [0, 8, 12][self._level] index = self._index + n/2 if index >= n: index -= n return int(index) def onTimer(self): """ onTimer() Invoked on each timer iteration. Will call the static drawing methods if in level 0. Otherwise will invoke drawInMotion(). This method also checks if we should change levels and calculates how this is best achieved. """ if self._level == 0: # Turn of timer self._timer.stop() # Draw if self._status in ['ready', 'more']: self.drawReady() elif self._status == 'debug': self.drawDebug() elif self._status == 'dead': self.drawDead() else: self.drawDead() elif self._level == 1: # Draw self.drawInMotion() # Next, this is always intermediate self._nextIndex() self._count += 1 elif self._level == 2: # Draw self.drawInMotion() # Next self._nextIndex() def drawReady(self): """ drawReady() Draw static icon for when in ready mode. """ artist = IconArtist("application") artist.addLayer(self._blob, *self.POSITION) self.setIcon(artist.finish()) def drawDebug(self): """ drawDebug() Draw static icon for when in debug mode. """ artist = IconArtist("application") artist.addLayer(self._blob, *self.POSITION) artist.addLayer(self._legs) self.setIcon(artist.finish()) def drawDead(self): """ drawDead() Draw static empty icon for when the kernel is dead. """ artist = IconArtist("application") self.setIcon(artist.finish()) def drawInMotion(self): """ drawInMotion() Draw one frame of the icon in motion. Position of the blobs is determined from the index and the list of locations. """ # Init drawing artist = IconArtist("application") # Define params dx, dy = self.POSITION[0]-3, self.POSITION[1]-3 blob = self._blob # if self._level == 1: positions = self.POSITIONS1 elif self._level == 2: positions = self.POSITIONS2 # Draw pos1 = positions[self._index1()] pos2 = positions[self._index2()] artist.addLayer(blob, pos1[0]+dx, pos1[1]+dy) artist.addLayer(blob, pos2[0]+dx, pos2[1]+dy) # Done self.setIcon(artist.finish()) pyzo-4.4.3/pyzo/core/kernelbroker.py0000666000000000000000000006664713123037260015665 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module kernelBroker This module implements the interface between Pyzo and the kernel. """ import os, sys, time import subprocess import signal import threading import ctypes import yoton import pyzo # local Pyzo (can be on a different box than where the user is) from pyzo.util import zon as ssdf # zon is ssdf-light # To allow interpreters relative to (frozen) Pyzo app EXE_DIR = os.path.abspath(os.path.dirname(sys.executable)) if EXE_DIR.endswith('.app/Contents/MacOS'): EXE_DIR = os.path.dirname(EXE_DIR.rsplit('.app')[0]) # Important: the yoton event loop should run somehow! class KernelInfo(ssdf.Struct): """ KernelInfo Describes all information for a kernel. This class can be used at the IDE as well as the kernelbroker. This information goes a long way from the pyzo config file to the kernel. The list pyzo.config.shellConfigs2 contains the configs for all kernels. These objects are edited in-place by the shell config. The shell keeps a reference of the shell config used to start the kernel. On each restart all information is resend. In this way, if a user changes a setting in the shell config, it is updated when the shell restarts. The broker also keeps a copy of the shell config. In this way, the shell might send no config information (or only partially update the config information) on a restart. This is not so relevant now, but it can be when we are running multiple people on a single kernel, and there is only one user who has the original config. """ def __init__(self, info=None): super().__init__() # ----- Fixed parameters that define a shell ----- # scriptFile is used to define the mode. If given, we run in # script-mode. Otherwise we run in interactive mode. # The name of this shell config. Can be used to name the kernel self.name = 'Python' # The executable. This can be '/usr/bin/python3.1' or # 'c:/program files/python2.6/python.exe', etc. self.exe = '' # The GUI toolkit to embed the event loop of. # Instantiate with a value that is settable self.gui = 'Auto' # The Python path. Paths should be separated by newlines. # '$PYTHONPATH' is replaced by environment variable by broker self.pythonPath = '' # The path of the current project, the kernel will prepend this # to the sys.path. The broker could prepend to PYTHONPATH, but # in this way it is more explicit (kernel can tell the user that # the project path was prepended). self.projectPath = '' # The full filename of the script to run. # If given, the kernel should run in script-mode. # The kernel will check whether this file exists, and will # revert to interactive mode if it doesn't. self.scriptFile = '' # Interactive-mode only: # The initial directory. Only used for interactive-mode; in # script-mode the initial directory is the dir of the script. self.startDir = '' # The Startup script (only used for interactive-mode). # - Empty string means run nothing, # - Single line means file name # - multiple lines means source code. # - '$PYTHONSTARTUP' uses the code in that file. Broker replaces this. self.startupScript = '' # Additional command line arguments, set by the kernel self.argv = '' # Additional environment variables self.environ = '' # Load info from ssdf struct. Make sure they are all strings if info: # Get struct if isinstance(info, dict): s = info elif isinstance(info, str): s = ssdf.loads(info) else: raise ValueError('Kernel info should be a string or ssdf struct, not %s' % str(type(info))) # Inject values for key in s: val = s[key] if not val: val = '' self[key] = val def tostring(self): return ssdf.saves(self) def getCommandFromKernelInfo(info, port): info = KernelInfo(info) # Apply default exe exe = info.exe or 'python' if exe.startswith('.'): exe = os.path.abspath(os.path.join(EXE_DIR, exe)) # Correct path when it contains spaces if exe.count(' ') and exe[0] != '"': exe = '"{}"'.format(exe) # Get start script startScript = os.path.join( pyzo.pyzoDir, 'pyzokernel', 'start.py') startScript = '"{}"'.format(startScript) # Build command command = exe + ' ' + startScript + ' ' + str(port) # Done return command def getEnvFromKernelInfo(info): info = KernelInfo(info) pythonPath = info.pythonPath # Set default pythonPath (replace only first occurrence of $PYTHONPATH ENV_PP = os.environ.get('PYTHONPATH','') pythonPath = pythonPath.replace('$PYTHONPATH', '\n'+ENV_PP+'\n', 1) pythonPath = pythonPath.replace('$PYTHONPATH', '') # Split paths, allow newlines and os.pathsep for splitChar in '\n\r' + os.pathsep: pythonPath = pythonPath.replace(splitChar, '\n') pythonPaths = [p.strip() for p in pythonPath.split('\n') if p] # Recombine using the OS's path separator pythonPath = os.pathsep.join(pythonPaths) # Add entry to Pythopath, so that we can import yoton # Note: an empty entry might cause trouble if the start-directory is # somehow overriden (see issue 128). pythonPath = pyzo.pyzoDir + os.pathsep + pythonPath # Prepare environment, remove references to tk libraries, # since they're wrong when frozen. Python will insert the # correct ones if required. env = os.environ.copy() # env.pop('TK_LIBRARY','') env.pop('TCL_LIBRARY','') env['PYTHONPATH'] = pythonPath # Jython does not use PYTHONPATH but JYTHONPATH env['JYTHONPATH'] = pyzo.pyzoDir + os.pathsep + os.environ.get('JYTHONPATH', '') env['TERM'] = 'dumb' # we have a "dumb" terminal (see #422) # Add environment variables specified in shell config for line in info.environ.splitlines(): line = line.strip() if '=' in line: key, val = line.split('=', 1) if key: env[key] = os.path.expandvars(val) # Done return env class KernelBroker: """ KernelBroker(info) This class functions as a broker between a kernel process and zero or more IDE's (clients). This class has a single context assosiated with it, that lives as long as this object. It is used to connect to a kernel process and to 0 or more IDE's (clients). The kernel process can be "restarted", meaning that it is terminated and a new process started. The broker is cleaned up if there is no kernel process AND no connections. """ def __init__(self, manager, info, name=''): self._manager = manager # Store info that defines the kernel self._originalInfo = KernelInfo(info) # Make a copy for the current version. This copy is re-created on # each restart self._info = ssdf.copy(self._originalInfo) # Store name (or should the name be defined in the info struct) self._name = name # Create context for the connection to the kernel and IDE's # This context is persistent (it stays as long as this KernelBroker # instance is alive). self._context = yoton.Context() self._kernelCon = None self._ctrl_broker = None # Create yoton-based timer self._timer = yoton.Timer(0.2, oneshot=False) self._timer.bind(self.mainLoopIter) # Kernel process and connection (these are replaced on restarting) self._reset() # For restarting after terminating self._pending_restart = None ## Startup and teardown def _create_channels(self): ct = self._context # Close any existing channels first self._context.close_channels() # Create stream channels. # Stdout is for the C-level stdout/stderr streams. self._strm_broker = yoton.PubChannel(ct, 'strm-broker') self._strm_raw = yoton.PubChannel(ct, 'strm-raw') self._strm_prompt = yoton.PubChannel(ct, 'strm-prompt') # Create control channel so that the IDE can control restarting etc. self._ctrl_broker = yoton.SubChannel(ct, 'ctrl-broker') # Status channel to pass startup parameters to the kernel self._stat_startup = yoton.StateChannel(ct, 'stat-startup', yoton.OBJECT) # We use the stat-interpreter to set the status to dead when kernel dies self._stat_interpreter = yoton.StateChannel(ct, 'stat-interpreter') # Create introspect channel so we can interrupt and terminate self._reqp_introspect = yoton.ReqChannel(ct, 'reqp-introspect') def _reset(self, destroy=False): """ _reset(destroy=False) Reset state. if destroy, does a full clean up, closing the context and removing itself from the KernelManager's list. """ # Close connection (it might be in a wait state if the process # failed to start) if self._kernelCon is not None: self._kernelCon.close() # Set process and kernel connection to None self._process = None self._kernelCon = None self._terminator = None self._streamReader = None if destroy: # Stop timer self._timer.unbind(self.mainLoopIter) self._timer.stop() self._timer = None # Clean up this kernelbroker instance L = self._manager._kernels while self in L: L.remove(self) # Remove references # if self._context is not None: self._context.close() self._context = None # self._strm_broker = None self._strm_raw = None self._stat_startup = None self._stat_interpreter = None self._strm_prompt = None # self._ctrl_broker = None self._reqp_introspect = None def startKernelIfConnected(self, timeout=10.0): """ startKernelIfConnected(timout=10.0) Start the kernel as soon as there is a connection. """ self._process = time.time() + timeout self._timer.start() def startKernel(self): """ startKernel() Launch the kernel in a subprocess, and connect to it via the context and two Pypes. """ # Create channels self._create_channels() # Create info dict info = {} for key in self._info: info[key] = self._info[key] # Send info stuff so that the kernel has access to the information self._stat_startup.send(info) # Get directory to start process in cwd = pyzo.pyzoDir # Host connection for the kernel to connect # (tries several port numbers, staring from 'PYZO') self._kernelCon = self._context.bind('localhost:PYZO', max_tries=256, name='kernel') # Get command to execute, and environment to use command = getCommandFromKernelInfo(self._info, self._kernelCon.port1) env = getEnvFromKernelInfo(self._info) # Wrap command in call to 'cmd'? if sys.platform.startswith('win'): # as the author from Pype writes: #if we don't run via a command shell, then either sometimes we #don't get wx GUIs, or sometimes we can't kill the subprocesses. # And I also see problems with Tk. # But we only use it if we are sure that cmd is available. # See pyzo issue #240 try: subprocess.check_output('cmd /c "cd"', shell=True) except (IOError, subprocess.SubprocessError): pass # Do not use cmd else: command = 'cmd /c "{}"'.format(command) # Start process self._process = subprocess.Popen( command, shell=True, env=env, cwd=cwd, stdin=subprocess.PIPE, # Fixes issue 165 stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) # Set timeout for connection, i.e. after how much time of # unresponsive ness is the kernel found to be running extension code # Better set this before connecting self._kernelCon.timeout = 0.5 # Bind to events self._kernelCon.closed.bind(self._onKernelConnectionClose) self._kernelCon.timedout.bind(self._onKernelTimedOut) # Create reader for stream self._streamReader = StreamReader(self._process, self._strm_raw, self._strm_broker) # Start streamreader and timer self._streamReader.start() self._timer.start() # Reset some variables self._pending_restart = None def hostConnectionForIDE(self, address='localhost'): """ hostConnectionForIDE() Host a connection for an IDE to connect to. Returns the port to which the ide can connect. """ c = self._context.bind(address+':PYZO+256', max_tries=32) return c.port1 ## Callbacks def _onKernelTimedOut(self, c, timedout): """ _onKernelTimedOut(c, timeout) The kernel timed out (i.e. did not send heartbeat messages for a while. It is probably running extension code. """ if timedout: self._stat_interpreter.send('Very busy') else: self._stat_interpreter.send('Busy') def _onKernelConnectionClose(self, c, why): """ _onKernelConnectionClose(c, why) Connection with kernel lost. Tell clients why. """ # If we receive this event while the current kernel connection # is not the one that generated the event, ignore it. if self._kernelCon is not c: return # The only reasonable way that the connection # can be lost without the kernel closing, is if the yoton context # crashed or was stopped somehow. In both cases, we lost control, # and should put it down! if not self._terminator: self.terminate('because connecton was lost', 'KILL', 0.5) def _onKernelDied(self, returncode=0): """ _onKernelDied() Kernel process died. Clean up! """ # If the kernel did not start yet, probably the command is invalid if self._kernelCon and self._kernelCon.is_waiting: msg = 'The process failed to start (invalid command?).' elif not self.isTerminating(): msg = 'Kernel process exited.' elif not self._terminator._prev_action: # We did not actually take any terminating action # This happens, because if the kernel is killed from outside, # _onKernelConnectionClose() triggers a terminate sequence # (but with a delay). # Note the "The" to be able to distinguish this case msg = 'The kernel process exited.' else: msg = self._terminator.getMessage('Kernel process') if self._context.connection_count: # Notify returncodeMsg = '\n%s (%s)\n\n' % (msg, str(returncode)) self._strm_broker.send(returncodeMsg) # Empty prompt and signal dead self._strm_prompt.send('\b') self._stat_interpreter.send('Dead') self._context.flush() # Cleanup (get rid of kernel process references) self._reset() # Handle any pending action if self._pending_restart: self.startKernel() ## Main loop and termination def terminate(self, reason='by user', action='TERM', timeout=0.0): """ terminate(reason='by user', action='TERM', timeout=0.0) Initiate termination procedure for the current kernel. """ # The terminatation procedure is started by creating # a KernelTerminator instance. This instance's iteration method # iscalled from _mailLoopIter(). self._terminator = KernelTerminator(self, reason, action, timeout) def isTerminating(self): """ isTerminating() Get whether the termination procedure has been initiated. This simply checks whether there is a self._terminator instance. """ return bool(self._terminator) def mainLoopIter(self): """ mainLoopIter() Periodically called. Kind of the main loop iteration for this kernel. """ # Get some important status info hasProcess = self._process is not None hasKernelConnection = bool(self._kernelCon and self._kernelCon.is_connected) hasClients = False if self._context: hasClients = self._context.connection_count > int(hasKernelConnection) # Should we clean the whole thing up? if not (hasProcess or hasClients): self._reset(True) # Also unregisters this timer callback return # Waiting to get started; waiting for client to connect if isinstance(self._process, float): if self._context.connection_count: self.startKernel() elif self._process > time.time(): self._process = None return # If we have a process ... if self._process: # Test if process is dead process_returncode = self._process.poll() if process_returncode is not None: self._onKernelDied(process_returncode) return # Are we in the process of terminating? elif self.isTerminating(): self._terminator.next() elif self.isTerminating(): # We cannot have a terminator if we have no process self._terminator = None # handle control messages if self._ctrl_broker: for msg in self._ctrl_broker.recv_all(): if msg == 'INT': self._commandInterrupt() elif msg == 'TERM': self._commandTerminate() elif msg.startswith('RESTART'): self._commandRestart(msg) else: pass # Message is not for us def _commandInterrupt(self): if self._process is None: self._strm_broker.send('Cannot interrupt: process is dead.\n') # Kernel receives and acts elif sys.platform.startswith('win'): self._reqp_introspect.interrupt() else: # Use POSIX to interrupt, which is more reliable # (the introspect thread might not get a chance) # but also does not work if running extension code pid = self._kernelCon.pid2 os.kill(pid, signal.SIGINT) def _commandTerminate(self): # Start termination procedure # Kernel will receive term and act (if it can). # If it wont, we will act in a second or so. if self._process is None: self._strm_broker.send('Cannot terminate: process is dead.\n') elif self.isTerminating(): # The user gave kill command while the kill process # is running. We could do an immediate kill now, # or we let the terminate process run its course. pass else: self.terminate('by user') def _commandRestart(self, msg): # Almost the same as terminate, but now we have a pending action self._pending_restart = True # Recreate the info struct self._info = ssdf.copy(self._originalInfo) # Update the info struct new_info = ssdf.loads(msg.split('RESTART',1)[1]) for key in new_info: self._info[key] = new_info[key] # Restart now, wait, or initiate termination procedure? if self._process is None: self.startKernel() elif self.isTerminating(): pass # Already terminating else: self.terminate('for restart') class KernelTerminator: """ KernelTerminator(broker, reason='user terminated', action='TERM', timeout=0.0) Simple class to help terminating the kernel. It has a next() method that should be periodically called. It keeps track whether the timeout has passed and will undertake increaslingly ruder actions to terminate the kernel. """ def __init__(self, broker, reason='by user', action='TERM', timeout=0.0): # Init/store self._broker = broker self._reason = reason self._next_action = '' # Go self._do(action, timeout) def _do(self, action, timeout): self._prev_action = self._next_action self._next_action = action self._timeout = time.time() + timeout if not timeout: self.next() def next(self): # Get action action = self._next_action if time.time() < self._timeout: # Time did not pass yet pass elif action == 'TERM': self._broker._reqp_introspect.terminate() self._do('INT', 0.5) elif action == 'INT': # Count if not hasattr(self, '_count'): self._count = 0 self._count +=1 # Handle if self._count < 5: self._broker._reqp_introspect.interrupt() self._do('INT', 0.1) else: self._do('KILL', 0) elif action == 'KILL': # Get pid and signal pid = self._broker._kernelCon.pid2 sigkill = signal.SIGTERM if hasattr(signal,'SIGKILL'): sigkill = signal.SIGKILL # Kill if hasattr(os,'kill'): os.kill(pid, sigkill) elif sys.platform.startswith('win'): kernel32 = ctypes.windll.kernel32 handle = kernel32.OpenProcess(1, 0, pid) kernel32.TerminateProcess(handle, 0) #os.system("TASKKILL /PID " + str(pid) + " /F") # Set what we did self._do('NOTHING', 9999999999999999) def getMessage(self, what): # Get nice string of that D = { '': 'exited', 'TERM': 'terminated', 'INT': 'terminated (after interrupting)', 'KILL': 'killed'} actionMsg = D.get(self._prev_action, 'stopped for unknown reason') # Compile stop-string return '{} {} {}.'.format( what, actionMsg, self._reason) class StreamReader(threading.Thread): """ StreamReader(process, channel) Reads stdout of process and send to a yoton channel. This needs to be done in a separate thread because reading from a PYPE blocks. """ def __init__(self, process, strm_raw, strm_broker): threading.Thread.__init__(self) self._process = process self._strm_raw = strm_raw self._strm_broker = strm_broker self.deamon = True self._exit = False def stop(self, timeout=1.0): self._exit = True self.join(timeout) def run(self): while not self._exit: time.sleep(0.001) # Read any stdout/stderr messages and route them via yoton. msg = self._process.stdout.readline() # <-- Blocks here if not isinstance(msg, str): msg = msg.decode('utf-8', 'ignore') try: self._strm_raw.send(msg) except IOError: pass # Channel is closed # Process dead? if not msg:# or self._process.poll() is not None: break #self._strm_broker.send('streamreader exit\n') class Kernelmanager: """ Kernelmanager This class manages a set of kernels. These kernels run on the same machine as this broker. IDE's can ask which kernels are available and can connect to them via this broker. The Pyzo process runs an instance of this class that connects at localhost. At a later stage, we may make it possible to create a kernel-server at a remote machine. """ def __init__(self, public=False): # Set whether other machines in this network may connect to our kernels self._public = public # Init list of kernels self._kernels = [] def createKernel(self, info, name=None): """ create_kernel(info, name=None) Create a new kernel. Returns the port number to connect to the broker's context. """ # Set name if not given if not name: i = len(self._kernels) + 1 name = 'kernel %i' % i # Create kernel kernel = KernelBroker(self, info, name) self._kernels.append(kernel) # Host a connection for the ide port = kernel.hostConnectionForIDE() # Tell broker to start as soon as the IDE connects with the broker kernel.startKernelIfConnected() # Done return port def getKernelList(self): # Get info of each kernel as an ssdf struct infos = [] for kernel in self._kernels: info = kernel._info info = ssdf.loads(info.tostring()) info.name = kernel._name infos.append(info) # Done return infos def terminateAll(self): """ terminateAll() Terminates all kernels. Required when shutting down Pyzo. When this function returns, all kernels will be terminated. """ for kernel in [kernel for kernel in self._kernels]: # Try closing the process gently: by closing stdin terminator = KernelTerminator(kernel, 'for closing down') # Terminate while (kernel._kernelCon and kernel._kernelCon.is_connected and kernel._process and (kernel._process.poll() is None) ): time.sleep(0.02) terminator.next() # Clean up kernel._reset(True) pyzo-4.4.3/pyzo/core/main.py0000666000000000000000000005133713127421055014115 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module main This module contains the main frame. Implements the main window. Also adds some variables to the pyzo namespace, such as the callLater function which is also defined here. """ import os, sys, time import base64 from queue import Queue, Empty import pyzo from pyzo.core.icons import IconArtist from pyzo.core import commandline from pyzo.util import qt from pyzo.util.qt import QtCore, QtGui, QtWidgets from pyzo.core.splash import SplashWidget from pyzo.util import paths from pyzo.util import zon as ssdf # zon is ssdf-light from pyzo import translate class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None, locale=None): QtWidgets.QMainWindow.__init__(self, parent) self._closeflag = 0 # Used during closing/restarting # Init window title and application icon # Set title to something nice. On Ubuntu 12.10 this text is what # is being shown at the fancy title bar (since it's not properly # updated) self.setMainTitle() loadAppIcons() self.setWindowIcon(pyzo.icon) # Restore window geometry before drawing for the first time, # such that the window is in the right place self.resize(800, 600) # default size self.restoreGeometry() # Show splash screen (we need to set our color too) w = SplashWidget(self, distro='no distro') self.setCentralWidget(w) self.setStyleSheet("QMainWindow { background-color: #268bd2;}") # Show empty window and disable updates for a while self.show() self.paintNow() self.setUpdatesEnabled(False) # Determine timeout for showing splash screen splash_timeout = time.time() + 1.0 # Set locale of main widget, so that qt strings are translated # in the right way if locale: self.setLocale(locale) # Store myself pyzo.main = self # Init dockwidget settings self.setTabPosition(QtCore.Qt.AllDockWidgetAreas,QtWidgets.QTabWidget.South) self.setDockOptions( QtWidgets.QMainWindow.AllowNestedDocks | QtWidgets.QMainWindow.AllowTabbedDocks #| QtWidgets.QMainWindow.AnimatedDocks ) # Set window atrributes self.setAttribute(QtCore.Qt.WA_AlwaysShowToolTips, True) # Load icons and fonts loadIcons() loadFonts() # Set qt style and test success self.setQtStyle(None) # None means init! # Hold the splash screen if needed while time.time() < splash_timeout: QtWidgets.qApp.flush() QtWidgets.qApp.processEvents() time.sleep(0.05) # Populate the window (imports more code) self._populate() # Revert to normal background, and enable updates self.setStyleSheet('') self.setUpdatesEnabled(True) # Restore window state, force updating, and restore again self.restoreState() self.paintNow() self.restoreState() # Present user with wizard if he/she is new. if False: # pyzo.config.state.newUser: from pyzo.util.pyzowizard import PyzoWizard w = PyzoWizard(self) w.show() # Use show() instead of exec_() so the user can interact with pyzo # Create new shell config if there is None if not pyzo.config.shellConfigs2: from pyzo.core.kernelbroker import KernelInfo pyzo.config.shellConfigs2.append( KernelInfo() ) # Focus on editor e = pyzo.editors.getCurrentEditor() if e is not None: e.setFocus() # Handle any actions commandline.handle_cmd_args() # To force drawing ourselves def paintEvent(self, event): QtWidgets.QMainWindow.paintEvent(self, event) self._ispainted = True def paintNow(self): """ Enforce a repaint and keep calling processEvents until we are repainted. """ self._ispainted = False self.update() while not self._ispainted: QtWidgets.qApp.flush() QtWidgets.qApp.processEvents() time.sleep(0.01) def _populate(self): # Delayed imports from pyzo.core.editorTabs import EditorTabs from pyzo.core.shellStack import ShellStackWidget from pyzo.core import codeparser from pyzo.core.history import CommandHistory from pyzo.tools import ToolManager # Instantiate tool manager pyzo.toolManager = ToolManager() # Check to install conda now ... #from pyzo.util.bootstrapconda import check_for_conda_env #check_for_conda_env() # Instantiate and start source-code parser if pyzo.parser is None: pyzo.parser = codeparser.Parser() pyzo.parser.start() # Create editor stack and make the central widget pyzo.editors = EditorTabs(self) self.setCentralWidget(pyzo.editors) # Create floater for shell self._shellDock = dock = QtWidgets.QDockWidget(self) if pyzo.config.settings.allowFloatingShell: dock.setFeatures(dock.DockWidgetMovable | dock.DockWidgetFloatable) else: dock.setFeatures(dock.DockWidgetMovable) dock.setObjectName('shells') dock.setWindowTitle('Shells') self.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) # Create shell stack pyzo.shells = ShellStackWidget(self) dock.setWidget(pyzo.shells) # Initialize command history pyzo.command_history = CommandHistory('command_history.py') # Create the default shell when returning to the event queue callLater(pyzo.shells.addShell) # Create statusbar if pyzo.config.view.showStatusbar: pyzo.status = self.statusBar() else: pyzo.status = None self.setStatusBar(None) # Create menu from pyzo.core import menu pyzo.keyMapper = menu.KeyMapper() menu.buildMenus(self.menuBar()) # Add the context menu to the editor pyzo.editors.addContextMenu() pyzo.shells.addContextMenu() # Load tools if pyzo.config.state.newUser and not pyzo.config.state.loadedTools: pyzo.toolManager.loadTool('pyzosourcestructure') pyzo.toolManager.loadTool('pyzofilebrowser', 'pyzosourcestructure') elif pyzo.config.state.loadedTools: for toolId in pyzo.config.state.loadedTools: pyzo.toolManager.loadTool(toolId) def setMainTitle(self, path=None): """ Set the title of the main window, by giving a file path. """ if not path: # Plain title title = "Interactive Editor for Python" else: # Title with a filename name = os.path.basename(path) if os.path.isfile(path): pass elif name == path: path = translate("main", 'unsaved') else: pass # We hope the given path is informative # Set title tmp = { 'fileName':name, 'filename':name, 'name':name, 'fullPath':path, 'fullpath':path, 'path':path } title = pyzo.config.advanced.titleText.format(**tmp) # Set self.setWindowTitle(title) def saveWindowState(self): """ Save: * which tools are loaded * geometry of the top level windows * layout of dockwidgets and toolbars """ # Save tool list tools = pyzo.toolManager.getLoadedTools() pyzo.config.state.loadedTools = tools # Store window geometry geometry = self.saveGeometry() try: geometry = bytes(geometry) # PyQt4 except: geometry = bytes().join(geometry) # PySide geometry = base64.encodebytes(geometry).decode('ascii') pyzo.config.state.windowGeometry = geometry # Store window state state = self.saveState() try: state = bytes(state) # PyQt4 except: state = bytes().join(state) # PySide state = base64.encodebytes(state).decode('ascii') pyzo.config.state.windowState = state def restoreGeometry(self, value=None): # Restore window position and whether it is maximized if value is not None: return super().restoreGeometry(value) # No value give, try to get it from the config if pyzo.config.state.windowGeometry: try: geometry = pyzo.config.state.windowGeometry geometry = base64.decodebytes(geometry.encode('ascii')) self.restoreGeometry(geometry) except Exception as err: print('Could not restore window geomerty: ' + str(err)) def restoreState(self, value=None): # Restore layout of dock widgets and toolbars if value is not None: return super().restoreState(value) # No value give, try to get it from the config if pyzo.config.state.windowState: try: state = pyzo.config.state.windowState state = base64.decodebytes(state.encode('ascii')) self.restoreState(state) except Exception as err: print('Could not restore window state: ' + str(err)) def setQtStyle(self, stylename=None): """ Set the style and the palette, based on the given style name. If stylename is None or not given will do some initialization. If bool(stylename) evaluates to False will use the default style for this system. Returns the QStyle instance. """ if stylename is None: # Initialize # Get native pallette (used below) QtWidgets.qApp.nativePalette = QtWidgets.qApp.palette() # Obtain default style name pyzo.defaultQtStyleName = str(QtWidgets.qApp.style().objectName()) # Other than gtk+ and mac, Fusion/Cleanlooks looks best (in my opinion) if 'gtk' in pyzo.defaultQtStyleName.lower(): pass # Use default style elif 'macintosh' in pyzo.defaultQtStyleName.lower(): pass # Use default style elif qt.QT_VERSION > '5': pyzo.defaultQtStyleName = 'Fusion' else: pyzo.defaultQtStyleName = 'Cleanlooks' # Set style if there is no style yet if not pyzo.config.view.qtstyle: pyzo.config.view.qtstyle = pyzo.defaultQtStyleName # Init if not stylename: stylename = pyzo.config.view.qtstyle # Check if this style exist, set to default otherwise styleNames = [name.lower() for name in QtWidgets.QStyleFactory.keys()] if stylename.lower() not in styleNames: stylename = pyzo.defaultQtStyleName # Try changing the style qstyle = QtWidgets.qApp.setStyle(stylename) # Set palette if qstyle: QtWidgets.qApp.setPalette(QtWidgets.qApp.nativePalette) # Done return qstyle def closeEvent(self, event): """ Override close event handler. """ # Are we restaring? restarting = time.time() - self._closeflag < 1.0 # Save settings pyzo.saveConfig() pyzo.command_history.save() # Stop command server commandline.stop_our_server() # Proceed with closing... result = pyzo.editors.closeAll() if not result: self._closeflag = False event.ignore() return else: self._closeflag = True #event.accept() # Had to comment on Windows+py3.3 to prevent error # Proceed with closing shells pyzo.localKernelManager.terminateAll() for shell in pyzo.shells: shell._context.close() # Close tools for toolname in pyzo.toolManager.getLoadedTools(): tool = pyzo.toolManager.getTool(toolname) tool.close() # Stop all threads (this should really only be daemon threads) import threading for thread in threading.enumerate(): if hasattr(thread, 'stop'): try: thread.stop(0.1) except Exception: pass # # Wait for threads to die ... # # This should not be necessary, but I used it in the hope that it # # would prevent the segfault on Python3.3. It didn't. # timeout = time.time() + 0.5 # while threading.activeCount() > 1 and time.time() < timeout: # time.sleep(0.1) # print('Number of threads alive:', threading.activeCount()) # Proceed as normal QtWidgets.QMainWindow.closeEvent(self, event) # Harder exit to prevent segfault. Not really a solution, # but it does the job until Pyside gets fixed. if sys.version_info >= (3,3,0) and not restarting: if hasattr(os, '_exit'): os._exit(0) def restart(self): """ Restart Pyzo. """ self._closeflag = time.time() # Close self.close() if self._closeflag: # Get args args = [arg for arg in sys.argv] if not paths.is_frozen(): # Prepend the executable name (required on Linux) lastBit = os.path.basename(sys.executable) args.insert(0, lastBit) # Replace the process! os.execv(sys.executable, args) def createPopupMenu(self): # Init menu menu = QtWidgets.QMenu() # Insert two items for item in ['Editors', 'Shells']: action = menu.addAction(item) action.setCheckable(True) action.setChecked(True) action.setEnabled(False) # Insert tools for tool in pyzo.toolManager.loadToolInfo(): action = menu.addAction(tool.name) action.setCheckable(True) action.setChecked(bool(tool.instance)) action.menuLauncher = tool.menuLauncher # Show menu and process result a = menu.popup(QtGui.QCursor.pos()) if a: a.menuLauncher(not a.menuLauncher(None)) def loadAppIcons(): """ loadAppIcons() Load the application iconsr. """ # Get directory containing the icons appiconDir = os.path.join(pyzo.pyzoDir, 'resources', 'appicons') # Determine template for filename of the application icon-files. fnameT = 'pyzologo{}.png' # Construct application icon. Include a range of resolutions. Note that # Qt somehow does not use the highest possible res on Linux/Gnome(?), even # the logo of qt-designer when alt-tabbing looks a bit ugly. pyzo.icon = QtGui.QIcon() for sze in [16, 32, 48, 64, 128, 256]: fname = os.path.join(appiconDir, fnameT.format(sze)) if os.path.isfile(fname): pyzo.icon.addFile(fname, QtCore.QSize(sze, sze)) # Set as application icon. This one is used as the default for all # windows of the application. QtWidgets.qApp.setWindowIcon(pyzo.icon) # Construct another icon to show when the current shell is busy artist = IconArtist(pyzo.icon) # extracts the 16x16 version artist.setPenColor('#0B0') for x in range(11, 16): d = x-11 # runs from 0 to 4 artist.addLine(x,6+d,x,15-d) pm = artist.finish().pixmap(16,16) # pyzo.iconRunning = QtGui.QIcon(pyzo.icon) pyzo.iconRunning.addPixmap(pm) # Change only 16x16 icon def loadIcons(): """ loadIcons() Load all icons in the icon dir. """ # Get directory containing the icons iconDir = os.path.join(pyzo.pyzoDir, 'resources', 'icons') # Construct other icons dummyIcon = IconArtist().finish() pyzo.icons = ssdf.new() for fname in os.listdir(iconDir): if fname.endswith('.png'): try: # Short and full name name = fname.split('.')[0] name = name.replace('pyzo_', '') # discart prefix ffname = os.path.join(iconDir,fname) # Create icon icon = QtGui.QIcon() icon.addFile(ffname, QtCore.QSize(16,16)) # Store pyzo.icons[name] = icon except Exception as err: pyzo.icons[name] = dummyIcon print('Could not load icon %s: %s' % (fname, str(err))) def loadFonts(): """ loadFonts() Load all fonts that come with Pyzo. """ import pyzo.codeeditor # we need pyzo and codeeditor namespace here # Get directory containing the icons fontDir = os.path.join(pyzo.pyzoDir, 'resources', 'fonts') # Get database object db = QtGui.QFontDatabase() # Set default font pyzo.codeeditor.Manager.setDefaultFontFamily('DejaVu Sans Mono') # Load fonts that are in the fonts directory if os.path.isdir(fontDir): for fname in os.listdir(fontDir): if 'oblique' in fname.lower(): # issue #461 continue if os.path.splitext(fname)[1].lower() in ['.otf', '.ttf']: try: db.addApplicationFont( os.path.join(fontDir, fname) ) except Exception as err: print('Could not load font %s: %s' % (fname, str(err))) class _CallbackEventHandler(QtCore.QObject): """ Helper class to provide the callLater function. """ def __init__(self): QtCore.QObject.__init__(self) self.queue = Queue() def customEvent(self, event): while True: try: callback, args = self.queue.get_nowait() except Empty: break try: callback(*args) except Exception as why: print('callback failed: {}:\n{}'.format(callback, why)) def postEventWithCallback(self, callback, *args): self.queue.put((callback, args)) QtWidgets.qApp.postEvent(self, QtCore.QEvent(QtCore.QEvent.User)) def callLater(callback, *args): """ callLater(callback, *args) Post a callback to be called in the main thread. """ _callbackEventHandler.postEventWithCallback(callback, *args) # Create callback event handler instance and insert function in pyzo namespace _callbackEventHandler = _CallbackEventHandler() pyzo.callLater = callLater _SCREENSHOT_CODE = """ import random numerator = 4 def get_number(): # todo: something appears to be broken here val = random.choice(range(10)) return numerator / val class Groceries(list): \"\"\" Overloaded list class. \"\"\" def append_defaults(self): spam = 'yum' pie = 3.14159 self.extend([spam, pie]) class GroceriesPlus(Groceries): \"\"\" Groceries with surprises! \"\"\" def append_random(self): value = get_number() self.append(value) # Create some groceries g = GroceriesPlus() g.append_defaults() g.append_random() """ def screenshotExample(width=1244, height=700): e = pyzo.editors.newFile() e.editor.setPlainText(_SCREENSHOT_CODE) pyzo.main.resize(width, height) def screenshot(countdown=5): QtCore.QTimer.singleShot(countdown*1000, _screenshot) def _screenshot(): # Grab print('SNAP!') pix = QtGui.QPixmap.grabWindow(pyzo.main.winId()) #pix = QtGui.QPixmap.grabWidget(pyzo.main) # Get name i = 1 while i > 0: name = 'pyzo_screen_%s_%02i.png' % (sys.platform, i) fname = os.path.join(os.path.expanduser('~'), name) if os.path.isfile(fname): i += 1 else: i = -1 # Save screenshot and a thumb pix.save(fname) thumb = pix.scaledToWidth(500, QtCore.Qt.SmoothTransformation) thumb.save(fname.replace('screen', 'thumb')) print('Screenshot and thumb saved in', os.path.expanduser('~')) pyzo.screenshot = screenshot pyzo.screenshotExample = screenshotExample pyzo-4.4.3/pyzo/core/menu.py0000666000000000000000000026637313136120061014136 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module menu Implements a menu that can be edited very easily. Every menu item is represented by a class. Also implements a dialog to change keyboard shortcuts. """ import os, sys, re import webbrowser from urllib.request import urlopen import json from pyzo.util.qt import QtCore, QtGui, QtWidgets import pyzo from pyzo.core.compactTabWidget import CompactTabWidget from pyzo.core.pyzoLogging import print # noqa from pyzo.core.assistant import PyzoAssistant from pyzo import translate def buildMenus(menuBar): """ Build all the menus """ menus = [ FileMenu(menuBar, translate("menu", "File")), EditMenu(menuBar, translate("menu", "Edit")), ViewMenu(menuBar, translate("menu", "View")), SettingsMenu(menuBar, translate("menu", "Settings")), ShellMenu(menuBar, translate("menu", "Shell")), RunMenu(menuBar, translate("menu", "Run")), ToolsMenu(menuBar, translate("menu", "Tools")), HelpMenu(menuBar, translate("menu", "Help")), ] menuBar._menumap = {} menuBar._menus = menus for menu in menuBar._menus: menuBar.addMenu(menu) menuName = menu.__class__.__name__.lower().split('menu')[0] menuBar._menumap[menuName] = menu # Enable tooltips def onHover(action): # This ugly bit of code makes sure that the tooltip is refreshed # (thus raised above the submenu). This happens only once and after # ths submenu has become visible. if action.menu(): if not hasattr(menuBar, '_lastAction'): menuBar._lastAction = None menuBar._haveRaisedTooltip = False if action is menuBar._lastAction: if ((not menuBar._haveRaisedTooltip) and action.menu().isVisible()): QtWidgets.QToolTip.hideText() menuBar._haveRaisedTooltip = True else: menuBar._lastAction = action menuBar._haveRaisedTooltip = False # Set tooltip tt = action.statusTip() if hasattr(action, '_shortcutsText'): tt = tt + ' ({})'.format(action._shortcutsText) # Add shortcuts text in it QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), tt) menuBar.hovered.connect(onHover) # todo: syntax styles now uses a new system. Make dialog for it! # todo: put many settings in an advanced settings dialog: # - autocomp use keywords # - autocomp case sensitive # - autocomp select chars # - Default parser / indentation (width and tabsOrSpaces) / line endings # - Shell wrapping to 80 columns? # - number of lines in shell # - more stuff from pyzo.config.advanced? def getShortcut(fullName): """ Given the full name or an action, get the shortcut from the pyzo.config.shortcuts2 dict. A tuple is returned representing the two shortcuts. """ if isinstance(fullName, QtWidgets.QAction): fullName = fullName.menuPath # the menuPath property is set in Menu._addAction shortcut = '', '' if fullName in pyzo.config.shortcuts2: shortcut = pyzo.config.shortcuts2[fullName] if shortcut.count(','): shortcut = tuple(shortcut.split(',')) else: shortcut = shortcut, '' return shortcut def translateShortcutToOSNames(shortcut): """ Translate Qt names to OS names (e.g. Ctrl -> cmd symbol for Mac, Meta -> Windows for windows """ if sys.platform == 'darwin': replace = (('Ctrl+','\u2318'),('Shift+','\u21E7'), ('Alt+','\u2325'),('Meta+','^')) else: replace = () for old, new in replace: shortcut = shortcut.replace(old, new) return shortcut class KeyMapper(QtCore.QObject): """ This class is accessable via pyzo.keyMapper pyzo.keyMapper.keyMappingChanged is emitted when keybindings are changed """ keyMappingChanged = QtCore.Signal() def setShortcut(self, action): """ When an action is created or when keymappings are changed, this method is called to set the shortcut of an action based on its menuPath (which is the key in pyzo.config.shortcuts2, e.g. shell__clear_screen) """ if action.menuPath in pyzo.config.shortcuts2: # Set shortcut so Qt can do its magic shortcuts = pyzo.config.shortcuts2[action.menuPath] action.setShortcuts(shortcuts.split(',')) pyzo.main.addAction(action) # issue #470, http://stackoverflow.com/questions/23916623 # Also store shortcut text (used in display of tooltip shortcuts = shortcuts.replace(',',', ').replace(' ', ' ') action._shortcutsText = shortcuts.rstrip(', ') def unwrapText(text): """ Unwrap text to display in message boxes. This just removes all newlines. If you want to insert newlines, use \\r.""" # Removes newlines text = text.replace('\n', '') # Remove double/triple/etc spaces text = text.lstrip() for i in range(10): text = text.replace(' ', ' ') # Convert \\r newlines text = text.replace('\r', '\n') # Remove spaces after newlines text = text.replace('\n ', '\n') return text class Menu(QtWidgets.QMenu): """ Menu(parent=None, name=None) Base class for all menus. Has methods to add actions of all sorts. The add* methods all have the name and icon as first two arguments. This is not so consistent with the Qt API for addAction, but it allows for cleaner code to add items; the first item can be quite long because it is a translation. In the current API, the second and subsequent arguments usually fit nicely on the second line. """ def __init__(self, parent=None, name=None): QtWidgets.QMenu.__init__(self, parent) # Make sure that the menu has a title if name: self.setTitle(name) else: raise ValueError # Set tooltip too? if hasattr(name, 'tt'): self.setStatusTip(name.tt) # Action groups within the menu keep track of the selected value self._groups = {} # menuPath is used to bind shortcuts, it is ,e.g. shell__clear_screen if hasattr(parent,'menuPath'): self.menuPath = parent.menuPath + '__' else: self.menuPath = '' #This is a top-level menu # Get key for this menu key = name if hasattr(name, 'key'): key = name.key self.menuPath += self._createMenuPathName(key) # Build the menu. Happens only once self.build() def _createMenuPathName(self, name): """ Convert a menu title into a menuPath component name e.g. Interrupt current shell -> interrupt_current_shell """ # hide anything between brackets name = re.sub('\(.*\)', '', name) # replace invalid chars name = name.replace(' ', '_') if name and name[0] in '0123456789_': name = "_" + name name = re.sub('[^a-zA-z_0-9]','',name) return name.lower() def _addAction(self, text, icon, selected=None): """ Convenience function that makes the right call to addAction(). """ # Add the action if icon is None: a = self.addAction(text) else: a = self.addAction(icon, text) # Checkable? if selected is not None: a.setCheckable(True) a.setChecked(selected) # Set tooltip if we can find it if hasattr(text, 'tt'): a.setStatusTip(text.tt) # Find the key (untranslated name) for this menu item key = a.text() if hasattr(text, 'key'): key = text.key a.menuPath = self.menuPath + '__' + self._createMenuPathName(key) # Register the action so its keymap is kept up to date pyzo.keyMapper.keyMappingChanged.connect(lambda: pyzo.keyMapper.setShortcut(a)) pyzo.keyMapper.setShortcut(a) return a def build(self): """ Add all actions to the menu. To be overridden. """ pass def addMenu(self, menu, icon=None): """ Add a (sub)menu to this menu. """ # Add menu in the conventional way a = QtWidgets.QMenu.addMenu(self, menu) a.menuPath = menu.menuPath # Set icon if icon is not None: a.setIcon(icon) return menu def addItem(self, text, icon=None, callback=None, value=None): """ Add an item to the menu. If callback is given and not None, connect triggered signal to the callback. If value is None or not given, callback is called without parameteres, otherwise it is called with value as parameter """ # Add action a = self._addAction(text, icon) # Connect the menu item to its callback if callback: if value is not None: a.triggered.connect(lambda b=None, v=value: callback(v)) else: a.triggered.connect(lambda b=None: callback()) return a def addGroupItem(self, text, icon=None, callback=None, value=None, group=None): """ Add a 'select-one' option to the menu. Items with equal group value form a group. If callback is specified and not None, the callback is called for the new active item, with the value for that item as parameter whenever the selection is changed """ # Init action a = self._addAction(text, icon) a.setCheckable(True) # Connect the menu item to its callback (toggled is a signal only # emitted by checkable actions, and can also be called programmatically, # e.g. in QActionGroup) if callback: def doCallback(b, v): if b: callback(v) a.toggled.connect(lambda b=None, v=value: doCallback(a.isChecked(), v)) # Add the menu item to a action group if group is None: group = 'default' if group not in self._groups: #self._groups contains tuples (actiongroup, dict-of-actions) self._groups[group] = (QtWidgets.QActionGroup(self), {}) actionGroup,actions = self._groups[group] actionGroup.addAction(a) actions[value]=a return a def addCheckItem(self, text, icon=None, callback=None, value=None, selected=False): """ Add a true/false item to the menu. If callback is specified and not None, the callback is called when the item is changed. If value is not specified or None, callback is called with the new state as parameter. Otherwise, it is called with the new state and value as parameters """ # Add action a = self._addAction(text, icon, selected) # Connect the menu item to its callback if callback: if value is not None: a.triggered.connect(lambda b=None, v=value: callback(a.isChecked(),v)) else: a.triggered.connect(lambda b=None: callback(a.isChecked())) return a def setCheckedOption(self, group, value): """ Set the selected value of a group. This will also activate the callback function of the item that gets selected. if group is None the default group is used. """ if group is None: group = 'default' actionGroup, actions = self._groups[group] if value in actions: actions[value].setChecked(True) class GeneralOptionsMenu(Menu): """ GeneralOptionsMenu(parent, name, callback, options=None) Menu to present the user with a list from which to select one item. We need this a lot. """ def __init__(self, parent=None, name=None, callback=None, options=None): Menu.__init__(self, parent, name) self._options_callback = callback if options: self.setOptions(options) def build(self): pass # We build when the options are given def setOptions(self, options, values=None): """ Set the list of options, clearing any existing options. The options are added ad group items and registered to the callback given at initialization. """ # Init self.clear() cb = self._options_callback # Get values if values is None: values = options for option, value in zip(options, values): self.addGroupItem(option, None, cb, value) class IndentationMenu(Menu): """ Menu for the user to control the type of indentation for a document: tabs vs spaces and the amount of spaces. Part of the File menu. """ def build(self): self._items = [ self.addGroupItem(translate("menu", "Use tabs"), None, self._setStyle, False, group="style"), self.addGroupItem(translate("menu", "Use spaces"), None, self._setStyle, True, group="style") ] self.addSeparator() spaces = translate("menu", "spaces", "plural of spacebar character") self._items += [ self.addGroupItem("%d %s" % (i, spaces), None, self._setWidth, i, group="width") for i in range(2,9) ] def _setWidth(self, width): editor = pyzo.editors.getCurrentEditor() if editor is not None: editor.setIndentWidth(width) def _setStyle(self, style): editor = pyzo.editors.getCurrentEditor() if editor is not None: editor.setIndentUsingSpaces(style) class FileMenu(Menu): def build(self): icons = pyzo.icons self._items = [] # Create indent menu t = translate("menu", "Indentation ::: The indentation used of the current file.") self._indentMenu = IndentationMenu(self, t) # Create parser menu from pyzo import codeeditor t = translate("menu", "Syntax parser ::: The syntax parser of the current file.") self._parserMenu = GeneralOptionsMenu(self, t, self._setParser) self._parserMenu.setOptions(["Plain"] + codeeditor.Manager.getParserNames()) # Create line ending menu t = translate("menu", "Line endings ::: The line ending character of the current file.") self._lineEndingMenu = GeneralOptionsMenu(self, t, self._setLineEndings) self._lineEndingMenu.setOptions(['LF', 'CR', 'CRLF']) # Create encoding menu t = translate("menu", "Encoding ::: The character encoding of the current file.") self._encodingMenu = GeneralOptionsMenu(self, t, self._setEncoding) # Bind to signal pyzo.editors.currentChanged.connect(self.onEditorsCurrentChanged) # Build menu file management stuff self.addItem(translate('menu', 'New ::: Create a new (or temporary) file.'), icons.page_add, pyzo.editors.newFile) self.addItem(translate("menu", "Open... ::: Open an existing file from disk."), icons.folder_page, pyzo.editors.openFile) # self._items += [ self.addItem(translate("menu", "Save ::: Save the current file to disk."), icons.disk, pyzo.editors.saveFile), self.addItem(translate("menu", "Save as... ::: Save the current file under another name."), icons.disk_as, pyzo.editors.saveFileAs), self.addItem(translate("menu", "Save all ::: Save all open files."), icons.disk_multiple, pyzo.editors.saveAllFiles), self.addItem(translate("menu", "Close ::: Close the current file."), icons.page_delete, pyzo.editors.closeFile), self.addItem(translate("menu", "Close all ::: Close all files."), icons.page_delete_all, pyzo.editors.closeAllFiles), self.addItem(translate("menu", "Export to PDF ::: Export current file to PDF (e.g. for printing)."), None, self._print), ] # Build file properties stuff self.addSeparator() self._items += [ self.addMenu(self._indentMenu, icons.page_white_gear), self.addMenu(self._parserMenu, icons.page_white_gear), self.addMenu(self._lineEndingMenu, icons.page_white_gear), self.addMenu(self._encodingMenu, icons.page_white_gear), ] # Closing of app self.addSeparator() self.addItem(translate("menu", "Restart Pyzo ::: Restart the application."), icons.arrow_rotate_clockwise, pyzo.main.restart) self.addItem(translate("menu","Quit Pyzo ::: Close the application."), icons.cancel, pyzo.main.close) # Start disabled self.setEnabled(False) def setEnabled(self, enabled): """ Enable or disable all items. If disabling, also uncheck all items """ for child in self._items: child.setEnabled(enabled) def onEditorsCurrentChanged(self): editor = pyzo.editors.getCurrentEditor() if editor is None: self.setEnabled(False) #Disable / uncheck all editor-related options else: self.setEnabled(True) # Update indentation self._indentMenu.setCheckedOption("style", editor.indentUsingSpaces()) self._indentMenu.setCheckedOption("width", editor.indentWidth()) # Update parser parserName = 'Plain' if editor.parser(): parserName = editor.parser().name() or 'Plain' self._parserMenu.setCheckedOption(None, parserName ) # Update line ending self._lineEndingMenu.setCheckedOption(None, editor.lineEndingsHumanReadable) # Update encoding self._updateEncoding(editor) def _setParser(self, value): editor = pyzo.editors.getCurrentEditor() if value.lower() == 'plain': value = None if editor is not None: editor.setParser(value) def _setLineEndings(self, value): editor = pyzo.editors.getCurrentEditor() editor.lineEndings = value def _updateEncoding(self, editor): # Dict with encoding aliases (official to aliases) D = { 'cp1250': ('windows-1252', ), 'cp1251': ('windows-1251', ), 'latin_1': ('iso-8859-1', 'iso8859-1', 'cp819', 'latin', 'latin1', 'L1')} # Dict with aliases mapping to "official value" Da = {} for key in D: for key2 in D[key]: Da[key2] = key # Encodings to list encodings = [ 'utf-8','ascii', 'latin_1', 'cp1250', 'cp1251'] # Get current encoding (add if not present) editorEncoding = editor.encoding if editorEncoding in Da: editorEncoding = Da[editorEncoding] if editorEncoding not in encodings: encodings.append(editorEncoding) # Handle aliases encodingNames, encodingValues = [], [] for encoding in encodings: encodingValues.append(encoding) if encoding in D: name = '%s (%s)' % (encoding, ', '.join(D[encoding])) encodingNames.append(name) else: encodingNames.append(encoding) # Update self._encodingMenu.setOptions(encodingNames, encodingValues) self._encodingMenu.setCheckedOption(None, editorEncoding) def _setEncoding(self, value): editor = pyzo.editors.getCurrentEditor() if editor is not None: editor.encoding = value def _print(self): editor = pyzo.editors.getCurrentEditor() if editor is not None: from pyzo.util.qt import QtPrintSupport printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution) if True: filename = QtWidgets.QFileDialog.getSaveFileName(None, 'Export PDF', os.path.expanduser("~"), "*.pdf *.ps") if isinstance(filename, tuple): # PySide filename = filename[0] if not filename: return printer.setOutputFileName(filename) else: d = QtWidgets.QPrintDialog(printer) d.setWindowTitle('Print code') d.setOption(d.PrintSelection, editor.textCursor().hasSelection()) d.setOption(d.PrintToFile, True) ok = d.exec_() if ok != d.Accepted: return # Print with line numbers lines = editor.toPlainText().splitlines() nzeros = len(str(len(lines))) lines.insert(0, '# ' + editor.filename) for i in range(1, len(lines)): lines[i] = str(i).rjust(nzeros, '0') + '| ' + lines[i] cursor0 = editor.textCursor() cursor = editor.textCursor() cursor.movePosition(cursor.Start) cursor.movePosition(cursor.End, cursor.KeepAnchor) cursor.insertText('\n'.join(lines)) try: editor.print_(printer) finally: editor.undo() editor.setTextCursor(cursor0) # todo: move to matching brace class EditMenu(Menu): def build(self): icons = pyzo.icons self.addItem(translate("menu", "Undo ::: Undo the latest editing action."), icons.arrow_undo, self._editItemCallback, "undo") self.addItem(translate("menu", "Redo ::: Redo the last undone editong action."), icons.arrow_redo, self._editItemCallback, "redo") self.addSeparator() self.addItem(translate("menu", "Cut ::: Cut the selected text."), icons.cut, self._editItemCallback, "cut") self.addItem(translate("menu", "Copy ::: Copy the selected text to the clipboard."), icons.page_white_copy, self._editItemCallback, "copy") self.addItem(translate("menu", "Paste ::: Paste the text that is now on the clipboard."), icons.paste_plain, self._editItemCallback, "paste") self.addItem(translate("menu", "Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation."), # noqa icons.paste_plain, self._editItemCallback, "pasteAndSelect") self.addItem(translate("menu", "Select all ::: Select all text."), icons.sum, self._editItemCallback, "selectAll") self.addSeparator() self.addItem(translate("menu", "Indent ::: Indent the selected line."), icons.text_indent, self._editItemCallback, "indentSelection") self.addItem(translate("menu", "Dedent ::: Unindent the selected line."), icons.text_indent_remove, self._editItemCallback, "dedentSelection") self.addItem(translate("menu", "Comment ::: Comment the selected line."), icons.comment_add, self._editItemCallback, "commentCode") self.addItem(translate("menu", "Uncomment ::: Uncomment the selected line."), icons.comment_delete, self._editItemCallback, "uncommentCode") self.addItem(translate("menu", "Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters."), icons.text_align_justify, self._editItemCallback, "justifyText") self.addItem(translate("menu", "Go to line ::: Go to a specific line number."), None, self._editItemCallback, "gotoLinePopup") self.addItem(translate("menu", "Duplicate line ::: Duplicate the selected line(s)."), None, self._editItemCallback, "duplicateLines") self.addItem(translate("menu", "Delete line ::: Delete the selected line(s)."), None, self._editItemCallback, "deleteLines") self.addSeparator() self.addItem(translate("menu", "Toggle breakpoint ::: Toggle breakpoint on the current line."), None, self._editItemCallback, "toggleBreakpoint") self.addSeparator() self.addItem(translate("menu", "Find or replace ::: Show find/replace widget. Initialize with selected text."), icons.find, pyzo.editors._findReplace.startFind) self.addItem(translate("menu", "Find selection ::: Find the next occurrence of the selected text."), None, pyzo.editors._findReplace.findSelection) self.addItem(translate("menu", "Find selection backward ::: Find the previous occurrence of the selected text."), None, pyzo.editors._findReplace.findSelectionBw) self.addItem(translate("menu", "Find next ::: Find the next occurrence of the search string."), None, pyzo.editors._findReplace.findNext) self.addItem(translate("menu", "Find previous ::: Find the previous occurrence of the search string."), None, pyzo.editors._findReplace.findPrevious) def _editItemCallback(self, action): widget = QtWidgets.qApp.focusWidget() #If the widget has a 'name' attribute, call it if hasattr(widget, action): getattr(widget, action)() class ZoomMenu(Menu): """ Small menu for the zooming. Part of the view menu. """ def build(self): self.addItem(translate("menu", 'Zoom in'), None, self._setZoom, +1) self.addItem(translate("menu", 'Zoom out'), None, self._setZoom, -1) self.addItem(translate("menu", 'Zoom reset'), None, self._setZoom, 0) def _setZoom(self, value): if not value: pyzo.config.view.zoom = 0 else: pyzo.config.view.zoom += value # Apply for editor in pyzo.editors: pyzo.config.view.zoom = editor.setZoom(pyzo.config.view.zoom) for shell in pyzo.shells: pyzo.config.view.zoom = shell.setZoom(pyzo.config.view.zoom) logger = pyzo.toolManager.getTool('pyzologger') if logger: logger.setZoom(pyzo.config.view.zoom) class FontMenu(Menu): def __init__(self, parent=None, name="Font", *args, **kwds): Menu.__init__(self, parent, name, *args, **kwds) self.aboutToShow.connect(self._updateFonts) def _updateFonts(self): self.clear() # Build list with known available monospace fonts names = pyzo.codeeditor.Manager.fontNames() defaultName = 'DejaVu Sans Mono' for name in sorted(names): default_suffix = ' (%s)' % translate('menu', 'default') txt = name + default_suffix if name == defaultName else name self.addGroupItem(txt, None, self._selectFont, value=name) # Select the current one self.setCheckedOption(None, pyzo.config.view.fontname) def _selectFont(self, name): pyzo.config.view.fontname = name # Apply for editor in pyzo.editors: editor.setFont(pyzo.config.view.fontname) for shell in pyzo.shells: shell.setFont(pyzo.config.view.fontname) logger = pyzo.toolManager.getTool('pyzologger') if logger: logger.setFont(pyzo.config.view.fontname) # todo: brace matching # todo: code folding? # todo: maybe move qt theme to settings class ViewMenu(Menu): def build(self): icons = pyzo.icons # Create edge column menu t = translate("menu", "Location of long line indicator ::: The location of the long-line-indicator.") self._edgeColumMenu = GeneralOptionsMenu(self, t, self._setEdgeColumn) values = [0] + [i for i in range(60,130,10)] names = [translate("menu","None")] + [str(i) for i in values[1:]] self._edgeColumMenu.setOptions(names, values) self._edgeColumMenu.setCheckedOption(None, pyzo.config.view.edgeColumn) # Create qt theme menu t = translate("menu", "Qt theme ::: The styling of the user interface widgets.") self._qtThemeMenu = GeneralOptionsMenu(self, t, self._setQtTheme) styleNames = list(QtWidgets.QStyleFactory.keys()) styleNames.sort() titles = [name for name in styleNames] styleNames = [name.lower() for name in styleNames] for i in range(len(titles)): if titles[i].lower() == pyzo.defaultQtStyleName.lower(): titles[i] += " (%s)" % translate('menu', 'default') self._qtThemeMenu.setOptions(titles, styleNames) self._qtThemeMenu.setCheckedOption(None, pyzo.config.view.qtstyle.lower()) # Build menu self.addItem(translate("menu", "Select shell ::: Focus the cursor on the current shell."), icons.application_shell, self._selectShell) self.addItem(translate("menu", "Select editor ::: Focus the cursor on the current editor."), icons.application_edit, self._selectEditor) self.addItem(translate("menu", "Select previous file ::: Select the previously selected file."), icons.application_double, pyzo.editors._tabs.selectPreviousItem) self.addSeparator() self.addEditorItem(translate("menu", "Show whitespace ::: Show spaces and tabs."), None, "showWhitespace") self.addEditorItem(translate("menu", "Show line endings ::: Show the end of each line."), None, "showLineEndings") self.addEditorItem(translate("menu", "Show indentation guides ::: Show vertical lines to indicate indentation."), None, "showIndentationGuides") self.addSeparator() self.addEditorItem(translate("menu", "Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling)."), None, "wrap") self.addEditorItem(translate("menu", "Highlight current line ::: Highlight the line where the cursor is."), None, "highlightCurrentLine") self.addEditorItem(translate("menu", "Highlight brackets ::: Highlight matched and unmatched brackets."), None, "highlightMatchingBracket") self.addSeparator() self.addItem(translate("menu", "Previous cell ::: Go back to the previous cell."), None, self._previousCell ) self.addItem(translate("menu", "Next cell ::: Advance to the next cell."), None, self._nextCell ) self.addItem(translate("menu", "Previous object ::: Go back to the previous top-level structure."), None, self._previousTopLevelObject ) self.addItem(translate("menu", "Next object ::: Advance to the next top-level structure."), None, self._nextTopLevelObject ) self.addSeparator() self.addMenu(self._edgeColumMenu, icons.text_padding_right) self.addMenu(FontMenu(self, translate("menu", "Font")), icons.style) self.addMenu(ZoomMenu(self, translate("menu", "Zooming")), icons.magnifier) self.addMenu(self._qtThemeMenu, icons.application_view_tile) def addEditorItem(self, name, icon, param): """ Create a boolean item that reperesents a property of the editors, whose value is stored in pyzo.config.view.param """ if hasattr(pyzo.config.view, param): default = getattr(pyzo.config.view, param) else: default = True self.addCheckItem(name, icon, self._configEditor, param, default) def _configEditor(self, state, param): """ Callback for addEditorItem items """ # Store this parameter in the config setattr(pyzo.config.view, param, state) # Apply to all editors, translate e.g. showWhitespace to setShowWhitespace setter = 'set' + param[0].upper() + param[1:] for editor in pyzo.editors: getattr(editor,setter)(state) def _selectShell(self): shell = pyzo.shells.getCurrentShell() if shell: shell.setFocus() def _selectEditor(self): editor = pyzo.editors.getCurrentEditor() if editor: editor.setFocus() def _setEdgeColumn(self, value): pyzo.config.view.edgeColumn = value for editor in pyzo.editors: editor.setLongLineIndicatorPosition(value) def _setQtTheme(self, value): pyzo.config.view.qtstyle = value pyzo.main.setQtStyle(value) def _previousCell(self): """ Rewind the curser to the previous cell (starting with '##'). """ self._previousTopLevelObject(type='cell') def _nextCell(self): """ Advance the curser to the next cell (starting with '##'). """ self._nextTopLevelObject(type='cell') def _previousTopLevelObject(self, type=None): # Get parser result result = pyzo.parser._getResult() if not result: return # Get editor editor = pyzo.editors.getCurrentEditor() if not editor: return # Get current line number ln = editor.textCursor().blockNumber() ln += 1 # is ln as in line number area runCursor = editor.textCursor() #The part that should be run runCursor.movePosition(runCursor.StartOfBlock) # Find the object which starts above current curser # position if there is any and move there for object in reversed(result.rootItem.children): # If type given, only consider objects of that type if type and type!=object.type: continue if ln and object.linenr < ln: startLineNr = object.linenr # Rewind cursor until the start of this object while True: if not runCursor.block().previous().isValid(): return runCursor.movePosition(runCursor.PreviousBlock) if runCursor.blockNumber() == startLineNr-1: break cursor = editor.textCursor() cursor.setPosition(runCursor.position()) editor.setTextCursor(cursor) return def _nextTopLevelObject(self, type=None): # Get parser result result = pyzo.parser._getResult() if not result: return # Get editor editor = pyzo.editors.getCurrentEditor() if not editor: return # Get current line number ln = editor.textCursor().blockNumber() ln += 1 # is ln as in line number area runCursor = editor.textCursor() #The part that should be run runCursor.movePosition(runCursor.StartOfBlock) # Find the object which starts below current curser # position if there is any and move there for object in result.rootItem.children: # If type given, only consider objects of that type if type and type!=object.type: continue if ln and object.linenr > ln: startLineNr = object.linenr endLineNr = object.linenr2 # Advance cursor until the start of this object while True: if not runCursor.block().next().isValid(): return runCursor.movePosition(runCursor.NextBlock) if runCursor.blockNumber() == startLineNr-1: break realCursorPosition = runCursor.position() # Advance cursor until the end of this object (to know # how far it extends and make sure it is most visible) while True: if not runCursor.block().next().isValid(): break runCursor.movePosition(runCursor.NextBlock) if runCursor.blockNumber() == endLineNr-1: break cursor = editor.textCursor() cursor.setPosition(runCursor.position()) editor.setTextCursor(cursor) cursor.setPosition(realCursorPosition) editor.setTextCursor(cursor) return class ShellMenu(Menu): def __init__(self, parent=None, name="Shell"): self._shellCreateActions = [] self._shellActions = [] Menu.__init__(self, parent, name) pyzo.shells.currentShellChanged.connect(self.onCurrentShellChanged) self.aboutToShow.connect(self._updateShells) def onCurrentShellChanged(self): """ Enable/disable shell actions based on wether a shell is available """ for shellAction in self._shellActions: shellAction.setEnabled(bool(pyzo.shells.getCurrentShell())) def buildShellActions(self): """ Create the menu items which are also avaliable in the ShellTabContextMenu Returns a list of all items added""" icons = pyzo.icons return [ self.addItem(translate("menu", 'Clear screen ::: Clear the screen.'), icons.application_eraser, self._shellAction, "clearScreen"), self.addItem(translate("menu", 'Interrupt ::: Interrupt the current running code (does not work for extension code).'), icons.application_lightning, self._shellAction, "interrupt"), self.addItem(translate("menu", 'Restart ::: Terminate and restart the interpreter.'), icons.application_refresh, self._shellAction, "restart"), self.addItem(translate("menu", 'Terminate ::: Terminate the interpreter, leaving the shell open.'), icons.application_delete, self._shellAction, "terminate"), self.addItem(translate("menu", 'Close ::: Terminate the interpreter and close the shell.'), icons.cancel, self._shellAction, "closeShell"), ] def buildShellDebugActions(self): """ Create the menu items for debug shell actions. Returns a list of all items added""" icons = pyzo.icons return [ self.addItem(translate("menu", 'Debug next: proceed until next line'), icons.debug_next, self._debugAction, "NEXT"), self.addItem(translate("menu", 'Debug step into: proceed one step'), icons.debug_step, self._debugAction, "STEP"), self.addItem(translate("menu", 'Debug return: proceed until returns'), icons.debug_return, self._debugAction, "RETURN"), self.addItem(translate("menu", 'Debug continue: proceed to next breakpoint'), icons.debug_continue, self._debugAction, "CONTINUE"), self.addItem(translate("menu", 'Stop debugging'), icons.debug_quit, self._debugAction, "STOP"), ] def getShell(self): """ Returns the shell on which to apply the menu actions. Default is the current shell, this is overridden in the shell/shell tab context menus""" return pyzo.shells.getCurrentShell() def build(self): """ Create the items for the shells menu """ # Normal shell actions self._shellActions = self.buildShellActions() self.addSeparator() # Debug stuff self._debug_clear_text = translate('menu', 'Clear all {} breakpoints') self._debug_clear = self.addItem('', pyzo.icons.bug_delete, self._clearBreakPoints) self._debug_pm = self.addItem( translate('menu', 'Postmortem: debug from last traceback'), pyzo.icons.bug_delete, self._debugAction, "START") self._shellDebugActions = self.buildShellDebugActions() # self.aboutToShow.connect(self._updateDebugButtons) self.addSeparator() # Shell config self.addItem(translate("menu", 'Edit shell configurations... ::: Add new shell configs and edit interpreter properties.'), pyzo.icons.application_wrench, self._editConfig2) self.addItem(translate("menu", 'Create new Python environment... ::: Install miniconda.'), pyzo.icons.application_cascade, self._newPythonEnv) self.addSeparator() # Add shell configs self._updateShells() def _updateShells(self): """ Remove, then add the items for the creation of each shell """ for action in self._shellCreateActions: self.removeAction(action) self._shellCreateActions = [] for i, config in enumerate(pyzo.config.shellConfigs2): name = translate('menu', 'Create shell %s: (%s)') % (i+1, config.name) action = self.addItem(name, pyzo.icons.application_add, pyzo.shells.addShell, config) self._shellCreateActions.append(action) def _updateDebugButtons(self): # Count breakpoints bpcount = 0 for e in pyzo.editors: bpcount += len(e.breakPoints()) self._debug_clear.setText(self._debug_clear_text.format(bpcount)) # Determine state of PM and clear button debugmode = pyzo.shells._debugmode self._debug_pm.setEnabled(debugmode==0) self._debug_clear.setEnabled(debugmode==0) # The _shellDebugActions are enabled/disabled by the shellStack def _shellAction(self, action): """ Call the method specified by 'action' on the current shell. """ shell = self.getShell() if shell: # Call the specified action getattr(shell,action)() def _debugAction(self, action): shell = self.getShell() if shell: # Call the specified action command = action.upper() shell.executeCommand('DB %s\n' % command) def _clearBreakPoints(self, action=None): for e in pyzo.editors: e.clearBreakPoints() def _editConfig2(self): """ Edit, add and remove configurations for the shells. """ from pyzo.core.shellInfoDialog import ShellInfoDialog d = ShellInfoDialog() d.exec_() def _newPythonEnv(self): from pyzo.util.bootstrapconda import Installer d = Installer(pyzo.main) d.exec_() class ShellButtonMenu(ShellMenu): def build(self): self._shellActions = [] self.addItem(translate("menu", 'Edit shell configurations... ::: Add new shell configs and edit interpreter properties.'), pyzo.icons.application_wrench, self._editConfig2) submenu = Menu(self, translate("menu", 'New shell ... ::: Create new shell to run code in.')) self._newShellMenu = self.addMenu(submenu, pyzo.icons.application_add) self.addSeparator() def _updateShells(self): """ Remove, then add the items for the creation of each shell """ for action in self._shellCreateActions: self._newShellMenu.removeAction(action) self._shellCreateActions = [] for i, config in enumerate(pyzo.config.shellConfigs2): name = translate('menu', 'Create shell %s: (%s)') % (i+1, config.name) action = self._newShellMenu.addItem(name, pyzo.icons.application_add, pyzo.shells.addShell, config) self._shellCreateActions.append(action) class ShellContextMenu(ShellMenu): """ This is the context menu for the shell """ def __init__(self, shell, parent=None): ShellMenu.__init__(self, parent or shell, name='Shellcontextmenu') self._shell = shell def build(self): """ Build menu """ self.buildShellActions() icons = pyzo.icons # This is a subset of the edit menu. Copied manually. self.addSeparator() self.addItem(translate("menu", "Cut ::: Cut the selected text."), icons.cut, self._editItemCallback, "cut") self.addItem(translate("menu", "Copy ::: Copy the selected text to the clipboard."), icons.page_white_copy, self._editItemCallback, "copy") self.addItem(translate("menu", "Paste ::: Paste the text that is now on the clipboard."), icons.paste_plain, self._editItemCallback, "paste") self.addItem(translate("menu", "Select all ::: Select all text."), icons.sum, self._editItemCallback, "selectAll") self.addSeparator() self.addItem(translate("menu", "Open current directory in file browser"), None, self._editItemCallback, "opendir") self.addItem(translate("menu", "Change current directory to the file browser's path"), None, self._editItemCallback, "changedir") self.addItem(translate("menu", "Change current directory to editor file path"), None, self._editItemCallback, "changedirtoeditor") def getShell(self): """ Shell actions of this menu operate on the shell specified in the constructor """ return self._shell def _editItemCallback(self, action): #If the widget has a 'name' attribute, call it if action == 'opendir': curdir = self._shell.get_kernel_cd() fileBrowser = pyzo.toolManager.getTool('pyzofilebrowser') if curdir and fileBrowser: fileBrowser.setPath(curdir) elif action == 'changedir': fileBrowser = pyzo.toolManager.getTool('pyzofilebrowser') if fileBrowser: self._shell.executeCommand('cd ' + fileBrowser.path() + '\n') elif action == 'changedirtoeditor': msg = '' editor = pyzo.editors.getCurrentEditor() if editor is None: msg += translate("menu", "No editor selected.") # Show error dialog if msg: m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Could not change dir")) m.setText(translate("menu", "Could not change dir" + ":\n\n" + msg)) m.setIcon(m.Warning) m.exec_() else: self._shell.executeCommand('cd ' + os.path.dirname(editor.filename) + '\n') else: getattr(self._shell, action)() def _updateShells(self): pass class ShellTabContextMenu(ShellContextMenu): """ The context menu for the shell tab is similar to the shell context menu, but only has the shell actions defined in ShellMenu.buildShellActions()""" def build(self): """ Build menu """ self.buildShellActions() def _updateShells(self): pass class EditorContextMenu(Menu): """ This is the context menu for the editor """ def __init__(self, editor, name='EditorContextMenu' ): self._editor = editor Menu.__init__(self, editor, name) def build(self): """ Build menu """ icons = pyzo.icons # This is a subset of the edit menu. Copied manually. self.addItem(translate("menu", "Cut ::: Cut the selected text."), icons.cut, self._editItemCallback, "cut") self.addItem(translate("menu", "Copy ::: Copy the selected text to the clipboard."), icons.page_white_copy, self._editItemCallback, "copy") self.addItem(translate("menu", "Paste ::: Paste the text that is now on the clipboard."), icons.paste_plain, self._editItemCallback, "paste") self.addItem(translate("menu", "Select all ::: Select all text."), icons.sum, self._editItemCallback, "selectAll") self.addSeparator() self.addItem(translate("menu", "Indent ::: Indent the selected line."), icons.text_indent, self._editItemCallback, "indentSelection") self.addItem(translate("menu", "Dedent ::: Unindent the selected line."), icons.text_indent_remove, self._editItemCallback, "dedentSelection") self.addItem(translate("menu", "Comment ::: Comment the selected line."), icons.comment_add, self._editItemCallback, "commentCode") self.addItem(translate("menu", "Uncomment ::: Uncomment the selected line."), icons.comment_delete, self._editItemCallback, "uncommentCode") self.addItem(translate("menu", "Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters."), icons.text_align_justify, self._editItemCallback, "justifyText") self.addSeparator() self.addItem(translate("menu", "Goto Definition ::: Go to definition of word under cursor."), icons.debug_return, self._editItemCallback, "gotoDef") self.addItem(translate("menu", "Open directory in file browser"), None, self._editItemCallback, "opendir") self.addSeparator() self.addItem(translate("menu", "Find or replace ::: Show find/replace widget. Initialize with selected text."), icons.find, pyzo.editors._findReplace.startFind) self.addItem(translate("menu", "Find selection ::: Find the next occurrence of the selected text."), None, pyzo.editors._findReplace.findSelection) self.addItem(translate("menu", "Find selection backward ::: Find the previous occurrence of the selected text."), None, pyzo.editors._findReplace.findSelectionBw) # This is a subset of the run menu. Copied manually. self.addSeparator() self.addItem(translate("menu", 'Run selection ::: Run the current editor\'s selected lines, selected words on the current line, or current line if there is no selection.'), # noqa icons.run_lines, self._runSelected) def _editItemCallback(self, action): #If the widget has a 'name' attribute, call it if action == 'opendir': fileBrowser = pyzo.toolManager.getTool('pyzofilebrowser') if fileBrowser: fileBrowser.setPath(os.path.dirname(self._editor.filename)) else: getattr(self._editor, action)() def _runSelected(self): runMenu = pyzo.main.menuBar()._menumap['run'] runMenu._runSelected() class EditorTabContextMenu(Menu): def __init__(self, *args, **kwds): Menu.__init__(self, *args, **kwds) self._index = -1 def setIndex(self, index): self._index = index def build(self): """ Build menu """ icons = pyzo.icons # Copied (and edited) manually from the File memu self.addItem(translate("menu", "Save ::: Save the current file to disk."), icons.disk, self._fileAction, "saveFile") self.addItem(translate("menu", "Save as... ::: Save the current file under another name."), icons.disk_as, self._fileAction, "saveFileAs") self.addItem(translate("menu", "Close ::: Close the current file."), icons.page_delete, self._fileAction, "closeFile") self.addItem(translate("menu", "Close others::: Close all files but this one."), None, self._fileAction, "close_others") self.addItem(translate("menu", "Close all ::: Close all files."), icons.page_delete_all, self._fileAction, "close_all") self.addItem(translate("menu", "Rename ::: Rename this file."), None, self._fileAction, "rename") self.addSeparator() self.addItem(translate("menu", "Copy path ::: Copy the full path of this file."), None, self._fileAction, "copypath") self.addItem(translate("menu", "Open directory in file browser"), None, self._fileAction, "opendir") self.addSeparator() # todo: remove feature to pin files? self.addItem(translate("menu", "Pin/Unpin ::: Pinned files get closed less easily."), None, self._fileAction, "pin") self.addItem(translate("menu", "Set/Unset as MAIN file ::: The main file can be run while another file is selected."), icons.star, self._fileAction, "main") self.addSeparator() self.addItem(translate("menu", "Run file ::: Run the code in this file."), icons.run_file, self._fileAction, "run") self.addItem(translate("menu", "Run file as script ::: Run this file as a script (restarts the interpreter)."), icons.run_file_script, self._fileAction, "run_script") def _fileAction(self, action): """ Call the method specified by 'action' on the selected shell """ item = pyzo.editors._tabs.getItemAt(self._index) if action in ["saveFile", "saveFileAs", "closeFile"]: getattr(pyzo.editors, action)(item.editor) elif action == "close_others" or action == "close_all": if action == "close_all": item = None #The item not to be closed is not there items = pyzo.editors._tabs.items() for i in reversed(range(pyzo.editors._tabs.count())): if items[i] is item or items[i].pinned: continue pyzo.editors._tabs.tabCloseRequested.emit(i) elif action == "rename": filename = item.filename pyzo.editors.saveFileAs(item.editor) if item.filename != filename: try: os.remove(filename) except Exception: pass elif action == 'copypath': filename = item.filename QtWidgets.qApp.clipboard().setText(filename) elif action == 'opendir': fileBrowser = pyzo.toolManager.getTool('pyzofilebrowser') if fileBrowser: fileBrowser.setPath(os.path.dirname(item.filename)) elif action == "pin": item._pinned = not item._pinned elif action == "main": if pyzo.editors._tabs._mainFile == item.id: pyzo.editors._tabs._mainFile = None else: pyzo.editors._tabs._mainFile = item.id elif action == "run": menu = pyzo.main.menuBar().findChild(RunMenu) if menu: menu._runFile((False, False), item.editor) elif action == "run_script": menu = pyzo.main.menuBar().findChild(RunMenu) if menu: menu._runFile((True, False), item.editor) pyzo.editors._tabs.updateItems() class RunMenu(Menu): def build(self): icons = pyzo.icons self.addItem(translate("menu", 'Run file as script ::: Restart and run the current file as a script.'), # noqa icons.run_file_script, self._runFile, (True, False)) self.addItem(translate("menu", 'Run main file as script ::: Restart and run the main file as a script.'), # noqa icons.run_mainfile_script, self._runFile, (True, True)) self.addSeparator() self.addItem(translate("menu", 'Execute selection ::: Execute the current editor\'s selected lines, selected words on the current line, or current line if there is no selection.'), # noqa icons.run_lines, self._runSelected) self.addItem(translate("menu", 'Execute cell ::: Execute the current editors\'s cell in the current shell.'), # noqa icons.run_cell, self._runCell) #In the _runFile calls, the parameter specifies (asScript, mainFile) self.addItem(translate("menu", 'Execute file ::: Execute the current file in the current shell.'), icons.run_file, self._runFile,(False, False)) self.addItem(translate("menu", 'Execute main file ::: Execute the main file in the current shell.'), icons.run_mainfile, self._runFile,(False, True)) self.addSeparator() self.addItem(translate("menu", 'Execute selection and advance'), icons.run_lines, self._runSelectedAdvance) self.addItem(translate("menu", 'Execute cell and advance ::: Execute the current editors\'s cell and advance to the next cell.'), icons.run_cell, self._runCellAdvance) self.addSeparator() self.addCheckItem(translate("menu", 'Change directory when executing file ::: like Run File As Script does'), None, self._cdonfileexec, None, pyzo.config.settings.changeDirOnFileExec) self.addItem(translate("menu", 'Help on running code ::: Open the pyzo wizard at the page about running code.'), icons.information, self._showHelp) def _cdonfileexec(self, value): pyzo.config.settings.changeDirOnFileExec = bool(value) def _showHelp(self): """ Show more information about ways to run code. """ from pyzo.util.pyzowizard import PyzoWizard w = PyzoWizard(self) w.show('RuncodeWizardPage1') # Start wizard at page about running code def _getShellAndEditor(self, what, mainEditor=False): """ Get the shell and editor. Shows a warning dialog when one of these is not available. """ # Init empty error message msg = '' # Get shell shell = pyzo.shells.getCurrentShell() if shell is None: msg += translate("menu", "No shell to run code in.").rstrip() + ' ' #shell = pyzo.shells.addShell() # issue #335, does not work, somehow # Get editor if mainEditor: editor = pyzo.editors.getMainEditor() if editor is None: msg += translate("menu", "There is no main file selected.") else: editor = pyzo.editors.getCurrentEditor() if editor is None: msg += translate("menu", "No editor selected.") # Show error dialog if msg: m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Could not run")) m.setText(translate("menu", "Could not run " + what + ":\n\n" + msg)) m.setIcon(m.Warning) m.exec_() # Return return shell, editor def _advance(self, runCursor): # Get editor and shell shell, editor = self._getShellAndEditor('selection') if not shell or not editor: return cursor = editor.textCursor() cursor.setPosition(runCursor.position()) cursor.movePosition(cursor.NextBlock) editor.setTextCursor(cursor) def _runSelectedAdvance(self): self._runSelected(advance=True) def _runSelected(self, advance=False): """ Run the selected whole lines in the current shell. """ # Get editor and shell shell, editor = self._getShellAndEditor('selection') if not shell or not editor: return # Get position to sample between (only sample whole lines) screenCursor = editor.textCursor() #Current selection in the editor runCursor = editor.textCursor() #The part that should be run runCursor.setPosition(screenCursor.selectionStart()) runCursor.movePosition(runCursor.StartOfBlock) #This also moves the anchor lineNumber1 = runCursor.blockNumber() runCursor.setPosition(screenCursor.selectionEnd(),runCursor.KeepAnchor) if not (screenCursor.hasSelection() and runCursor.atBlockStart()): #If the end of the selection is at the beginning of a block, don't extend it runCursor.movePosition(runCursor.EndOfBlock,runCursor.KeepAnchor) lineNumber2 = runCursor.blockNumber() # Does this look like a statement? isStatement = lineNumber1 == lineNumber2 and screenCursor.hasSelection() if isStatement: # Get source code of statement code = screenCursor.selectedText().replace('\u2029', '\n').strip() # add code to history pyzo.command_history.append(code) # Execute statement shell.executeCommand(code+'\n') else: # Get source code code = runCursor.selectedText().replace('\u2029', '\n') # Notify user of what we execute self._showWhatToExecute(editor, runCursor) # Get filename and run code fname = editor.id() # editor._name or editor._filename shell.executeCode(code, fname, lineNumber1) if advance: self._advance(runCursor) def _runCellAdvance(self): self._runCell(advance=True) def _runCell(self, advance=False): """ Run the code between two cell separaters ('##'). """ #TODO: ignore ## in multi-line strings # Maybe using source-structure information? # Get editor and shell shell, editor = self._getShellAndEditor('cell') if not shell or not editor: return cellName = '' # Get current cell # Move up until the start of document # or right after a line starting with '##' runCursor = editor.textCursor() #The part that should be run runCursor.movePosition(runCursor.StartOfBlock) while True: line = runCursor.block().text().lstrip() if line.startswith('##') or line.startswith('#%%') or line.startswith('# %%'): # ## line, move to the line following this one if not runCursor.block().next().isValid(): #The user tried to execute the last line of a file which #started with ##. Do nothing return runCursor.movePosition(runCursor.NextBlock) cellName = line.lstrip('#% ').strip() break if not runCursor.block().previous().isValid(): break #Start of document runCursor.movePosition(runCursor.PreviousBlock) # This is the line number of the start lineNumber = runCursor.blockNumber() if len(cellName) > 20: cellName = cellName[:17]+'...' # Move down until a line before one starting with'##' # or to end of document while True: line = runCursor.block().text().lstrip() if line.startswith('##') or line.startswith('#%%') or line.startswith('# %%'): #This line starts with ##, move to the end of the previous one runCursor.movePosition(runCursor.Left,runCursor.KeepAnchor) break if not runCursor.block().next().isValid(): #Last block of the document, move to the end of the line runCursor.movePosition(runCursor.EndOfBlock,runCursor.KeepAnchor) break runCursor.movePosition(runCursor.NextBlock,runCursor.KeepAnchor) # Get source code code = runCursor.selectedText().replace('\u2029', '\n') # Notify user of what we execute self._showWhatToExecute(editor, runCursor) # Get filename and run code fname = editor.id() # editor._name or editor._filename shell.executeCode(code, fname, lineNumber, cellName) if advance: self._advance(runCursor) def _showWhatToExecute(self, editor, runCursor=None): # Get runCursor for whole document if not given if runCursor is None: runCursor = editor.textCursor() runCursor.movePosition(runCursor.Start) runCursor.movePosition(runCursor.End, runCursor.KeepAnchor) editor.showRunCursor(runCursor) def _getCodeOfFile(self, editor): # Obtain source code text = editor.toPlainText() # Show what we execute self._showWhatToExecute(editor) # Get filename and return fname = editor.id() # editor._name or editor._filename return fname, text def _runFile(self, runMode, givenEditor=None): """ Run a file runMode is a tuple (asScript, mainFile) """ asScript, mainFile = runMode # Get editor and shell description = 'main file' if mainFile else 'file' if asScript: description += translate('menu', ' (as script)') shell, editor = self._getShellAndEditor(description, mainFile) if givenEditor: editor = givenEditor if not shell or not editor: return if asScript: # Go self._runScript(editor, shell) else: # Obtain source code and fname fname, text = self._getCodeOfFile(editor) shell.executeCode(text, fname, changeDir=pyzo.config.settings.changeDirOnFileExec) def _runScript(self, editor, shell): # Obtain fname and try running err = "" if editor._filename: saveOk = pyzo.editors.saveFile(editor) # Always try to save if saveOk or not editor.document().isModified(): self._showWhatToExecute(editor) if shell._startup_info.get('ipython', '') == 'yes': # If we have a ipython shell we use %run -i instead # This works better when python autoreload is used d = os.path.normpath(os.path.normcase(os.path.dirname(editor._filename))) shell._ctrl_command.send('%%cd %s' % d) shell._ctrl_command.send('%%run -i %s\n' % editor._filename) else: shell.restart(editor._filename) else: err = translate("menu", "Could not save the file.") else: err = translate("menu", "Can only run scripts that are in the file system.") # If not success, notify if err: m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Could not run script.")) m.setText(err) m.setIcon(m.Warning) m.exec_() class ToolsMenu(Menu): def __init__(self, *args, **kwds): self._toolActions = [] Menu.__init__(self, *args, **kwds) def build(self): self.addItem(translate("menu", 'Reload tools ::: For people who develop tools.'), pyzo.icons.plugin_refresh, pyzo.toolManager.reloadTools) self.addSeparator() self.onToolInstanceChange() # Build initial menu pyzo.toolManager.toolInstanceChange.connect(self.onToolInstanceChange) def onToolInstanceChange(self): # Remove all exisiting tools from the menu for toolAction in self._toolActions: self.removeAction(toolAction) # Add all tools, with checkmarks for those that are active self._toolActions = [] for tool in pyzo.toolManager.getToolInfo(): action = self.addCheckItem(tool.name, pyzo.icons.plugin, tool.menuLauncher, selected=bool(tool.instance)) self._toolActions.append(action) class HelpMenu(Menu): def build(self): icons = pyzo.icons self.addUrlItem(translate("menu", "Pyzo website ::: Open the Pyzo website in your browser."), icons.help, "http://www.pyzo.org") self.addUrlItem(translate("menu", "Pyzo guide ::: Open the Pyzo guide in your browser."), icons.help, "http://guide.pyzo.org") self.addItem(translate("menu", "Pyzo wizard ::: Get started quickly."), icons.wand, self._showPyzoWizard) self.addSeparator() self.addUrlItem(translate("menu", "Ask a question ::: Need help?"), icons.comments, "http://community.pyzo.org") self.addUrlItem(translate("menu", "Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request?"), icons.error_add, "http://issues.pyzo.org") self.addItem(translate("menu", "Local documentation ::: Documentation on Python and the Scipy Stack."), icons.help, self._showPyzoDocs) self.addSeparator() #self.addItem(translate("menu", "View code license ::: Legal stuff."), # icons.script, lambda: pyzo.editors.loadFile(os.path.join(pyzo.pyzoDir,"license.txt"))) self.addItem(translate("menu", "Check for updates ::: Are you using the latest version?"), icons.application_go, self._checkUpdates) self.addItem(translate("menu", "About Pyzo ::: More information about Pyzo."), icons.information, self._aboutPyzo) def addUrlItem(self, name, icon, url): self.addItem(name, icon, lambda: webbrowser.open(url)) def _showPyzoWizard(self): from pyzo.util.pyzowizard import PyzoWizard w = PyzoWizard(self) w.show() # Use show() instead of exec_() so the user can interact with pyzo def _checkUpdates(self): """ Check whether a newer version of pyzo is available. """ # Get versions available url = 'https://api.github.com/repos/pyzo/pyzo/releases' releases = json.loads(urlopen(url).read()) versions = [] for release in releases: tag = release.get('tag_name', '') if tag.startswith('v'): version = tuple(int(i) for i in tag[1:].split('.')) versions.append(version) versions.sort() latest_version = '.'.join(str(i) for i in versions[-1]) if versions else '?' # Define message text = "Your version of Pyzo is: {}\n" text += "Latest available version is: {}\n\n" text = text.format(pyzo.__version__, latest_version) text += "Do you want to open the download page?\n" # Show message box m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Check for the latest version.")) m.setStandardButtons(m.Yes | m.Cancel) m.setDefaultButton(m.Cancel) m.setText(text) m.setIcon(m.Information) result = m.exec_() # Goto webpage if user chose to if result == m.Yes: webbrowser.open("http://www.pyzo.org/start.html") def _aboutPyzo(self): from pyzo.core.about import AboutDialog m = AboutDialog(self) m.exec_() def _showPyzoDocs(self): # Show widget with docs: self._assistant = PyzoAssistant() self._assistant.show() class AutocompMenu(Menu): """ Menu for the user to control autocompletion. """ def build(self): # Part for selecting mode modes = [translate('menu', 'No autocompletion'), translate('menu', 'Automatic popup'), translate('menu', 'Only show popup when pressing Tab')] for value, mode in enumerate(modes): self.addGroupItem(mode, None, self._setMode, value, group='mode') self.setCheckedOption('mode', pyzo.config.settings.autoComplete) self.addSeparator() # Part for accept key accept_keys = ['Tab', 'Enter', 'Tab, Enter', 'Tab, (, [', 'Tab, Enter, (, ['] prefix = translate("menu", "Accept autocompletion with:") for keys in accept_keys: self.addGroupItem(prefix + ' ' + keys, None, self._setAcceptKeys, keys, group="acceptkeys") self.setCheckedOption('acceptkeys', pyzo.config.settings.autoComplete_acceptKeys) self.addSeparator() # Booleans self.addCheckItem(translate("menu", 'Autocomplete keywords ::: The autocompletion list includes keywords.'), None, self._setCompleteKeywords, None, pyzo.config.settings.autoComplete_keywords) def _setAcceptKeys(self, autocompkeys): # Skip if setting is not changes if pyzo.config.settings.autoComplete_acceptKeys == autocompkeys: return # Save new setting pyzo.config.settings.autoComplete_acceptKeys = autocompkeys # Apply for e in pyzo.editors: e.setAutoCompletionAcceptKeysFromStr(autocompkeys) for s in pyzo.shells: s.setAutoCompletionAcceptKeysFromStr(autocompkeys) def _setMode(self, autocompmode): pyzo.config.settings.autoComplete = int(autocompmode) def _setCompleteKeywords(self, value): pyzo.config.settings.autoComplete_keywords = bool(value) class SettingsMenu(Menu): def build(self): icons = pyzo.icons # Create language menu from pyzo.util._locale import LANGUAGES, LANGUAGE_SYNONYMS # Update language setting if necessary cur = pyzo.config.settings.language pyzo.config.settings.language = LANGUAGE_SYNONYMS.get(cur, cur) # Create language menu t = translate("menu", "Select language ::: The language used by Pyzo.") self._languageMenu = GeneralOptionsMenu(self, t, self._selectLanguage) values = [key for key in sorted(LANGUAGES)] self._languageMenu.setOptions(values, values) self._languageMenu.setCheckedOption(None, pyzo.config.settings.language) self.addBoolSetting(translate("menu", 'Automatically indent ::: Indent when pressing enter after a colon.'), 'autoIndent', lambda state, key: [e.setAutoIndent(state) for e in pyzo.editors]) self.addBoolSetting(translate("menu", 'Enable calltips ::: Show calltips with function signatures.'), 'autoCallTip') self.addMenu(AutocompMenu(self, translate("menu", 'Autocompletion'))) self.addSeparator() self.addItem(translate("menu", 'Edit key mappings... ::: Edit the shortcuts for menu items.'), icons.keyboard, lambda: KeymappingDialog().exec_()) self.addItem(translate("menu", 'Edit syntax styles... ::: Change the coloring of your code.'), icons.style, self._editStyles) self.addMenu(self._languageMenu, icons.flag_green) self.addItem(translate("menu", 'Advanced settings... ::: Configure Pyzo even further.'), icons.cog, self._advancedSettings) def _editStyles(self): """ Edit the style file. """ text = """ Chosing or editing the syntax style is currently not available. We selected a style which we like a lot. It's based on the solarized theme (http://ethanschoonover.com/solarized) isn't it pretty? \r\r In case you really want to change the style, you can change the source code at:\r {} """.format(os.path.join(pyzo.pyzoDir, 'codeeditor', 'base.py')) m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Edit syntax styling")) m.setText(unwrapText(text)) m.setIcon(m.Information) m.setStandardButtons(m.Ok | m.Cancel) m.setDefaultButton(m.Ok) m.exec_() def _advancedSettings(self): """ How to edit the advanced settings. """ text = translate("menu", """ More settings are available via the logger-tool: \r\r - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings.\r - Call "pyzo.resetConfig()" to reset all settings.\r - Call "pyzo.resetConfig(True)" to reset all settings and state.\r \r\r Note that most settings require a restart for the change to take effect. """) m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Advanced settings")) m.setText(unwrapText(text)) m.setIcon(m.Information) m.exec_() def addBoolSetting(self, name, key, callback = None): def _callback(state, key): setattr(pyzo.config.settings, key, state) if callback is not None: callback(state, key) self.addCheckItem(name, None, _callback, key, getattr(pyzo.config.settings,key)) #Default value def _selectLanguage(self, languageName): # Skip if the same if pyzo.config.settings.language == languageName: return # Save new language pyzo.config.settings.language = languageName # Notify user text = translate('menu dialog', """ The language has been changed. Pyzo needs to restart for the change to take effect. """) m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Language changed")) m.setText(unwrapText(text)) m.setIcon(m.Information) m.exec_() ## Classes to enable editing the key mappings class KeyMapModel(QtCore.QAbstractItemModel): """ The model to view the structure of the menu and the shortcuts currently mapped. """ def __init__(self, *args): QtCore.QAbstractItemModel.__init__(self, *args) self._root = None def setRootMenu(self, menu): """ Call this after starting. """ self._root = menu def data(self, index, role): if not index.isValid() or role not in [0, 8]: return None # get menu or action item item = index.internalPointer() # get text and shortcuts key1, key2 = '', '' if isinstance(item, QtWidgets.QMenu): value = item.title() else: value = item.text() if not value: value = '-'*10 elif index.column()>0: key1, key2 = ' ', ' ' shortcuts = getShortcut(item) if shortcuts[0]: key1 = shortcuts[0] if shortcuts[1]: key2 = shortcuts[1] # translate to text for the user key1 = translateShortcutToOSNames(key1) key2 = translateShortcutToOSNames(key2) # obtain value value = [value,key1,key2, ''][index.column()] # return if role == 0: # display role return value elif role == 8: # 8: BackgroundRole if not value: return None elif index.column() == 1: return QtGui.QBrush(QtGui.QColor(200,220,240)) elif index.column() == 2: return QtGui.QBrush(QtGui.QColor(210,230,250)) else: return None else: return None def rowCount(self, parent): if parent.isValid(): menu = parent.internalPointer() return len(menu.actions()) else: return len(self._root.actions()) def columnCount(self, parent): return 4 def headerData(self, section, orientation, role): if role == 0:# and orientation==1: tmp = ['Menu action','Shortcut 1','Shortcut 2', ''] return tmp[section] def parent(self, index): if not index.isValid(): return QtCore.QModelIndex() item = index.internalPointer() pitem = item.parent() if pitem is self._root: return QtCore.QModelIndex() else: L = pitem.parent().actions() row = 0 if pitem in L: row = L.index(pitem) return self.createIndex(row, 0, pitem) def hasChildren(self, index): # no items have parents (except the root item) if index.row()<0: return True else: return isinstance(index.internalPointer(), QtWidgets.QMenu) def index(self, row, column, parent): # if not self.hasIndex(row, column, parent): # return QtCore.QModelIndex() # establish parent if not parent.isValid(): parentMenu = self._root else: parentMenu = parent.internalPointer() # produce index and make menu if the action represents a menu childAction = parentMenu.actions()[row] if childAction.menu(): childAction = childAction.menu() return self.createIndex(row, column, childAction) # This is the trick. The internal pointer is the way to establish # correspondence between ModelIndex and underlying data. # Key to string mappings k = QtCore.Qt keymap = {k.Key_Enter:'Enter', k.Key_Return:'Return', k.Key_Escape:'Escape', k.Key_Tab:'Tab', k.Key_Backspace:'Backspace', k.Key_Pause:'Pause', k.Key_Backtab: 'Tab', #Backtab is actually shift+tab k.Key_F1:'F1', k.Key_F2:'F2', k.Key_F3:'F3', k.Key_F4:'F4', k.Key_F5:'F5', k.Key_F6:'F6', k.Key_F7:'F7', k.Key_F8:'F8', k.Key_F9:'F9', k.Key_F10:'F10', k.Key_F11:'F11', k.Key_F12:'F12', k.Key_Space:'Space', k.Key_Delete:'Delete', k.Key_Insert:'Insert', k.Key_Home:'Home', k.Key_End:'End', k.Key_PageUp:'PageUp', k.Key_PageDown:'PageDown', k.Key_Left:'Left', k.Key_Up:'Up', k.Key_Right:'Right', k.Key_Down:'Down' } class KeyMapLineEdit(QtWidgets.QLineEdit): """ A modified version of a lineEdit object that catches the key event and displays "Ctrl" when control was pressed, and similarly for alt and shift, function keys and other keys. """ textUpdate = QtCore.Signal() def __init__(self, *args, **kwargs): QtWidgets.QLineEdit.__init__(self, *args, **kwargs) self.clear() # keep a list of native keys, so that we can capture for example # "shift+]". If we would use text(), we can only capture "shift+}" # which is not a valid shortcut. self._nativeKeys = {} # Override setText, text and clear, so as to be able to set shortcuts like # Ctrl+A, while the actually displayed value is an OS shortcut (e.g. on Mac # Cmd-symbol + A) def setText(self, text): QtWidgets.QLineEdit.setText(self, translateShortcutToOSNames(text)) self._shortcut = text def text(self): return self._shortcut def clear(self): QtWidgets.QLineEdit.setText(self, '') self._shortcut = '' def focusInEvent(self, event): #self.clear() QtWidgets.QLineEdit.focusInEvent(self, event) def event(self,event): # Override event handler to enable catching the Tab key # If the event is a KeyPress or KeyRelease, handle it with # self.keyPressEvent or keyReleaseEvent if event.type()==event.KeyPress: self.keyPressEvent(event) return True #Mark as handled if event.type()==event.KeyRelease: self.keyReleaseEvent(event) return True #Mark as handled #Default: handle events as usual return QtWidgets.QLineEdit.event(self,event) def keyPressEvent(self, event): # get key codes key = event.key() nativekey = event.nativeVirtualKey() # try to get text if nativekey < 128 and sys.platform != 'darwin': text = chr(nativekey).upper() elif key<128: text = chr(key).upper() else: text = '' # do we know this specic key or this native key? if key in keymap: text = keymap[key] elif nativekey in self._nativeKeys: text = self._nativeKeys[nativekey] # apply! if text: storeNativeKey, text0 = True, text if QtWidgets.qApp.keyboardModifiers() & k.AltModifier: text = 'Alt+' + text if QtWidgets.qApp.keyboardModifiers() & k.ShiftModifier: text = 'Shift+' + text storeNativeKey = False if QtWidgets.qApp.keyboardModifiers() & k.ControlModifier: text = 'Ctrl+' + text if QtWidgets.qApp.keyboardModifiers() & k.MetaModifier: text = 'Meta+' + text self.setText(text) if storeNativeKey and nativekey: # store native key if shift was not pressed. self._nativeKeys[nativekey] = text0 # notify listeners self.textUpdate.emit() class KeyMapEditDialog(QtWidgets.QDialog): """ The prompt that is shown when double clicking a keymap in the tree. It notifies the user when the entered shortcut is already used elsewhere and applies the shortcut (removing it elsewhere if required) when the apply button is pressed. """ def __init__(self, *args): QtWidgets.QDialog.__init__(self, *args) # set title self.setWindowTitle(translate("menu dialog", 'Edit shortcut mapping')) # set size size = 400,140 offset = 5 size2 = size[0], size[1]+offset self.resize(*size2) self.setMaximumSize(*size2) self.setMinimumSize(*size2) self._label = QtWidgets.QLabel("", self) self._label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) self._label.resize(size[0]-20, 100) self._label.move(10,2) self._line = KeyMapLineEdit('', self) self._line.resize(size[0]-80, 20) self._line.move(10,90) self._clear = QtWidgets.QPushButton("Clear", self) self._clear.resize(50, 20) self._clear.move(size[0]-60,90) self._apply = QtWidgets.QPushButton("Apply", self) self._apply.resize(50, 20) self._apply.move(size[0]-120,120) self._cancel = QtWidgets.QPushButton("Cancel", self) self._cancel.resize(50, 20) self._cancel.move(size[0]-60,120) # callbacks self._line.textUpdate.connect(self.onEdit) self._clear.clicked.connect(self.onClear) self._apply.clicked.connect(self.onAccept) self._cancel.clicked.connect(self.close) # stuff to fill in later self._fullname = '' self._intro = '' self._isprimary = True def setFullName(self, fullname, isprimary): """ To be called right after initialization to let the user know what he's updating, and show the current shortcut for that in the line edit. """ # store self._isprimary = isprimary self._fullname = fullname # create intro to show, and store + show it tmp = fullname.replace('__',' -> ').replace('_', ' ') primSec = ['secondary', 'primary'][int(isprimary)] self._intro = "Set the {} shortcut for:\n{}".format(primSec,tmp) self._label.setText(self._intro) # set initial value if fullname in pyzo.config.shortcuts2: current = pyzo.config.shortcuts2[fullname] if ',' not in current: current += ',' current = current.split(',') self._line.setText( current[0] if isprimary else current[1] ) def onClear(self): self._line.clear() self._line.setFocus() def onEdit(self): """ Test if already in use. """ # init shortcut = self._line.text() if not shortcut: self._label.setText(self._intro) return for key in pyzo.config.shortcuts2: # get shortcut and test whether it corresponds with what's pressed shortcuts = getShortcut(key) primSec = '' if shortcuts[0].lower() == shortcut.lower(): primSec = 'primary' elif shortcuts[1].lower() == shortcut.lower(): primSec = 'secondary' # if a correspondence, let the user know if primSec and key != self._fullname: tmp = "Warning: shortcut already in use for:\n" tmp += key.replace('__',' -> ').replace('_', ' ') self._label.setText(self._intro + '\n\n' + tmp + '\n') break else: self._label.setText(self._intro) def onAccept(self): shortcut = self._line.text() # remove shortcut if present elsewhere keys = [key for key in pyzo.config.shortcuts2] # copy for key in keys: # get shortcut, test whether it corresponds with what's pressed shortcuts = getShortcut(key) tmp = list(shortcuts) needUpdate = False if shortcuts[0].lower() == shortcut.lower(): tmp[0] = '' needUpdate = True if shortcuts[1].lower() == shortcut.lower(): tmp[1] = '' needUpdate = True if needUpdate: tmp = ','.join(tmp) tmp = tmp.replace(' ','') if len(tmp)==1: del pyzo.config.shortcuts2[key] else: pyzo.config.shortcuts2[key] = tmp # insert shortcut if self._fullname: # get current and make list of size two if self._fullname in pyzo.config.shortcuts2: current = list(getShortcut(self._fullname)) else: current = ['', ''] # update the list current[int(not self._isprimary)] = shortcut pyzo.config.shortcuts2[self._fullname] = ','.join(current) # close self.close() class KeymappingDialog(QtWidgets.QDialog): """ The main keymap dialog, it has tabs corresponding with the different menus and each tab has a tree representing the structure of these menus. The current shortcuts are displayed. On double clicking on an item, the shortcut can be edited. """ def __init__(self, *args): QtWidgets.QDialog.__init__(self, *args) # set title self.setWindowTitle(translate("menu dialog", 'Shortcut mappings')) # set size size = 600,400 offset = 0 size2 = size[0], size[1]+offset self.resize(*size2) self.setMaximumSize(*size2) self.setMinimumSize(* size2) self.tab = CompactTabWidget(self, padding=(4,4,6,6)) self.tab.resize(*size) self.tab.move(0,offset) self.tab.setMovable(False) # fill tab self._models = [] self._trees = [] for menu in pyzo.main.menuBar()._menus: # create treeview and model model = KeyMapModel() model.setRootMenu(menu) tree = QtWidgets.QTreeView(self.tab) tree.setModel(model) # configure treeview tree.clicked.connect(self.onClickSelect) tree.doubleClicked.connect(self.onDoubleClick) tree.setColumnWidth(0,150) # append to lists self._models.append(model) self._trees.append(tree) self.tab.addTab(tree, menu.title()) self.tab.currentChanged.connect(self.onTabSelect) def closeEvent(self, event): # update key setting pyzo.keyMapper.keyMappingChanged.emit() event.accept() def onTabSelect(self): pass def onClickSelect(self, index): # should we show a prompt? if index.column(): self.popupItem(index.internalPointer(), index.column()) def onDoubleClick(self, index): if not index.column(): self.popupItem(index.internalPointer()) def popupItem(self, item, shortCutId=1): """ Popup the dialog to change the shortcut. """ if isinstance(item, QtWidgets.QAction) and item.text(): # create prompt dialog dlg = KeyMapEditDialog(self) dlg.setFullName( item.menuPath, shortCutId==1 ) # show it dlg.exec_() pyzo-4.4.3/pyzo/core/pyzoLogging.py0000666000000000000000000000715513123037260015475 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module logging Functionality for logging in pyzo. """ import sys, time import pyzo pyzo.status = None # todo: enable logging to a file? # Define prompts try: sys.ps1 except AttributeError: sys.ps1 = ">>> " try: sys.ps2 except AttributeError: sys.ps2 = "... " class DummyStd: """ For when std is not available. """ def __init__(self): self._closed = False def write(self, text): pass def encoding(self): return 'utf-8' @property def closed(self): return self._closed def close(self): self._closed = False def flush(self): pass original_print = print def print(*args, **kwargs): # Obtain time string t = time.localtime() preamble = "{:02g}-{:02g}-{:04g} {:02g}:{:02g}:{:02g}: " preamble = preamble.format( t.tm_mday, t.tm_mon, t.tm_year, t.tm_hour, t.tm_min, t.tm_sec) # Prepend to args and print args = [preamble] + list(args) original_print(*tuple(args),**kwargs) def splitConsole(stdoutFun=None, stderrFun=None): """ splitConsole(stdoutFun=None, stderrFun=None) Splits the stdout and stderr streams. On each call to their write methods, in addition to the original write method being called, will call the given functions. Returns the history of the console (combined stdout and stderr). Used by the logger shell. """ # Split stdout and stderr sys.stdout = OutputStreamSplitter(sys.stdout) sys.stderr = OutputStreamSplitter(sys.stderr) # Make them share their history sys.stderr._history = sys.stdout._history # Set defer functions if stdoutFun: sys.stdout._deferFunction = stdoutFun if stderrFun: sys.stderr._deferFunction = stderrFun # Return history return ''.join(sys.stdout._history) class OutputStreamSplitter: """ This class is used to replace stdout and stderr output streams. It defers the stream to the original and to a function that can be registered. Used by the logger shell. """ def __init__(self, fileObject): # Init, copy properties if it was already a splitter if isinstance(fileObject, OutputStreamSplitter): self._original = fileObject._original self._history = fileObject._history self._deferFunction = fileObject._deferFunction else: self._original = fileObject self._history = [] self._deferFunction = self.dummyDeferFunction # Replace original with a dummy if None if self._original is None: self._original = DummyStd() def dummyDeferFunction(self, text): pass def write(self, text): """ Write method. """ self._original.write(text) self._history.append(text) try: self._deferFunction(text) except Exception: pass # self._original.write('error writing to deferred stream') # Show in statusbar if pyzo.status and len(text)>1: pyzo.status.showMessage(text, 5000) def flush(self): return self._original.flush() @property def closed(self): return self._original.closed def close(self): return self._original.close() def encoding(self): return self._original.encoding() # Split now, with no defering splitConsole() pyzo-4.4.3/pyzo/core/shell.py0000666000000000000000000015745713126424141014310 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module shell Defines the shell to be used in pyzo. This is done in a few inheritance steps: - BaseShell inherits BaseTextCtrl and adds the typical shell behaviour. - PythonShell makes it specific to Python. This module also implements ways to communicate with the shell and to run code in it. """ import sys, time import re import yoton import pyzo from pyzo.util import zon as ssdf # zon is ssdf-light from pyzo.util.qt import QtCore, QtGui, QtWidgets Qt = QtCore.Qt from pyzo.codeeditor.highlighter import Highlighter from pyzo.codeeditor import parsers from pyzo.core.baseTextCtrl import BaseTextCtrl from pyzo.core.pyzoLogging import print from pyzo.core.kernelbroker import KernelInfo, Kernelmanager from pyzo.core.menu import ShellContextMenu # Interval for polling messages. Timer for each kernel. I found # that this one does not affect performance much POLL_TIMER_INTERVAL = 30 # 30 ms 33Hz # Maximum number of lines in the shell MAXBLOCKCOUNT = pyzo.config.advanced.shellMaxLines # todo: we could make command shells to, with autocompletion and coloring... class YotonEmbedder(QtCore.QObject): """ Embed the Yoton event loop. """ def __init__(self): QtCore.QObject.__init__(self) yoton.app.embed_event_loop(self.postYotonEvent) def postYotonEvent(self): try: QtWidgets.qApp.postEvent(self, QtCore.QEvent(QtCore.QEvent.User)) except Exception: pass # If pyzo is shutting down, the app may be None def customEvent(self, event): """ This is what gets called by Qt. """ yoton.process_events(False) yotonEmbedder = YotonEmbedder() # Short constants for cursor movement A_KEEP = QtGui.QTextCursor.KeepAnchor A_MOVE = QtGui.QTextCursor.MoveAnchor # Instantiate a local kernel broker upon loading this module pyzo.localKernelManager = Kernelmanager(public=False) def finishKernelInfo(info, scriptFile=None): """ finishKernelInfo(info, scriptFile=None) Get a copy of the kernel info struct, with the scriptFile and the projectPath set. """ # Make a copy, we do not want to change the original info = ssdf.copy(info) # Set scriptFile (if '', the kernel will run in interactive mode) if scriptFile: info.scriptFile = scriptFile else: info.scriptFile = '' #If the file browser is active, and has the check box #'add path to Python path' set, set the PROJECTPATH variable fileBrowser = pyzo.toolManager.getTool('pyzofilebrowser') projectManager = pyzo.toolManager.getTool('pyzoprojectmanager') info.projectPath = '' if fileBrowser: info.projectPath = fileBrowser.getAddToPythonPath() if projectManager and not info.projectPath: # Only process project manager tool if file browser did not set a path. info.projectPath = projectManager.getAddToPythonPath() return info class ShellHighlighter(Highlighter): """ This highlighter implements highlighting for a shell; only the input lines are highlighted with this highlighter. """ def highlightBlock(self, line): # Make sure this is a Unicode Python string line = str(line) # Get previous state previousState = self.previousBlockState() # Get parser parser = None if hasattr(self._codeEditor, 'parser'): parser = self._codeEditor.parser() # Get function to get format nameToFormat = self._codeEditor.getStyleElementFormat # Last line? cursor1 = self._codeEditor._cursor1 cursor2 = self._codeEditor._cursor2 commandCursor = self._codeEditor._lastCommandCursor curBlock = self.currentBlock() # atLastPrompt, atCurrentPrompt = False, False if curBlock.position() == 0: pass elif curBlock.position() == commandCursor.block().position(): atLastPrompt = True elif curBlock.position() >= cursor1.block().position(): atCurrentPrompt = True if not atLastPrompt and not atCurrentPrompt: # Do not highlight anything but current and last prompts return if parser: if atCurrentPrompt: pos1, pos2 = cursor1.positionInBlock(), cursor2.positionInBlock() else: pos1, pos2 = 0, commandCursor.positionInBlock() # Check if we should *not* format this line. # This is the case for special "executing" text # A bit of a hack though ... is there a better way to signal this? specialinput = (not atCurrentPrompt) and line[pos2:].startswith('(executing ') self.setCurrentBlockState(0) if specialinput: pass # Let the kernel decide formatting else: for token in parser.parseLine(line, previousState): # Handle block state if isinstance(token, parsers.BlockState): self.setCurrentBlockState(token.state) else: # Get format try: format = nameToFormat(token.name).textCharFormat except KeyError: #print(repr(nameToFormat(token.name))) continue # Set format #format.setFontWeight(75) if token.start >= pos2: self.setFormat(token.start,token.end-token.start,format) # Set prompt to bold if atCurrentPrompt: format = QtGui.QTextCharFormat() format.setFontWeight(75) self.setFormat(pos1, pos2-pos1, format) #Get the indentation setting of the editors indentUsingSpaces = self._codeEditor.indentUsingSpaces() # Get user data bd = self.getCurrentBlockUserData() leadingWhitespace=line[:len(line)-len(line.lstrip())] if '\t' in leadingWhitespace and ' ' in leadingWhitespace: #Mixed whitespace bd.indentation = 0 format=QtGui.QTextCharFormat() format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline) format.setUnderlineColor(QtCore.Qt.red) format.setToolTip('Mixed tabs and spaces') self.setFormat(0,len(leadingWhitespace),format) elif ('\t' in leadingWhitespace and indentUsingSpaces) or \ (' ' in leadingWhitespace and not indentUsingSpaces): #Whitespace differs from document setting bd.indentation = 0 format=QtGui.QTextCharFormat() format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline) format.setUnderlineColor(QtCore.Qt.blue) format.setToolTip('Whitespace differs from document setting') self.setFormat(0,len(leadingWhitespace),format) else: # Store info for indentation guides # amount of tabs or spaces bd.indentation = len(leadingWhitespace) class BaseShell(BaseTextCtrl): """ The BaseShell implements functionality to make a generic shell. """ def __init__(self, parent, **kwds): super().__init__(parent, wrap=True, showLineNumbers=False, showBreakPoints=False, highlightCurrentLine=False, parser='python', **kwds) # Use a special highlighter that only highlights the input. self._setHighlighter(ShellHighlighter) # No undo in shells self.setUndoRedoEnabled(False) # variables we need self._more = False # We use two cursors to keep track of where the prompt is # cursor1 is in front, and cursor2 is at the end of the prompt. # They can be in the same position. # Further, we store a cursor that selects the last given command, # so it can be styled. self._cursor1 = self.textCursor() self._cursor2 = self.textCursor() self._lastCommandCursor = self.textCursor() self._lastline_had_cr = False # When inserting/removing text at the edit line (thus also while typing) # keep cursor2 at its place. Only when text is written before # the prompt (i.e. in write), this flag is temporarily set to False. # Same for cursor1, because sometimes (when there is no prompt) it # is at the same position. self._cursor1.setKeepPositionOnInsert(True) self._cursor2.setKeepPositionOnInsert(True) # Similarly, we use the _lastCommandCursor cursor really for pointing. self._lastCommandCursor.setKeepPositionOnInsert(True) # Variables to keep track of the command history usage self._historyNeedle = None # None means none, "" means look in all self._historyStep = 0 # Set minimum width so 80 lines do fit in smallest font size self.setMinimumWidth(200) # Hard wrapping. QTextEdit allows hard wrapping at a specific column. # Unfortunately, QPlainTextEdit does not. self.setWordWrapMode(QtGui.QTextOption.WrapAnywhere) # Limit number of lines self.setMaximumBlockCount(MAXBLOCKCOUNT) # Keep track of position, so we can disable editing if the cursor # is before the prompt self.cursorPositionChanged.connect(self.onCursorPositionChanged) self.setFocusPolicy(Qt.TabFocus) # See remark at mousePressEvent ## Cursor stuff def onCursorPositionChanged(self): #If the end of the selection (or just the cursor if there is no selection) #is before the beginning of the line. make the document read-only cursor = self.textCursor() promptpos = self._cursor2.position() if cursor.position() < promptpos or cursor.anchor() < promptpos: self.setReadOnly(True) else: self.setReadOnly(False) def ensureCursorAtEditLine(self): """ If the text cursor is before the beginning of the edit line, move it to the end of the edit line """ cursor = self.textCursor() promptpos = self._cursor2.position() if cursor.position() < promptpos or cursor.anchor() < promptpos: cursor.movePosition(cursor.End, A_MOVE) # Move to end of document self.setTextCursor(cursor) self.onCursorPositionChanged() def mousePressEvent(self, event): """ - Disable right MB and middle MB (which pastes by default). - Focus policy If a user clicks this shell, while it has no focus, we do not want the cursor position to change (since generally the user clicks the shell to give it the focus). We do this by setting the focus-policy to Qt::TabFocus, and we give the widget its focus manually from the mousePressedEvent event handler """ if not self.hasFocus(): self.setFocus() return if event.button() != QtCore.Qt.MidButton: super().mousePressEvent(event) def contextMenuEvent(self, event): """ Do not show context menu. """ pass def mouseDoubleClickEvent(self, event): BaseTextCtrl.mouseDoubleClickEvent(self, event) self._handleClickOnFilename(event.pos()) def _handleClickOnFilename(self, mousepos): """ Check whether the text that is clicked is a filename and open the file in the editor. If a line number can also be detected, open the file at that line number. """ # Get cursor and its current pos cursor = self.cursorForPosition(mousepos) ocursor = QtGui.QTextCursor(cursor) # Make a copy to use below pos = cursor.positionInBlock() # Get line of text for the cursor cursor.movePosition(cursor.EndOfBlock, cursor.MoveAnchor) cursor.movePosition(cursor.StartOfBlock, cursor.KeepAnchor) line = cursor.selectedText() if len(line) > 1024: return # safety # Get the thing that is clicked, assuming it is delimited with quotes line = line.replace('\'', '"') before = line[:pos].split('"')[-1] after = line[pos:].split('"')[0] piece = before + after # Check if it looks like a filename, quit if it does not if len(piece) < 4: return elif not ('/' in piece or '\\' in piece): return # if sys.platform.startswith('win'): if piece[1] != ':': return else: if not piece.startswith('/'): return # filename = piece # Split in parts for getting line number line = line[pos+len(after):] line = line.replace(',', ' ') parts = [p for p in line.split(' ') if p] # Iterate over parts linenr = None for i, part in enumerate(parts): if part in ('line', 'linenr', 'lineno'): try: linenr = int(parts[i+1]) except IndexError: pass # no more parts except ValueError: pass # not an integer else: break # Try again IPython style # IPython shows a few lines with the active line indicated by an arrow if linenr is None: for i in range(4): cursor.movePosition(cursor.NextBlock, cursor.MoveAnchor) cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) line = cursor.selectedText() if len(line) > 1024: continue # safety if not line.startswith('-'): continue parts = line.split(' ', 2) if parts[0] in ('->', '-->', '--->', '---->', '----->'): try: linenr = int(parts[1].strip()) except IndexError: pass # too few parts except ValueError: pass # not an integer else: break # Select word here (in shell) cursor = ocursor cursor.movePosition(cursor.Left, cursor.MoveAnchor, len(before)) cursor.movePosition(cursor.Right, cursor.KeepAnchor, len(piece)) self.setTextCursor(cursor) # For syntax errors we have the offset thingy in the file name if '.py+' in filename[-9:]: filename, _, offset = filename.rpartition('+') if linenr is not None: try: linenr += int(offset) except ValueError: pass # Try opening the file (at the line number if we have one) result = pyzo.editors.loadFile(filename) if result and linenr is not None: editor = result._editor editor.gotoLine(linenr) cursor = editor.textCursor() cursor.movePosition(cursor.StartOfBlock) cursor.movePosition(cursor.EndOfBlock, cursor.KeepAnchor) editor.setTextCursor(cursor) ##Indentation: override code editor behaviour def indentSelection(self): pass def dedentSelection(self): pass ## Key handlers def keyPressEvent(self,event): if event.key() in [Qt.Key_Return, Qt.Key_Enter]: # First check if autocompletion triggered if self.potentiallyAutoComplete(event): return else: # Enter: execute line # Remove calltip and autocomp if shown self.autocompleteCancel() self.calltipCancel() # reset history needle self._historyNeedle = None # process self.processLine() return if event.key() == Qt.Key_Escape: # Escape clears command if not ( self.autocompleteActive() or self.calltipActive() ): self.clearCommand() if event.key() == Qt.Key_Home: # Home goes to the prompt. cursor = self.textCursor() if event.modifiers() & Qt.ShiftModifier: cursor.setPosition(self._cursor2.position(), A_KEEP) else: cursor.setPosition(self._cursor2.position(), A_MOVE) # self.setTextCursor(cursor) self.autocompleteCancel() return if event.key() == Qt.Key_Insert: # Don't toggle between insert mode and overwrite mode. return True #Ensure to not backspace / go left beyond the prompt if event.key() in [Qt.Key_Backspace, Qt.Key_Left]: self._historyNeedle = None if self.textCursor().position() == self._cursor2.position(): if event.key() == Qt.Key_Backspace: self.textCursor().removeSelectedText() return #Ignore the key, don't go beyond the prompt if event.key() in [Qt.Key_Up, Qt.Key_Down] and not \ self.autocompleteActive(): # needle if self._historyNeedle is None: # get partly-written-command # # Select text cursor = self.textCursor() cursor.setPosition(self._cursor2.position(), A_MOVE) cursor.movePosition(cursor.End, A_KEEP) # Update needle text self._historyNeedle = cursor.selectedText() self._historyStep = 0 #Browse through history if event.key() == Qt.Key_Up: self._historyStep +=1 else: # Key_Down self._historyStep -=1 if self._historyStep < 1: self._historyStep = 1 # find the command c = pyzo.command_history.find_starting_with(self._historyNeedle, self._historyStep) if c is None: # found nothing-> reset self._historyStep = 0 c = self._historyNeedle # Replace text cursor = self.textCursor() cursor.setPosition(self._cursor2.position(), A_MOVE) cursor.movePosition(cursor.End, A_KEEP) cursor.insertText(c) self.ensureCursorAtEditLine() return else: # Reset needle self._historyNeedle = None #if a 'normal' key is pressed, ensure the cursor is at the edit line if event.text(): self.ensureCursorAtEditLine() #Default behaviour: BaseTextCtrl BaseTextCtrl.keyPressEvent(self,event) ## Cut / Copy / Paste / Drag & Drop def cut(self): """ Reimplement cut to only copy if part of the selected text is not at the prompt. """ if self.isReadOnly(): return self.copy() else: return BaseTextCtrl.cut(self) #def copy(self): # no overload needed def paste(self): """ Reimplement paste to paste at the end of the edit line when the position is at the prompt. """ self.ensureCursorAtEditLine() # Paste normally return BaseTextCtrl.paste(self) def dragEnterEvent(self, event): """ We only support copying of the text """ if event.mimeData().hasText(): event.setDropAction(QtCore.Qt.CopyAction) event.accept() def dragMoveEvent(self, event): self.dragEnterEvent(event) def dropEvent(self, event): """ The shell supports only a single line but the text may contain multiple lines. We insert at the editLine only the first non-empty line of the text """ if event.mimeData().hasText(): text = event.mimeData().text() insertText = '' for line in text.splitlines(): if line.strip(): insertText = line break # Move the cursor to the position indicated by the drop location, but # ensure it is at the edit line self.setTextCursor(self.cursorForPosition(event.pos())) self.ensureCursorAtEditLine() # Now insert the text cursor = self.textCursor() cursor.insertText(insertText) self.setFocus() ## Basic commands to control the shell def clearScreen(self): """ Clear all the previous output from the screen. """ # Select from beginning of prompt to start of document self._cursor1.clearSelection() self._cursor1.movePosition(self._cursor1.Start, A_KEEP) # Keep anchor self._cursor1.removeSelectedText() # Wrap up self.ensureCursorAtEditLine() self.ensureCursorVisible() def deleteLines(self): """ Called from the menu option "delete lines", just execute self.clearCommand() """ self.clearCommand() def clearCommand(self): """ Clear the current command, move the cursor right behind the prompt, and ensure it's visible. """ # Select from prompt end to length and delete selected text. cursor = self.textCursor() cursor.setPosition(self._cursor2.position(), A_MOVE) cursor.movePosition(cursor.End, A_KEEP) cursor.removeSelectedText() # Wrap up self.ensureCursorAtEditLine() self.ensureCursorVisible() def _handleBackspaces_split(self, text): # while NOT a backspace at first position, or none found i = 9999999999999 while i>0: i = text.rfind('\b',0,i) if i>0 and text[i-1]!='\b': text = text[0:i-1] + text[i+1:] # Strip the backspaces at the start text2 = text.lstrip('\b') n = len(text) - len(text2) # Done return n, text2 def _handleBackspacesOnList(self, texts): """ _handleBackspacesOnList(texts) Handle backspaces on a list of messages. When printing progress, many messages will simply replace each-other, which means we can process them much more effectively than when they're combined in a list. """ # Init number of backspaces at the start N = 0 for i in range(len(texts)): # Remove backspaces in text and how many are left n, text = self._handleBackspaces_split(texts[i]) texts[i] = text # Use remaining backspaces to remove backspaces in earlier texts while n and i > 0: i -= 1 text = texts[i] if len(text) > n: texts[i] = text[:-n] n = 0 else: texts[i] = '' n -= len(text) N += n # Insert tabs for start if N: texts[0] = '\b'*N + texts[0] # Return with empy elements popped return [t for t in texts if t] def _handleBackspaces(self, text): """ Apply backspaces in the string itself and if there are backspaces left at the start of the text, remove the appropriate amount of characters from the text. Returns the new text. """ # take care of backspaces if '\b' in text: # Remove backspaces and get how many were at the beginning nb, text = self._handleBackspaces_split(text) if nb: # Select what we remove and delete that self._cursor1.clearSelection() self._cursor1.movePosition(self._cursor1.Left, A_KEEP, nb) self._cursor1.removeSelectedText() # Return result return text def _handleCarriageReturnOnList(self, texts): """ Discard messages that end with CR and that are not followed with LF. Also discard messages followed by a line that starts with CR. Assumes that each message is one line. """ for i in range(len(texts)-1): if ((texts[i].endswith('\r') and not texts[i+1].startswith('\n')) or (texts[i+1].startswith('\r') and not texts[i+1][1:].startswith('\n'))): texts[i] = '' return [t for t in texts if t] def _handleCarriageReturn(self, text): """ Removes the last line if it ended with CR, or if the current new message starts with CR. Returns the text. """ if 'logger' in self.__class__.__name__.lower(): return text # Remove last line if it ended with CR cursor = self._cursor1 if ((self._lastline_had_cr and not text.startswith('\n')) or (text.startswith('\r') and not text[1:].startswith('\n'))): cursor.movePosition(cursor.PreviousBlock, cursor.KeepAnchor, 1) cursor.removeSelectedText() # Is this new line ending in CR? self._lastline_had_cr = text.endswith('\r') return text def _splitLinesForPrinting(self, text): """ Given a text, split the text in lines. Lines that are extremely long are split in pieces of 80 characters to increase performance for wrapping. This is kind of a failsafe for when the user accidentally prints a bitmap or huge list. See issue 98. """ for line in text.splitlines(True): if len(line) > 1024: # about 12 lines of 80 chars parts = [line[i:i+80] for i in range(0, len(line), 80)] yield '\n'.join(parts) else: yield line def write(self, text, prompt=0, color=None): """ write(text, prompt=0, color=None) Write to the shell. Fauto-ind If prompt is 0 (default) the text is printed before the prompt. If prompt is 1, the text is printed after the prompt, the new prompt becomes null. If prompt is 2, the given text becomes the new prompt. The color of the text can also be specified (as a hex-string). """ # From The Qt docs: Note that a cursor always moves when text is # inserted before the current position of the cursor, and it always # keeps its position when text is inserted after the current position # of the cursor. # Make sure there's text and make sure its a string if not text: return if isinstance(text, bytes): text = text.decode('utf-8') # Prepare format format = QtGui.QTextCharFormat() if color: format.setForeground(QtGui.QColor(color)) #pos1, pos2 = self._cursor1.position(), self._cursor2.position() # Just in case, clear any selection of the cursors self._cursor1.clearSelection() self._cursor2.clearSelection() if prompt == 0: # Insert text behind prompt (normal streams) self._cursor1.setKeepPositionOnInsert(False) self._cursor2.setKeepPositionOnInsert(False) text = self._handleCarriageReturn(text) text = self._handleBackspaces(text) self._insertText(self._cursor1, text, format) elif prompt == 1: # Insert command text after prompt, prompt becomes null (input) self._lastCommandCursor.setPosition(self._cursor2.position()) self._cursor1.setKeepPositionOnInsert(False) self._cursor2.setKeepPositionOnInsert(False) self._insertText(self._cursor2, text, format) self._cursor1.setPosition(self._cursor2.position(), A_MOVE) elif prompt == 2 and text == '\b': # Remove prompt (used when closing the kernel) self._cursor1.setPosition(self._cursor2.position(), A_KEEP) self._cursor1.removeSelectedText() self._cursor2.setPosition(self._cursor1.position(), A_MOVE) elif prompt == 2: # Insert text after prompt, inserted text becomes new prompt self._cursor1.setPosition(self._cursor2.position(), A_MOVE) self._cursor1.setKeepPositionOnInsert(True) self._cursor2.setKeepPositionOnInsert(False) self._insertText(self._cursor1, text, format) # Reset cursor states for the user to type his/her commands self._cursor1.setKeepPositionOnInsert(True) self._cursor2.setKeepPositionOnInsert(True) # Make sure that cursor is visible (only when cursor is at edit line) if not self.isReadOnly(): self.ensureCursorVisible() # Scroll along with the text if lines are popped from the top elif self.blockCount() == MAXBLOCKCOUNT: n = text.count('\n') sb = self.verticalScrollBar() sb.setValue(sb.value()-n) def _insertText(self, cursor, text, format): """ Insert text at the given cursor, and with the given format. This function processes ANSI escape code for formatting and colorization: http://en.wikipedia.org/wiki/ANSI_escape_code """ # If necessary, make a new cursor that moves along. We insert # the text in pieces, so we need to move along with the text! if cursor.keepPositionOnInsert(): cursor = QtGui.QTextCursor(cursor) cursor.setKeepPositionOnInsert(False) # Init. We use the solarised color theme pattern = r'\x1b\[(.*?)m' #CLRS = ['#000', '#F00', '#0F0', '#FF0', '#00F', '#F0F', '#0FF', '#FFF'] CLRS = ['#657b83', '#dc322f', '#859900', '#b58900', '#268bd2', '#d33682', '#2aa198', '#eee8d5'] i0 = 0 for match in re.finditer(pattern, text): # Insert pending text with the current format # Also update indices i1, i2 = match.span() cursor.insertText(text[i0:i1], format) i0 = i2 # The formay that we are now going to parse should apply to # the text that follow it ... # Get parameters try: params = [int(i) for i in match.group(1).split(';')] except ValueError: params = [] if not params: params = [0] # Process for param in params: if param == 0: format = QtGui.QTextCharFormat() elif param == 1: format.setFontWeight(75) # Bold elif param == 2: format.setFontWeight(25) # Faint elif param == 3: format.setFontItalic(True) # Italic elif param == 4: format.setFontUnderline(True) # Underline # elif param == 22: format.setFontWeight(50) # Not bold or faint elif param == 23: format.setFontItalic(False) # Not italic elif param == 24: format.setFontUnderline(False) # Not underline # elif 30 <= param <= 37: # Set foreground color clr = CLRS[param-30] format.setForeground(QtGui.QColor(clr)) elif 40 <= param <= 47: pass # Cannot set background text in QPlainTextEdit # else: pass # Not supported else: # At the end, process the remaining text text = text[i0:] # Process very long text more efficiently. # Insert per line (very long lines are split in smaller ones) if len(text) > 1024: for line in self._splitLinesForPrinting(text): cursor.insertText(line, format) else: cursor.insertText(text, format) ## Executing stuff def processLine(self, line=None, execute=True): """ processLine(self, line=None, execute=True) Process the given line or the current line at the prompt if not given. Called when the user presses enter. If execute is False will not execute the command. This way a message can be written while other ways are used to process the command. """ # Can we do this? if self.isReadOnly() and not line: return if line: # remove trailing newline(s) command = line.rstrip('\n') else: # Select command cursor = self.textCursor() cursor.setPosition(self._cursor2.position(), A_MOVE) cursor.movePosition(cursor.End, A_KEEP) # Sample the text from the prompt and remove it command = cursor.selectedText().replace('\u2029', '\n') .rstrip('\n') cursor.removeSelectedText() # Auto-indent. Note: this is rather Python-specific command_s = command.lstrip() indent = ' ' * (len(command) - len(command_s)) if command.strip().endswith(':'): indent += ' ' elif not command_s: indent = '' if indent: cursor.insertText(indent) if command: # Remember the command in this global history pyzo.command_history.append(command) if execute: command = command.replace('\r\n', '\n') self.executeCommand(command+'\n') def executeCommand(self, command): """ Execute the given command. Should be overridden. """ # this is a stupid simulation version self.write("you executed: "+command+'\n') self.write(">>> ", prompt=2) class PythonShell(BaseShell): """ The PythonShell class implements the python part of the shell by connecting to a remote process that runs a Python interpreter. """ # Emits when the status string has changed or when receiving a new prompt stateChanged = QtCore.Signal(BaseShell) # Emits when the debug status is changed debugStateChanged = QtCore.Signal(BaseShell) def __init__(self, parent, info): BaseShell.__init__(self, parent) # Get standard info if not given. if info is None and pyzo.config.shellConfigs2: info = pyzo.config.shellConfigs2[0] if not info: info = KernelInfo(None) # Store info so we can reuse it on a restart self._info = info # For the editor to keep track of attempted imports self._importAttempts = [] # To keep track of the response for introspection self._currentCTO = None self._currentACO = None # Write buffer to store messages in for writing self._write_buffer = None # Create timer to keep polling any results # todo: Maybe use yoton events to process messages as they arrive. # I tried this briefly, but it seemd to be less efficient because # messages are not so much bach-processed anymore. We should decide # on either method. self._timer = QtCore.QTimer(self) self._timer.setInterval(POLL_TIMER_INTERVAL) # ms self._timer.setSingleShot(False) self._timer.timeout.connect(self.poll) self._timer.start() # Add context menu self._menu = ShellContextMenu(shell=self, parent=self) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(lambda p: self._menu.popup(self.mapToGlobal(p+QtCore.QPoint(0,3)))) # Keep track of breakpoints pyzo.editors.breakPointsChanged.connect(self.sendBreakPoints) # Start! self.resetVariables() self.connectToKernel(info) def resetVariables(self): """ Resets some variables. """ # Reset read state self.setReadOnly(False) # Variables to store state, python version, builtins and keywords self._state = '' self._debugState = {} self._version = "" self._builtins = [] self._keywords = [] self._startup_info = {} self._start_time = 0 # (re)set import attempts self._importAttempts[:] = [] # Update self.stateChanged.emit(self) def connectToKernel(self, info): """ connectToKernel() Create kernel and connect to it. """ # Create yoton context self._context = ct = yoton.Context() # Create stream channels self._strm_out = yoton.SubChannel(ct, 'strm-out') self._strm_err = yoton.SubChannel(ct, 'strm-err') self._strm_raw = yoton.SubChannel(ct, 'strm-raw') self._strm_echo = yoton.SubChannel(ct, 'strm-echo') self._strm_prompt = yoton.SubChannel(ct, 'strm-prompt') self._strm_broker = yoton.SubChannel(ct, 'strm-broker') self._strm_action = yoton.SubChannel(ct, 'strm-action', yoton.OBJECT) # Set channels to sync mode. This means that if the pyzo cannot process # the messages fast enough, the sending side is blocked for a short # while. We don't want our users to miss any messages. for c in [self._strm_out, self._strm_err]: c.set_sync_mode(True) # Create control channels self._ctrl_command = yoton.PubChannel(ct, 'ctrl-command') self._ctrl_code = yoton.PubChannel(ct, 'ctrl-code', yoton.OBJECT) self._ctrl_broker = yoton.PubChannel(ct, 'ctrl-broker') # Create status channels self._stat_interpreter = yoton.StateChannel(ct, 'stat-interpreter') self._stat_cd = yoton.StateChannel(ct, 'stat-cd') self._stat_debug = yoton.StateChannel(ct, 'stat-debug', yoton.OBJECT) self._stat_startup = yoton.StateChannel(ct, 'stat-startup', yoton.OBJECT) self._stat_breakpoints = yoton.StateChannel(ct, 'stat-breakpoints', yoton.OBJECT) self._stat_startup.received.bind(self._onReceivedStartupInfo) # Create introspection request channel self._request = yoton.ReqChannel(ct, 'reqp-introspect') # Connect! The broker will only start the kernel AFTER # we connect, so we do not miss out on anything. slot = pyzo.localKernelManager.createKernel(finishKernelInfo(info)) self._brokerConnection = ct.connect('localhost:%i'%slot) self._brokerConnection.closed.bind(self._onConnectionClose) # Force updating of breakpoints pyzo.editors.updateBreakPoints() # todo: see polling vs events # # Detect incoming messages # for c in [self._strm_out, self._strm_err, self._strm_raw, # self._strm_echo, self._strm_prompt, self._strm_broker, # self._strm_action, # self._stat_interpreter, self._stat_debug]: # c.received.bind(self.poll) def get_kernel_cd(self): """ Get current working dir of kernel. """ return self._stat_cd.recv() def _onReceivedStartupInfo(self, channel): startup_info = channel.recv() # Store the whole dict self._startup_info = startup_info # Store when we received this self._start_time = time.time() # Set version version = startup_info.get('version', None) if isinstance(version, tuple): version = [str(v) for v in version] self._version = '.'.join(version[:2]) # Set keywords L = startup_info.get('keywords', None) if isinstance(L, list): self._keywords = L # Set builtins L = startup_info.get('builtins', None) if isinstance(L, list): self._builtins = L # Notify self.stateChanged.emit(self) ## Introspection processing methods def processCallTip(self, cto): """ Processes a calltip request using a CallTipObject instance. """ # Try using buffer first (not if we're not the requester) if self is cto.textCtrl: if cto.tryUsingBuffer(): return # Clear buffer to prevent doing a second request # and store cto to see whether the response is still wanted. cto.setBuffer('') self._currentCTO = cto # Post request future = self._request.signature(cto.name) future.add_done_callback(self._processCallTip_response) future.cto = cto def _processCallTip_response(self, future): """ Process response of shell to show signature. """ # Process future if future.cancelled(): #print('Introspect cancelled') # No kernel return elif future.exception(): print('Introspect-exception: ', future.exception()) return else: response = future.result() cto = future.cto # First see if this is still the right editor (can also be a shell) editor1 = pyzo.editors.getCurrentEditor() editor2 = pyzo.shells.getCurrentShell() if cto.textCtrl not in [editor1, editor2]: # The editor or shell starting the autocomp is no longer active cto.textCtrl.autocompleteCancel() return # Invalid response if response is None: cto.textCtrl.autocompleteCancel() return # If still required, show tip, otherwise only store result if cto is self._currentCTO: cto.finish(response) else: cto.setBuffer(response) def processAutoComp(self, aco): """ Processes an autocomp request using an AutoCompObject instance. """ # Try using buffer first (not if we're not the requester) if self is aco.textCtrl: if aco.tryUsingBuffer(): return # Include builtins and keywords? if not aco.name: aco.addNames(self._builtins) if pyzo.config.settings.autoComplete_keywords: aco.addNames(self._keywords) # Set buffer to prevent doing a second request # and store aco to see whether the response is still wanted. aco.setBuffer() self._currentACO = aco # Post request future = self._request.dir(aco.name) future.add_done_callback(self._processAutoComp_response) future.aco = aco def _processAutoComp_response(self, future): """ Process the response of the shell for the auto completion. """ # Process future if future.cancelled(): #print('Introspect cancelled') # No living kernel return elif future.exception(): print('Introspect-exception: ', future.exception()) return else: response = future.result() aco = future.aco # First see if this is still the right editor (can also be a shell) editor1 = pyzo.editors.getCurrentEditor() editor2 = pyzo.shells.getCurrentShell() if aco.textCtrl not in [editor1, editor2]: # The editor or shell starting the autocomp is no longer active aco.textCtrl.autocompleteCancel() return # Add result to the list foundNames = [] if response is not None: foundNames = response aco.addNames(foundNames) # Process list if aco.name and not foundNames: # No names found for the requested name. This means # it does not exist, let's try to import it importNames, importLines = pyzo.parser.getFictiveImports(editor1) baseName = aco.nameInImportNames(importNames) if baseName: line = importLines[baseName].strip() if line not in self._importAttempts: # Do import self.processLine(line + ' # auto-import') self._importAttempts.append(line) # Wait a barely noticable time to increase the chances # That the import is complete when we repost the request. time.sleep(0.2) # To be sure, decrease the experiration date on the buffer aco.setBuffer(timeout=1) # Repost request future = self._request.signature(aco.name) future.add_done_callback(self._processAutoComp_response) future.aco = aco else: # If still required, show list, otherwise only store result if self._currentACO is aco: aco.finish() else: aco.setBuffer() ## Methods for executing code def executeCommand(self, text): """ executeCommand(text) Execute one-line command in the remote Python session. """ # Ensure edit line is selected (to reset scrolling to end) self.ensureCursorAtEditLine() self._ctrl_command.send(text) def executeCode(self, text, fname, lineno=None, cellName=None, changeDir=False): """ executeCode(text, fname, lineno, cellName=None) Execute (run) a large piece of code in the remote shell. text: the source code to execute filename: the file from which the source comes lineno: the first lineno of the text in the file, where 0 would be the first line of the file... The text (source code) is first pre-processed: - convert all line-endings to \n - remove all empty lines at the end - remove commented lines at the end - convert tabs to spaces - dedent so minimal indentation is zero """ # Convert tabs to spaces text = text.replace("\t"," "*4) # Make sure there is always *some* text if not text: text = ' ' if lineno is None: lineno = 0 cellName = fname # run all # Examine the text line by line... # - check for empty/commented lined at the end # - calculate minimal indentation lines = text.splitlines() lastLineOfCode = 0 minIndent = 99 for linenr in range(len(lines)): # Get line line = lines[linenr] # Check if empty (can be commented, but nothing more) tmp = line.split("#",1)[0] # get part before first # if tmp.count(" ") == len(tmp): continue # empty line, proceed else: lastLineOfCode = linenr # Calculate indentation tmp = line.lstrip(" ") indent = len(line) - len(tmp) if indent < minIndent: minIndent = indent # Copy all proper lines to a new list, # remove minimal indentation, but only if we then would only remove # spaces (in the case of commented lines) lines2 = [] for linenr in range(lastLineOfCode+1): line = lines[linenr] # Remove indentation, if line[:minIndent].count(" ") == minIndent: line = line[minIndent:] else: line = line.lstrip(" ") lines2.append( line ) # Ensure edit line is selected (to reset scrolling to end) self.ensureCursorAtEditLine() # Send message text = "\n".join(lines2) msg = {'source':text, 'fname':fname, 'lineno':lineno, 'cellName': cellName, 'changeDir': int(changeDir)} self._ctrl_code.send(msg) def sendBreakPoints(self, breaks): """ Send all breakpoints. """ # breaks is a dict of filenames to integers self._stat_breakpoints.send(breaks) ## The polling methods and terminating methods def poll(self, channel=None): """ poll() To keep the shell up-to-date. Call this periodically. """ if self._write_buffer: # There is still data in the buffer sub, M = self._write_buffer else: # Check what subchannel has the latest message pending sub = yoton.select_sub_channel(self._strm_out, self._strm_err, self._strm_echo, self._strm_raw, self._strm_broker, self._strm_prompt ) # Read messages from it if sub: M = sub.recv_selected() #M = [sub.recv()] # Slow version (for testing) # Optimization: handle backspaces on stack of messages if sub is self._strm_out: M = self._handleCarriageReturnOnList(M) M = self._handleBackspacesOnList(M) # New prompt? if sub is self._strm_prompt: self.stateChanged.emit(self) # Write all pending messages that are later than any other message if sub: # Select messages to process N = 256 M, buffer = M[:N], M[N:] # Buffer the rest if buffer: self._write_buffer = sub, buffer else: self._write_buffer = None # Get how to deal with prompt prompt = 0 if sub is self._strm_echo: prompt = 1 elif sub is self._strm_prompt: prompt = 2 # Get color color = None if sub is self._strm_broker: color = '#000' elif sub is self._strm_raw: color = '#888888' # Halfway elif sub is self._strm_err: color = '#F00' # Write self.write(''.join(M), prompt, color) # Do any actions? action = self._strm_action.recv(False) if action: if action == 'cls': self.clearScreen() elif action.startswith('open '): parts = action.split(' ') parts.pop(0) try: linenr = int(parts[0]) parts.pop(0) except ValueError: linenr = None fname = ' '.join(parts) editor = pyzo.editors.loadFile(fname) if editor and linenr: editor._editor.gotoLine(linenr) else: print('Unkown action: %s' % action) # ----- status # Do not update status when the kernel is not really up and running # self._version is set when the startup info is received if not self._version: return # Update status state = self._stat_interpreter.recv() if state != self._state: self._state = state self.stateChanged.emit(self) # Update debug status state = self._stat_debug.recv() if state != self._debugState: self._debugState = state self.debugStateChanged.emit(self) def interrupt(self): """ interrupt() Send a Keyboard interrupt signal to the main thread of the remote process. """ # Ensure edit line is selected (to reset scrolling to end) self.ensureCursorAtEditLine() self._ctrl_broker.send('INT') def restart(self, scriptFile=None): """ restart(scriptFile=None) Terminate the shell, after which it is restarted. Args can be a filename, to execute as a script as soon as the shell is back up. """ # Ensure edit line is selected (to reset scrolling to end) self.ensureCursorAtEditLine() # Get info info = finishKernelInfo(self._info, scriptFile) # Create message and send msg = 'RESTART\n' + ssdf.saves(info) self._ctrl_broker.send(msg) # Reset self.resetVariables() def terminate(self): """ terminate() Terminates the python process. It will first try gently, but if that does not work, the process shall be killed. To be notified of the termination, connect to the "terminated" signal of the shell. """ # Ensure edit line is selected (to reset scrolling to end) self.ensureCursorAtEditLine() self._ctrl_broker.send('TERM') def closeShell(self): # do not call it close(); that is a reserved method. """ closeShell() Very simple. This closes the shell. If possible, we will first tell the broker to terminate the kernel. The broker will be cleaned up if there are no clients connected and if there is no active kernel. In a multi-user environment, we should thus be able to close the shell without killing the kernel. But in a closed 1-to-1 environment we really want to prevent loose brokers and kernels dangling around. In both cases however, it is the responsibility of the broker to terminate the kernel, and the shell will simply assume that this will work :) """ # If we can, try to tell the broker to terminate the kernel if self._context and self._context.connection_count: self.terminate() self._context.flush() # Important, make sure the message is send! self._context.close() # Adios pyzo.shells.removeShell(self) def _onConnectionClose(self, c, why): """ To be called after disconnecting. In general, the broker will not close the connection, so it can be considered an error-state if this function is called. """ # Stop context if self._context: self._context.close() # New (empty prompt) self._cursor1.movePosition(self._cursor1.End, A_MOVE) self._cursor2.movePosition(self._cursor2.End, A_MOVE) self.write('\n\n') self.write('Lost connection with broker:\n') self.write(why) self.write('\n\n') # Set style to indicate dead-ness self.setReadOnly(True) # Goto end such that the closing message is visible cursor = self.textCursor() cursor.movePosition(cursor.End, A_MOVE) self.setTextCursor(cursor) self.ensureCursorVisible() if __name__ == '__main__': b = BaseShell(None) b.show() pyzo-4.4.3/pyzo/core/shellInfoDialog.py0000666000000000000000000005273313123037260016232 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module shellInfoDialog Implements shell configuration dialog. """ import os, sys from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa import pyzo from pyzo.core.pyzoLogging import print from pyzo.core.kernelbroker import KernelInfo from pyzo import translate ## Implement widgets that have a common interface class ShellInfoLineEdit(QtWidgets.QLineEdit): def setTheText(self, value): self.setText(value) def getTheText(self): return self.text() class ShellInfo_name(ShellInfoLineEdit): def __init__(self, *args, **kwargs): ShellInfoLineEdit.__init__(self, *args, **kwargs) self.editingFinished.connect(self.onValueChanged) t = translate('shell', 'name ::: The name of this configuration.') self.setPlaceholderText(t.tt) def setTheText(self, value): ShellInfoLineEdit.setTheText(self, value) self.onValueChanged() def onValueChanged(self): self.parent().parent().parent().setTabTitle(self.getTheText()) class ShellInfo_exe(QtWidgets.QComboBox): def __init__(self, *args): QtWidgets.QComboBox.__init__(self, *args) def _interpreterName(self, p): if p.is_conda: return '%s [v%s, conda]' % (p.path, p.version) else: return '%s [v%s]' % (p.path, p.version) def setTheText(self, value): # Init self.clear() self.setEditable(True) self.setInsertPolicy(self.InsertAtTop) # Get known interpreters from shellDialog (which are sorted by version) shellDialog = self while not isinstance(shellDialog, ShellInfoDialog): shellDialog = shellDialog.parent() interpreters = shellDialog.interpreters exes = [p.path for p in interpreters] # Hande current value if value in exes: value = self._interpreterName( interpreters[exes.index(value)] ) else: self.addItem(value) # Add all found interpreters for p in interpreters: self.addItem(self._interpreterName(p)) # Set current text self.setEditText(value) def getTheText(self): #return self.currentText().split('(')[0].rstrip() value = self.currentText() if value.endswith(']') and '[' in value: value = value.rsplit('[', 1)[0] return value.strip() class ShellInfo_ipython(QtWidgets.QCheckBox): def __init__(self, parent): QtWidgets.QCheckBox.__init__(self, parent) t = translate('shell', 'ipython ::: Use IPython shell if available.') self.setText(t.tt) self.setChecked(False) def setTheText(self, value): if value.lower() in ['', 'no', 'false']: # Also for empty string; default is False self.setChecked(False) else: self.setChecked(True) def getTheText(self): if self.isChecked(): return 'yes' else: return 'no' class ShellInfo_gui(QtWidgets.QComboBox): # For (backward) compatibility COMPAT = {'QT4':'PYQT4'} # GUI names GUIS = [ ('None', 'no GUI support'), ('Auto', 'Use what is available (recommended)'), ('PyQt5', 'GPL/commercial licensed wrapper to Qt'), ('PyQt4', 'GPL/commercial licensed wrapper to Qt'), ('PySide', 'LGPL licensed wrapper to Qt'), ('PySide2', 'LGPL licensed wrapper to Qt5'), ('Tornado', 'Tornado asynchronous networking library'), ('Tk', 'Tk widget toolkit'), ('WX', 'wxPython'), ('FLTK', 'The fast light toolkit'), ('GTK', 'GIMP Toolkit'), ] # GUI descriptions def setTheText(self, value): # Process value value = value.upper() value = self.COMPAT.get(value, value) # Set options ii = 0 self.clear() for i in range(len(self.GUIS)): gui, des = self.GUIS[i] if value == gui.upper(): ii = i self.addItem('%s - %s' % (gui, des)) # Set current text self.setCurrentIndex(ii) def getTheText(self): text = self.currentText().lower() return text.partition('-')[0].strip() class ShellinfoWithSystemDefault(QtWidgets.QVBoxLayout): DISABLE_SYSTEM_DEFAULT = sys.platform == 'darwin' SYSTEM_VALUE = '' def __init__(self, parent, widget): # Do not pass parent, because is a sublayout QtWidgets.QVBoxLayout.__init__(self) # Layout self.setSpacing(1) self.addWidget(widget) # Create checkbox widget if not self.DISABLE_SYSTEM_DEFAULT: t = translate('shell', 'Use system default') self._check = QtWidgets.QCheckBox(t, parent) self._check.stateChanged.connect(self.onCheckChanged) self.addWidget(self._check) # The actual value of this shell config attribute self._value = '' # A buffered version, so that clicking the text box does not # remove the value at once self._bufferedValue = '' def onEditChanged(self): if self.DISABLE_SYSTEM_DEFAULT or not self._check.isChecked(): self._value = self.getWidgetText() def onCheckChanged(self, state): if state: self._bufferedValue = self._value self.setTheText(self.SYSTEM_VALUE) else: self.setTheText(self._bufferedValue) def setTheText(self, value): if self.DISABLE_SYSTEM_DEFAULT: # Just set the value self._edit.setReadOnly(False) self.setWidgetText(value) elif value != self.SYSTEM_VALUE: # Value given, enable edit self._check.setChecked(False) self._edit.setReadOnly(False) # Set the text self.setWidgetText(value) else: # Use system default, disable edit widget self._check.setChecked(True) self._edit.setReadOnly(True) # Set text using system environment self.setWidgetText(None) # Store value self._value = value def getTheText(self): return self._value class ShellInfo_pythonPath(ShellinfoWithSystemDefault): SYSTEM_VALUE = '$PYTHONPATH' def __init__(self, parent): # Create sub-widget self._edit = QtWidgets.QTextEdit(parent) self._edit.zoomOut(1) self._edit.setMaximumHeight(80) self._edit.setMinimumWidth(200) self._edit.textChanged.connect(self.onEditChanged) # Instantiate ShellinfoWithSystemDefault.__init__(self, parent, self._edit) def getWidgetText(self): return self._edit.toPlainText() def setWidgetText(self, value=None): if value is None: pp = os.environ.get('PYTHONPATH','') pp = pp.replace(os.pathsep, '\n').strip() value = '$PYTHONPATH:\n%s\n' % pp self._edit.setText(value) # class ShellInfo_startupScript(ShellinfoWithSystemDefault): # # SYSTEM_VALUE = '$PYTHONSTARTUP' # # def __init__(self, parent): # # # Create sub-widget # self._edit = QtWidgets.QLineEdit(parent) # self._edit.textEdited.connect(self.onEditChanged) # # # Instantiate # ShellinfoWithSystemDefault.__init__(self, parent, self._edit) # # # def getWidgetText(self): # return self._edit.text() # # # def setWidgetText(self, value=None): # if value is None: # pp = os.environ.get('PYTHONSTARTUP','').strip() # if pp: # value = '$PYTHONSTARTUP: "%s"' % pp # else: # value = '$PYTHONSTARTUP: None' # # self._edit.setText(value) class ShellInfo_startupScript(QtWidgets.QVBoxLayout): DISABLE_SYSTEM_DEFAULT = sys.platform == 'darwin' SYSTEM_VALUE = '$PYTHONSTARTUP' RUN_AFTER_GUI_TEXT = '# AFTER_GUI - remove to run the code BEFORE integrating the GUI\n' def __init__(self, parent): # Do not pass parent, because is a sublayout QtWidgets.QVBoxLayout.__init__(self) # Create sub-widget self._edit1 = QtWidgets.QLineEdit(parent) self._edit1.textEdited.connect(self.onEditChanged) if sys.platform.startswith('win'): self._edit1.setPlaceholderText('C:\\path\\to\\script.py') else: self._edit1.setPlaceholderText('/path/to/script.py') # self._edit2 = QtWidgets.QTextEdit(parent) self._edit2.zoomOut(1) self._edit2.setMaximumHeight(80) self._edit2.setMinimumWidth(200) self._edit2.textChanged.connect(self.onEditChanged) # Layout self.setSpacing(1) self.addWidget(self._edit1) self.addWidget(self._edit2) # Create radio widget for system default t = translate('shell', 'Use system default') self._radio_system = QtWidgets.QRadioButton(t, parent) self._radio_system.toggled.connect(self.onCheckChanged) self.addWidget(self._radio_system) if self.DISABLE_SYSTEM_DEFAULT: self._radio_system.hide() # Create radio widget for file t = translate('shell', 'File to run at startup') self._radio_file = QtWidgets.QRadioButton(t, parent) self._radio_file.toggled.connect(self.onCheckChanged) self.addWidget(self._radio_file) # Create radio widget for code t = translate('shell', 'Code to run at startup') self._radio_code = QtWidgets.QRadioButton(t, parent) self._radio_code.toggled.connect(self.onCheckChanged) self.addWidget(self._radio_code) # The actual value of this shell config attribute self._value = '' # A buffered version, so that clicking the text box does not # remove the value at once self._valueFile = '' self._valueCode = '\n' def onEditChanged(self): if self._radio_file.isChecked(): self._value = self._valueFile = self._edit1.text().strip() elif self._radio_code.isChecked(): # ensure newline! self._value = self._valueCode = self._edit2.toPlainText().strip() + '\n' def onCheckChanged(self, state): if self._radio_system.isChecked(): self.setWidgetText(self.SYSTEM_VALUE) elif self._radio_file.isChecked(): self.setWidgetText(self._valueFile) elif self._radio_code.isChecked(): self.setWidgetText(self._valueCode) def setTheText(self, value): self.setWidgetText(value, True) self._value = value def setWidgetText(self, value, init=False): self._value = value if value == self.SYSTEM_VALUE and not self.DISABLE_SYSTEM_DEFAULT: # System default if init: self._radio_system.setChecked(True) pp = os.environ.get('PYTHONSTARTUP','').strip() if pp: value = '$PYTHONSTARTUP: "%s"' % pp else: value = '$PYTHONSTARTUP: None' # self._edit1.setReadOnly(True) self._edit1.show() self._edit2.hide() self._edit1.setText(value) elif not '\n' in value: # File if init: self._radio_file.setChecked(True) self._edit1.setReadOnly(False) self._edit1.show() self._edit2.hide() self._edit1.setText(value) else: # Code if init: self._radio_code.setChecked(True) self._edit1.hide() self._edit2.show() if not value.strip(): value = self.RUN_AFTER_GUI_TEXT self._edit2.setText(value) def getTheText(self): return self._value class ShellInfo_startDir(ShellInfoLineEdit): def __init__(self, parent): ShellInfoLineEdit.__init__(self, parent) if sys.platform.startswith('win'): self.setPlaceholderText('C:\\path\\to\\your\\python\\modules') else: self.setPlaceholderText('/path/to/your/python/modules') class ShellInfo_argv(ShellInfoLineEdit): def __init__(self, parent): ShellInfoLineEdit.__init__(self, parent) self.setPlaceholderText('arg1 arg2 "arg with spaces"') class ShellInfo_environ(QtWidgets.QTextEdit): EXAMPLE = 'EXAMPLE_VAR1=value1\nPYZO_PROCESS_EVENTS_WHILE_DEBUGGING=1' def __init__(self, parent): QtWidgets.QTextEdit.__init__(self, parent) self.zoomOut(1) self.setText(self.EXAMPLE) def _cleanText(self, txt): return '\n'.join([line.strip() for line in txt.splitlines()]) def setTheText(self, value): value = self._cleanText(value) if value: self.setText(value) else: self.setText(self.EXAMPLE) def getTheText(self): value = self.toPlainText() value = self._cleanText(value) if value == self.EXAMPLE: return '' else: return value ## The dialog class and container with tabs class ShellInfoTab(QtWidgets.QScrollArea): INFO_KEYS = [ translate('shell', 'name ::: The name of this configuration.'), translate('shell', 'exe ::: The Python executable.'), translate('shell', 'ipython ::: Use IPython shell if available.'), translate('shell', 'gui ::: The GUI toolkit to integrate (for interactive plotting, etc.).'), translate('shell', 'pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.'), # noqa translate('shell', 'startupScript ::: The script to run at startup (not in script mode).'), translate('shell', 'startDir ::: The start directory (not in script mode).'), translate('shell', 'argv ::: The command line arguments (sys.argv).'), translate('shell', 'environ ::: Extra environment variables (os.environ).'), ] def __init__(self, parent): QtWidgets.QScrollArea.__init__(self, parent) # Init the scroll area self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.setWidgetResizable(True) self.setFrameShape(QtWidgets.QFrame.NoFrame) # Create widget and a layout self._content = QtWidgets.QWidget(parent) self._formLayout = QtWidgets.QFormLayout(self._content) # Collect classes of widgets to instantiate classes = [] for t in self.INFO_KEYS: className = 'ShellInfo_' + t.key cls = globals()[className] classes.append((t, cls)) # Instantiate all classes self._shellInfoWidgets = {} for t, cls in classes: # Instantiate and store instance = cls(self._content) self._shellInfoWidgets[t.key] = instance # Create label label = QtWidgets.QLabel(t, self._content) label.setToolTip(t.tt) # Add to layout self._formLayout.addRow(label, instance) # Add delete button t = translate('shell', 'Delete ::: Delete this shell configuration') label = QtWidgets.QLabel('', self._content) instance = QtWidgets.QPushButton(pyzo.icons.cancel, t, self._content) instance.setToolTip(t.tt) instance.setAutoDefault(False) instance.clicked.connect(self.parent().parent().onTabClose) deleteLayout = QtWidgets.QHBoxLayout() deleteLayout.addWidget(instance, 0) deleteLayout.addStretch(1) # Add to layout self._formLayout.addRow(label, deleteLayout) # Apply layout self._formLayout.setSpacing(15) self._content.setLayout(self._formLayout) self.setWidget(self._content) def setTabTitle(self, name): tabWidget = self.parent().parent() tabWidget.setTabText(tabWidget.indexOf(self), name) def setInfo(self, info=None): """ Set the shell info struct, and use it to update the widgets. Not via init, because this function also sets the tab name. """ # If info not given, use default as specified by the KernelInfo struct if info is None: info = KernelInfo() # Name n = self.parent().parent().count() if n > 1: info.name = "Shell config %i" % n # Store info self._info = info # Set widget values according to info try: for key in info: widget = self._shellInfoWidgets.get(key, None) if widget is not None: widget.setTheText(info[key]) except Exception as why: print("Error setting info in shell config:", why) print(info) def getInfo(self): info = self._info # Set struct values according to widgets try: for key, widget in self._shellInfoWidgets.items(): info[key] = widget.getTheText() except Exception as why: print("Error getting info in shell config:", why) print(info) # Return the original (but modified) ssdf Dict object return info class ShellInfoDialog(QtWidgets.QDialog): """ Dialog to edit the shell configurations. """ def __init__(self, *args): QtWidgets.QDialog.__init__(self, *args) self.setModal(True) # Set title self.setWindowTitle(pyzo.translate('shell', 'Shell configurations')) # Create tab widget self._tabs = QtWidgets.QTabWidget(self) #self._tabs = CompactTabWidget(self, padding=(4,4,5,5)) #self._tabs.setDocumentMode(False) self._tabs.setMovable(True) # Get known interpreters (sorted them by version) # Do this here so we only need to do it once ... from pyzo.util.interpreters import get_interpreters self.interpreters = list(reversed(get_interpreters('2.4'))) # Introduce an entry if there's none if not pyzo.config.shellConfigs2: w = ShellInfoTab(self._tabs) self._tabs.addTab(w, '---') w.setInfo() # Fill tabs for item in pyzo.config.shellConfigs2: w = ShellInfoTab(self._tabs) self._tabs.addTab(w, '---') w.setInfo(item) # Enable making new tabs and closing tabs self._add = QtWidgets.QToolButton(self) self._tabs.setCornerWidget(self._add) self._add.clicked.connect(self.onAdd) self._add.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self._add.setIcon(pyzo.icons.add) self._add.setText(translate('shell', 'Add config')) # #self._tabs.setTabsClosable(True) self._tabs.tabCloseRequested.connect(self.onTabClose) # Create buttons cancelBut = QtWidgets.QPushButton("Cancel", self) okBut = QtWidgets.QPushButton("Done", self) cancelBut.clicked.connect(self.close) okBut.clicked.connect(self.applyAndClose) # Layout for buttons buttonLayout = QtWidgets.QHBoxLayout() buttonLayout.addStretch(1) buttonLayout.addWidget(cancelBut) buttonLayout.addSpacing(10) buttonLayout.addWidget(okBut) # Layout the widgets mainLayout = QtWidgets.QVBoxLayout(self) mainLayout.addSpacing(8) mainLayout.addWidget(self._tabs,0) mainLayout.addLayout(buttonLayout,0) self.setLayout(mainLayout) # Prevent resizing self.show() self.setMinimumSize(500, 400) self.resize(640, 500) #self.setMaximumHeight(500) def onAdd(self): # Create widget and add to tabs w = ShellInfoTab(self._tabs) self._tabs.addTab(w, '---') w.setInfo() # Select self._tabs.setCurrentWidget(w) w.setFocus() def onTabClose(self): index = self._tabs.currentIndex() self._tabs.removeTab( index ) def applyAndClose(self, event=None): self.apply() self.close() def apply(self): """ Apply changes for all tabs. """ # Clear pyzo.config.shellConfigs2 = [] # Set new versions. Note that although we recreate the list, # the list is filled with the orignal structs, so having a # reference to such a struct (as the shell has) will enable # you to keep track of any made changes. for i in range(self._tabs.count()): w = self._tabs.widget(i) pyzo.config.shellConfigs2.append( w.getInfo() ) pyzo-4.4.3/pyzo/core/shellStack.py0000666000000000000000000006221413124664336015272 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module shellStack Implements the stack of shells. Also implements the nifty debug button and a dialog to edit the shell configurations. """ import time import webbrowser from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa import pyzo from pyzo import translate from pyzo.core.shell import PythonShell from pyzo.core.pyzoLogging import print # noqa from pyzo.core.menu import ShellTabContextMenu, ShellButtonMenu from pyzo.core.icons import ShellIconMaker def shellTitle(shell, moreinfo=False): """ Given a shell instance, build the text title to represent it. """ # Get name nameText = shell._info.name # Build version text if shell._version: versionText = 'v{}'.format(shell._version) else: versionText = 'v?' # Build gui text guiText = shell._startup_info.get('gui') guiText = guiText or '' if guiText.lower() in ['none', '']: guiText = 'without gui' else: guiText = 'with ' + guiText + ' gui' # Build state text stateText = shell._state or '' # Build text for elapsed time elapsed = time.time() - shell._start_time hh = elapsed//3600 mm = (elapsed - hh*3600)//60 ss = elapsed - hh*3600 - mm*60 runtimeText = 'runtime: %i:%02i:%02i' % (hh, mm, ss) # Build text if not moreinfo: text = nameText else: text = "'%s' (%s %s) - %s, %s" % (nameText, versionText, guiText, stateText, runtimeText) # Done return text class ShellStackWidget(QtWidgets.QWidget): """ The shell stack widget provides a stack of shells. It wrapps a QStackedWidget that contains the shell objects. This stack is used as a reference to synchronize the shell selection with. We keep track of what is the current selected shell and apply updates if necessary. Therefore, changing the current shell in the stack should be enough to invoke a full update. """ # When the current shell changes. currentShellChanged = QtCore.Signal() # When the current shells state (or debug state) changes, # or when a new prompt is received. # Also fired when the current shell changes. currentShellStateChanged = QtCore.Signal() def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) # create toolbar self._toolbar = QtWidgets.QToolBar(self) self._toolbar.setMaximumHeight(26) self._toolbar.setIconSize(QtCore.QSize(16,16)) # create stack self._stack = QtWidgets.QStackedWidget(self) # Populate toolbar self._shellButton = ShellControl(self._toolbar, self._stack) self._debugmode = 0 self._dbs = DebugStack(self._toolbar) # self._toolbar.addWidget(self._shellButton) self._toolbar.addSeparator() # self._toolbar.addWidget(self._dbc) -> delayed, see addContextMenu() self._condahelp = CondaHelper(self) # widget layout layout = QtWidgets.QVBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._toolbar) layout.addWidget(self._stack, 0) layout.addWidget(self._condahelp, 0) self.setLayout(layout) # make callbacks self._stack.currentChanged.connect(self.onCurrentChanged) self.showCondaHelper() def __iter__(self): i = 0 while i < self._stack.count(): w = self._stack.widget(i) i += 1 yield w def showCondaHelper(self, show=True): self._condahelp.setVisible(show) self._toolbar.setVisible(not show) self._stack.setVisible(not show) if show: self._condahelp.detect() def addShell(self, shellInfo=None): """ addShell() Add a shell to the widget. """ # Create shell and add to stack shell = PythonShell(self, shellInfo) self._stack.addWidget(shell) # Bind to signals shell.stateChanged.connect(self.onShellStateChange) shell.debugStateChanged.connect(self.onShellDebugStateChange) # Select it and focus on it (invokes onCurrentChanged) self._stack.setCurrentWidget(shell) shell.setFocus() return shell def removeShell(self, shell): """ removeShell() Remove an existing shell from the widget """ self._stack.removeWidget(shell) def onCurrentChanged(self, index): """ When another shell is selected, update some things. """ # Get current shell = self.getCurrentShell() # Call functions self.onShellStateChange(shell) self.onShellDebugStateChange(shell) # Emit Signal self.currentShellChanged.emit() def onShellStateChange(self, shell): """ Called when the shell state changes, and is called by onCurrentChanged. Sets the mainwindow's icon if busy. """ # Keep shell button and its menu up-to-date self._shellButton.updateShellMenu(shell) if shell is self.getCurrentShell(): # can be None # Update application icon if shell and shell._state in ['Busy']: pyzo.main.setWindowIcon(pyzo.iconRunning) else: pyzo.main.setWindowIcon(pyzo.icon) # Send signal self.currentShellStateChanged.emit() def onShellDebugStateChange(self, shell): """ Called when the shell debug state changes, and is called by onCurrentChanged. Sets the debug button. """ if shell is self.getCurrentShell(): # Update debug info if shell and shell._debugState: info = shell._debugState self._debugmode = info['debugmode'] for action in self._debugActions: action.setEnabled(self._debugmode==2) self._debugActions[-1].setEnabled(self._debugmode>0) # Stop self._dbs.setTrace(shell._debugState) else: for action in self._debugActions: action.setEnabled(False) self._debugmode = 0 self._dbs.setTrace(None) # Send signal self.currentShellStateChanged.emit() def getCurrentShell(self): """ getCurrentShell() Get the currently active shell. """ w = None if self._stack.count(): w = self._stack.currentWidget() if not w: return None else: return w def getShells(self): """ Get all shell in stack as list """ shells = [] for i in range(self._stack.count()): shell = self.getShellAt(i) if shell is not None: shells.append(shell) return shells def getShellAt(self, i): return """ Get shell at current tab index """ return self._stack.widget(i) def addContextMenu(self): # A bit awkward... but the ShellMenu needs the ShellStack, so it # can only be initialized *after* the shellstack is created ... # Give shell tool button a menu self._shellButton.setMenu(ShellButtonMenu(self, 'Shell button menu')) self._shellButton.menu().aboutToShow.connect(self._shellButton._elapsedTimesTimer.start) # Also give it a context menu self._shellButton.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._shellButton.customContextMenuRequested.connect(self.contextMenuTriggered) # Add actions for action in pyzo.main.menuBar()._menumap['shell']._shellActions: action = self._toolbar.addAction(action) self._toolbar.addSeparator() # Add debug actions self._debugActions = [] for action in pyzo.main.menuBar()._menumap['shell']._shellDebugActions: self._debugActions.append(action) action = self._toolbar.addAction(action) # Delayed-add debug control buttons self._toolbar.addWidget(self._dbs) def contextMenuTriggered(self, p): """ Called when context menu is clicked """ # Get index of shell belonging to the tab shell = self.getCurrentShell() if shell: p = self._shellButton.mapToGlobal(self._shellButton.rect().bottomLeft()) ShellTabContextMenu(shell=shell, parent=self).popup(p) def onShellAction(self, action): shell = self.getCurrentShell() if shell: getattr(shell, action)() class ShellControl(QtWidgets.QToolButton): """ A button that can be used to select a shell and start a new shell. """ def __init__(self, parent, shellStack): QtWidgets.QToolButton.__init__(self, parent) # Store reference of shell stack self._shellStack = shellStack # Keep reference of actions corresponding to shells self._shellActions = [] # Set text and tooltip self.setText('Warming up ...') self.setToolTip(translate("shells", "Click to select shell.")) self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.setPopupMode(self.InstantPopup) # Set icon self._iconMaker = ShellIconMaker(self) self._iconMaker.updateIcon('busy') # Busy initializing # Create timer self._elapsedTimesTimer = QtCore.QTimer(self) self._elapsedTimesTimer.setInterval(200) self._elapsedTimesTimer.setSingleShot(False) self._elapsedTimesTimer.timeout.connect(self.onElapsedTimesTimer) def updateShellMenu(self, shellToUpdate=None): """ Update the shell menu. Ensure that there is a menu item for each shell. If shellToUpdate is given, updates the corresponding menu item. """ menu = self.menu() # Get shells now active currentShell = self._shellStack.currentWidget() shells = [self._shellStack.widget(i) for i in range(self._shellStack.count())] # Synchronize actions. Remove invalid actions for action in self._shellActions: # Check match with shells if action._shell in shells: shells.remove(action._shell) else: menu.removeAction(action) # Update checked state if action._shell is currentShell and currentShell: action.setChecked(True) else: action.setChecked(False) # Update text if necessary if action._shell is shellToUpdate: action.setText(shellTitle(shellToUpdate, True)) # Any items left in shells need a menu item # Dont give them an icon, or the icon is used as checkbox thingy for shell in shells: text = shellTitle(shell) action = menu.addItem(text, None, self._shellStack.setCurrentWidget, shell) action._shell = shell action.setCheckable(True) self._shellActions.append(action) # Is the shell being updated the current? if currentShell is shellToUpdate and currentShell is not None: self._iconMaker.updateIcon(currentShell._state) self.setText(shellTitle(currentShell)) elif currentShell is None: self._iconMaker.updateIcon('') self.setText('No shell selected') def onElapsedTimesTimer(self): # Automatically turn timer off is menu is hidden if not self.menu().isVisible(): self._elapsedTimesTimer.stop() return # Update text for each shell action for action in self._shellActions: action.setText(shellTitle(action._shell, True)) # todo: remove this? # class DebugControl(QtWidgets.QToolButton): # """ A button to control debugging. # """ # # def __init__(self, parent): # QtWidgets.QToolButton.__init__(self, parent) # # # Flag # self._debugmode = False # # # Set text # self.setText(translate('debug', 'Debug')) # self.setIcon(pyzo.icons.bug) # self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) # #self.setPopupMode(self.InstantPopup) # # # Bind to triggers # self.triggered.connect(self.onTriggered) # self.pressed.connect(self.onPressed) # self.buildMenu() # # # def buildMenu(self): # # # Count breakpoints # bpcount = 0 # for e in pyzo.editors: # bpcount += len(e.breakPoints()) # # # Prepare a text # clearallbps = translate('debug', 'Clear all {} breakpoints') # clearallbps = clearallbps.format(bpcount) # # # Set menu # menu = QtWidgets.QMenu(self) # self.setMenu(menu) # # for cmd, enabled, icon, text in [ # ('CLEAR', self._debugmode==0, pyzo.icons.bug_delete, clearallbps), # ('PM', self._debugmode==0, pyzo.icons.bug_error, # translate('debug', 'Postmortem: debug from last traceback')), # ('STOP', self._debugmode>0, pyzo.icons.debug_quit, # translate('debug', 'Stop debugging')), # # ('NEXT', self._debugmode==2, pyzo.icons.debug_next, # # translate('debug', 'Next: proceed until next line')), # # ('STEP', self._debugmode==2, pyzo.icons.debug_step, # # translate('debug', 'Step: proceed one step')), # # ('RETURN', self._debugmode==2, pyzo.icons.debug_return, # # translate('debug', 'Return: proceed until returns')), # # ('CONTINUE', self._debugmode==2, pyzo.icons.debug_continue, # # translate('debug', 'Continue: proceed to next breakpoint')), # ]: # if cmd is None: # menu.addSeparator() # else: # if icon is not None: # a = menu.addAction(icon, text) # else: # a = menu.addAction(text) # if hasattr(text, 'tt'): # a.setToolTip(text.tt) # a.cmd = cmd # a.setEnabled(enabled) # # # def onPressed(self, show=True): # self.buildMenu() # self.showMenu() # # # def onTriggered(self, action): # if action.cmd == 'PM': # # Initiate postmortem debugging # shell = pyzo.shells.getCurrentShell() # if shell: # shell.executeCommand('DB START\n') # # elif action.cmd == 'CLEAR': # # Clear all breakpoints # for e in pyzo.editors: # e.clearBreakPoints() # # else: # command = action.cmd.upper() # shell = pyzo.shells.getCurrentShell() # if shell: # shell.executeCommand('DB %s\n' % command) # # # def setTrace(self, info): # """ Determine whether we are in debug mode. # """ # if info is None: # self._debugmode = 0 # else: # self._debugmode = info['debugmode'] class DebugStack(QtWidgets.QToolButton): """ A button that shows the stack trace. """ def __init__(self, parent): QtWidgets.QToolButton.__init__(self, parent) # Set text and tooltip self._baseText = translate('debug', 'Stack') self.setText('%s:' % self._baseText) self.setIcon(pyzo.icons.text_align_justify) self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self.setPopupMode(self.InstantPopup) # Bind to triggers self.triggered.connect(self.onTriggered) def onTriggered(self, action): # Get shell shell = pyzo.shells.getCurrentShell() if not shell: return # Change stack index if not action._isCurrent: shell.executeCommand('DB FRAME {}\n'.format(action._index)) # Open file and select line if True: line = action.text().split(': ',1)[1] self.debugFocus(line) def setTrace(self, info): """ Set the stack trace. This method is called from the shell that receives the trace via its status channel directly from the interpreter. If trace is None, removes the trace """ # Get info if info: index, frames, debugmode = info['index'], info['frames'], info['debugmode'] else: index, frames = -1, [] if (not frames) or (debugmode==0): # Remove trace self.setMenu(None) self.setText('') #(self._baseText) self.setEnabled(False) pyzo.editors.setDebugLineIndicators(None) else: # Get the current frame theAction = None # Create menu and add __main__ menu = QtWidgets.QMenu(self) self.setMenu(menu) # Fill trace for i in range(len(frames)): thisIndex = i + 1 # Set text for action text = '{}: File "{}", line {}, in {}' text = text.format(thisIndex, *frames[i]) action = menu.addAction(text) action._index = thisIndex action._isCurrent = False if thisIndex == index: action._isCurrent = True theAction = action self.debugFocus(text.split(': ',1)[1]) # Load editor # Get debug indicators debugIndicators = [] for i in range(len(frames)): thisIndex = i + 1 filename, linenr, func = frames[i] debugIndicators.append((filename, linenr)) if thisIndex == index: break # Set debug indicators pyzo.editors.setDebugLineIndicators(*debugIndicators) # Highlight current item and set the button text if theAction: menu.setDefaultAction(theAction) #self.setText(theAction.text().ljust(20)) i = theAction._index text = "{} ({}/{}): ".format(self._baseText, i, len(frames)) self.setText(text) self.setEnabled(True) def debugFocus(self, lineFromDebugState): """ debugFocus(lineFromDebugState) Open the file and show the linenr of the given lineFromDebugState. """ # Get filenr and item try: tmp = lineFromDebugState.split(', in ')[0].split(', line ') filename = tmp[0][len('File '):].strip('"') linenr = int(tmp[1].strip()) except Exception: return 'Could not focus!' # Cannot open if filename == '': return 'Stack frame is .' elif filename.startswith(' '3': self._the_exe = conda_interpreters[0].path text = """Pyzo detected a conda environment in:
%s

You can use this environment (recommended), or manually specify an interpreter by setting the exe in the shell config.

Click one of the links above, or refresh. """ % (self._the_exe) elif conda_interpreters: text = """Pyzo detected a conda environment, but it is Python 2. We strongly recommend using Python 3 instead.

If you installed %s in a non-default location, or if you want to manually specify an interpreter, set the exe in the shell config.

Click one of the links above, or refresh. """ % link elif interpreters: text = """Pyzo detected a Python interpreter, but for scientific programming we recommend %s. If you want to manually specify the interpreter, set the exe in the shell config.

Click one of the links above, or refresh. """ % link else: text = """Pyzo did not detect any Python interpreters. We recomment installing %s for scientific programming (and click refresh when done).

If you installed miniconda in a non-default location, or if you want to manually specify the interpreter, set the exe in the shell config. """ % link link_style = 'font-weight: bold; color:#369; text-decoration:underline;' self._label.setText(text.replace('{text_title}

{text_version} {version}

{text_os} http://pyzo.org

""" class LogoWidget(QtWidgets.QFrame): def __init__(self, parent): QtWidgets.QFrame.__init__(self, parent) self.setMinimumSize(256, 256) self.setMaximumSize(256, 256) class LabelWidget(QtWidgets.QWidget): def __init__(self, parent, distro=None): QtWidgets.QWidget.__init__(self, parent) self.setMinimumSize(360, 256) # Ensure title fits nicely # Create label widget and costumize self._label = QtWidgets.QLabel(self) self._label.setTextFormat(QtCore.Qt.RichText) self._label.setOpenExternalLinks(True) self._label.setWordWrap(True) self._label.setMargin(20) # Set font size (absolute value) font = self._label.font() font.setPointSize(11) #(font.pointSize()+1) self._label.setFont(font) # Build text_title = translate('splash', 'This is Pyzo
the Python IDE for scientific computing') text_version = translate('splash', 'Version') text_os = translate('splash', 'Pyzo is open source software and freely available for everyone.') text = splash_text.format(version=pyzo.__version__, text_title=text_title, text_version=text_version, text_os=text_os) # Set text self._label.setText(text) layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) layout.addStretch(1) layout.addWidget(self._label, 0) layout.addStretch(1) class SplashWidget(QtWidgets.QWidget): """ A splash widget. """ def __init__(self, parent, **kwargs): QtWidgets.QWidget.__init__(self, parent) self._left = LogoWidget(self) self._right = LabelWidget(self, **kwargs) # Layout layout = QtWidgets.QHBoxLayout(self) self.setLayout(layout) #layout.setContentsMargins(0,0,0,0) layout.setSpacing(25) layout.addStretch(1) layout.addWidget(self._left, 0) layout.addWidget(self._right, 0) layout.addStretch(1) # Change background of main window to create a splash-screen-efefct iconImage = 'pyzologo256.png' iconImage = os.path.join(pyzo.pyzoDir, 'resources','appicons', iconImage) iconImage = iconImage.replace(os.path.sep, '/') # Fix for Windows self.setStyleSheet(STYLESHEET % iconImage) if __name__ == '__main__': w = SplashWidget(None, distro='some arbitrary distro') w.resize(800,600) w.show() pyzo-4.4.3/pyzo/core/__init__.py0000666000000000000000000000034613123037260014717 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Package core - the core of Pyzo. """ pyzo-4.4.3/pyzo/license.txt0000666000000000000000000000273112663163734014057 0ustar 00000000000000Pyzo is subject to the (new) BSD license: Copyright (C) 2016, the Pyzo development team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of its contributors nor their affiliation may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PYZO DEVELOPMENT TEAM BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pyzo-4.4.3/pyzo/pyzokernel/0000777000000000000000000000000013166630335014066 5ustar 00000000000000pyzo-4.4.3/pyzo/pyzokernel/debug.py0000666000000000000000000004223013123037260015516 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import os import sys import time import bdb import traceback class Debugger(bdb.Bdb): """ Debugger for the pyzo kernel, based on bdb. """ def __init__(self): self._wait_for_mainpyfile = False # from pdb, do we need this? bdb.Bdb.__init__(self) self._debugmode = 0 # 0: no debug, 1: postmortem, 2: full debug self._files_with_offset = [] def clear_all_breaks(self): bdb.Bdb.clear_all_breaks(self) self._files_with_offset = [] def trace_dispatch(self, frame, event, arg): # Overload to deal with offset in filenames # (cells or lines being executed) ori_filename = frame.f_code.co_filename if '+' in ori_filename and ori_filename not in self._files_with_offset: clean_filename, offset = ori_filename.rsplit('+', 1) try: offset = int(offset) except Exception: offset = None if offset is not None: # This is a cell or selected lines being executed self._files_with_offset.append(ori_filename) if clean_filename.startswith('<'): self.fncache[ori_filename] = ori_filename for i in self.breaks.get(clean_filename, []): self.set_break(ori_filename, i-offset) return bdb.Bdb.trace_dispatch(self, frame, event, arg) def interaction(self, frame, traceback=None, pm=False): """ Enter an interaction-loop for debugging. No GUI events are processed here. We leave this event loop at some point, after which the conrol flow will proceed. This is called to enter debug-mode at a breakpoint, or to enter post-mortem debugging. """ interpreter = sys._pyzoInterpreter # Collect frames frames = [] while frame: if frame is self.botframe: break co_filename = frame.f_code.co_filename if 'pyzokernel' in co_filename: break # pyzo kernel if 'interactiveshell.py' in co_filename: break # IPython kernel frames.insert(0, frame) frame = frame.f_back # Tell interpreter our stack if frames: interpreter._dbFrames = frames interpreter._dbFrameIndex = len(interpreter._dbFrames) frame = interpreter._dbFrames[interpreter._dbFrameIndex-1] interpreter._dbFrameName = frame.f_code.co_name interpreter.locals = frame.f_locals interpreter.globals = frame.f_globals # Let the IDE know ( # "self._debugmode = 1 if pm else 2" does not work not on py2.4) if pm: self._debugmode = 1 else: self._debugmode = 2 self.writestatus() # Enter interact loop. We may hang in here for a while ... self._interacting = True while self._interacting: time.sleep(0.05) interpreter.process_commands() pe = os.getenv('PYZO_PROCESS_EVENTS_WHILE_DEBUGGING', '').lower() if pe in ('1', 'true', 'yes'): interpreter.guiApp.process_events() # Reset self._debugmode = 0 interpreter.locals = interpreter._main_locals interpreter.globals = None interpreter._dbFrames = [] self.writestatus() def stopinteraction(self): """ Stop the interaction loop. """ self._interacting = False def set_on(self): """ To turn debugging on right before executing code. """ # Reset and set bottom frame self.reset() self.botframe = sys._getframe().f_back # Don't stop except at breakpoints or when finished # We do: self._set_stopinfo(self.botframe, None, -1) from set_continue # But write it all out because py2.4 does not have _set_stopinfo self.stopframe = self.botframe self.returnframe = None self.quitting = False self.stoplineno = -1 # Set tracing or not if self.breaks: sys.settrace(self.trace_dispatch) else: sys.settrace(None) def message(self, msg): """ Alias for interpreter.write(), but appends a newline. Writes to stderr. """ sys._pyzoInterpreter.write(msg+'\n') def error(self, msg): """ method used in some code that we copied from pdb. """ raise self.message('*** '+msg) def writestatus(self): """ Write the debug status so the IDE can take action. """ interpreter = sys._pyzoInterpreter # Collect frames info frames = [] for f in interpreter._dbFrames: # Get fname and lineno, and correct if required fname, lineno = f.f_code.co_filename, f.f_lineno fname, lineno = interpreter.correctfilenameandlineno(fname, lineno) if not fname.startswith('<'): fname2 = os.path.abspath(fname) if os.path.isfile(fname2): fname = fname2 frames.append((fname, lineno, f.f_code.co_name)) # Build string #text = 'File "%s", line %i, in %s' % ( # fname, lineno, f.f_code.co_name) #frames.append(text) # Send info object state = { 'index': interpreter._dbFrameIndex, 'frames': frames, 'debugmode': self._debugmode} interpreter.context._stat_debug.send(state) ## Stuff that we need to overload # Overload set_break to also allow non-existing filenames like " len(interpreter._dbFrames): interpreter._dbFrameIndex = len(interpreter._dbFrames) # Set name and locals frame = interpreter._dbFrames[interpreter._dbFrameIndex-1] interpreter._dbFrameName = frame.f_code.co_name interpreter.locals = frame.f_locals interpreter.globals = frame.f_globals self.writestatus() def do_up(self, arg): """ Go one frame up the stack. """ interpreter = sys._pyzoInterpreter if not self._debugmode: self.message("Not in debug mode.") else: # Decrease frame index interpreter._dbFrameIndex -= 1 if interpreter._dbFrameIndex < 1: interpreter._dbFrameIndex = 1 # Set name and locals frame = interpreter._dbFrames[interpreter._dbFrameIndex-1] interpreter._dbFrameName = frame.f_code.co_name interpreter.locals = frame.f_locals interpreter.globals = frame.f_globals self.writestatus() def do_down(self, arg): """ Go one frame down the stack. """ interpreter = sys._pyzoInterpreter if not self._debugmode: self.message("Not in debug mode.") else: # Increase frame index interpreter._dbFrameIndex += 1 if interpreter._dbFrameIndex > len(interpreter._dbFrames): interpreter._dbFrameIndex = len(interpreter._dbFrames) # Set name and locals frame = interpreter._dbFrames[interpreter._dbFrameIndex-1] interpreter._dbFrameName = frame.f_code.co_name interpreter.locals = frame.f_locals interpreter.globals = frame.f_globals self.writestatus() def do_stop(self, arg): """ Stop debugging, terminate process execution. """ # Can be done both in postmortem and normal debugging if not self._debugmode: self.message("Not in debug mode.") else: self.set_quit() self.stopinteraction() def do_where(self, arg): """ Print the stack trace and indicate the current frame. """ interpreter = sys._pyzoInterpreter if not self._debugmode: self.message("Not in debug mode.") else: lines = [] for i in range(len(interpreter._dbFrames)): frameIndex = i+1 f = interpreter._dbFrames[i] # Get fname and lineno, and correct if required fname, lineno = f.f_code.co_filename, f.f_lineno fname, lineno = interpreter.correctfilenameandlineno(fname, lineno) # Build string text = 'File "%s", line %i, in %s' % ( fname, lineno, f.f_code.co_name) if frameIndex == interpreter._dbFrameIndex: lines.append('-> %i: %s'%(frameIndex, text)) else: lines.append(' %i: %s'%(frameIndex, text)) lines.append('') sys.stdout.write('\n'.join(lines)) def do_continue(self, arg): """ Continue the program execution. """ if self._debugmode == 0: self.message("Not in debug mode.") elif self._debugmode == 1: self.message("Cannot use 'continue' in postmortem debug mode.") else: self.set_continue() self.stopinteraction() def do_step(self, arg): """ Execute the current line, stop ASAP (step into). """ if self._debugmode == 0: self.message("Not in debug mode.") elif self._debugmode == 1: self.message("Cannot use 'step' in postmortem debug mode.") else: self.set_step() self.stopinteraction() def do_next(self, arg): """ Continue execution until the next line (step over). """ interpreter = sys._pyzoInterpreter if self._debugmode == 0: self.message("Not in debug mode.") elif self._debugmode == 1: self.message("Cannot use 'next' in postmortem debug mode.") else: frame = interpreter._dbFrames[-1] self.set_next(frame) self.stopinteraction() def do_return(self, arg): """ Continue execution until the current function returns (step out). """ interpreter = sys._pyzoInterpreter if self._debugmode == 0: self.message("Not in debug mode.") elif self._debugmode == 1: self.message("Cannot use 'return' in postmortem debug mode.") else: frame = interpreter._dbFrames[-1] self.set_return(frame) self.stopinteraction() def do_events(self, arg): """ Process GUI events for the integrated GUI toolkit. """ interpreter = sys._pyzoInterpreter interpreter.guiApp.process_events() pyzo-4.4.3/pyzo/pyzokernel/guiintegration.py0000666000000000000000000004454013123037260017466 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module to integrate GUI event loops in the Pyzo interpreter. This specifies classes that all have the same interface. Each class wraps one GUI toolkit. Support for PyQt4, WxPython, FLTK, GTK, TK. """ import sys import time from pyzokernel import printDirect # Warning message. mainloopWarning = """ Note: The GUI event loop is already running in the pyzo kernel. Be aware that the function to enter the main loop does not block. """.strip()+"\n" # Qt has its own message mainloopWarning_qt = """ Note on using QApplication.exec_(): The GUI event loop is already running in the pyzo kernel, and exec_() does not block. In most cases your app should run fine without the need for modifications. For clarity, this is what the pyzo kernel does: - Prevent deletion of objects in the local scope of functions leading to exec_() - Prevent system exit right after the exec_() call """.lstrip() # Print the main loop warning at most once _printed_warning = False def print_mainloop_warning(msg=None): global _printed_warning if not _printed_warning: _printed_warning = True msg = msg or mainloopWarning printDirect(msg) class App_base: """ Defines the interface. """ def process_events(self): pass def _keyboard_interrupt(self, signum=None, frame=None): interpreter = sys._pyzoInterpreter interpreter.write("\nKeyboardInterrupt\n") interpreter._resetbuffer() if interpreter.more: interpreter.more = 0 interpreter.newPrompt = True def run(self, repl_callback, sleeptime=0.01): """ Very simple mainloop. Subclasses can overload this to use the native event loop. Attempt to process GUI events at least every sleeptime seconds. """ _sleeptime = sleeptime # The toplevel while-loop is just to catch Keyboard interrupts # and then proceed. The inner while-loop is the actual event loop. while True: try: while True: time.sleep(_sleeptime) repl_callback() t0 = time.time() self.process_events() ptime = time.time() - t0 # Throttle if ptime > 0.001: _sleeptime = 0.0 else: _sleeptime = min(_sleeptime + 0.0001, sleeptime) except KeyboardInterrupt: self._keyboard_interrupt() except TypeError: # For some reason, when wx is integrated, keyboard interrupts # result in a TypeError. # I tried to find the source, but did not find it. If anyone # has an idea, please e-mail me! if '_wx' in self.__class__.__name__.lower(): self._keyboard_interrupt() def quit(self): raise SystemExit() class App_tk(App_base): """ Tries to import tkinter and returns a withdrawn tkinter root window. If tkinter is already imported or not available, this returns None. Modifies tkinter's mainloop with a dummy so when a module calls mainloop, it does not block. """ def __init__(self): # Try importing import sys if sys.version[0] == '3': import tkinter else: import Tkinter as tkinter # Replace mainloop. Note that a root object obtained with # tkinter.Tk() has a mainloop method, which will simply call # tkinter.mainloop(). def dummy_mainloop(*args,**kwargs): print_mainloop_warning() tkinter.Misc.mainloop = dummy_mainloop tkinter.mainloop = dummy_mainloop # Create tk "main window" that has a Tcl interpreter. # Withdraw so it's not shown. This object can be used to # process events for any other windows. r = tkinter.Tk() r.withdraw() # Store the app instance to process events self.app = r # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' tkinter._in_event_loop = 'Pyzo' def process_events(self): self.app.update() class App_fltk(App_base): """ Hijack fltk 1. This one is easy. Just call fl.wait(0.0) now and then. Note that both tk and fltk try to bind to PyOS_InputHook. Fltk will warn about not being able to and Tk does not, so we should just hijack (import) fltk first. The hook that they try to fetch is not required in pyzo, because the pyzo interpreter will keep all GUI backends updated when idle. """ def __init__(self): # Try importing import fltk as fl import types # Replace mainloop with a dummy def dummyrun(*args,**kwargs): print_mainloop_warning() fl.Fl.run = types.MethodType(dummyrun, fl.Fl) # Store the app instance to process events self.app = fl.Fl # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' fl._in_event_loop = 'Pyzo' def process_events(self): self.app.wait(0) class App_fltk2(App_base): """ Hijack fltk 2. """ def __init__(self): # Try importing import fltk2 as fl # Replace mainloop with a dummy def dummyrun(*args,**kwargs): print_mainloop_warning() fl.run = dummyrun # Return the app instance to process events self.app = fl # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' def process_events(self): # is this right? self.app.wait(0) class App_tornado(App_base): """ Hijack Tornado event loop. Tornado does have a function to process events, but it does not work when the event loop is already running. Therefore we don't enter the real Tornado event loop, but just poll it regularly. """ def __init__(self): # Try importing import tornado.ioloop # Get the "app" instance self.app = tornado.ioloop.IOLoop.instance() # Replace mainloop with a dummy def dummy_start(): print_mainloop_warning() sys._pyzoInterpreter.ignore_sys_exit = True self.app.add_callback(reset_sys_exit) def dummy_stop(): pass def reset_sys_exit(): sys._pyzoInterpreter.ignore_sys_exit = False def run_sync(func, timeout=None): self.app.start = self.app._original_start try: self.app._original_run_sync(func, timeout) finally: self.app.start = self.app._dummy_start # self.app._original_start = self.app.start self.app._dummy_start = dummy_start self.app.start = self.app._dummy_start # self.app._original_stop = self.app.stop self.app._dummy_stop = dummy_stop self.app.stop = self.app._dummy_stop # self.app._original_run_sync = self.app.run_sync self.app.run_sync = run_sync # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' self._warned_about_process_events = False def process_events(self): if not self._warned_about_process_events: print('Warning: cannot process events synchronously in Tornado') self._warned_about_process_events = True #self.app.run_sync(lambda x=None: None) def run(self, repl_callback, sleeptime=None): from tornado.ioloop import PeriodicCallback # Create timer self._timer = PeriodicCallback(repl_callback, 0.05*1000) self._timer.start() # Enter mainloop self.app._original_start() while True: try: self.app._original_start() except KeyboardInterrupt: self._keyboard_interrupt() self.app._original_stop() continue break def quit(self): self.app._original_stop() class App_qt(App_base): """ Common functionality for pyqt and pyside """ def __init__(self): import types # Try importing qt QtGui, QtCore = self.importCoreAndGui() self._QtGui, self._QtCore = QtGui, QtCore # Store the real application class if not hasattr(QtGui, 'real_QApplication'): QtGui.real_QApplication = QtGui.QApplication class QApplication_hijacked(QtGui.QApplication): """ QApplication_hijacked(*args, **kwargs) Hijacked QApplication class. This class has a __new__() method that always returns the global application instance, i.e. QtGui.qApp. The QtGui.qApp instance is an instance of the original QtGui.QApplication, but with its __init__() and exec_() methods replaced. You can subclass this class; the global application instance will be given the methods and attributes so it will behave like the subclass. """ def __new__(cls, *args, **kwargs): # Get the singleton application instance theApp = QApplication_hijacked.instance() # Instantiate an original QApplication instance if we need to if theApp is None: theApp = QtGui.real_QApplication(*args, **kwargs) QtGui.qApp = theApp # Add attributes of cls to the instance to make it # behave as if it were an instance of that class for key in dir(cls): # Skip all magic methods except __init__ if key.startswith('__') and key != '__init__': continue # Skip attributes that we already have val = getattr(cls, key) if hasattr(theApp.__class__, key): if hash(val) == hash(getattr(theApp.__class__, key)): continue # Make method? if hasattr(val, '__call__'): if hasattr(val, 'im_func'): val = val.im_func # Python 2.x val = types.MethodType(val, theApp.__class__) # Set attribute on app instance (not the class!) try: setattr(theApp, key, val) except Exception: pass # tough luck # Call init function (in case the user overloaded it) theApp.__init__(*args, **kwargs) # Return global app object (modified to the users needs) return theApp def __init__(self, *args, **kwargs): pass def exec_(self, *args, **kwargs): """ This function does nothing, except printing a warning message. The point is that a Qt App can crash quite hard if an object goes out of scope, and the error is not obvious. """ print_mainloop_warning(mainloopWarning_qt) # Store local namespaces (scopes) of any functions that # precede this call. It might have a widget or application # object that should not be deleted ... import inspect, __main__ for caller in inspect.stack()[1:]: frame, name = caller[0], caller[3] if name.startswith('<'): # most probably "" break else: __main__.__dict__[name+'_locals'] = frame.f_locals # Tell interpreter to ignore any system exits sys._pyzoInterpreter.ignore_sys_exit = True # But re-enable it as soon as *this event* is processed def reEnableSysExit(): sys._pyzoInterpreter.ignore_sys_exit = False self._reEnableSysExitTimer = timer = QtCore.QTimer() timer.singleShot(0, reEnableSysExit) def quit(self, *args, **kwargs): """ Do not quit if Qt app quits. """ pass # Instantiate application object self.app = QApplication_hijacked(['']) # Keep it alive even if all windows are closed self.app.setQuitOnLastWindowClosed(False) # Replace app class QtGui.QApplication = QApplication_hijacked # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' QtGui._in_event_loop = 'Pyzo' # Use sys.excepthook to catch keyboard interrupts that occur # in event handlers. We also want to call the curren hook self._original_excepthook = sys.excepthook sys.excepthook = self._excepthook def _excepthook(self, type, value, traceback): if issubclass(type, KeyboardInterrupt): self._keyboard_interrupt() elif self._original_excepthook is not None: return self._original_excepthook(type, value, traceback) def process_events(self): self.app.flush() self.app.processEvents() def run(self, repl_callback, sleeptime=None): # Create timer timer = self._timer = self._QtCore.QTimer() timer.setSingleShot(False) timer.setInterval(0.05*1000) # ms timer.timeout.connect(repl_callback) timer.start() # Enter Qt mainloop #self._QtGui.real_QApplication.exec_(self.app) self._QtGui.real_QApplication.exec_() def quit(self): # A nicer way to quit self._QtGui.real_QApplication.quit() class App_pyqt5(App_qt): """ Hijack the PyQt5 mainloop. """ def importCoreAndGui(self): # Try importing qt import PyQt5 # noqa from PyQt5 import QtGui, QtCore, QtWidgets # noqa return QtWidgets, QtCore # QApp sits on QtWidgets class App_pyqt4(App_qt): """ Hijack the PyQt4 mainloop. """ def importCoreAndGui(self): # Try importing qt import PyQt4 # noqa from PyQt4 import QtGui, QtCore return QtGui, QtCore class App_pyside2(App_qt): """ Hijack the PySide2 mainloop. """ def importCoreAndGui(self): # Try importing qt import PySide2 # noqa from PySide2 import QtGui, QtCore, QtWidgets # noqa return QtWidgets, QtCore # QApp sits on QtWidgets class App_pyside(App_qt): """ Hijack the PySide mainloop. """ def importCoreAndGui(self): # Try importing qt import PySide # noqa from PySide import QtGui, QtCore return QtGui, QtCore class App_wx(App_base): """ Hijack the wxWidgets mainloop. """ def __init__(self): # Try importing try: import wx except ImportError: # For very old versions of WX import wxPython as wx # Create dummy mainloop to replace original mainloop def dummy_mainloop(*args, **kw): print_mainloop_warning() # Depending on version, replace mainloop ver = wx.__version__ orig_mainloop = None if ver[:3] >= '2.5': if hasattr(wx, '_core_'): core = getattr(wx, '_core_') elif hasattr(wx, '_core'): core = getattr(wx, '_core') else: raise ImportError orig_mainloop = core.PyApp_MainLoop core.PyApp_MainLoop = dummy_mainloop elif ver[:3] == '2.4': orig_mainloop = wx.wxc.wxPyApp_MainLoop wx.wxc.wxPyApp_MainLoop = dummy_mainloop else: # Unable to find either wxPython version 2.4 or >= 2.5." raise ImportError self._orig_mainloop = orig_mainloop # Store package wx self.wx = wx # Get and store the app instance to process events app = wx.GetApp() if app is None: app = wx.App(False) self.app = app # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' wx._in_event_loop = 'Pyzo' def process_events(self): wx = self.wx # This bit is really needed old = wx.EventLoop.GetActive() eventLoop = wx.EventLoop() wx.EventLoop.SetActive(eventLoop) while eventLoop.Pending(): eventLoop.Dispatch() # Process and reset self.app.ProcessIdle() # otherwise frames do not close wx.EventLoop.SetActive(old) class App_gtk(App_base): """ Modifies pyGTK's mainloop with a dummy so user code does not block IPython. processing events is done using the module' main_iteration function. """ def __init__(self): # Try importing gtk import gtk # Replace mainloop with a dummy def dummy_mainloop(*args, **kwargs): print_mainloop_warning() gtk.mainloop = dummy_mainloop gtk.main = dummy_mainloop # Replace main_quit with a dummy too def dummy_quit(*args, **kwargs): pass gtk.main_quit = dummy_quit gtk.mainquit = dummy_quit # Make sure main_iteration exists even on older versions if not hasattr(gtk, 'main_iteration'): gtk.main_iteration = gtk.mainiteration # Store 'app object' self.app = gtk # Notify that we integrated the event loop self.app._in_event_loop = 'Pyzo' def process_events(self): gtk = self.app while gtk.events_pending(): gtk.main_iteration(False) pyzo-4.4.3/pyzo/pyzokernel/guisupport.py0000666000000000000000000001400213123037260016645 0ustar 00000000000000#!/usr/bin/env python # coding: utf-8 """ Support for creating GUI apps and starting event loops. IPython's GUI integration allows interative plotting and GUI usage in IPython session. IPython has two different types of GUI integration: 1. The terminal based IPython supports GUI event loops through Python's PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically whenever raw_input is waiting for a user to type code. We implement GUI support in the terminal by setting PyOS_InputHook to a function that iterates the event loop for a short while. It is important to note that in this situation, the real GUI event loop is NOT run in the normal manner, so you can't use the normal means to detect that it is running. 2. In the two process IPython kernel/frontend, the GUI event loop is run in the kernel. In this case, the event loop is run in the normal manner by calling the function or method of the GUI toolkit that starts the event loop. In addition to starting the GUI event loops in one of these two ways, IPython will *always* create an appropriate GUI application object when GUi integration is enabled. If you want your GUI apps to run in IPython you need to do two things: 1. Test to see if there is already an existing main application object. If there is, you should use it. If there is not an existing application object you should create one. 2. Test to see if the GUI event loop is running. If it is, you should not start it. If the event loop is not running you may start it. This module contains functions for each toolkit that perform these things in a consistent manner. Because of how PyOS_InputHook runs the event loop you cannot detect if the event loop is running using the traditional calls (such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is set These methods will return a false negative. That is, they will say the event loop is not running, when is actually is. To work around this limitation we proposed the following informal protocol: * Whenever someone starts the event loop, they *must* set the ``_in_event_loop`` attribute of the main application object to ``True``. This should be done regardless of how the event loop is actually run. * Whenever someone stops the event loop, they *must* set the ``_in_event_loop`` attribute of the main application object to ``False``. * If you want to see if the event loop is running, you *must* use ``hasattr`` to see if ``_in_event_loop`` attribute has been set. If it is set, you *must* use its value. If it has not been set, you can query the toolkit in the normal manner. * If you want GUI support and no one else has created an application or started the event loop you *must* do this. We don't want projects to attempt to defer these things to someone else if they themselves need it. The functions below implement this logic for each GUI toolkit. If you need to create custom application subclasses, you will likely have to modify this code for your own purposes. This code can be copied into your own project so you don't have to depend on IPython. """ #----------------------------------------------------------------------------- # Copyright (C) 2008-2010 The IPython Development Team # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Imports #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # wx #----------------------------------------------------------------------------- def get_app_wx(*args, **kwargs): """Create a new wx app or return an exiting one.""" import wx app = wx.GetApp() if app is None: if 'redirect' not in kwargs: kwargs['redirect'] = False # app = wx.PySimpleApp(*args, **kwargs) Deprecated! app = wx.App(*args, **kwargs) return app def is_event_loop_running_wx(app=None): """Is the wx event loop running.""" if app is None: app = get_app_wx() if hasattr(app, '_in_event_loop'): return app._in_event_loop else: return app.IsMainLoopRunning() def start_event_loop_wx(app=None): """Start the wx event loop in a consistent manner.""" if app is None: app = get_app_wx() if not is_event_loop_running_wx(app): app._in_event_loop = True app.MainLoop() app._in_event_loop = False else: app._in_event_loop = True #----------------------------------------------------------------------------- # qt4 #----------------------------------------------------------------------------- def get_app_qt4(*args, **kwargs): """Create a new qt4 app or return an existing one.""" from PyQt4 import QtWidgets app = QtWidgets.QApplication.instance() if app is None: if not args: args = ([''],) app = QtWidgets.QApplication(*args, **kwargs) return app def is_event_loop_running_qt4(app=None): """Is the qt4 event loop running.""" if app is None: app = get_app_qt4(['']) if hasattr(app, '_in_event_loop'): return app._in_event_loop else: # Does qt4 provide a other way to detect this? return False def start_event_loop_qt4(app=None): """Start the qt4 event loop in a consistent manner.""" if app is None: app = get_app_qt4(['']) if not is_event_loop_running_qt4(app): app._in_event_loop = True app.exec_() app._in_event_loop = False else: app._in_event_loop = True #----------------------------------------------------------------------------- # Tk #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # gtk #----------------------------------------------------------------------------- pyzo-4.4.3/pyzo/pyzokernel/interpreter.py0000666000000000000000000012742113156305504017006 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module pyzokernel.interpreter Implements the Pyzo interpreter. Notes on IPython ---------------- We integrate IPython via the IPython.core.interactiveshell.InteractiveShell. * The namespace is set to __main__ * We call its run_cell method to execute code * Debugging/breakpoints are "enabled using the pre_run_code_hook * Debugging occurs in our own debugger * GUI integration is all handled by pyzo * We need special prompts for IPython input """ import os import sys import time import logging import platform import struct import shlex from codeop import CommandCompiler import traceback import keyword import inspect # noqa - Must be in this namespace import bdb from distutils.version import LooseVersion as LV import yoton from pyzokernel import guiintegration, printDirect from pyzokernel.magic import Magician from pyzokernel.debug import Debugger # Init last traceback information sys.last_type = None sys.last_value = None sys.last_traceback = None # Set Python version and get some names PYTHON_VERSION = sys.version_info[0] if PYTHON_VERSION < 3: ustr = unicode # noqa bstr = str input = raw_input # noqa else: ustr = str bstr = bytes class PS1: """ Dynamic prompt for PS1. Show IPython prompt if available, and show current stack frame when debugging. """ def __init__(self, pyzo): self._pyzo = pyzo def __str__(self): if self._pyzo._dbFrames: # When debugging, show where we are, do not use IPython prompt preamble = '('+self._pyzo._dbFrameName+')' return '\n\x1b[0;32m%s>>>\x1b[0m ' % preamble elif self._pyzo._ipython: # IPython prompt return '\n\x1b[0;32mIn [\x1b[1;32m%i\x1b[0;32m]:\x1b[0m ' % ( self._pyzo._ipython.execution_count) #return 'In [%i]: ' % (self._ipython.execution_count) else: # Normal Python prompt return '\n\x1b[0;32m>>>\x1b[0m ' class PS2: """ Dynamic prompt for PS2. """ def __init__(self, pyzo): self._pyzo = pyzo def __str__(self): if self._pyzo._dbFrames: # When debugging, show where we are, do not use IPython prompt preamble = '('+self._pyzo._dbFrameName+')' return '\x1b[0;32m%s...\x1b[0m ' % preamble elif self._pyzo._ipython: # Dots ala IPython nspaces = len(str(self._pyzo._ipython.execution_count)) + 2 return '\x1b[0;32m%s...:\x1b[0m ' % (nspaces*' ') else: # Just dots return '\x1b[0;32m...\x1b[0m ' class PyzoInterpreter: """ PyzoInterpreter The pyzo interpreter is the part that makes the pyzo kernel interactive. It executes code, integrates the GUI toolkit, parses magic commands, etc. The pyzo interpreter has been designed to emulate the standard interactive Python console as much as possible, but with a lot of extra goodies. There is one instance of this class, stored at sys._pyzoInterpreter and at the __pyzo__ variable in the global namespace. The global instance has a couple of interesting attributes: * context: the yoton Context instance at the kernel (has all channels) * introspector: the introspector instance (a subclassed yoton.RepChannel) * magician: the object that handles the magic commands * guiApp: a wrapper for the integrated GUI application * sleeptime: the amount of time (in seconds) to sleep at each iteration """ # Simular working as code.InteractiveConsole. Some code was copied, but # the following things are changed: # - prompts are printed in the err stream, like the default interpreter does # - uses an asynchronous read using the yoton interface # - support for hijacking GUI toolkits # - can run large pieces of code # - support post mortem debugging # - support for magic commands def __init__(self, locals, filename=""): # Init variables for locals and globals (globals only for debugging) self.locals = locals self.globals = None # Store filename self._filename = filename # Store ref of locals that is our main self._main_locals = locals # Flag to ignore sys exit, to allow running some scripts # interactively, even if they call sys.exit. self.ignore_sys_exit = False # Information for debugging. If self._dbFrames, we're in debug mode # _dbFrameIndex starts from 1 self._dbFrames = [] self._dbFrameIndex = 0 self._dbFrameName = '' # Init datase to store source code that we execute self._codeCollection = ExecutedSourceCollection() # Init buffer to deal with multi-line command in the shell self._buffer = [] # Init sleep time. 0.001 result in 0% CPU usage at my laptop (Windows), # but 8% CPU usage at my older laptop (on Linux). self.sleeptime = 0.01 # 100 Hz # Create compiler if sys.platform.startswith('java'): import compiler self._compile = compiler.compile # or 'exec' does not work else: self._compile = CommandCompiler() # Instantiate magician and tracer self.magician = Magician() self.debugger = Debugger() # To keep track of whether to send a new prompt, and whether more # code is expected. self.more = 0 self.newPrompt = True # Code and script to run on first iteration self._codeToRunOnStartup = None self._scriptToRunOnStartup = None # Remove "THIS" directory from the PYTHONPATH # to prevent unwanted imports. Same for pyzokernel dir thisPath = os.getcwd() for p in [thisPath, os.path.join(thisPath,'pyzokernel')]: while p in sys.path: sys.path.remove(p) def run(self): """ Run (start the mainloop) Here we enter the main loop, which is provided by the guiApp. This event loop calls process_commands on a regular basis. We may also enter the debug intereaction loop, either from a request for post-mortem debugging, or *during* execution by means of a breakpoint. When in this debug-loop, the guiApp event loop lays still, but the debug-loop does call process-commands for user interaction. When the user wants to quit, SystemExit is raised (one way or another). This is detected in process_commands and the exception instance is stored in self._exitException. Then the debug-loop is stopped if necessary, and the guiApp is told to stop its event loop. And that brings us back here, where we exit using in order of preference: self._exitException, the exception with which the event loop was exited (if any), or a new exception. """ # Prepare self._prepare() self._exitException = None # Enter main try: self.guiApp.run(self.process_commands, self.sleeptime) except SystemExit: # Set self._exitException if it is not set yet type, value, tb = sys.exc_info() del tb if self._exitException is None: self._exitException = value # Exit if self._exitException is None: self._exitException = SystemExit() raise self._exitException def _prepare(self): """ Prepare for running the main loop. Here we do some initialization like obtaining the startup info, creating the GUI application wrapper, etc. """ # Reset debug status self.debugger.writestatus() # Get startup info (get a copy, or setting the new version wont trigger!) while self.context._stat_startup.recv() is None: time.sleep(0.02) self.startup_info = startup_info = self.context._stat_startup.recv().copy() # Set startup info (with additional info) if sys.platform.startswith('java'): import __builtin__ as builtins # Jython else: builtins = __builtins__ if not isinstance(builtins, dict): builtins = builtins.__dict__ startup_info['builtins'] = [builtin for builtin in builtins.keys()] startup_info['version'] = tuple(sys.version_info) startup_info['keywords'] = keyword.kwlist self.context._stat_startup.send(startup_info) # Prepare the Python environment self._prepare_environment(startup_info) # Run startup code (before loading GUI toolkit or IPython self._run_startup_code(startup_info) # Write Python banner (to stdout) thename = 'Python' if sys.version_info[0] == 2: thename = 'Legacy Python' if '__pypy__' in sys.builtin_module_names: thename = 'Pypy' if sys.platform.startswith('java'): thename = 'Jython' # Jython cannot do struct.calcsize("P") import java.lang real_plat = java.lang.System.getProperty("os.name").lower() plat = '%s/%s' % (sys.platform, real_plat) elif sys.platform.startswith('win'): NBITS = 8 * struct.calcsize("P") plat = 'Windows (%i bits)' % NBITS else: NBITS = 8 * struct.calcsize("P") plat = '%s (%i bits)' % (sys.platform, NBITS) printDirect("%s %s on %s.\n" % (thename, sys.version.split('[')[0].rstrip(), plat)) # Integrate GUI guiName, guiError = self._integrate_gui(startup_info) # Write pyzo part of banner (including what GUI loop is integrated) if True: pyzoBanner = 'This is the Pyzo interpreter' if guiError: pyzoBanner += '. ' + guiError + '\n' elif guiName: pyzoBanner += ' with integrated event loop for ' pyzoBanner += guiName + '.\n' else: pyzoBanner += '.\n' printDirect(pyzoBanner) # Try loading IPython if startup_info.get('ipython', '').lower() in ('', 'no', 'false'): self._ipython = None else: try: self._load_ipyhon() except Exception: type, value, tb = sys.exc_info() del tb printDirect('IPython could not be loaded: %s\n' % str(value)) self._ipython = None # Set prompts sys.ps1 = PS1(self) sys.ps2 = PS2(self) # Notify about project path projectPath = startup_info['projectPath'] if projectPath: printDirect('Prepending the project path %r to sys.path\n' % projectPath) # Write tips message. if self._ipython: import IPython printDirect("\nUsing IPython %s -- An enhanced Interactive Python.\n" % IPython.__version__) printDirect( "? -> Introduction and overview of IPython's features.\n" "%quickref -> Quick reference.\n" "help -> Python's own help system.\n" "object? -> Details about 'object', " "use 'object??' for extra details.\n") else: printDirect("Type 'help' for help, " + "type '?' for a list of *magic* commands.\n") # Notify the running of the script if self._scriptToRunOnStartup: printDirect('\x1b[0;33mRunning script: "'+self._scriptToRunOnStartup+'"\x1b[0m\n') # Prevent app nap on OSX 9.2 and up # The _nope module is taken from MINRK's appnope package if sys.platform == "darwin" and LV(platform.mac_ver()[0]) >= LV("10.9"): from pyzokernel import _nope _nope.nope() # Setup post-mortem debugging via appropriately logged exceptions class PMHandler(logging.Handler): def emit(self, record): if record.exc_info: sys.last_type, sys.last_value, sys.last_traceback = record.exc_info return record # root_logger = logging.getLogger() if not root_logger.handlers: root_logger.addHandler(logging.StreamHandler()) root_logger.addHandler(PMHandler()) def _prepare_environment(self, startup_info): """ Prepare the Python environment. There are two possibilities: either we run a script or we run interactively. """ # Get whether we should (and can) run as script scriptFilename = startup_info['scriptFile'] if scriptFilename: if not os.path.isfile(scriptFilename): printDirect('Invalid script file: "'+scriptFilename+'"\n') scriptFilename = None # Get project path projectPath = startup_info['projectPath'] if scriptFilename.endswith('.ipynb'): # Run Jupyter notebook import notebook.notebookapp sys.argv = ['jupyter_notebook', scriptFilename] sys.exit(notebook.notebookapp.main()) elif scriptFilename: # RUN AS SCRIPT # Set __file__ (note that __name__ is already '__main__') self.locals['__file__'] = scriptFilename # Set command line arguments sys.argv[:] = [] sys.argv.append(scriptFilename) sys.argv.extend(shlex.split(startup_info.get('argv', ''))) # Insert script directory to path theDir = os.path.abspath( os.path.dirname(scriptFilename) ) if theDir not in sys.path: sys.path.insert(0, theDir) if projectPath is not None: sys.path.insert(0,projectPath) # Go to script dir os.chdir( os.path.dirname(scriptFilename) ) # Run script later self._scriptToRunOnStartup = scriptFilename else: # RUN INTERACTIVELY # No __file__ (note that __name__ is already '__main__') self.locals.pop('__file__','') # Remove all command line arguments, set first to empty string sys.argv[:] = [] sys.argv.append('') sys.argv.extend(shlex.split(startup_info.get('argv', ''))) # Insert current directory to path sys.path.insert(0, '') if projectPath: sys.path.insert(0,projectPath) # Go to start dir startDir = startup_info['startDir'] if startDir and os.path.isdir(startDir): os.chdir(startDir) else: os.chdir(os.path.expanduser('~')) # home dir def _run_startup_code(self, startup_info): """ Execute the startup code or script. """ # Run startup script (if set) script = startup_info['startupScript'] # Should we use the default startupScript? if script == '$PYTHONSTARTUP': script = os.environ.get('PYTHONSTARTUP','') if '\n' in script: # Run code later or now firstline = script.split('\n')[0].replace(' ', '') if firstline.startswith('#AFTER_GUI'): self._codeToRunOnStartup = script else: self.context._stat_interpreter.send('Busy') msg = {'source': script, 'fname': '', 'lineno': 0} self.runlargecode(msg, True) elif script and os.path.isfile(script): # Run script self.context._stat_interpreter.send('Busy') self.runfile(script) else: # Nothing to run pass def _integrate_gui(self, startup_info): """ Integrate event loop of GUI toolkit (or use pure Python event loop). """ self.guiApp = guiintegration.App_base() self.guiName = guiName = startup_info['gui'].upper() guiError = '' try: if guiName in ['', 'NONE']: guiName = '' elif guiName == 'AUTO': for tryName, tryApp in [('PYQT5', guiintegration.App_pyqt5), ('PYQT4', guiintegration.App_pyqt4), ('PYSIDE2', guiintegration.App_pyside2), ('PYSIDE', guiintegration.App_pyside), #('WX', guiintegration.App_wx), ('TK', guiintegration.App_tk), ]: try: self.guiApp = tryApp() except Exception: continue guiName = tryName break else: guiName = '' elif guiName == 'TK': self.guiApp = guiintegration.App_tk() elif guiName == 'WX': self.guiApp = guiintegration.App_wx() elif guiName == 'TORNADO': self.guiApp = guiintegration.App_tornado() elif guiName == 'PYSIDE': self.guiApp = guiintegration.App_pyside() elif guiName == 'PYSIDE2': self.guiApp = guiintegration.App_pyside2() elif guiName in ['PYQT5', 'QT5']: self.guiApp = guiintegration.App_pyqt5() elif guiName in ['PYQT4', 'QT4']: self.guiApp = guiintegration.App_pyqt4() elif guiName == 'FLTK': self.guiApp = guiintegration.App_fltk() elif guiName == 'GTK': self.guiApp = guiintegration.App_gtk() else: guiError = 'Unkown gui: %s' % guiName guiName = '' except Exception: # Catch any error # Get exception info (we do it using sys.exc_info() because # we cannot catch the exception in a version independent way. type, value, tb = sys.exc_info() del tb guiError = 'Failed to integrate event loop for %s: %s' % ( guiName, str(value)) return guiName, guiError def _load_ipyhon(self): """ Try loading IPython shell. The result is set in self._ipython (can be None if IPython not available). """ # Init self._ipython = None import __main__ # Try importing IPython try: import IPython except ImportError: return # Version ok? if IPython.version_info < (1,): return # Create an IPython shell from IPython.core.interactiveshell import InteractiveShell self._ipython = InteractiveShell(user_module=__main__) # Set some hooks / event callbacks # Run hook (pre_run_code_hook is depreacted in 2.0) pre_run_cell_hook = self.ipython_pre_run_cell_hook if IPython.version_info < (2,): self._ipython.set_hook('pre_run_code_hook', pre_run_cell_hook) else: self._ipython.events.register('pre_run_cell', pre_run_cell_hook) # Other hooks self._ipython.set_hook('editor', self.ipython_editor_hook) self._ipython.set_custom_exc((bdb.BdbQuit,), self.dbstop_handler) # Some patching self._ipython.ask_exit = self.ipython_ask_exit # Make output be shown on Windows if sys.platform.startswith('win'): # IPython wraps std streams just like we do below, but # pyreadline adds *another* wrapper, which is where it # goes wrong. Here we set it back to bypass pyreadline. from IPython.utils import io io.stdin = io.IOStream(sys.stdin) io.stdout = io.IOStream(sys.stdout) io.stderr = io.IOStream(sys.stderr) # Ipython uses msvcrt e.g. for pausing between pages # but this does not work in pyzo import msvcrt msvcrt.getwch = msvcrt.getch = input # input is deffed above def process_commands(self): """ Do one iteration of processing commands (the REPL). """ try: self._process_commands() except SystemExit: # It may be that we should ignore sys exit now... if self.ignore_sys_exit: self.ignore_sys_exit = False # Never allow more than once return # Get and store the exception so we can raise it later type, value, tb = sys.exc_info() del tb self._exitException = value # Stop debugger if it is running self.debugger.stopinteraction() # Exit from interpreter. Exit in the appropriate way self.guiApp.quit() # Is sys.exit() by default def _process_commands(self): # Run startup code/script inside the loop (only the first time) # so that keyboard interrupt will work if self._codeToRunOnStartup: self.context._stat_interpreter.send('Busy') self._codeToRunOnStartup, tmp = None, self._codeToRunOnStartup self.pushline(tmp) if self._scriptToRunOnStartup: self.context._stat_interpreter.send('Busy') self._scriptToRunOnStartup, tmp = None, self._scriptToRunOnStartup self.runfile(tmp) # Flush real stdout / stderr sys.__stdout__.flush() sys.__stderr__.flush() # Set status and prompt? # Prompt is allowed to be an object with __str__ method if self.newPrompt: self.newPrompt = False ps = [sys.ps1, sys.ps2][bool(self.more)] self.context._strm_prompt.send(str(ps)) if True: # Determine state. The message is really only send # when the state is different. Note that the kernelbroker # can also set the state ("Very busy", "Busy", "Dead") if self._dbFrames: self.context._stat_interpreter.send('Debug') elif self.more: self.context._stat_interpreter.send('More') else: self.context._stat_interpreter.send('Ready') self.context._stat_cd.send(os.getcwd()) # Are we still connected? if sys.stdin.closed or not self.context.connection_count: # Exit from main loop. # This will raise SystemExit and will shut us down in the # most appropriate way sys.exit() # Get channel to take a message from ch = yoton.select_sub_channel(self.context._ctrl_command, self.context._ctrl_code) if ch is None: pass # No messages waiting elif ch is self.context._ctrl_command: # Read command line1 = self.context._ctrl_command.recv(False) # Command if line1: # Notify what we're doing self.context._strm_echo.send(line1) self.context._stat_interpreter.send('Busy') self.newPrompt = True # Convert command # (only a few magics are supported if IPython is active) line2 = self.magician.convert_command(line1.rstrip('\n')) # Execute actual code if line2 is not None: for line3 in line2.split('\n'): # not splitlines! self.more = self.pushline(line3) else: self.more = False self._resetbuffer() elif ch is self.context._ctrl_code: # Read larger block of code (dict) msg = self.context._ctrl_code.recv(False) if msg: # Notify what we're doing # (runlargecode() sends on stdin-echo) self.context._stat_interpreter.send('Busy') self.newPrompt = True # Execute code self.runlargecode(msg) # Reset more stuff self._resetbuffer() self.more = False else: # This should not happen, but if it does, just flush! ch.recv(False) ## Running code in various ways # In all cases there is a call for compilecode and a call to execcode def _resetbuffer(self): """Reset the input buffer.""" self._buffer = [] def pushline(self, line): """Push a line to the interpreter. The line should not have a trailing newline; it may have internal newlines. The line is appended to a buffer and the interpreter's _runlines() method is called with the concatenated contents of the buffer as source. If this indicates that the command was executed or invalid, the buffer is reset; otherwise, the command is incomplete, and the buffer is left as it was after the line was appended. The return value is 1 if more input is required, 0 if the line was dealt with in some way (this is the same as _runlines()). """ # Get buffer, join to get source buffer = self._buffer buffer.append(line) source = "\n".join(buffer) # Clear buffer and run source self._resetbuffer() more = self._runlines(source, self._filename) # Create buffer if needed if more: self._buffer = buffer return more def _runlines(self, source, filename="", symbol="single"): """Compile and run some source in the interpreter. Arguments are as for compile_command(). One several things can happen: 1) The input is incorrect; compile_command() raised an exception (SyntaxError or OverflowError). A syntax traceback will be printed by calling the showsyntaxerror() method. 2) The input is incomplete, and more input is required; compile_command() returned None. Nothing happens. 3) The input is complete; compile_command() returned a code object. The code is executed by calling self.execcode() (which also handles run-time exceptions, except for SystemExit). The return value is True in case 2, False in the other cases (unless an exception is raised). The return value can be used to decide whether to use sys.ps1 or sys.ps2 to prompt the next line. """ use_ipython = self._ipython and not self._dbFrames # Try compiling. # The IPython kernel does not handle incomple lines, so we check # that ourselves ... error = None try: code = self.compilecode(source, filename, symbol) except (OverflowError, SyntaxError, ValueError): error = sys.exc_info()[1] code = False if use_ipython: if code is None: # Case 2 #self._ipython.run_cell('', True) return True else: # Case 1 and 3 handled by IPython self._ipython.run_cell(source, True, False) return False else: if code is None: # Case 2 return True elif not code: # Case 1, a bit awkward way to show the error, but we need # to call showsyntaxerror in an exception handler. try: raise error except Exception: self.showsyntaxerror(filename) return False else: # Case 3 self.execcode(code) return False def runlargecode(self, msg, silent=False): """ To execute larger pieces of code. """ # Get information source, fname, lineno = msg['source'], msg['fname'], msg['lineno'] cellName = msg.get('cellName', '') source += '\n' # Change directory? if msg.get('changeDir', False) and os.path.isfile(fname): d = os.path.normpath(os.path.normcase(os.path.dirname(fname))) if d != os.getcwd(): os.chdir(d) # Construct notification message lineno1 = lineno + 1 lineno2 = lineno + source.count('\n') fname_show = fname if not fname.startswith('<'): fname_show = os.path.split(fname)[1] if cellName == fname: runtext = '(executing file "%s")\n' % fname_show elif cellName: runtext = '(executing cell "%s" (line %i of "%s"))\n' % (cellName, lineno1, fname_show) elif lineno1 == lineno2: runtext = '(executing line %i of "%s")\n' % (lineno1, fname_show) else: runtext = '(executing lines %i to %i of "%s")\n' % ( lineno1, lineno2, fname_show) # Notify IDE if not silent: self.context._strm_echo.send('\x1b[0;33m%s\x1b[0m' % runtext) # Increase counter if self._ipython: self._ipython.execution_count += 1 # Put the line number in the filename (if necessary) # Note that we could store the line offset in the _codeCollection, # but then we cannot retrieve it for syntax errors. if lineno: fname = "%s+%i" % (fname, lineno) # Try compiling the source code = None try: # Compile code = self.compilecode(source, fname, "exec") except (OverflowError, SyntaxError, ValueError): self.showsyntaxerror(fname) return if code: # Store the source using the (id of the) code object as a key self._codeCollection.store_source(code, source) # Execute the code self.execcode(code) else: # Incomplete code self.write('Could not run code because it is incomplete.\n') def runfile(self, fname): """ To execute the startup script. """ # Get text (make sure it ends with a newline) try: bb = open(fname, 'rb').read() encoding = 'UTF-8' firstline = bb.split('\n'.encode(), 1)[0].decode('ascii', 'ignore') if firstline.startswith('#') and 'coding' in firstline: encoding = firstline.split('coding', 1)[-1].strip(' \t\r\n:=-*') source = bb.decode(encoding) except Exception: printDirect('Could not read script (decoding using %s): %r\n' % (encoding, fname)) return try: source = source.replace('\r\n', '\n').replace('\r','\n') if source[-1] != '\n': source += '\n' except Exception: printDirect('Could not execute script: "' + fname + '"\n') return # Try compiling the source code = None try: # Compile code = self.compilecode(source, fname, "exec") except (OverflowError, SyntaxError, ValueError): time.sleep(0.2) # Give stdout time to be send self.showsyntaxerror(fname) return if code: # Store the source using the (id of the) code object as a key self._codeCollection.store_source(code, source) # Execute the code self.execcode(code) else: # Incomplete code self.write('Could not run code because it is incomplete.\n') def compilecode(self, source, filename, mode, *args, **kwargs): """ Compile source code. Will mangle coding definitions on first two lines. * This method should be called with Unicode sources. * Source newlines should consist only of LF characters. """ # This method solves pyzo issue 22 # Split in first two lines and the rest parts = source.split('\n', 2) # Replace any coding definitions ci = 'coding is' contained_coding = False for i in range(len(parts)-1): tmp = parts[i] if tmp and tmp[0] == '#' and 'coding' in tmp: contained_coding = True parts[i] = tmp.replace('coding=', ci).replace('coding:', ci) # Combine parts again (if necessary) if contained_coding: source = '\n'.join(parts) # Convert filename to UTF-8 if Python version < 3 if PYTHON_VERSION < 3: filename = filename.encode('utf-8') # Compile return self._compile(source, filename, mode, *args, **kwargs) def execcode(self, code): """Execute a code object. When an exception occurs, self.showtraceback() is called to display a traceback. All exceptions are caught except SystemExit, which is reraised. A note about KeyboardInterrupt: this exception may occur elsewhere in this code, and may not always be caught. The caller should be prepared to deal with it. The globals variable is used when in debug mode. """ try: if self._dbFrames: self.apply_breakpoints() exec(code, self.globals, self.locals) else: # Turn debugger on at this point. If there are no breakpoints, # the tracing is disabled for better performance. self.apply_breakpoints() self.debugger.set_on() exec(code, self.locals) except bdb.BdbQuit: self.dbstop_handler() except Exception: time.sleep(0.2) # Give stdout some time to send data self.showtraceback() except KeyboardInterrupt: # is a BaseException, not an Exception time.sleep(0.2) self.showtraceback() def apply_breakpoints(self): """ Breakpoints are updated at each time a command is given, including commands like "db continue". """ try: breaks = self.context._stat_breakpoints.recv() if self.debugger.breaks: self.debugger.clear_all_breaks() if breaks: # Can be None for fname in breaks: for linenr in breaks[fname]: self.debugger.set_break(fname, linenr) except Exception: type, value, tb = sys.exc_info(); del tb print('Error while setting breakpoints: %s' % str(value)) ## Handlers and hooks def ipython_pre_run_cell_hook(self, ipython=None): """ Hook that IPython calls right before executing code. """ self.apply_breakpoints() self.debugger.set_on() def ipython_editor_hook(self, ipython, filename, linenum=None, wait=True): # Correct line number for cell offset filename, linenum = self.correctfilenameandlineno(filename, linenum or 0) # Get action string if linenum: action = 'open %i %s' % (linenum, os.path.abspath(filename)) else: action = 'open %s' % os.path.abspath(filename) # Send self.context._strm_action.send(action) def ipython_ask_exit(self): # Ask the user a = input("Do you really want to exit ([y]/n)? ") a = a or 'y' # Close stdin if necessary if a.lower() == 'y': sys.stdin._channel.close() def dbstop_handler(self, *args, **kwargs): print("Program execution stopped from debugger.") ## Writing and error handling def write(self, text): """ Write errors. """ sys.stderr.write( text ) def showsyntaxerror(self, filename=None): """Display the syntax error that just occurred. This doesn't display a stack trace because there isn't one. If a filename is given, it is stuffed in the exception instead of what was there before (because Python's parser always uses "" when reading from a string). Pyzo version: support to display the right line number, see doc of showtraceback for details. """ # Get info (do not store) type, value, tb = sys.exc_info() del tb # Work hard to stuff the correct filename in the exception if filename and type is SyntaxError: try: # unpack information msg, (dummy_filename, lineno, offset, line) = value # correct line-number fname, lineno = self.correctfilenameandlineno(filename, lineno) except: # Not the format we expect; leave it alone pass else: # Stuff in the right filename value = SyntaxError(msg, (fname, lineno, offset, line)) sys.last_value = value # Show syntax error strList = traceback.format_exception_only(type, value) for s in strList: self.write(s) def showtraceback(self, useLastTraceback=False): """Display the exception that just occurred. We remove the first stack item because it is our own code. The output is written by self.write(), below. In the pyzo version, before executing a block of code, the filename is modified by appending " [x]". Where x is the index in a list that we keep, of tuples (sourcecode, filename, lineno). Here, showing the traceback, we check if we see such [x], and if so, we extract the line of code where it went wrong, and correct the lineno, so it will point at the right line in the editor if part of a file was executed. When the file was modified since the part in question was executed, the fileno might deviate, but the line of code shown shall always be correct... """ # Traceback info: # tb_next -> go down the trace # tb_frame -> get the stack frame # tb_lineno -> where it went wrong # # Frame info: # f_back -> go up (towards caller) # f_code -> code object # f_locals -> we can execute code here when PM debugging # f_globals # f_trace -> (can be None) function for debugging? ( # # The traceback module is used to obtain prints from the # traceback. try: if useLastTraceback: # Get traceback info from buffered type = sys.last_type value = sys.last_value tb = sys.last_traceback else: # Get exception information and remove first, since that's us type, value, tb = sys.exc_info() tb = tb.tb_next # Store for debugging, but only store if not in debug mode if not self._dbFrames: sys.last_type = type sys.last_value = value sys.last_traceback = tb # Get traceback to correct all the line numbers # tblist = list of (filename, line-number, function-name, text) tblist = traceback.extract_tb(tb) # Get frames frames = [] while tb: frames.append(tb.tb_frame) tb = tb.tb_next # Walk through the list for i in range(len(tblist)): tbInfo = tblist[i] # Get filename and line number, init example fname, lineno = self.correctfilenameandlineno(tbInfo[0], tbInfo[1]) if not isinstance(fname, ustr): fname = fname.decode('utf-8') example = tbInfo[3] # Reset info tblist[i] = (fname, lineno, tbInfo[2], example) # Format list strList = traceback.format_list(tblist) if strList: strList.insert(0, "Traceback (most recent call last):\n") strList.extend( traceback.format_exception_only(type, value) ) # Write traceback for s in strList: self.write(s) # Clean up (we cannot combine except and finally in Python <2.5 tb = None frames = None except Exception: type, value, tb = sys.exc_info() tb = None frames = None t = 'An error occured, but then another one when trying to write the traceback: ' t += str(value) + '\n' self.write(t) def correctfilenameandlineno(self, fname, lineno): """ Given a filename and lineno, this function returns a modified (if necessary) version of the two. As example: "foo.py+7", 22 -> "foo.py", 29 """ j = fname.rfind('+') if j>0: try: lineno += int(fname[j+1:]) fname = fname[:j] except ValueError: pass return fname, lineno class ExecutedSourceCollection: """ Stores the source of executed pieces of code, so that the right traceback can be reproduced when an error occurs. The filename (including the +lineno suffix) is used as a key. We monkey-patch the linecache module so that we first try our cache to look up the lines. In that way we also allow third party modules (e.g. IPython) to get the lines for executed cells. """ def __init__(self): self._cache = {} self._patch() def store_source(self, codeObject, source): self._cache[codeObject.co_filename] = source def _patch(self): def getlines(filename, module_globals=None): # Try getting the source from our own cache, # otherwise fallback to linecache's own cache src = self._cache.get(filename, '') if src: return [line+'\n' for line in src.splitlines()] else: import linecache if module_globals is None: return linecache._getlines(filename) # only valid sig in 2.4 else: return linecache._getlines(filename, module_globals) # Monkey patch import linecache linecache._getlines = linecache.getlines linecache.getlines =getlines # I hoped this would remove the +lineno for IPython tracebacks, # but it doesn't # def extract_tb(tb, limit=None): # print('aasdasd') # import traceback # list1 = traceback._extract_tb(tb, limit) # list2 = [] # for (filename, lineno, name, line) in list1: # filename, lineno = sys._pyzoInterpreter.correctfilenameandlineno(filename, lineno) # list2.append((filename, lineno, name, line)) # return list2 # # import traceback # traceback._extract_tb = traceback.extract_tb # traceback.extract_tb = extract_tb pyzo-4.4.3/pyzo/pyzokernel/introspection.py0000666000000000000000000003233113131372626017340 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import sys import yoton import inspect # noqa - used in eval() try: import thread # Python 2 except ImportError: import _thread as thread # Python 3 class PyzoIntrospector(yoton.RepChannel): """ This is a RepChannel object that runs a thread to respond to requests from the IDE. """ def _getNameSpace(self, name=''): """ _getNameSpace(name='') Get the namespace to apply introspection in. If name is given, will find that name. For example sys.stdin. """ # Get namespace NS1 = sys._pyzoInterpreter.locals NS2 = sys._pyzoInterpreter.globals if not NS2: NS = NS1 else: NS = NS2.copy() NS.update(NS1) # Look up a name? if not name: return NS else: try: # Get object ob = eval(name, None, NS) # Get namespace for this object if isinstance(ob, dict): NS = ob elif isinstance(ob, (list, tuple)): NS = {} count = -1 for el in ob: count += 1 NS['[%i]'%count] = el else: keys = dir(ob) NS = {} for key in keys: try: NS[key] = getattr(ob, key) except Exception: NS[key] = '' # Done return NS except Exception: return {} def _getSignature(self, objectName): """ _getSignature(objectName) Get the signature of builtin, function or method. Returns a tuple (signature_string, kind), where kind is a string of one of the above. When none of the above, both elements in the tuple are an empty string. """ # if a class, get init # not if an instance! -> try __call__ instead # what about self? # Get valid object names parts = objectName.rsplit('.') objectNames = ['.'.join(parts[-i:]) for i in range(1,len(parts)+1)] # find out what kind of function, or if a function at all! NS = self._getNameSpace() fun1 = eval("inspect.isbuiltin(%s)"%(objectName), None, NS) fun2 = eval("inspect.isfunction(%s)"%(objectName), None, NS) fun3 = eval("inspect.ismethod(%s)"%(objectName), None, NS) fun4 = False fun5 = False if not (fun1 or fun2 or fun3): # Maybe it's a class with an init? if eval("hasattr(%s,'__init__')"%(objectName), None, NS): objectName += ".__init__" fun4 = eval("inspect.ismethod(%s)"%(objectName), None, NS) # Or a callable object? elif eval("hasattr(%s,'__call__')"%(objectName), None, NS): objectName += ".__call__" fun5 = eval("inspect.ismethod(%s)"%(objectName), None, NS) sigs = "" if True: # the first line in the docstring is usually the signature tmp = eval("%s.__doc__"%(objectNames[-1]), {}, NS ) sigs = '' if tmp: sigs = tmp.splitlines()[0].strip() # Test if doc has signature hasSig = False for name in objectNames: # list.append -> L.apend(objec) -- blabla name +="(" if name in sigs: hasSig = True # If not a valid signature, do not bother ... if (not hasSig) or (sigs.count("(") != sigs.count(")")): sigs = "" if fun1 or fun2 or fun3 or fun4 or fun5: if fun1: kind = 'builtin' elif fun2: kind = 'function' elif fun3: kind = 'method' elif fun4: kind = 'class' elif fun5: kind = 'callable' if not sigs: # Use intospection # collect try: tmp = eval("inspect.getargspec(%s)"%(objectName), None, NS) except Exception: # the above fails on 2.4 (+?) for builtins tmp = None kind = '' if tmp is not None: args, varargs, varkw, defaults = tmp # prepare defaults if defaults is None: defaults = () defaults = list(defaults) defaults.reverse() # make list (back to forth) args2 = [] for i in range(len(args)-fun4): arg = args.pop() if i < len(defaults): args2.insert(0, "%s=%s" % (arg, defaults[i]) ) else: args2.insert(0, arg ) # append varargs and kwargs if varargs: args2.append( "*"+varargs ) if varkw: args2.append( "**"+varkw ) # append the lot to our string funname = objectName.split('.')[-1] sigs = "%s(%s)" % ( funname, ", ".join(args2) ) elif sigs: kind = "function" else: sigs = "" kind = "" return sigs, kind # todo: variant that also says whether it's a property/function/class/other def dir(self, objectName): """ dir(objectName) Get list of attributes for the given name. """ #sys.__stdout__.write('handling '+objectName+'\n') #sys.__stdout__.flush() # Get namespace NS = self._getNameSpace() # Init names names = set() # Obtain all attributes of the class try: command = "dir(%s.__class__)" % (objectName) d = eval(command, {}, NS) except Exception: pass else: names.update(d) # Obtain instance attributes try: command = "%s.__dict__.keys()" % (objectName) d = eval(command, {}, NS) except Exception: pass else: names.update(d) # That should be enough, but in case __dir__ is overloaded, # query that as well try: command = "dir(%s)" % (objectName) d = eval(command, {}, NS) except Exception: pass else: names.update(d) # Respond return list(names) def dir2(self, objectName): """ dir2(objectName) Get variable names in currently active namespace plus extra information. Returns a list with strings, which each contain a (comma separated) list of elements: name, type, kind, repr. """ try: name = '' names = ['',''] def storeInfo(name, val): # Determine type typeName = type(val).__name__ # Determine kind kind = typeName if typeName != 'type': if hasattr(val, '__array__') and hasattr(val, 'dtype'): kind = 'array' elif isinstance(val, list): kind = 'list' elif isinstance(val, tuple): kind = 'tuple' # Determine representation if kind == 'array': tmp = 'x'.join([str(s) for s in val.shape]) if tmp: repres = '' % (tmp, val.dtype.name) elif val.size: tmp = str(float(val)) if 'int' in val.dtype.name: tmp = str(int(val)) repres = '' % (val.dtype.name, tmp) else: repres = '' % (val.dtype.name) elif kind == 'list': repres = '' % len(val) elif kind == 'tuple': repres = '' % len(val) else: repres = repr(val) if len(repres) > 80: repres = repres[:77] + '...' # Store tmp = ','.join([name, typeName, kind, repres]) names.append(tmp) # Get locals NS = self._getNameSpace(objectName) for name in NS.keys(): # name can be a key in a dict, i.e. not str if hasattr(name, 'startswith') and name.startswith('__'): continue try: storeInfo(str(name), NS[name]) except Exception: pass return names except Exception: return [] def signature(self, objectName): """ signature(objectName) Get signature. """ try: text, kind = self._getSignature(objectName) return text except Exception: return None def doc(self, objectName): """ doc(objectName) Get documentation for an object. """ # Get namespace NS = self._getNameSpace() try: # collect docstring h_text = '' # Try using the class (for properties) try: className = eval("%s.__class__.__name__"%(objectName), {}, NS) if '.' in objectName: tmp = objectName.rsplit('.',1) tmp[1] += '.' else: tmp = [objectName, ''] if className not in ['type', 'module', 'builtin_function_or_method', 'function']: cmd = "%s.__class__.%s__doc__" h_text = eval(cmd % (tmp[0],tmp[1]), {}, NS) except Exception: pass # Normal doc if not h_text: h_text = eval("%s.__doc__"%(objectName), {}, NS ) # collect more data h_repr = eval("repr(%s)"%(objectName), {}, NS ) try: h_class = eval("%s.__class__.__name__"%(objectName), {}, NS ) except Exception: h_class = "unknown" # docstring can be None, but should be empty then if not h_text: h_text = "" # get and correct signature h_fun, kind = self._getSignature(objectName) if kind == 'builtin' or not h_fun: h_fun = "" # signature already in docstring or not available # cut repr if too long if len(h_repr) > 200: h_repr = h_repr[:200] + "..." # replace newlines so we can separates the different parts h_repr = h_repr.replace('\n', '\r') # build final text text = '\n'.join([objectName, h_class, h_fun, h_repr, h_text]) except Exception: type, value, tb = sys.exc_info() del tb text = '\n'.join([objectName, '', '', '', 'No help available. ', str(value)]) # Done return text def eval(self, command): """ eval(command) Evaluate a command and return result. """ # Get namespace NS = self._getNameSpace() try: # here globals is None, so we can look into sys, time, etc... return eval(command, None, NS) except Exception: return 'Error evaluating: ' + command def interrupt(self, command=None): """ interrupt() Interrupt the main thread. This does not work if the main thread is running extension code. A bit of a hack to do this in the introspector, but it's the easeast way and prevents having to launch another thread just to wait for an interrupt/terminare command. Note that on POSIX we can send an OS INT signal, which is faster and maybe more effective in some situations. """ thread.interrupt_main() def terminate(self, command=None): """ terminate() Ask the kernel to terminate by closing the stdin. """ sys.stdin._channel.close() pyzo-4.4.3/pyzo/pyzokernel/magic.py0000666000000000000000000004712113131370723015517 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Magic commands for the Pyzo kernel. No need to use printDirect here, magic commands are just like normal Python commands, in the sense that they print something etc. """ import sys import os import re import time import inspect # Set Python version and get some names PYTHON_VERSION = sys.version_info[0] if PYTHON_VERSION < 3: input = raw_input # noqa MESSAGE = """List of *magic* commands: ? - show this message ?X or X? - show docstring of X ??X or X?? - help(X) cd - show current directory cd X - change directory ls - list current directory who - list variables in current workspace whos - list variables plus their class and representation timeit X - times execution of command X open X - open file X or the Python module that defines object X run X - run file X install - install a new package update - update an installed package remove - remove (i.e. uninstall) an installed package conda - manage packages using conda pip - manage packages using pip db X - debug commands cls - clear screen notebook - launch the Jupyter notebook """ TIMEIT_MESSAGE = """Time execution duration. Usage: timeit fun # where fun is a callable timeit 'expression' timeit expression timeit 20 fun/expression # tests 20 passes """ class Magician: def _eval(self, command): # Get namespace NS1 = sys._pyzoInterpreter.locals NS2 = sys._pyzoInterpreter.globals if not NS2: NS = NS1 else: NS = NS2.copy() NS.update(NS1) # Evaluate in namespace return eval(command, {}, NS) def convert_command(self, line): """ convert_command(line) Convert a given command from a magic command to Python code. Returns the converted command if it was a magic command, or the original otherwise. """ # Get converted command, catch and report errors try: res = self._convert_command(line) except Exception: # Try informing about line number type, value, tb = sys.exc_info() msg = 'Error in handling magic function:\n' msg += ' %s\n' % str(value) if tb.tb_next: tb = tb.tb_next.tb_next while tb: msg += ' line %i in %s\n' % (tb.tb_lineno, tb.tb_frame.f_code.co_filename) tb = tb.tb_next # Clear del tb # Write print(msg) return None # Process if res is None: return line else: return res def _convert_command(self, line): # Get interpreter interpreter = sys._pyzoInterpreter # Check if it is a variable command = line.rstrip() if ' ' not in command: if command in interpreter.locals: return if interpreter.globals and command in interpreter.globals: return # Clean and make case insensitive command = line.upper().rstrip() if not command: return # Commands to always support (also with IPython) elif command.startswith('DB'): return self.debug(line, command) elif command.startswith('NOTEBOOK'): return self.notebook(line, command) elif command.startswith('INSTALL'): return self.install(line, command) elif command.startswith('UPDATE') or command.startswith('UPGRADE'): line = line.replace('upgrade', 'update') command = command.replace('UPGRADE', 'UPDATE') return self.update(line, command) elif command.startswith('REMOVE') or command.startswith('UNINSTALL'): line = line.replace('uninstall', 'remove') command = command.replace('UNINSTALL', 'REMOVE') return self.remove(line, command) elif command.startswith('CONDA'): return self.conda(line, command) elif command.startswith('PIP'): return self.pip(line, command) elif command == 'CLS': return self.cls(line, command) elif command.startswith('OPEN '): return self.open(line, command) elif interpreter._ipython: # Patch IPython magic # EDIT do not run code after editing if command.startswith('EDIT ') and not ' -X ' in command: return 'edit -x ' + line[5:] # In all cases stop processing magic commands return # Commands that we only support in the absense of IPtython elif command == '?': return 'print(%s)' % repr(MESSAGE) elif command.startswith("??"): return 'help(%s)' % line[2:].rstrip() elif command.endswith("??"): return 'help(%s)' % line.rstrip()[:-2] elif command.startswith("?"): return 'print(%s.__doc__)' % line[1:].rstrip() elif command.endswith("?"): return 'print(%s.__doc__)' % line.rstrip()[:-1] elif command.startswith('CD'): return self.cd(line, command) elif command.startswith('LS'): return self.ls(line, command) elif command.startswith('TIMEIT'): return self.timeit(line, command) elif command == 'WHO': return self.who(line, command) elif command == 'WHOS': return self.whos(line, command) elif command.startswith('RUN '): return self.run(line, command) def debug(self, line, command): if command == 'DB': line = 'db help' elif not command.startswith('DB '): return # Get interpreter interpreter = sys._pyzoInterpreter # Get command and arg line += ' ' _, cmd, arg = line.split(' ', 2) cmd = cmd.lower() # Get func func = getattr(interpreter.debugger, 'do_'+cmd, None) # Call or show warning if func is not None: func(arg.strip()) else: interpreter.write("Unknown debug command: %s.\n" % cmd) # Done (no code to execute) return '' def cd(self, line, command): if command == 'CD' or command.startswith("CD ") and '=' not in command: path = line[3:].strip() if path: try: os.chdir(path) except Exception: print('Could not change to directory "%s".' % path) return '' newPath = os.getcwd() else: newPath = os.getcwd() # Done print(repr(newPath)) return '' def ls(self, line, command): if command == 'LS' or command.startswith("LS ") and '=' not in command: path = line[3:].strip() if not path: path = os.getcwd() L = [p for p in os.listdir(path) if not p.startswith('.')] text = '\n'.join(sorted(L)) # Done print(text) return '' def timeit(self, line, command): if command == "TIMEIT": return 'print(%s)' % repr(TIMEIT_MESSAGE) elif command.startswith("TIMEIT "): expression = line[7:] # Get number of iterations N = 1 tmp = expression.split(' ',1) if len(tmp)==2: try: N = int(tmp[0]) expression = tmp[1] except Exception: N = 1 if expression[0] not in '\'\"': isidentifier = lambda x: bool(re.match(r'[a-z_]\w*$', x, re.I)) if not isidentifier(expression): expression = "'%s'" % expression # Compile expression line2 = 'import timeit; t=timeit.Timer(%s);' % expression line2 += 'print(str( t.timeit(%i)/%i ) ' % (N, N) line2 += '+" seconds on average for %i iterations." )' % N # return line2 def who(self, line, command): L = self._eval('dir()\n') L = [k for k in L if not k.startswith('__')] if L: print(', '.join(L)) else: print("There are no variables defined in this scope.") return '' def _justify(self, line, width, offset): realWidth = width - offset if len(line) > realWidth: line = line[:realWidth-3] + '...' return line.ljust(width) def whos(self, line, command): # Get list of variables L = self._eval('dir()\n') L = [k for k in L if not k.startswith('__')] # Anny variables? if not L: print("There are no variables defined in this scope.") return '' else: text = "VARIABLE: ".ljust(20,' ') + "TYPE: ".ljust(20,' ') text += "REPRESENTATION: ".ljust(20,' ') + '\n' # Create list item for each variablee for name in L: ob = self._eval(name) cls = '' if hasattr(ob, '__class__'): cls = ob.__class__.__name__ rep = repr(ob) text += self._justify(name,20,2) + self._justify(cls,20,2) text += self._justify(rep,40,2) + '\n' # Done print(text) return '' def cls(self, line, command): sys._pyzoInterpreter.context._strm_action.send('cls') return '' def open(self, line, command): # Get what to open name = line.split(' ',1)[1].strip() fname = '' linenr = None # Is it a file name? tmp = os.path.join(os.getcwd(), name) # if name[0] in '"\'' and name[-1] in '"\'': # Explicitly given fname = name[1:-1] elif os.path.isfile(tmp): fname = tmp elif os.path.isfile(name): fname = name else: # Then it maybe is an object # Get the object try: ob = self._eval(name) except NameError: print('There is no object known as "%s"' % name) return '' # Try get filename for iter in range(3): # Try successive steps if iter == 0: ob = ob elif iter == 1 and not isinstance(ob, type): ob = ob.__class__ elif iter == 2 and hasattr(ob, '__module__'): ob = sys.modules[ob.__module__] # Try get fname fname = '' try: fname = inspect.getsourcefile(ob) except Exception: pass # Returned fname may simply be x.replace('.pyc', '.py') if os.path.isfile(fname): break # Try get line number if fname: try: lines, linenr = inspect.getsourcelines(ob) except Exception: pass # Almost done # IPython's edit now support this via our hook in interpreter.py if not fname: print('Could not determine file name for object "%s".' % name) elif linenr is not None: action = 'open %i %s' % (linenr, os.path.abspath(fname)) sys._pyzoInterpreter.context._strm_action.send(action) else: action = 'open %s' % os.path.abspath(fname) sys._pyzoInterpreter.context._strm_action.send(action) # return '' def run(self, line, command): # Get what to open name = line.split(' ',1)[1].strip() fname = '' # Enable dealing with qoutes if name[0] in '"\'' and name[-1] in '"\'': name = name[1:-1] # Is it a file name? tmp = os.path.join(os.getcwd(), name) # if os.path.isfile(tmp): fname = tmp elif os.path.isfile(name): fname = name # Go run! if not fname: print('Could not find file to run "%s".' % name) else: sys._pyzoInterpreter.runfile(fname) # return '' def install(self, line, command): if not (command == 'INSTALL' or command.startswith('INSTALL ')): return text = "\x1b[34m\x1b[1m" text += "Trying installation via conda. If this does not work, try:\n" text += " pip " + line + "\n" text += "\x1b[0m" print(text) time.sleep(0.2) self.conda('conda ' + line, 'CONDA') return '' def update(self, line, command): if not (command == 'UPDATE' or command.startswith('UPDATE ')): return text = "\x1b[34m\x1b[1m" text += "Trying update via conda. If this does not work, try:\n" text += " pip " + line.replace('update', 'install') + " --upgrade\n" text += "\x1b[0m" print(text) time.sleep(0.2) self.conda('conda ' + line, 'CONDA') return '' def remove(self, line, command): if not (command == 'REMOVE' or command.startswith('REMOVE ')): return text = "\x1b[34m\x1b[1m" text += "Trying remove via conda. If this does not work, try:\n" text += " pip " + line.replace('remove', 'uninstall') + "\n" text += "\x1b[0m" print(text) time.sleep(0.2) self.conda('conda ' + line, 'CONDA') return '' def conda(self, line, command): if not (command == 'CONDA' or command.startswith('CONDA ')): return # Get command args args = line.split(' ') args = [w for w in args if w] args.pop(0) # remove 'conda' # Stop if user does "conda = ..." if args and '=' in args[0]: return # Add channels when using install, this gets added last, so # that user-specified channels take preference channel_list = [] if args and args[0] == 'install': channel_list = ['-c', 'conda-forge', '-c', 'pyzo'] def write_no_dots(x): if x.strip() == '.': # note, no "x if y else z" in Python 2.4 return 0 return stderr_write(x) # Go! # Weird double-try, to make work on Python 2.4 oldargs = sys.argv stderr_write = sys.stderr.write try: try: # older version of conda would spew dots to stderr during downloading sys.stderr.write = write_no_dots import conda # noqa from conda.cli import main sys.argv = ['conda'] + list(args) + channel_list main() except SystemExit: # as err: type, err, tb = sys.exc_info(); del tb err = str(err) if len(err) > 4: # Only print if looks like a message print(err) except Exception: # as err: type, err, tb = sys.exc_info(); del tb print('Error in conda command:') print(err) finally: sys.argv = oldargs sys.stderr.write = stderr_write return '' # todo: I think we can deprecate this def _check_imported_modules(self): KNOWN_PURE_PYHON = ('conda', 'yaml', 'IPython', 'requests', 'readline', 'pyreadline') if not sys.platform.startswith('win'): return # Only a problem on Windows # Check what modules are currently imported loaded_modules = set() for name, mod in sys.modules.items(): if 'site-packages' in getattr(mod, '__file__', ''): name = name.split('.')[0] if name.startswith('_') or name in KNOWN_PURE_PYHON: continue loaded_modules.add(name) # Add PySide PyQt4 from IEP if prefix is the same if os.getenv('IEP_PREFIX', '') == sys.prefix: loaded_modules.add(os.getenv('IEP_QTLIB', 'qt')) # Warn if we have any such modules loaded_modules = [m.lower() for m in loaded_modules if m] if loaded_modules: print('WARNING! The following modules are currently loaded:\n') print(' ' + ', '.join(sorted(loaded_modules))) print('\nUpdating or removing them may fail if they are not ' 'pure Python.\nIf none of the listed packages is updated or ' 'removed, it is safe\nto proceed (use "f" if necessary).\n') print('-'*80) time.sleep(2.0) # Give user time to realize there is a warning def pip(self, line, command): if not (command == 'PIP' or command.startswith('PIP ')): return # Get command args args = line.split(' ') args = [w for w in args if w] args.pop(0) # remove 'pip' # Stop if user does "pip = ..." if args and '=' in args[0]: return # Tweak the args if args and args[0] == 'uninstall': args.insert(1, '--yes') # Go! try: from pyzokernel.pipper import pip_command pip_command(*args) except SystemExit: # as err: type, err, tb = sys.exc_info(); del tb err = str(err) if len(err) > 4: # Only print if looks like a message print(err) except Exception:# as err: type, err, tb = sys.exc_info(); del tb print('Error in pip command:') print(err) return '' def notebook(self, line, command): if not (command == 'NOTEBOOK' or command.startswith('NOTEBOOK ')): return # Get command args args = line.split(' ') args = [w.replace('%20', ' ') for w in args if w] args.pop(0) # remove 'notebook' # Stop if user does "conda = ..." if args and '=' in args[0]: return # Go! # Weird double-try, to make work on Python 2.4 oldargs = sys.argv try: try: import notebook.notebookapp sys.argv = ['jupyter_notebook'] + list(args) notebook.notebookapp.main() except SystemExit: # as err: type, err, tb = sys.exc_info(); del tb err = str(err) if len(err) > 4: # Only print if looks like a message print(err) except Exception: # as err: type, err, tb = sys.exc_info(); del tb print('Error in notebook command:') print(err) finally: sys.argv = oldargs return '' pyzo-4.4.3/pyzo/pyzokernel/pipper.py0000666000000000000000000000331513123037260015730 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, Almar Klein # From pyzo/pyzokernel import sys import time import subprocess def subprocess_with_callback(callback, cmd, **kwargs): """ Execute command in subprocess, stdout is passed to the callback function. Returns the returncode of the process. If callback is None, simply prints any stdout. """ # Set callback to void if None if callback is None: callback = lambda x:None # Execute command try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs) except OSError: type, err, tb = sys.exc_info(); del tb callback(str(err)+'\n') return -1 pending = [] while p.poll() is None: time.sleep(0.001) # Read text and process c = p.stdout.read(1).decode('utf-8', 'ignore') pending.append(c) if c in '.\n': callback(''.join(pending)) pending = [] # Process remaining text pending.append( p.stdout.read().decode('utf-8') ) callback( ''.join(pending) ) # Done return p.returncode def print_(p): sys.stdout.write(p) sys.stdout.flush() def pip_command_exe(exe, *args): """ Do a pip command in the interpreter with the given exe. """ # Get pip command cmd = [exe, '-m', 'pip'] + list(args) # Execute it subprocess_with_callback(print_, cmd) def pip_command(*args): """ Do a pip command, e.g. "install networkx". Installs in the current interpreter. """ pip_command_exe(sys.executable, *args) if __name__ == '__main__': pip_command('install', 'networkx') pyzo-4.4.3/pyzo/pyzokernel/start.py0000666000000000000000000001327613123037260015575 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ pyzokernel/start.py Starting script for remote processes in pyzo. This script connects to the pyzo ide using the yoton interface and imports remote2 to start the interpreter and introspection thread. Channels -------- There are four groups of channels. The ctrl channels are streams from the ide to the kernel and/or broker. The strm channels are streams to the ide. The stat channels are status channels. The reqp channels are req/rep channels. All channels are TEXT except for a few OBJECT channels. ctrl-command: to give simple commands to the interpreter (ala stdin) ctrl-code (OBJECT): to let the interpreter execute blocks of code ctrl-broker: to control the broker (restarting etc) strm-out: the stdout of the interpreter strm-err: the stderr of the interpreter strm-raw: the C-level stdout and stderr of the interpreter (captured by broker) strm-echo: the interpreters echos commands here strm-prompt: to send the prompts explicitly strm-broker: for the broker to send messages to the ide strm-action: for the kernel to push actions to the ide stat-interpreter): status of the interpreter (ready, busy, very busy, more, etc) stat-debug (OBJECT): debug status stat-startup (OBJECT): Used to pass startup parameters to the kernel reqp-introspect (OBJECT): To query information from the kernel (and for interruping) """ # This file is executed with the active directory one up from this file. import os import sys import time import yoton import __main__ # we will run code in the __main__.__dict__ namespace ## Make connection object and get channels # Create a yoton context. All channels are stored at the context. ct = yoton.Context() # Create control channels ct._ctrl_command = yoton.SubChannel(ct, 'ctrl-command') ct._ctrl_code = yoton.SubChannel(ct, 'ctrl-code', yoton.OBJECT) # Create stream channels ct._strm_out = yoton.PubChannel(ct, 'strm-out') ct._strm_err = yoton.PubChannel(ct, 'strm-err') ct._strm_echo = yoton.PubChannel(ct, 'strm-echo') ct._strm_prompt = yoton.PubChannel(ct, 'strm-prompt') ct._strm_action = yoton.PubChannel(ct, 'strm-action', yoton.OBJECT) # Create status channels ct._stat_interpreter = yoton.StateChannel(ct, 'stat-interpreter') ct._stat_cd = yoton.StateChannel(ct, 'stat-cd') ct._stat_debug = yoton.StateChannel(ct, 'stat-debug', yoton.OBJECT) ct._stat_startup = yoton.StateChannel(ct, 'stat-startup', yoton.OBJECT) ct._stat_breakpoints = yoton.StateChannel(ct, 'stat-breakpoints', yoton.OBJECT) # Connect (port number given as command line argument) # Important to do this *before* replacing the stdout etc, because if an # error occurs here, it will be printed in the shell. port = int(sys.argv[1]) ct.connect('localhost:'+str(port), timeout=1.0) # Create file objects for stdin, stdout, stderr sys.stdin = yoton.FileWrapper( ct._ctrl_command, echo=ct._strm_echo, isatty=True) sys.stdout = yoton.FileWrapper( ct._strm_out, 256, isatty=True) sys.stderr = yoton.FileWrapper( ct._strm_err, 256, isatty=True) # Set fileno on both sys.stdout.fileno = sys.__stdout__.fileno sys.stderr.fileno = sys.__stderr__.fileno ## Set Excepthook def pyzo_excepthook(type, value, tb): import sys def writeErr(err): sys.__stderr__.write(str(err)+'\n') sys.__stderr__.flush() writeErr("Uncaught exception in interpreter:") writeErr(value) if not isinstance(value, (OverflowError, SyntaxError, ValueError)): while tb: writeErr("-> line %i of %s." % ( tb.tb_frame.f_lineno, tb.tb_frame.f_code.co_filename) ) tb = tb.tb_next import time time.sleep(0.3) # Give some time for the message to be send # Uncomment to detect error in the interpreter itself. # But better not use it by default. For instance errors in qt events # are also catched by this function. I think that is because it would # allow you to show such exceptions in an error dialog. #sys.excepthook = pyzo_excepthook ## Init interpreter and introspector request channel # Delay import, so we can detect syntax errors using the except hook from pyzokernel.interpreter import PyzoInterpreter from pyzokernel.introspection import PyzoIntrospector # Create interpreter instance and give dict in which to run all code __pyzo__ = PyzoInterpreter( __main__.__dict__, '') sys._pyzoInterpreter = __pyzo__ # Store context __pyzo__.context = ct # Create introspection req channel (store at interpreter instance) __pyzo__.introspector = PyzoIntrospector(ct, 'reqp-introspect') ## Clean up # Delete local variables del yoton, PyzoInterpreter, PyzoIntrospector, pyzo_excepthook del ct, port del os, sys, time # Delete stuff we do not want for name in [ '__file__', # __main__ does not have a corresponding file '__loader__' # prevent lines from this file to be shown in tb ]: globals().pop(name, None) del name ## Start and stop # Start introspector and enter the interpreter __pyzo__.introspector.set_mode('thread') try: __pyzo__.run() finally: # Restore original streams, so that SystemExit behaves as intended import sys try: sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__ except Exception: pass # Flush pending messages (raises exception if times out) try: __pyzo__.context.flush(0.1) except Exception: pass # Nicely exit by closing context (closes channels and connections). If we do # not do this on Python 3.2 (at least Windows) the exit delays 10s. (issue 79) try: __pyzo__.introspector.set_mode(0) __pyzo__.context.close() except Exception: pass pyzo-4.4.3/pyzo/pyzokernel/_nope.py0000666000000000000000000000713713123037260015537 0ustar 00000000000000#----------------------------------------------------------------------------- # Copyright (C) 2013 Min RK # # Distributed under the terms of the 2-clause BSD License. #----------------------------------------------------------------------------- from contextlib import contextmanager import ctypes import ctypes.util objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc')) void_p = ctypes.c_void_p ull = ctypes.c_uint64 objc.objc_getClass.restype = void_p objc.sel_registerName.restype = void_p objc.objc_msgSend.restype = void_p objc.objc_msgSend.argtypes = [void_p, void_p] msg = objc.objc_msgSend def _utf8(s): """ensure utf8 bytes""" if not isinstance(s, bytes): s = s.encode('utf8') return s def n(name): """create a selector name (for methods)""" return objc.sel_registerName(_utf8(name)) def C(classname): """get an ObjC Class by name""" return objc.objc_getClass(_utf8(classname)) # constants from Foundation NSActivityIdleDisplaySleepDisabled = (1 << 40) NSActivityIdleSystemSleepDisabled = (1 << 20) NSActivitySuddenTerminationDisabled = (1 << 14) NSActivityAutomaticTerminationDisabled = (1 << 15) NSActivityUserInitiated = (0x00FFFFFF | NSActivityIdleSystemSleepDisabled) NSActivityUserInitiatedAllowingIdleSystemSleep = (NSActivityUserInitiated & ~NSActivityIdleSystemSleepDisabled) NSActivityBackground = 0x000000FF NSActivityLatencyCritical = 0xFF00000000 def beginActivityWithOptions(options, reason=""): """Wrapper for: [ [ NSProcessInfo processInfo] beginActivityWithOptions: (uint64)options reason: (str)reason ] """ NSProcessInfo = C('NSProcessInfo') NSString = C('NSString') reason = msg(NSString, n("stringWithUTF8String:"), _utf8(reason)) info = msg(NSProcessInfo, n('processInfo')) activity = msg(info, n('beginActivityWithOptions:reason:'), ull(options), void_p(reason) ) return activity def endActivity(activity): """end a process activity assertion""" NSProcessInfo = C('NSProcessInfo') info = msg(NSProcessInfo, n('processInfo')) msg(info, n("endActivity:"), void_p(activity)) _theactivity = None def nope(): """disable App Nap by setting NSActivityUserInitiatedAllowingIdleSystemSleep""" global _theactivity _theactivity = beginActivityWithOptions( NSActivityUserInitiatedAllowingIdleSystemSleep, "Because Reasons" ) def nap(): """end the caffeinated state started by `nope`""" global _theactivity if _theactivity is not None: endActivity(_theactivity) _theactivity = None def napping_allowed(): """is napping allowed?""" return _theactivity is None @contextmanager def nope_scope( options=NSActivityUserInitiatedAllowingIdleSystemSleep, reason="Because Reasons" ): """context manager for beginActivityWithOptions. Within this context, App Nap will be disabled. """ activity = beginActivityWithOptions(options, reason) try: yield finally: endActivity(activity) __all__ = [ "NSActivityIdleDisplaySleepDisabled", "NSActivityIdleSystemSleepDisabled", "NSActivitySuddenTerminationDisabled", "NSActivityAutomaticTerminationDisabled", "NSActivityUserInitiated", "NSActivityUserInitiatedAllowingIdleSystemSleep", "NSActivityBackground", "NSActivityLatencyCritical", "beginActivityWithOptions", "endActivity", "nope", "nap", "napping_allowed", "nope_scope", ] pyzo-4.4.3/pyzo/pyzokernel/__init__.py0000666000000000000000000000150313123037260016165 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ The pyzokernel package contains the code for the Pyzo kernel process. This kernel is designed to be relatively lightweight; i.e. most of the work is done by the IDE. See pyzokernel/start.py for more information. """ def printDirect(msg): """ Small function that writes directly to the strm_out channel. This means that regardless if stdout was hijacked, the message ends up at the Pyzo shell. This keeps any hijacked stdout clean, and gets the message where you want it. In most cases this is just cosmetics: the Python banner is one example. """ import sys sys._pyzoInterpreter.context._strm_out.send(msg) pyzo-4.4.3/pyzo/resources/0000777000000000000000000000000013166630335013676 5ustar 00000000000000pyzo-4.4.3/pyzo/resources/appicons/0000777000000000000000000000000013166630335015512 5ustar 00000000000000pyzo-4.4.3/pyzo/resources/appicons/py.icns0000666000000000000000000002634412662716363017037 0ustar 00000000000000icns,ih32ͱ ד;&S߂ еܒ& ڑ& Ļې& & & Ԯꬍ& ®餘& ɟ&ڊ&U˲Ƚ4tȉ#򷷣㵬Ͽĉ ȉ &$҉#<Ԁ"8ۉ6 &b@9:;Cg&889:;<=>?%#;Duv;<=>??M58uv<==>?@A 9:;<<=>?@AB&΁?@ABC";C899:;<=>?@AABC57789:;<=>?@@ABCDa789:;<=>>?@ABCDE$%>89:;<<=>?@ABCDEZ":89:;;<=>?@ABCCDZЄ599:;"8d<==>4<=>?J?@%ހ!8ۿɿʊ3ҳ$ 8ͻպԿ2&##!7ʹҸʱ_p2D[6Bm <& דX2 Zеܒ2 ڑ2 Ļې\2\   Ԯꬍ ®餘 ɟڊ˲Ƚ4tȉ#򷷣㵬Ͽĉ ȉ &$҉#<Ԁ"8ۉ6 &ѝ~|z}&~|zxvtr%#;{yvtrpx5ywuspnl ~{ywusqomk&΁qomki";~|zxvtrpmkig5ф}{xvtrpnljge}{ywurpnljhfd+%}{ywusqomjhfdu":$~|zwusqomkigdu5~|zx|zxv¿%ʀ߀{ywt¿"8"ywus4wusq|qo%ހ!8ۿɿ¿3ҳӼѾ%$% 8ͻպԿ2&##!7ʹҸʱ_p2D[6Bm <& דЁڢW/ Yеܒ/ ڑ/ Ļې[/[   !!Ԯꬍ ®餘 ɟڊɁܧ˲Ƚ4tȉ#򷷣㵬Ͽĉ ȉ &$҉#<Ԁ"8ۉ6 &ͼ&Ώ%#;ʮ5Ǫ &΁ך";ſIEL5FA=9}B>:5^+%ý~?:625":$;73.*5m83/+'s_[WRNJEA=840,'#%ʀ߀s`\WSOJFB>951,($("8"a\XTPKGC>:61-)% O4ߧ]YUPLHC?;72.*%!ѥZUQMID@<73/+&"-%ހVRNIE!8ۿɿSNJFB=950,(3ҳOKGB>:5pi$WGC?;62nf0%$D@;73/*&"% 8ͻպԿe?4/+.R2&##!7ʹҸʱ_p2D[6Bm <& il32 ˍ/*Iiͷӌ#&&KֻӋ&Jӊ#&&E¹塉#&&JDz'&&L濇,(?f-ḃW^˷ļl|ʇ ؇}kA;<=FpP<=>?@Y|9=>?@AC:;<=?@ABD ABCEzP89;<=>?ABCDEi89:<=>?@BCDEE?9:;=>?@ACDEEFy9:;<>?@ABDEi:;<=p;<=>w}C=>?m>?@V@A u{ Ɉ Ј ܴv&!\e65ˍއTLMZͷӌֈv3223ֻӋՍXSS]ӊՀ¹塉րDz׀濇ބ-ḃiq̹Žl|ʇ ؇}衃|yvyyvtqn~|wtqnli}zxurolif mjgdz蓁~{yvspmjgec|yvsqnkhecc}zwtqnkifccdy}zwurolifdcc~{xu{xvsþw}~vspĿtqnolu{ܴv&!\e65 ˍʎQJKWͷӌ x0//0ֻӋʒXOO\ӊŊYwza¹塉cQ9DzaO6濇s#dS(-ḃtƫl|ʇ ؇}ι﫥| Ӑ~zʼ|PJ\ҽ||LF@m}|}GA;=y}||B<61r=72,bba[VPJD?93-'w}bba]WQKE@:4.(+bb^XRLFA;5/)$Yb_YSMGB<60+%<u{`ZTO[UPJD>82-VQKE?9(ܴv&gLF@;5?!\e65 vH<602\is32 ā PT̿ >@&>8G=KUP>?AGſCE =<>ACEG E<>@BEF;=qI?E  ā S`̿ M\&‚ux~qkigc(Āxsmhdeytniddzu.Ƚ~qŹo, ā ˺Z^̿ ϷCG&ϳ'+޲ѻ@DꙎЅ}(ඩ}}S~}GH.v>3c`VK@58БcaXMB70aZ,_QF4S=?h8mk  ]|‡Y@$* 56,4&4&,"254&"54&"54&"54&"54&"54&"2,55,4&}}EODP,)+3Z6(,?63" 3, W䱋+/ #& !!$&&"  l8mk {(|gs46njҁaVnjaVnjaVnjaVnjaVnjaVӺ7qڢjD1"0JQDA0$ ȁs8mkoICpyzo-4.4.3/pyzo/resources/appicons/py.ico0000666000000000000000000004651612662716363016660 0ustar 00000000000000 v(^ h. 00 %  >8 hH( @ppp33p338833333333D333338D333333DD333333?DD33333?DDH33?DDDDDD3?DDDDDDH3?DDDDDD3DDDDDD8DDDDDDDDHDDHDDxpppwwwwwwww???????( ppxpx338?3O3338O333H3DDD3DDDO8OHDODwwwwwx~( @~dFu?w>z=~;gEkCoAr@:IbpƏKУk(,1?5<\CKQsU[cbð((&&&&)2,&&/..)).($$&),,)./......)..)),,.3()/$&&&)(&$&&.3$&/&()(.).#.3/////////+ ,.3()///.$/  .3$&////(/  .3////////1".3()////&/ 12.$&33//)/   2.333333//  .()3../3)3  .$&3).)3)3+  2.333333333!.()3&(3&()3)/3 .$&)./../3/33 ++ .333333++  .(.()))/)3/(.+  +.&&../.33.)33.33333/.)&.(./.)(../.33/333/$$&&.&&.//./3..33(&(&&((.(3/.).)3.(.(($/$&/.$&)//.(3$ .$/.3&.//./.()//.)&( ././.$&3)//).3/.)& /./.33.&).////////////////3/???( @~eGx>iErCE~7?HVbx          ?<???????????????(0` % !!$& &"   WܱNj+<<:5pi$!!!4888&SNJFB=950,(%%%5ǎVRNIEѹ|Jq?o@ZUQMID@<73/+&"-Ɍ444"ք̴wq?]YUPLHC?;72.*%!"""4888&dya\XTPKGC>:61-)% OÑ%%%5ʎA{;ys`\WSOJFB>951,($(9|:z;xq?o@mAkBiCgCdDuZ;73.*Ǒ%%%5̎È>89}:{;yq?o@mAjBhCfD~dEuZ?:625Оa789}:{;yr>p?n@lAjBhCfD}dEB>:5^ό555"܄ѳ„7789}:{;xr?p@n@lAjBgCeDFA=9"""4;;;&ŌC89~9|:z;xr?p@mAkAiBgCIELɑ&&&5ώĮq?o@mAkBiC9~:{;yq?o@mAkBь555"߄8Ϧuǣvyp?n@lA###4;;;&ċDҧuʤv{;yr?p?xM̑&&&5ю88~9|:z;xr?̷ѳ͝b@~9|:z;}Cg˶ӍԌ666"&&&2###4<<<&"""4888&,̑&&&5ӎ$$$6,* Y@$Ƀ&ҋ&ҋ&ҋ&ҋ&ܢU444]ttt|ȇҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ڥҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ& !!ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ݧҋ&ҋ&ҋ&ҋ&ҋ&ҋ&[\/2/2/2/2/2[\ܦҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2/2/2/2/2ۥҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2/2/2/2/2ڤА;ҋ&ҋ&ҋ&ҋ&ڠSWX/2/2/2/2/2YZܤׇ _?_??_?_?_?_?_?_?_?_?_?_?______________?_?_?_?_?_?_?_?_?_?_?________?____( @ !!!0\\\JeeeQ666D555A0$ vH<602\7qڴvvvj&&&D1"gLF@;5?кVQKE?9(aV[UPJD>82-uuun{{{j`ZTOVo@lAb_YSMGB<60+%<aVmt>q?n@bb^XRLFA;5/)$Ywwwn}}}j~Cv=s>p?bba]WQKE@:4.(+{;xbba[VPJD?93-'aV~:{;xr?o@lAiBfD}dE|cE|cEiB<61?9}:z;w=t>q?n@kAiCfD}cE|cE}dFGA;=aVңi89|:yq?n@kBhCeD|cE|cELF@mzzznjʓP8~9{;yp?mAjBgCeD|cEPJ\¯mAjBgC~dEaV}:z;xq?n@lAiCőPyq?n@~YaVΡkA|;y3Eyn@iB~dE}dFGHġ=xmAhC}dE}eGS˿gC}cEǾўUP@D~>q?kAiGĹϓ>8'+¢Gu=xKʺϓ>@CMG\˘PTZS^`~&&&&&&&&&&&&&&&&pyzo-4.4.3/pyzo/resources/appicons/pyzologo.icns0000666000000000000000000015107412662716363020270 0ustar 00000000000000icnsAw8~Dƶ5Xo 8p.}Rq?z TNsCk}zϸ*)Sj'Z+'vH{mbK_a>` *5VC5eBLrbyA[ɮ~gl9Z1*b O^ݲ Z5x:K4iq_KiLJTgAU1V%4Fx3F='Ü /% ŁyVӯlϼ} @ +;ΐ)pqvh=u& 5DӴrD'rVlOD+ar(^¦S6yLg%N>S Y40UPNrp$.C6C1;y7*C קYlU-CLe_ϼ}0 +K_\ \%x̄,VSL)XdlSKDu\BrK r2V/ '1T 6ˌ:6 5lRAz gcG]}AҲ\əX'fi&)ߕ)>V>`d0> +DѐLdej[HrϗMܳfzO. lxwI“OvJfə3~h0HUhB z߫4rBڭ"d~Idh !9^|!N]lB(|p_3x5;bQ#aVf-2\WoXAP{tPMRJ^Atڦ>xCO Yڠ.iC$j5MT ڍ?b߫PF@a'U#gS`<^ok|gB4˾( .I.͜y」0<^ܝ6&$(˯@e5ە>ƽtc~=27e"ٓsa2Gzv 83,iۖʩo s S<~bf;Gʐφ5 ke(sNKMgQ%w;4DKEVSu WbgƯeBI9WVts1BE4Oa^Wɯ# $or?гE kXSY0WlyAw 6h^37E~)xKkd :/؈հX:ze7 V.(qi:%[қLfMgJϾ}/{dǛ% s)$<ºnTStr \QMc/kj jlv2Cú "kjbL% ͑y +)3S. f({) P拧,8I]BS䏇#4Jf o7{:˜0IU"r7v^>E s*C?:OXYn{ ۇǃDh@zUԹinn УȦ,'"c\aiR|Z]#\H;L7A P}^-Q{W_ڇߓ3O,zzL/ŏJ*Vb(N@Lܮ ,#-_~ҋ>+uR"95:'vnhj(Bkw[0XCy>>0ܖ/P^p9@yXQ[B1:?N`$iH|tސxk)uq?eݨxF@nBObV%gWV {+ڹ H& GH| 3!R"xVs6EwDO KZ%TM=?^WL6p]W;=Э@;VC )ו{*87.jfyBt92G*+,Agh=(1/ 5IJ[]VڠTqhzdHZ ?=V P9ŌIcyIckM ]N{1Wԝp}-&='ȷ*dF9ȊQ}@YM6jqw UrXZd6:2Ee!rqތ~_p(#_8mr.K(;Cs. LFbEўu# Ѐ<{UG*hhS6 Yn@VrҎBB#^Zv`#4݇*p}$?J>nnDAa5I3oخ*70M# 1CVLKQJQE;ej|ڇ3_H9z#2*֩\tdn-L~$<&ߑua  T^{B`3X'K&^0hΙznG PgSbXEQ8Vg%!ϛ[ik#+/fVb߿:BI- ϿOoM@]I%S:)!eeʇg w<Ry^m>ґU|z?v=-<(U1M&'198e}d6T6/X,]Z(d+V`ߠAjR5#=LiFobVj9nnLap0Gp߁TkQvDE6)϶fĤ_M -l{aF)ff=nFYY)mF] 4Ǟ/㘰DZB X9AC\'ڙc +'M6<DR|c'VFIɹi[N5'q|,Ieo'o*Ǝ'?Q;{*Ap_ %\>S!Tw?ĂJy<S) %GLy(o|,ϱ98's G\:7 )`xӷ&%J>] %V5V8j|'ބB}f;Apt:q !nVD"7Ӗќ0 +68O`Ϊk#OϿJH]I%=`k2E] TCk^7լNJvRSFG]R4oz'uOǝ*Xohz}Ul3"V!ڊS&jdJ@5;s_dp!9Md.~;dcO唧n0n.2*jطV7a9n64KiMnHŘRmV/~FDZB,~meAK̮zH(D.f%:U#|IW*n?᰽Z7 { /YV()U@ƪ_F!{ȆMumri_QIQRp$~E ?aQdmOSK<9=hGJnae1e .k2Xݬb/}Ϡ&ѳc ^2fALE"ɜu(O6X9!*7H MR"[P]z+OEU#`5+Z7 ڇ^jfaa #OG>C r 1C0.w7SdS\? ‹SLI%0!\KY[ͳ2'ڳ1Tz=e֊&ĚA`.}PL[_ȏ֐[TOs VLrI+=bN1n5V|$fB79 6-na\ɾvܱt-M59%<<PujYnd 1,E%YPvxZrniCd# C$?ܗ(l&re;NpϾGŧ#'PR|~1oL̪)ϓ̇\lYo+?TA5jezjLWrd}\Gm~ݔC"cxO\}VpkAErsPcxIY,¯] kk`Mv hcҵx/B C+  ڒFi.݉IE6ʄ, D}AZ/u#[4De?q"dM Ig찀_jtU篝L5|^`Nf$3RZ"J2_e>|N0QMDq^Oe!s/dAK-8@a4?l2e )}*aŴ/hn^'h r߱x*4Ig3j};$5l9K (D:-\w-iW%M;'e,lM@XZuō};hJ/yCc*|n\P7fUwT]Dl-!c*G%%s-H#0(<ë _~\׊?3hpR5cDkRkqlA(^=/'@#^D`s8u&bs&){ DHne*~7J{ F~WiMb#谣#pAѧ>P&pu@:掹KL%d43$^gGEرF*aSl!N|TTv7!Y_Ÿ:ЯHMMp:58v)\pOM/" o _B\\. *@hu՚Mab-XslEKDF 0,%o(4g߮J*foW,^_c皬]c!vMivb.5S?"q?gȋ*J=qeR9^Q9F:c5e;d|N [}M7iɝm0,k"Ftql}݆ܤEIMcJxf}? i% :ܗ@{$->7bVgЕV&Rn.=4b_2B/ȩQPoۦ=y$pZU] ^=wPV)KN@RB̴u E`bt;ޥCӈ:0Qh\/˙'0F{jܦ75!Y˷Im"o VewpwlɆsHQHb>kxO[b9GDA_M4hސŘPr-w˷ۍ>H2 &Pcj꿭{O#ʍu4i0(n6[$I$u!QG7 %6o-M:S;`EWQiU)bz+<;2N'ɥ~r[q!Į^CtIU2" ޳T@fg+ CE6[3ZU܍8sDjx'ט6oZՒI$IDu#S"!]ب`Ҿ')O|Rgeeo+١'F?Z-KO#. FbVx0YEQSVf :5,r]km*syLdtH]' #m/}sfG d ǜmC3}E&! >2wAPh92.}.r4LW׺ˌ7ym%gZ<-X.F)+DJ[?/df; lq>/ny]Ѩ9gMݽ@n|I2UE{_Ҥ,f5g'ə:NG^EGʎ 4%#Hlpkè;~րPX%ϲܱk#<ݵ܏ );i@4O.C;V1ӹVG$% Y/YKb Pލ$ǘ @=XFϼYM?ϐؐ813뚧J$qz! ;y̑^'T?oSdec}Z1^xM#EpL-gQDf@$̱;-b%DcE_R˓nEdys?#ܕ j|n9TsrgWvRS.X@/ָg{kõMw"/Sg1xmInt 0#aCnMgYWnЈ:ڜpZp] ` >"Hk!Κ!cyֆ{[lYM )D-3|8L׭x) #שpŹ~l-$Ԃ)Hvpr1K @'0|C%Ud៼Ҳ{dݸ#%gCPZy`7a15fJ8LY)).M=poq%V!M@{ܜѽ*=9 N>+u>J w}U*hnO7 &K* UeY؏ dz'Zb N=D8دkP-_+E5c=Ѕ>cayނH7aHͧх:@ꩃ_u(h@m[Y]݇WΙ 5۲e,cG( Lw!SwʬZz5[¬ Q$yG|alnj${4ݧ} VVY7|EZx}-C|Cto1v\Bk1_\tso7פՊ?å+>B~ab6a9Ȥr>ΎɱǐAyfNeBJ0j9zjLr4DnC>lXHr/9ԼX "d0p^q)UD%nA #A BD`Tm5d1}EK=tE7k2&32m0~ׂjdj8*L6D&Y(is>j59*uPIvD'ٜ{ډ[' nY(6L%kENj'gi2\BzZs]جvpq\EAOW( NeŢv؈NaInhV'*֭ѸSA(F@brdŗ!6dGXf7Cz5Z'HUq1MsCXR@v>ąA F2r'O{Aۑ?Tp\'+X];xN⃓ y,bZI( ,Ujh#yNJ"+"],*\*3 SU#k"aI7,W0XjxO`>W?ف2$ְsR!7:/:BTU7M{"-'15޾/xAʢKEp e/ǷSO1Jz/w #ጧ@l{,<,M!f!E/GBۋǝ{ r$TÍF^J(ij|,L9/(t }qϽF+~L{™Gt_ %JR =pt1ũ5(@1GPG~0֤EQ!!JM]hyAdh;IuvP ,.E;oG@r\2'R,kHni7Gů&aDkwX1(Ρ",Q++G$I$J&q h0A@whP@;n085P0%S$R"cXh Ş(^kaחѩC%+^юOH"+q1G>PrI$Ā6I$Lb:@9i'Jf'BnK-aq7Na-Ҧ kN`G'NC}EGbDcE/Ͽxg߸@B]DǤ>P|^?AOݍV(8~=yš,"d>ŮȃaRK޽ 魺oLon?~M80Q.ӐL &_Ok&]mi m{Hz*|}:'LTXՃO؈u6 P]ؾ|>4]@ruCc ƪ"7^b?_ WcA]mx GVvɚωgr>d &_HF֔ ʗlAgԖt[w1c6$UJ~䟂F*z"f+T9R2I~=D` u7ݪB;| 6A9E7Dy2WXa}ILq\Ƹ.]yp?{QU# F%n7j Կp;#<];qv*NIlF sccԒ[mmE3f&<*&}]bXe|:[ e UJD sx11XA1RqmI~qE7@>WzhgK-$I$9@#@Rl^}dr8|{Iq셶m܈ ]=> .< oB=I$I%6mmPt#@#*GܒI$]eePf^͆ ݎQ*YבE% g 5rfKBtS iN(&#i22 Z IP!1"/$鞷oVKڵ(f'Lf|f Q. D4_v̕e˕cguUKR pfXoꦅ쳮2pM<s'[~P.x$KjZ_S A~8E{a'P,S#|U5.~%gʣV0^c@Oߩax?a5‹m}zyz!욊uf:ʧ/ XPVBG.NنTB_Lz&DqT5=2/VI=)K=_qzNe=\~~s`xQ"%lcʁ7 <׈ȀOq w`pUj0SpP;CQ)]pFd41\9!>uFis>Z%嬁 5Qz`'sr=OYAdSuAZ,b2rb-ПO\~2HhT_<eǰ㱛 dp`f-Fr,ҝ4ѐhb=ڮPfgDy2Z )p]=5c8,E"α0~Nƪwi.eΫ"<N[-nK̓UAvׯ֔غy1|RƦB[ ^ Ԃ3,)f8hq'\(tx';ˌ0Q'"T ިxכ(]ˋSK/?ש)ؖgIJ4MM #s"% [ԙ&H%5jhm ԦC"'ۿj4^RӭAubĚ+W_@$ "&)2o_r트4[|ƪ4LAH ܍}6^CNVnG=؏ e);M>Nz}5AߍKq ׋G{8s]^ls%(dž'%X/y5YeZf9)#|_,#褃,GiAN,ClNҒԐ[dml^/rĭ3-Vq"Py}[褣m :[Kiuly)p/Ih&̉M=]~Bԓ΂ĘMA /Ya_ra2/k"bP^R]FgW'损@-GAk5M^sS|H]N3CMtH,+jJ·]0vPsNΤSaz1e!M֯ PAm&3}> $h$C' ~L̗ jX?Tk("? *㺆 ;c(moY ڟalLDaJ@ h.vff_g` k7 6)=25Af4^(i6s.`\IZg $a&u߭ \X ~-PMoх+/4(.[a.jc>{lXCp@m0GFǑ0ň+Y!9~Z)bmˬVNCcaXu=ʗ_tV!/caW"3+GڋDXP7}W eX҇2d]!P9̝M.7Tv0v|V\(0ؗЍ)}c7acD$@@ AZ9WjXo7k#sf~ R[QyQ(x1qQ?wSr ڹyEgBVnZyx`A_ [H ?^mBc۠[WixUVȈ61/C+BAtK(vJ3٪ Zݎ()Ĕcd dEz*40 yEFnHH=ےIpgK^H wd K #qQ˦Keڈ츨6LbےHU; l?ZCժ6gC%EQ@~s@WlQv۸d]=*qPPmmC%ےIA P  wAl-&7C/l^%mR6#'WKP IDrK>9)"14 Bl[A浀r?'2ati'e7idaR.P˿}B~e.A}0yF+S=0GQg <4@=j${&.@L[a $yƵn:'_0vqd>>T-/v5T P#5aKM}pL"Lu _o$C;c x_٦6[^:0 u`o7m3]XU ٭9eׯ"]gbXiiH4 ?Wſs]-Y # BfؽfkFB ߧ9m:Ct-Y Ǒw?퍀`!l_J0N+2U*{-~zoKWuULNLpZ yLn ȷT]"+%aH K0LdޘeLRMLW){s$>>(.Թ@MfwY/԰cOu,C欍SnVy}t*;%UW!ZRti.)?n(=@P:NG.̈;D\avo,\r3+2 dM''U?hs8el7LPN>vXR屡xgn]KL1ƓGk> ?ѫ~aُOSѿyz!욊uf=FH~5?0i-BK^,(98pJ1kSv>P"86]$[1S4%O8G6PC"|JBUI29\y!YpoT/bDв Ti7f{9՞c+!;<>+s|3Y)ī 15w/@Tאn.:S=IMfpP@9L$ٍtT'l$&e(?NIXc*ǎX4.sr+g1fDJXN)I[G-'wSM+ɸ Q.2$RBLi>ҠE`9Q8Xf"ӫ? L@6 bJK?50~JEȨ$f(p^9q; 3CBH]AX"T MNf98-iqBU'SSWalk$d1zS~5lVl Ac&6>\dY7,fLϙZ%bЎ*:o,ȆLZ}7 ,A;/ mUv*ڇ0a!܃O!jò:f$A9y3b0;F7 l; 2ھ1RQ0|D/̉jm}zr~`5AߍKq ׋G]=?1@2yI έ"|K0b |dRXF[TEDQzмu>;6_Mo5qӑDd16W^h7o?Њ;*rz`rtzwQ}`P>NqFڀt.GwLjm*(En)RT]6U- z0KP𿞖ÔGJ*ΚkkR}j8wthG5m#'5@/Fg< ϿzfKK2}6 y$W"83--3낱 ߎ (*SZi|3~x;W>ӲssjvK_9Pud>t2j@:XwO7H  P+rq1r]0I_U5s@qO9V4U ɢ޹\ec%1 ʱ ` /?0H K0 g` >>yuRc 8~/ 2)!AqwFFy0zysCPJAz pPK|Ha_f8 ڌU'K eN])kN`/~ 7>Jl .QT%V v};:ܥOSDF(tRāHV>+pAއA6u?)&u KA/Y GmrsfѲ"6Щ)j{Ҽr@+_HPV1 O "bD9V']|t]ÐP `:E_%ZUȻNQעO>yߓ6$fe B3fUc.Rg0=t(ٳʄVsF|sP=Rg l0 ܀;3C#*4;eyOUs\úQ.]:ZLCt|X~ ,9ɛ'}O>~M 5u%awEۂ[旞 a`&XK/F^msW>ɽaZ8 ?ۻ5U~A0H;* ~w,N- P8VN1W*X^tDzPZ،s7@|PP|ɛIJiFh?f#m NzV5ݷ9_na> #I;\o$= D0ےI<FI$_!p"mrv 픯s MVo wAl-&7C/jP=N-"sʱ6BAC?-BU/ {dUExԱ9+ЎF{H۠B|H2+tcwn;l&}XtV:Wٻt KK=mF+t SVn M",8UW 5w @/^#ɑEuutE>M-0Fhk)zVg΂X4vi pƽ&|?&ܼec,z,_0T-'z,e+ISSg!2dm=YLqxԧ.iN˵qsܤl4!BohB'aC!z.-wT^jd |>ǝOf߄\hpi?XY3vAe2H4E8Tt$x/)6P*y˺(awY/԰cP*kOu!tH+ YFkF5rpMzQ(yj~-t0aMjNq4錃}Y ( |6.Nc?Y!eb m)썔 ]Vu ?ZcHBߥq+2Nsn.:S -N[WBܷxToԁ5}xuʜwZKF -o4@$T!Ɇo^TʴwlX:`?遙x6ސ9P PVU+R@EHV?zE ~I5{Y?E\Cud*%܀ܣRW`J>a͐ˏ֞9pdžP^Vɑful+(%c\"T 0 TO,&5xPbx6i⚁h$j};y6eYe<.5U~K.;wȟYa򗔼+ge4d3jD̫==DO# wK:6Ps (0eEܕ,n4a`|Vatb"o"?zfBYeu9FB,ј,%VEXc9-N:Il)\f eGÚ$ZNW~ǟi|O$6S~w4CLR4nT apFKny3F]IS$tDF.)·"ܔ-*&ϔo;[xtt& Gt=0ωoJQ CN؟S،Ʈ^xFg< Ͽzg}o|D $(X&K@j1-:cjTdE?[UG V{@۫z_@x}( m7:V)ƞscV;%ɪ+nt&)M(R'.:.q2ūsf7i~h!M,V FIcpBļ vx}I)w zЄ?#+xsf⍀~|lHfCV3'-ީ&vrE89P@NxO[7 Ќ3$+/u57p֥ dl fY A{A䃂OP(ѿp $#}XqLU;,X0XGw TJ ud m|2T YD"@#VS^:ݬ9'.0zU4KrP3uP*צ֗ ~%J*v٘OqJd45*@Ɨ"rfL`Hvle![fBx6$8lZ1ѿ`]t{W&w@6}[\GUTLXJ|b0?3,TY( JLSM x1d%dhcW]#VmkUR^($Wuf/[/ ܒ0<3ƠےJXz[?^;rI#cTk[ےFx ܑF}[H !ے(ϱ ut wAl-&7C0BnZ;ÕW(:Q!l63&;]g,YɒW?h%'Bf=Vdf}-.~+   e@::2J%*#5 3xҁU!FIno v3LF+t SVn:v|.eAclpQ-:ה%^'pedBZkE?8 }qXF;~c8DQʮnY.X_Y ϝ.Kc~RY.hv7҄2Lܴl űƖ ]5)"x G"FA3U_8,~iī9kHr&ВuiCuA(H:N9*H6 s /s~,dVuixvd&ZH2F$/=x 7!yqA™عPZwl] zA0}PI{l@dn4K>/jc+\H7kyS̱MyKDPW(K9e\uoTsYM1jE7ofuF)_ }ew3oI8ҭqMN$MaDV88__0/ nFt3 B-Y<,h~R_ȽP;׍=,'~P9b C5 _l2Ҝ=9m),ޒd 6KX5-jh󼄺4A|q %o BTud-u ӵ2@4re$s`DRBr8E<6s$?*S$I$I$I$9ӂ*V9b~`ﺏRRbxB-"SG { .ɁĄ;/XlJ놪 m}ˉ8P-pP}A$I$I$I$I${Sbr/Ffa6^e)ҡ%~Je `P@/SwvtZ*I$I$I$I${!7VebE`ьj!)$I$I$I$IԞn_pE9bHT8iwQmmmmՊq̀ekTù" :>@ʞK<˝rI$I$I$I$|)iy6 <4:01TԂ!n7a7lfX P/^ऍYiB (ġgv|$'1-܆~ 3*lemdaS;_AP&5PJ} {w؆=ܢė0`B.0Hm]#D'jm'AcC~.SAdž?snI#[@@I$)uvjvCpe,$H?Wy$I#b$N/F̗ qJ!@p# y $R#A$jKrHаUZ #Z/\I I$I#JXȚI$IT)3;Dj΅[ZCƽI$R$HI$݌F*$Is $H꫺!<4]TnI$CdpjܒI$A==V `. ̾E)O( >D7l*kWLuo0T>W5@wH>y5a3!%]zz#!Hx^k_oƴc5+#$q}Mdyt!ه+[_c^-!c! XC6q ٲ7R_o,J(߻5@Ar>&d=ܥܴp}IsikPđEh#ny_Udz5v^v[MZ UWn(aCU‰ɛ }k!Q7R?מҾ԰bx旄j(;S3[Y|՗ݣNb>qn=/eIdf꒖u26OAhh L-vX^{,TMI/ƍKa3(z]l0^27;3*/oYmBB#KK oRU.Enit32Y:'&'4"݈*&#"݇:&'&$҇&܇&܇&܇&܇&èèè܇&܇&܇&ʂʂʂ܇&ǂǂǂ܇&ǂǂǂ܇&ǂǂǂ܇&#&ǂǂǂ܇&!!&ǂǂǂ܇&&ǁǁǁ܇&&܇&$%&ʆʆʅ܇&dždždž܇&dždžLJ܇&dždžLj܇&dždžlj܇&dždžNJ܇&LJLJNj܇&LjLjnj܇&նʉնʉնʍ܇&܇&܇&܇&#&܇&!!&܇&&܇&&܇&$%&܇&܇&܇&܇&҇&&O&&fO&&&&#&&!!&̏̏&&̗̗&&hh&$%&&&&&&&&&&&&&#&&!!&&&&&&$%&&&&&&&&&&&&'&$0& ssG& ..%&\\ " *&%$/މܐ&R& р܀&ززز܀&&#&& ثثث܀& ܹܹ܀& ǀ܀&܀&&#&&܀&,&ۋf&U&&#&& gX&=1&&&&#&&&'&'&<<gH6211!@2 22//22//22//2221(/21(/21(/22hp2 1&221&221&222 )-22)-22)-22 22.-22.-22.-22hp252 32Uhp qcF:hp@HHևЗF2/-.Z/ //,,//,,//,,///.&,/.&,/.&,//Ą/ .$//.$//.$/// '*//'*//'*// //,*//,*//,*//Ą/0/1/UĄ  ҐҐĂҐҐҐщ,щbis32A&&&&&&&&&&2222////҆҇҆҆҆Ҷt8mk@  SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS((((  @@@8@8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@4@@@(@@9W@@@_@<@@CϷW@@@@ 8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<@@$0666666666666666600000000000000006666666666666666000@@  @@SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS@@((@@((@@  @@@@@@@8@8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@4@4@4@(@(@(9W@9W@9W@_@<_@<_@<CϷW@@CϷW@@CϷW@@ 8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@< 8@@@@@@@@@@@@@@@@@@@@@@< 8@@@@@@@@@@@@@@@@@@@@@@<$00000000000000000000000000000000000000000000$00000000000000000000$00000000000000000000h8mk     3_3_& B@@@@@@@@@@@@@@@@@@@@@@@@@@A,@c_ @   @3_3_D& BB@@@@@@@@@@@@@@@@@@@@@@@@@@@@A,A,A,c_ c_ c_ l8mk~~WS@@@@@@@@@@@@@@@K@C@ C@@@@@@CC@@@8 B~~CWS@@@@@@@@@@@@@@@KKKCCC@@@@@@@@@@8 @@@@8 @@@@8 s8mk@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@pyzo-4.4.3/pyzo/resources/appicons/pyzologo.ico0000666000000000000000000031452112662716363020104 0ustar 00000000000000 f (I@@ (Bq00 %^  A hPNG  IHDR\rfIDATxl}<&dUN,b'd mM tt s6 -0xsb.: M2(`lka *IJ-"KɲCRȻ{4yG߻<Ȼw{}x#[|w@01` c @01` c @01` c @01` c @01` c**4Y4,X>/<f53C9YFرx󚊣qcb2Ws.6KYi4G-brYLRs/(DY#&-8J+ P~eU0 c4[}o1c6Z'zhN4|{S\Cw7{ r^vE])thqS" p>֕__9%>(RK./ĻSԭywhX8W[/+hU~~n3@9o_x˯n8E!n_t&#MoM󽸔;4sV+T~O}n\/C[+y|HT"e/gk>uߤ$Y?9 嘦ѯiJD1{8rHC浼'?FyKK{e-#FM*z/~3󺳳sjpp0yos-羙hl9RB&B#yp:~p6Tu YHû <<+l޼96>>I5zަ=]@`N2Myv! B)/.޴pZ=+ B"QZ2o_ukj`Nu-|ڍ{v@̩ uf#sT2Tp9 `AeS ȟ΋F3 Vp 9s#T! J@Ս{SKU70GT7g0G՟T7g߀%sTXu#?HF2Sd,4t2J4?i:i\ ~0n*wyJL}Xv^#ؼ-em)H F2W`d ԫ66Pe!OΕ׭ЭާU4@ #qF 7R(~FoGZ_"zx +)G7t7@.;^kcb+dM^)W@3#d.Y ִuP8MC`+ޮ"=:+zr7 CeoIkS9!9N!!= UP]` `Ns?9=|@̩TW~`N-s 9@0?Z-ܪQoj `>E?qUs9@Ti fYoZDi4W=+ B,8Op;y aj #S=R0 ?#*~Hj' 0GZ @`9o @50bd:\I4?i:i\ ޸Bx@h~͛(^6 ޸Bx6`al@P;/<> kp#;K^5rp$( @9B!sJlem+ޮ"=:+z  T7s @́x0 7sr 5 c&6s `BLW~c5z?AuEzp4kjC(2>-TWz66V֢TCH /n F0b_Uޟ_hHiu QHrFH^La/yOzDeX{NPJS^='Zz@bWoU/ ' Y,[ @2'HzU3(> yOX$a|o4_}G>2ғˁbx @roCxϿl޲#"prc93ȳK-C"zvʙzƺ9w?秾ld)v(Qm"mV,_^Wt@ H"@LHdYK Ulgzy/!(kY]]]Dp}}TO<7.^󣣣D"aV3P(Xסˆ/ug|@b ^^?$51X!S>IvNL+"is Q+~HN}6䲘n{7lM]#YbⶊCR4+ ǟK12 D6̉h~Bui 0bb{ +K7E◕iH>,>qn]Oyiv5>j.< 4?Mli/kVX'`K~;]Sn'¡uwt<ꗿv_x{ـe,o]atW!ȯ_$#y_9q <AT;wu~c.=`oEM5Z%_gEژ9y曚_ &}%۸9~;p񑘮\+>v{ q ;ʛ`4̭иoD5ro o8M?8]cE8oG {2U3iM[ڌ݃A${57v'}.e)&ۭ:=:+z  N6l|Tg}EA&F<cZaڸOEWzgʋ\>ůTgc۷#G[T}!a'~粰@=ߢ;;;Wn-~“x+g^sk'<vxE~f?=W87O/ ` @ 10 @@c ` @ 10 @@c ` BRLC'3S}JG#HOU@WlDv@ X-?ܧ 8Z^gnT?8ZTp$x^@bf̥Eǫrh~ZAf@@zz $Tol%-R@c ` @ 10 @@c ` @ 10 @@c ` @ 1PQH ؔMf2/86SALVy@A~/=(U~Ͼy&FBY4`rl>ۢ:Xp3LﭬEc즲˯`I'y%BY/ @",*w w<K_U,ɟ|go//e-i:]w8\du{7sZ(HAup&yK)"g2y" Q$+NT4On"QDt23{ 2tOߩ>:x BcUG/OVu:Ks3'ǞyPbp膮)Y:BEƟMI'|U@^%<=wn]+ͮߟݳ[~>ԅˈlii~yj*//!J @"6G)rwKK.A >" z"qS! _idq݈,x/rvv8㯖.1` c @01` c @01` c @01` c @01` c `%IENDB`( $00000000000000000000000000000000000000000000$00000000000000000000$00000000000000000000 8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@< 8@@@@@@@@@@@@@@@@@@@@@@< 8@@@@@@@@@@@@@@@@@@@@@@<b@Cv """"""""""""""""""""""""""""""""""""""""""""϶v L1 W@@H~ҋ*ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&щ%Њ$g/FH~26/2/2/2/2/2/2/2/2/2/2/2-1.1ܯ!(  @@@@@@@@@@@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@@@@@@@@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@/2/2/2/2/2/2/2@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@/2/2/2/2/2/2/2@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@/2/2/2/2/2/2/2@ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&@/2/2/2/2/2/2/2@&&&&&&&&&&&&&&&&pyzo-4.4.3/pyzo/resources/appicons/pyzologo128.bmp0000666000000000000000000014006612665701505020337 0ustar 00000000000000BM66(ttҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Έ%w pppppppppppppppppppppppppppppppppppppppppppp{!Έ%ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Έ%w pppppppppppppppppppp{!Έ%ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Έ%w pppppppppppppppppppp{!Έ%ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ȅ$lhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhj}"ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ȅ$lhhhhhhhhhhhhhhhhhhhhhhj}"ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ȅ$lhhhhhhhhhhhhhhhhhhhhhhj}"ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&w}!Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"Ă"}!lhh#ҋ&ҋ&ҋ&ҋ&ҋ&y,;]'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j;]n)hh#ҋ&ҋ&ҋ&ҋ&ҋ&y,;]'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j'j;]n)hh#ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ы&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&uhjΈ%ҋ&ҋ&ҋ&IbzChjΈ%ҋ&ҋ&ҋ&IbzChjΈ%ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ԏ-ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&lh}"ҋ&ҋ&?n)h}"ҋ&ҋ&?n)h}"ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ҍ)Օ8ӎ-ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&}!hu Ҍ)Օ8Ph;]hu Ҍ)Օ8Ph;]hu ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ւ4֖<ҋ(ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ʆ$hnՒ4֖<{whnՒ4֖<{whnҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ˆ$rpppppppppppppppppppppppprΈ%ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&lhhhhhhhhhhhhhhhhhhhhhhhhhhnҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&lhhhhhhhhhhhhhhhhhhhhhhhhhhhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&{!jhhhhhhhhhhhhhhhhhhhhhhhhh{!ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#Ă#ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hh֖<֖<hhҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖<hl֖<֖<hlҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ˆ$rpppppppppppppppppppppppppppppppprΈ%ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hhՕ8֖<{wh#Օ8֖<{wh#ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&lhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhnҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hhԑ1֖/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2<7Ýhnҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&֖<֖<ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&hh֖<֖/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2<7Ýh#ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ԑ1֖<Ԑ0ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&y!y!ҋ&ԑ1֖/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2D>ǁbDž0ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ԑ1֖<֖<֖<֖<֖<Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6Փ6֖<֖<֖<ӎ,ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ԑ1֖<֖<֖<֖<֖<}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c}c֖<֖<֖<ӎ,ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ӎ+Ԓ3֕:֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<Ԑ1ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ӎ+Ԓ3֕:֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<֖<Ԑ1ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+Ӎ+ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&pyzo-4.4.3/pyzo/resources/appicons/pyzologo128.png0000666000000000000000000000343312662716363020346 0ustar 00000000000000PNG  IHDR>aIDATxMhUGnmXVEEz+BK/=z^,4Pެ[C[HAH/` =HsJ,ZF ئ|mikv~| }yٙ}@DDDDDDDDDDDDDDDDDD!-iZ i!DNo_-j v!.W{WKUiZ@oMοYwf&HϬ} N:} ~9QW P9s 7GpVsCAOPL= ?{`BSSK#j,6lU {&;foi~sϦ]Usx i\B OME|*ԌWa5 q.\q{ⰺS=6~iBqK  u w.Sc1ϴPqK v̯ƧvBoUکO-!7f-T\T4WUφ*_/~3f2zAqVTN|*l._/ Cz;bS:`jA˧7p:0L<@"._MĢȬ?r٦+oe>(OT`*`dv7!@XrD@fAG18^d\Qވ'@cu=V+C_~e-"2+) |~H]q9i:>%CMW6f;(nOVm9|5 @ vw0UyVw>ZH[v`ܺowz1uju] x;SˮO._/D0]^<HH$wNHj 9Ҝ:$p5ᙚe53?d*@)cXk {[Clc%*ץ9WN\xgWߙ^Sג7p:0z{|iSxw 9?0 Wgli+hs+0/:el Z/@f\ x 4M4^A/0.~/j6_=?Qw.Gn͞J-k,|A%2I?{U1ޙ'GOqlwJ ujLV.]~qB|;8~ N_Ä[ ?+fɋnu]=G`V+9OΪ~"""""""""""""""""""r\r(ϜIENDB`pyzo-4.4.3/pyzo/resources/appicons/pyzologo16.png0000666000000000000000000000033312662716363020256 0ustar 00000000000000PNG  IHDRasRGBIDAT8c`h6pYϦs9IXLP6xL1'BHʹx4u&<E I|&FF_PG3gj"QьPPF`, baIENDB`pyzo-4.4.3/pyzo/resources/appicons/pyzologo256.png0000666000000000000000000001070512662716363020350 0ustar 00000000000000PNG  IHDR\rfIDATxo}3399D7E*QڊM")TABҪjh R +AH$^@& 6 }>;۽{3{{33HH޽=3}fvv """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""҇Ť@.6X]BYuB`m$K2a(ꖍp\#:i23ԓصq[wΞ= ò⓻޺tß,na rqk҃^tČadk7ZR<p7 oXhf;};HpvQ8p_=C{y"lٳ~"pvVߔ>ߛpjǬf.=ah'?g:ǯ%M#ĎFTRnz~׽ `um59V5K{hE#Gj?~vX]_G]ʊ U߻H #@dl ߝLb,tҽ~z3Hņ_,sLG@e2tP7뇁@9-.AF{003ty6h6[F{00ȗHy+@Ac/35l#н~@AbP[To ڦz#н~;t BF{v1(07Cc-!]Wф`0)ÂH~|Xwݦ]L `v)dP;uٮ>ͪ|kyQ וry3_i^V Byj]+sB2&10U@zei~PlPm{' *97vbT̉l0(Mn Zy}rtbh薓:WNnu.8ZF~-e~mʫW_YZlt ]x8hP_ﳟ mQ}TESBm1|cкY|ImV\~0h3;咃DLU{ 駟Z,eɤ[7ۥ_Ae-`hLJ)PyOH`S3gx:L͡2-&/~8;jր6es~e ]NG_?NJץJeri<~⁷vKᓷ[HLσٹř[uꖼϿto󥶋*dg>ģklE7>[so^ ώn}W8ѫw𫧞h-E ˾%@־&*G7<ҽ>[ߖx [V" aSfa꛻0 "`{4D ŠGYX&xٚ o?ޭAmuSHD]&驎6Q\B,.TwBTkr#Brrg]FK~OƷ? ʾ~rߔrӅ1t?m%;´`~ u$L֬~ڡњ392Ooܡ[Qpo ⩐Ԧ3ibɓ'շu?*! k=B1mPl[h=AtS,-T݆0Y֦w@fndbk*ۮ@1Dci@1Dci@1Dci@1Dci@1Ɣ\\:6ru KyMaV~Ȱ`$ROe"Ai|eC>ͪAK.98fi<]dk@K ];r DDPr120U7ȗHH x%#emP"_"?HD]6\xz H'i. sxF,D))kd\Ei"S4 Hc "14 Hc "14 Hc "14 Hc "14 Hc "5 !Dfgg?Ƶm -!{әP5~:t%7L!.iE.\ku' o/_ksZn ČN^yX?~1so3x:(X;v$i6;8?> P;~;e1o|NpgeRr8Qw >~:an._[1(Oy^?u:]ww%Ѵ,%OF:<V>O5Vy/txN?oe}"]ϻ!-{y;ZnOWR^&vN?W>_e׉N1{G5Qkvnq&w @egpt 8he1sN4X쿯w;w |wԀ]y?U6nمlW9h:g3ע ]?F̀@%}z=g{@fZG%M.m:|ƏA4zb/Zy""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""~خHF6IENDB`pyzo-4.4.3/pyzo/resources/appicons/pyzologo32.png0000666000000000000000000000112112662716363020250 0ustar 00000000000000PNG  IHDR szzIDATX=oP뒤I.LiTPU EbbS@#LHH, X*u)*_ (JTӄĹ %/O>\DϥIENDB`pyzo-4.4.3/pyzo/resources/appicons/pyzologo48.bmp0000666000000000000000000001546612666132156020266 0ustar 00000000000000BM66(00ttЩͩvȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrȥrǺd```````d```````ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ѝ/ӿ ӿ đҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ˆ$y!y!y!y!y!y!y!y!y!{!ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&Ă#jhhhhhhhhpҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&oƺoƺĒҋ&ҋ&ˆ$y!y!y!y!y!y!y!y!y!y!y!y!{!ҋ&ҋ&ҋ&__Ēҋ&ҋ&Ă#jhhhhhhhhhhhpҋ&ҋ&ҋ&O/@O/@Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&{|wxwxwxwxwxwxwxwxwxwxwxwxwxwxwxwxwxwxwxͧĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/279Ēҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2ܿĒҋ&ҋ&ˆ$y!y!y!y!y!y!y!y!y!y!y!y!{!ҋ&ҋ&ҋ&/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2ܿĒҋ&ҋ&Ă#jhhhhhhhhhhhpҋ&ҋ&ҋ&/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2-0),/2/2/2/2-0),/2/2/2/2-0),/2/2/2/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2,.#%')/2/2/2,.#%')/2/2/2,.#%')/2/2/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2*-#%')/2/2/2*-#%')/2/2/2*-#%')/2/2/2/2ܿĒҋ&ҋ&ˆ$y!y!y!y!y!y!y!y!y!y!y!y!{!ҋ&ҋ&ҋ&/2/2/2/2*-#%(*/2/2/2*-#%(*/2/2/2*-#%(*/2/2/2ܿĒҋ&ҋ&Ă#jhhhhhhhhhhhpҋ&ҋ&ҋ&/2/2/2/2,/#%&)/2/2/2,/#%&)/2/2/2,/#%&)/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2,/#%%'.1/2/2,/#%%'.1/2/2,/#%%'.1/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2-0#%%'.1/2/2-0#%%'.1/2/2-0#%%'.1/2/2/2/2ܿĒҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2,.&).1/2/2/2,.&).1/2/2/2,.&).1/2/2/2/2/2ܿ潅ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2ܿwҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&}/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2i٠Mҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ҋ&ԑ3ݧ\ԮprVW/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2/2`r 9N,Uf7''G 0`r6@D>av׀B^{ظCv/똵 w4EbLM,]!DiV0H$X J%.Ir5v|BqJd[]}~q B54 HW72?8cƙ/㺭v@J ^18 @򄱥 fRKW9ڝu|VNÑM8c:ɡGLIENDB`pyzo-4.4.3/pyzo/resources/appicons/pyzologo64.png0000666000000000000000000000210012662716363020253 0ustar 00000000000000PNG  IHDR@@iqIDATxϋeǿ3,lE {A/.,BA FOEBۃHʲ`'mi`t1Y$zNM'l'\2O>ÛwyD"H$c dBP4@-N (B@R 6!DD"`?1 'VUzϟ^3{8Z'* AX/;3_4aLEjuQG2y 8/83O_u{S_m/XҳKRB$z%yp&X$)>jxXSeG ŶiniU _49g1ln$*YbNOp_Jh$_Afဨ)?:gFw;]0i>݉ f@GcWԥbk\jvjv;4:a 01ڝ$nlg~n$JJ1dUgՉn$'$u@TZ{|h7fv;8j5R+qhWZp )0W.g0 ~Zn5f쇯5W$XOsLf@滻rҟغo?p{0\0뀅>IY֣[Duhu:W߾ Eq0xY}זV{bT=7R[X*J_ #VlOVO-:f ;^$I'Q2{L@͡\4"ѕ7V$=:ni6#u V!ĠInZV:ލ;BxsD.NrAXfa?u5qNH)<.ŵ!0A{rFQH$D"]T!7oIENDB`pyzo-4.4.3/pyzo/resources/defaultConfig.ssdf0000666000000000000000000001062513127420321017322 0ustar 00000000000000# This file contains the default configuration for Pyzo. The user # configuration is stored in the Pyzo application data directory. # Some parameters are named xxx2. This was done in case the representation # of a parameter was changed, or if we wanted to "refresh" the parameter for # another reason. This enables using the same config file for all versions, # which means that users that start using a new version dont have to recreate # all their settings each time. state = dict: find_regExp = 0 find_matchCase = 0 find_wholeWord = 0 find_show = 0 find_autoHide = 1 editorState2 = [] # What files where loaded and where the cursor was loadedTools = [] windowState = '' # Of window and tools windowGeometry = '' # Position and size of window, whether maximized, etc. newUser = 1 # So we can guide new users a bit view = dict: showWhitespace = 0 showLineEndings = 0 showWrapSymbols = 0 showIndentationGuides = 1 showStatusbar = 0 # wrap = 1 highlightCurrentLine = 1 highlightMatchingBracket = 1 doBraceMatch = 1 # Both in editor and shell codeFolding = 0 autoComplete_popupSize = [300, 100] # qtstyle = '' edgeColumn = 80 fontname = 'DejaVu Sans Mono' zoom = 0 # Both for editor and shell tabWidth = 4 settings= dict: language = '' defaultStyle = 'python' defaultIndentWidth = 4 defaultIndentUsingSpaces = 1 defaultLineEndings = '' # Set depending on OS autoIndent = 1 autoCallTip = 1 autoComplete_keywords = 1 autoComplete = 1 autoComplete_caseSensitive = 0 autoComplete_fillups = '\n' autoComplete_acceptKeys = 'Tab' justificationWidth = 70 removeTrailingWhitespaceWhenSaving = 0 changeDirOnFileExec = 0 allowFloatingShell = 0 advanced = dict: shellMaxLines = 10000 fileExtensionsToLoadFromDir = 'py,pyw,pyx,txt,bat' autoCompDelay = 200 titleText = '{fileName} ({fullPath}) - Interactive Editor for Python' homeAndEndWorkOnDisplayedLine = 0 find_autoHide_timeout = 10 tools = dict: pyzologger = dict: pyzointeractivehelp = dict: noNewlines = 1 pyzosourcestructure = dict: showTypes = ['def', 'cell', 'todo', 'class'] level = 3 shellConfigs2 = list: # Empty, let pyzo create one on startup shortcuts2 = dict: edit__comment = 'Ctrl+R,' edit__copy = 'Ctrl+C,Ctrl+Insert' edit__cut = 'Ctrl+X,Shift+Delete' edit__dedent = 'Shift+Tab,' edit__duplicate_line = 'Ctrl+Shift+D,' edit__delete_line = 'Ctrl+Shift+K,' edit__find_next = 'Ctrl+G,F3' edit__find_or_replace = 'Ctrl+F,' edit__find_previous = 'Ctrl+Shift+G,Shift+F3' edit__find_selection = 'Ctrl+F3,' edit__find_selection_backward = 'Ctrl+Shift+F3,' edit__indent = 'Tab,' edit__justify_commentdocstring = 'Ctrl+J,' edit__paste = 'Ctrl+V,Shift+Insert' edit__paste_and_select = 'Ctrl+Shift+V' edit__redo = 'Ctrl+Y,' edit__select_all = 'Ctrl+A,' edit__uncomment = 'Ctrl+T,' edit__undo = 'Ctrl+Z,' edit__toggle_breakpoint = 'Ctrl+B,' file__close = 'Ctrl+W,' file__new = 'Ctrl+N,' file__open = 'Ctrl+O,' file__save = 'Ctrl+S,' run__run_file_as_script = 'Ctrl+Shift+E,Ctrl+F5' run__run_main_file_as_script = 'Ctrl+Shift+M,Ctrl+F6' run__execute_selection = 'Alt+Return,F9' run__execute_cell = 'Ctrl+Return,Ctrl+Enter' run__execute_cell_and_advance = 'Ctrl+Shift+Return,Ctrl+Shift+Enter' run__execute_selection_and_advance = 'Shift+F9,Shift+Alt+Return' run__execute_file = 'Ctrl+E,F5' run__execute_main_file = 'Ctrl+M,F6' shell__clear_screen = 'Ctrl+L,' shell__close = 'Alt+K,' shell__create_shell_1_ = 'Ctrl+1,' shell__create_shell_2_ = 'Ctrl+2,' shell__create_shell_3_ = 'Ctrl+3,' shell__create_shell_4_ = 'Ctrl+4,' shell__create_shell_5_ = 'Ctrl+5,' shell__create_shell_6_ = 'Ctrl+6,' shell__create_shell_7_ = 'Ctrl+7,' shell__create_shell_8_ = 'Ctrl+8,' shell__interrupt = 'Ctrl+I,Meta+C' shell__restart = 'Ctrl+K,' shell__terminate = 'Ctrl+Shift+K,' shell__postmortem_debug_from_last_traceback = 'Ctrl+P,' view__select_editor = 'Ctrl+9,F2' view__select_previous_file = 'Ctrl+Tab,' # On Mac, this is replaced by Alt+Tab in pyzo.py view__select_shell = 'Ctrl+0,F1' view__zooming__zoom_in = 'Ctrl+=,Ctrl++' view__zooming__zoom_out = 'Ctrl+-,' view__zooming__zoom_reset = 'Ctrl+\,' pyzo-4.4.3/pyzo/resources/fonts/0000777000000000000000000000000013166630335015027 5ustar 00000000000000pyzo-4.4.3/pyzo/resources/fonts/DejaVuSansMono-Bold.ttf0000666000000000000000000114500012662716363021267 0ustar 00000000000000 FFTMYR,GDEF`THtGPOS3 E&GSUB$$OS/2%!VcmapN! &cvt I*,fpgmq49j-gasp.8 glyf".D|head6hhea $hmtxqr@loca,\0tmaxp0 namet2h0!Bpost.rR4rprepLQ3.ɐ!ɐ!lst        arab cyrl.grek@lao LlatnX SRB 4ISM 4KSM 4LSM 4MOL 4NSM 4ROM 4SKS 4SSM 4markmark&mark.mkmk4 $,4<DLTHn  x" j~yj&0  j:jz}wx{jQ]jjh $6HZl~ P< x P< < |@ 4X P< p vy~j~jj  $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|l@Lh\hP p OOSs"  L3 O Ro U Xs h hw o x    vy~j~jj $6HZl~ hU hE hc hG h h h h  tuwxz{|} &,28>DJPVj hjQ]jj\jhj jJh  &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~bzb2_bVS ;Ckkk2zo.2r brw>*{v&#_BCbO&cnkkkk}<xx%iFWW@TT[:B`^L?c:Es[Tv;]48x, X&(oXoXloPXdToXOJB<ExKUTTTRlbOW&ZZs{;#7fkf**.z"/*3[c k:Bo:s62C2> ^n~*6.Zw.k~BVKO2 Gh8pBh<U=7~e<PL#j_ 38~~{)Kprf{+>V;'o"?4W OOQs$  L5 O Rq U Xu h hy k nz q ~     tuwxz{|} &,28>DJPVj hjQ]jj\jhj jJh y $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpx~hh6zhhhhhhhh6hhhhhhhh`h`hg`hh`h2h`h`h`h`h`h`h`hh`h`h`h`h`h`zh`h`hh`hh`hhh`hh6h`-```h`hh`h`h`g`h`h`h`h`h`h`h`h`h`h``h`h`hh`h`h`h`h`h`h`h``h`h``h`'}{gfid id6hhhhh`hhhA` $=DKN]"234  57BB9HH:;<=?ABCgoDqMceghil m n p q s  u wV\bhntzh`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`/ ntz "(.4:@FLRX^djpv|  >DJPV\bhntz`  arab cyrl4grekFlao PlatnZSRB 4ISM ligaDloclJloclPmediVrlig\"*2:BJ@D N  R  .     Jhp5 j l n p r v x | ~ &    "  6 2 : > D B * F J P V NPhjs#  4T' t z T (   $  8 4 < @ , H L R XTTVVX\ahjprs"T' s y  S '    #  7 3 ; ? + G K Q WTTVVX\ahjprs"   v  v > $  p  l  j  p  l  j  O LI33f  &(PfEd m`~!AEM?CXauz~_csV_  :UZmt{ .<[ex{-McyEMWY[]}  # & 7 : > I K _ q !!!!!!!"!$!&!+!.!_" """" "-"="i"""""""####!#(#+#5#>#D#I#M#P#T#\#`#e#i#p#z#}#####$#&/&&&&'' '''K'M'R'V'^'u''''''))*/+,d,p,w,z,..%..'t $CLPCXatz~br1Ya  !@Z`ty~,0>bw{0Th| HPY[]_   & / 9 < E K _ p t !!! !!!!"!$!&!*!.!S!"""""'"4"A"m""""""#####%#+#5#7#A#G#K#P#R#W#^#c#h#k#s#}#####$#%&8&&&''' ')'M'O'V'X'a''''''))*/+,d,m,u,y,|..".."RpvogUQNIHGFE75' A@?72/.)% )(&%# hdb\XVURHFB@86,*(&$ {yrmljfeda_; |snO4310/.+*( ߽tlhgfܽcccc}c|IkY    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a!rdei#xpkvjX%s\]gw ,l|cn!T@m}%b j:q/0  "y'gwqstuzxvh3#N\{#'##\ #\\w#3b##\b b b%%1/#/ 9bRsq#oVd\Hf```{\{`o7L'5%={D=!/s, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-,%%I%%I` ch #:e:-hh@ b/10!%!!hsr) @ d <2991/0!!!#  !#qe@ d1<20!!!++J@0g g    91/<<<<<<<2220333#3!####5!#5!#3_^aJ^^^^J^Jvuu%vD /P@+)%$(hh!/$, ( 0<21/<22290>54&#.'.546753.'?FE>??ˍWggU̹GP>[ٴ O=>O-C:9L-.+=BI' (0Ϭ ! '3V@-(  (kk kj.k%1"+1""499991/999904632#"&"32654& 4632#"&"32654&!?9PP9:OP)˹=8ON9:QRXO::OO:9PP`^P::OP99Q%*8@Z)(*()(-,.+23456718%8+(%#1) #1p#popnr)($ +.#  .8 )$. 9999999991/99990KSX999Y"%#"5467.54632.#">54&'3!32676767fKWꍋ20ID@APR=D9DI"BCr;H23ёXN<$%86$~f &b993V+wIz  d10!+ @ut  29910#&547䟚@=5R @ut  299103#6545䞛䄀=?y9TJ@(   n   <2<2991<22990 %#'-73%TJLLLLKLXXB\ #@ v    <<1<<0!!#!5!RPRPjo@ w 10!#9co-10!!-wow 1/0!!MoqB`@ d103#m{V #$@ x! xn!r$  $10@////////// / / ?????????? ? ? OOKKKKK K O TTTPPPTT T ddd```dd d    T////////// / /   $]]4632#"&"326&32#"H45HH54H|f^^fg^^5HH54HGB~{|~o $@xxdx  1/20!%!!!J5JML J/s'[@/% zy xnx    991/990KSX9Y"!!577>54&#">32uL=KByoOkk^;H5/FVAdm?<')ݿX^D}L(G@)p zy#p z y pnr) &  )9190#32654&#">32!"&'32654&%nyynTgg\qd^xxWOS]*( !εǢ&$/1o^s}fu B@   % xd   <291/<290KSXY" !!3#!!y5jBF<@!xzyx xd r  190!!>32!"&'32654&#"+$R.`fSXOQ  ))u''b $5@{{ z y{"nr%   %190"32654&.#">32# !2`ee``gg#OC/c..F싄--AAn75@%xd 991/0KSXY"!!!P #/D@% {'{-{nr'0 $*  ! 0991990"32654&%.54632#"&54632654&#"hczzcc{zqvto|eWXeeXVf}gg~eg}}'yغx(&ĉ׊TXggXWefoN$7@{ zy{ {nr% " %19073267#"543 !"&2654&#"OC/dF7_ee_`gg .,AAi}}'@ ww <21/0!!!!MM's' "@ww   <210!#!!MNMoXmy@29190 5y!X'y@vv<210!!!!X!!Xmy@<919055X!`a)$q@8   %$  fhn !  %<2999991/9990KSX99Y"!!!546?>54&#">32  >PZ?-\\T`beD^XD&cNY=P+CDGF 89L\VBT= s 4]@1(+$ 4| | }'$|+}|+15'(   ! .529919999904&#"326#5#"&5463254&#"3267# !2fYYeeYYf&gHȥGl"0PE\Qm`v4!qqrR511/)//77! @;      %h~d     KSK QZX @8Y91/<90KSXY"@,0000 5::5s|]]!!!!!hi\uZq+q} >@$h hdh   !2991/9032654&#32654&#%!2)čqvp_anF`wyjFP\\S뽼 İ9.@op op nr! 210%# !2.#"32679FU>.UDLLLL+$$xy$$FAAFu(@p dp !  "99991/0326&#! )P<nB? tsJ *@ppd p#  2<1/0)!!!!!J^?{yX %@ppd# 21/0!!!!!XBuujS@!ppopnr%! 1990@]]#5!# !2.#"326hUu?-ZL>`.DTIKsy30PQH &@pd  " 221/<20!!!!!!'q'9+h% #@pd p 221/220!!!!!y))3m.@ opp dr  199073265!!!"&mVctl_JVX\t 4u a@3  %d   291/<290KSXY"!! !!u'N)Nw@ pd1/03!!'w/V{ @  % % 91@ d /<290@  %KSXY" ]@R )=??   & )/708?    ]]K TX 88Y!!###V`bq+sTwX S@%d&& 991/<2990KSXY"]@  ]]!!!!w=^=+=\u #@ppnr !! '10"326&! ! hqhhqrhh    xx{,@pp d !  299991/032654&#%! !#!yuu`5nJbyyb\u<@ pp n ! !'9919990# ! "326&   ~xqhhqrhh~xL  f@6  %p pd   !  "29991/<9990KSX9Y"!&'&+!! 32654&#',A/ Ok^ yihz A^y暶 i_mm^V'p@>'' '%' o!p o pnr('$ ($"(9999190KSX99Y".54$32.#"!"&'32654&'ߞge_`krSohvlmxPLU/.CFVP>Q10Bڦ541TRcYCeZw@ pd1/20)!!!jf*@  pr d  1299990!3265!! j'reer''ppR9m@%%dKSK QZX@8Y91/290KSXY"@  ]]%!!!h)g)+ @@      % d   /91/<<90KSXY"]@6fe   )&ghj f g wzsx]]!3!! !kT;H+ @C    % d   KSK QZX@8Y91/<290KSXY"@  ]]) ! ! !V11X%!S@(%d 9991/290KSXY"! !!>"#>3XwLs 8@%pdp 991/0KSXY"!!!5!Lw#$@ut10!#3!ZoB`@ d9910 #Nm/+ @ut9910!53#5+޾9@ d91290 # #--/10!5/۾f"1K TKT[X@8Y0 #fx^T{ %@/   ggh g#r #  .)&22991/99990@-400 4!urr  ]"326=%!5#"&546!354&#"5>3 ZMt#5d gdikapTfLZq}JPʵĻ1GI5:(&w 6@  h hrt2 0221/9904&#"326>32#"&'!!Rl__nn__lh6Ze.$-6]]ba%}.@yh yh r 75210%# !2.#"3267%Jb&ZS@RUB9++89*,7;9:Z;6@hhrt2 )221/990!!5#"3232654&#"$/eZl__nn__lSab40]\}{L@#   hy h r .)190@ ]%# 32!3267.#"Nfv ekspex 7**->w:?itw{q;4@ t  <<2991/22990!!!!5!546;#"XB/bNʜ0bXH} (I@(  &' hyhh#')& 2 ))221999904&#"326!"&'326=#"325!#r]\qq\]r%\]S[|v+f`+%B .,u|yPN, 8ZR/.@ h t 7  521/<99990!4&#"!!>32/ENPZ#j)yh}]f *@  s  <<1/20!!!5!!!!Dlm%`XR 7@  s  <2991990%#!53265!5!5!!RbRN%+nTV @@! %t  .5 291/<90KSXY"!! !!%`cXd}^B `TZF %@t  991/990!5!;!"&NRbѶ`nR{"@'  h :=:=:#K TK T[X@8Y91/<<<299990@7      /////////?? ? ? ? ?????]>32#4&#"#4&#"#3>32!fJo&22((22&nDDpGD}TV{1{VT}1`tBMQ/{0@  h 7  521/<99990!4&#"!!>32/ENO[#j)zi~`]fbo{ #@hhr . )10"32654&32#"hixxijxx==Vw{9@hhr 20221990%!!>32#"&4&#"326$.eZbl__nn__l ab]񢸸ZV;{ 5@ h hr 2)22199032654&#"#"325!!l__nn__l6Ze/$1^]04ba#{)@ h  21/990.#"!!>320M]&%+w&ln``i+{'@=    %  yhyh%r( >7"5(99991990KSX99Y"(!]@+ $)) ) ) ) ) ),//,))))) (!$'].#"#"&'32654&/.54632QXbd Tema^gjKQ_=4598P2&##7:<98<"&o11@  <<2991/<2990!!;#"&5!5!IUKA>%`0@  hr   7521/299990!3265!!5#"&%DOOY%iTyh~]fP`i@%%1/91/290KSXY"X]@U  ZUUY]] !!)``j` @@      %    /91/<<90KSXY"h ]@@     )&') 96k c wxuy   ]]333! !ywꈇ`5ZF7` @B    %  1/ 91/<290KSXY" ]@    96]] ! ! !yV`yHN;X`w@A     %    1 /9129990KSX9Y"+5326?!!;vwZT+V44y=o<A)9` 8@%6/ 2991/0KSXY"!!!5!MiN``$^@0 %   !  t% $  %<<29999999199999990#"&=4&+5326=46;#"3k>>k{hFb~~bFhחsrݓTѥ͐Ut10#!$`@1%   #t%# %<2<9999999199999990326=467.=4&+532;#"+yhHb}~aGiym>>mVͤэVחstݔXy++@  19990#"'&'.#"5>32326yKOZq Mg3NJNS5dJ t]F+<73 ":?=6 7= @ d <2991/0!!!3 !#+e !F@& yh yh r" -/"<<<2212<220.'>7#&573 8x?@w84xC=ur{~5*1L0+" :9!wb>@  zyhn x <<1/222990.#"!!!!3#535632N9EjcPS)+N^T /c@8  *( -'! ) -0)'!$ * ( $02299912299904&#"3267'#"&''7.5467'7>32;dJIeeIJd$P0'T-)S.'QIccIJffq)S,/Q$*S)/Q&u@<%  d    229991/2<2<290KSXY"! !3!!!!!5!'!53>"#>VddVX𻗽J@ <210##  =% >y@C/0*1 06 'f&*f# f f54%.#"#"&'532654/$5467.5463271a71|U6LU v_fFAѺYUUIMR haG@ЬJX$E(6X`$E+Ql:4Ki neY/2{Q95Fs^!2xR-;1@ 1<203#%3#u1}N1ID@%  >2>&JDB C8B ,/210.#"3267#"&546322#"&'.5467>"3267>54&'.P4[0akjb5`*7j2ʩ7iZZ\[[[~}[[[\ZZ~cIGHHGGcdFHHHHHh__g$ZZ[~}[[[[[[}~[ZZGIGebHHIIHHbeGIG %)d@4 $#(&$& n*&(' #*229991999905#"326#"&546;54&#"5>32#!!fmd=6Vn2yN[[R@ILP4818g96u11!"@ww# -@   U U <2991<2990 5 5++#Xjy@ v 10!#!X!,-10!!-w}N 4L\@3-*+'0!52+A M*',$0-!1E3+E$CGB3C;B/29991<99902#"&'.5467>#32654&'2#'.'##%"3267>54&'.hZZ\[[[~}[[[\ZZb@@998(NG&7O?9)cIGHHGHccGHHHHHNZZ[~}[[[[[[}~[ZZb((+)oXZAU 81:/qGIGebJGHHGJbeGIG-X10!!-wV@ n@@102#"&546"32654&hCz//12.0zDHdbHHdc30/xDBz./3dHHbcGHdXy *@v  v   <2<21/<<0!!#!5!!!fff!bb2@ ^9190!!56754&#"5>32f3Z:WK8MKB6-+KO4<#"}o~*(D@& #  )^&^ )9190#532654&#"5>32#"&'532654&FooJUOFEDECibouKJCPU_b2,-3teI^p\y@8=D f"1K TKT[X@8Y0!#fT` G@'  hr !    75!29912<<990!3265!3267#"&'#"&'RRQP!+J#H[%nF;ST XrsrsG> KSNP/0F;) "@d 91290!###.54$!fN۲w 10!!Moo39 )@   ^ 12035733!9x-+)N0@ n  9910!!4632#"&"32654&%PŦçkIUUIIUUV޾~mm}}mm~\# -@   U U<<991<29905-5-++#wvwv/w{'=V'{ /w{'{& tV/w'=V'u %x@<   %%  fhrd&# !  &<<99999919990KSX99Y"!!!3267#"&546?>54765  >PZ=-\\SabeD^XB&ocMY;Q,CDGF89L\V@T?!k&$ u@ ]1!k&$ u@ ]1!k&$ u  +@ ]1!m&$ u$ +@O$@]1!k&$ u   +@  p? 0/   ]1!m!@M ! !%h  i ~!   "999991/<9990KSXY"]@\ !000 0!o  !!' )!;44 ;!t |! ! !]] !!!.54632%32654&#"!/\uZ,*uv*N57MN65Nsq%a=uu>_6MM66MMAc@3  %p hpdp~  % /<291/<20KSXY"!!!!!!#36Vfnj{o9&u&Jk&( uJk&( uJk&( uJk&( u%k&, u%k&, u%k&, u+1%k&, u+K SKQZX@88Y1u 9@hp dp  ! "/<29991/2203#326&#! )#53P<nBĉr tswXm&1 u#+@O#@]1\uk&2 u@]1\uk&2 u@]1\uk&2 u +@]1\um&2 u1" +@O1@"]1\uk&2 u +1wXs 0@   <291<290  ' 7 XJJGJHJHH )s@> )*  '$ ($p$ pn$r*(! '!)!!!'*.99999991999999903264&'.#".5!27!"&''U8rh9N4pi## g=q''j@xb9= >5 33X>=s^xCBrjfk&8 u@]1jfk&8 u@]1jfk&8 u +@]1jfk&8 u +1k&< u{0@hh d !  2299991/032654&#!3 !#!ytu`'n5nIbxyd2X@2)#,2&h&h/tr*  #A  ) .+/399991/990#"&'532654'&/.5467.#"!4632 }t aRP=|C:s6IUsV;< b\^`#[Q@Y J>^ C:HX D0M~JIebf^Tf&DC^Tf&Dv^Tf&Dg @?&/&]1^T9&Dw^T1&Dj^T&Du{ A@K. 4, ;5#A  ggy 451g8g,>8& rB#;-, 4T)-RB<999912222999990@'33040536G3G4G5G6YYYYW3W6 A]5#"326554&#"!3267#"&'#"&546;54&#"5>32>32IWY@=?=2??2=d_A=Bo%}&HF\}f&HC'\}f&Hv'\}f&Hg' @?/]1\}1&Hj'f&Cf&vf&g1&jbf(@N (((#$%$"!"%%$%('%"! #&#h hr#t)'& !#(%" .))999919990KSX9Y".#"32654&#"432''%'!%+1b0izwhms udV(?7%\)B}-b2 wumt{u/9&Qwboh&Rbog&Rbog&gR+@]1bo&wR1 ++1bo&jR +1BV $@v  <<10!!!!!!55uK +s@>+,  )*&h&h&r,+,* # )#.),99999999199999990 32654&.#".5327#"&''?B&hxJZ>'jy--UC./SCc:"4Im=+-yLk--w%f&XC%f&Xv%f&Xg +@]1%1&Xj +1;Xf&\vVw9@hhrt 20221990%!!>32#"&4&#"326$.eZbl__nn__lab]񢸸;X1&\j, +@!p_PO@?0/ ]1!N& $ +@ O@/ ]1@]0^T&D!k&$ ^TF&D!o'vc$^ol{'vDAk&& upf&Fvf}k' u&Jf&giF9k&& d%1&FKjk&& uGf&Fhfu}&' Zg cAnA1G@8@/]1uZ$F@&"   "hhrt   2)%<<1/<2990!5!5!3#!5#"3232654&#"2$/eZl__nn__l"ttab40]JN& (@]0\}&'HJk&( \}F&H'Jk&( \}1&H'oK'v(\o}{'vHJk&( 5u\}k&Hh @?/]1ujk' yu*bXHf&gJujk&* 2bXHF&Jujk&* 2bXH1&Juj' *bXH'dJ@?]0Hk' u+ +1/k' uK +1K QX@8Y@O]0>@   pd    "<22<221/<2<<220!!5!3#!!!#53!5'q&ن'qhQ /B@" ht 7  5<<<1/<<299990!4&#"!#535!23!>32/ENP--ݠ#jQQ)yhGF}zz]fij%m' u,$ #+19&w@]1%N& ,+@/ ]1@]0&%k&, F&o%&,vBo&Lvg%k&, ` @ 1/20!!!5!!Dlm`D@# op p dpr ! 221/2<22990%32765!!#"&!#3!3>?HU'(STEl9ǾMVX\:: uv43 N G@$   s   ! <<<21/22<220%#!53265!5!5!!!!!5!#3#ѵbRN%o6!nTVm!k' @u-+1Xf&gu0&r(.0'(N` ?@  %  .5 291/<290KSXY"!! !!%`cXd`}^B `Tl' v/ZFl' vOK QX@8Y@O]00'(/Z0F&(O' Do/Z' kO'y/Z'yO @/]1 7@  pd    <2.9991/903'%!%!q'p}wo™%\D@%  t  <<99991/99905'!5!%;!"&duK'grSbѶӦâѠnwXl' v1/m&vQw0X&:(10/{&@(QwXk&1 )u/f&Qh$'QajVe2@ pndp 21/90+532654&#"!!>32e\\y%dR^jm;>'%~nlnˁoKN4X/{5@ hh 75 21/99990+532654'&#"!!>32/[Zy'dR"#NO.-#jQQTklnz45GG~`]fij\uN& 2+@ O@/ ]1@]0bo&R+@/ ]1\uk&2 boF&R\uk&2 bof&RD>@!p  pdp %  <99991/220! )!!!";C=.{\[|478nͲɮ{ )5o@1("!% !gy3g -%g r6*0T!*R(6<9912<22999990@ !"()]!3267#"&'#"32>32'54&#"4&#"326>c_>2:MN;;NM:ߐ;:,*JJMG8;;Bjfm' u8- #+@O@ ]1%9&wX* +1jfN& 8+@ O@/ ]1@]0%&X+@/ ]1jfk&8 %F&Xjfj&8uO @]0%&Xujfk&8 =f&Xj_f&8vo`&Xvbr' |:+1m&gZ+1r' |< +1;Xm&g\+@]1k&< u( +@ po `_ PO @? 0/  ]1sl' v=9m&v]sk&= 291&]sk&= u9f&]h;*@  t <991/90)!5!546;#"B/Nʜ0A w >32#"&'!#535!!!&"26Ze.܊$*lnn]]bazzD(?@$h hdh   !$'+9291/9032654&#3264&#%!2)"#546r-qv--p_anB1F`wyjFPS뽼 İ/B;ʜ{Hw"'!!!632 &"2\llhQlnnæ~D+%3264&+'3 !yuuy٢n5bbR-&"2>32#"&'!'lnn6Ze.ܖD]]ba9/@! <1@ op opn r0>3 !"&'3254#"DU.>UFLLLL$$rr$$HFAAFk %27# %6326;#"&#"E0-Xa\1)!Hx#@7.8"#%27# !2676;#"&#"&HZy'_.#K33sV89Vl7+rU?u"0#547676; !#26& 0KnB'&4MFmts?}#";! $545676?6%3!jĈvqeH+ejyw`+ Q2[w"7632!5!!526&"*hlʳl\lnn4Da=n{%/%#"'&'&'532?654#"32%26&#"~}cTy `&ZQh:#?ixxijxx5?NM ",f==KJJ +@   21@ p pdp/0!!!!!J^{?+B\u=@ ! !'991@ p p nppr9905!54&#">3 ! %3267\nxLLFU 9 iad__AFH$$xxٯ}L(>@  !!&)1@ pn)p) p#r)90.54$32.#";#"3267# $546ō\ggTnyynxx^dq%!(*]SOW}s^o1/$&٢lXe!!!+5276=!eSZVh)%C}+dl72}-[Xv!!#!532765!5!546;#&XT\c()obcl78TNʜ&k(%27#5!# %632676;#"'&# ]*̬@<< Za\1)}."TsC  l7.R:dO  '! !%3254hrACr%v ]u~ *'&'&'&#"#3>32?67653hbYDG?@$$xUAA X+19y} V*4GF}]fij 4 T^xh0!!!#"'&y&&fln.-vx%!!3#!!!#53y)) !!!676&|'@MD6$wGq3= 476;#"3! !![Zy'b+)`cXdAml75}^B `TZF3+;!"&=#53!5! RbѶN^n^BP! !''7!gkd)NUTEWR%#"&326532653#5"&#!fJo&d((d&nppGD<}TV{D{VT}D+tBMQV!+532765!!! T0<;+<^Z4K=+=V/{!4&#"!!>32/ENO[#jTzi~`]f\u  &5%63 &'&#"!3276hn-]O` I|:3U- Q )4qr4(txxpOD2P_xx]'2 'R &5%6327!#327&#"d(ECOu]rxSU+Cg//g&q+pi5x yhV{327!##""327&훀꓌ixxi,<< =|erП]]3264&#"#5476763! !#!quu 0K/5Jbb'&4MFmVw$&"2"'!#776;#"3632Rlnn k QZy'_.#\QDal7+Wy*!&5&+!!3 3264&#',!0Ok^' yihz _y+暶 i_^V(03267# $546?>54&#">32ّLPxmlvhoSrk`_eg7eCYcRT45B01Q>PVFC ./˞+{'>323267#"&546?654&#"Y_ꚨQKjg^ame㕠T dbXQ=&"<89<:7##&2P8954bx;Xv&(;#"&5# %53232#%&'&'32'3@䨇.2\K t8G (`@iHs@ lY265LoX1 !!;#+532767&'&5!5!IUUZy'b+"PYKA+fl7,eHQ>w"#5476762'!!,0!Z{i0!H "MAM-o147676;#"!!;#"&5!5[Ry'b+)IU`mb 75gKAZVw!!!!;#"&Tb'~ҭVl'8'XhZw##"47#5!32654&'!wxllxQNpgfqPRmn8mO֦M uR'53!"'&5!276%xliB'&&f-H9mȷn.-X!!776&S.a)=?51JL@\MX` +5326?!76&kwZT+u%p[RS4;^0"=o<A)_;;& #O9s!3!!!5#53!\Uw&4`!3!!!57#5!!Min`  "# '&5!327654'&+5!!>aEb)"'toNkIQm?tk9)qo dn  %2765!! &54767675!!# J1>6n GOoNot׊54&#">323+uLmT;KByoOkk^;H u5HtAVAdm?<')ݿX^H "#!!!2! '&5!32654'&+DtKQG>OJoKKfO@tfstt_95jcf54nHc` 2!"'&'32654'&+!!lxiGGghhf_[KKY0,fgst%)58jcf54v5?%#"'&'327654'&+#5;5!3[w٣IIDFFHHY43NTB61^ׅ#*+rB`GLr!¦QVr{!!6763254'&#";LL\DP22nN o%,p\NqBA!! +'I!!!!!!!5!5!5! ffff:!k&$ u^Tf&Dh%k&, uf&h\uk&2 ubof&Rhjfk&8 u%f&Xhjf' &8j%O'q;jf&8'  %'jf&8'  %'jf&8'  %'\}{!' 'j$^TO'q;!& &$j^TO&D&q;N' &ujk' yu*bXHf&hJuk' u.k' uN\_u&v2b_o{&vR\_uN& b_o& k' uynHcf&h8Xk&hujk' yu*bXHf&vJ333327653#"5##8EΝ9RJ%2:8}0qhwXk' u1/h&Qk' uf&vk& ug&!k&$ ^Tf&D!k&$ ^TF&DoJk&( \}f&H'Jk&( \}F&H'o%k&, f&%k&, F&\uk&2 bof&R\uk&2 boF&R=k&5 f&Udk&5 #F&Udjfk&8 %f&Xjfk&8 %F&XV&6+{&VZw&7o1&gW6R2 >54.#"57>54.#"632]XLqKF_38@Qv;!2QP(vzS"$yhv@.5NSk7@])U_!OBN-6O'8Xqj1nOc{05>54&#"57>54.#"5632EUx9 E|fCxK48Kj5.IG$ghFIVK&h%+?DV-JVEMB5@!,@ ~υ*CZ\0qHk' u+/k' uKjVe!4&#"!!>32e^jm;>'%~oKN4t%-%724#"'632#"'#67&'#"32!26&"-h*4X|u+D=$>L׵<0$DxEEx yaGyl$>40/-DsX!!#+532767!5!LSZy'b+"w#dl7,dX9`!!#+532767!5!MSZy'b+"N``dl7,d!k&$ ^T1&DoJ&(2\o}{&H2\u' 'j2boO'q;\u&2' jboO'q;\uk&2 bo1&R\u& 'j2boO'q;N& <;X&\1t %724#"7632#"'#67&'&5!5!-h*4BX|u* J[N) y1BXl}7&%t{/%724#"7632#"'#67&'&=4'&#"!!67632$R!)$Fd{]!u ~;I?##0T`j y1BXl+5GQt`l3!3ߨot1$%724#"'7632#"'#67&'&5!5!!!!V$S!*2%Fc{^"t ~;I%^j y1BXl>XR` ,@    991990%#!53265!5!RbRN+nT-26&"&"2>32#"&'#5#"323KMMKMM%e?Gi !hG?d&DD]]baab40]]SJ{-&"226&"#"3253>32#"&'#KMMDKMM&d?Gi !hG?e%DD]]04baab]]! !!#'#7'7'3'i<ʢ\BC-Z!,UK2bs{s[q^??y?R( &#"# ''&5!27&'3267}jFUן:>.UN$!7RLL_$$r1y Ms~"&AF''7&5!27&'3267#"'&#"?&ZS j&*=MUBJbyB uw9 ~y!Z9:++~~ !3#!!#5(vb !'!!7#7#٢/`4TTb_rBsI1\w+{1.#"#"';# /&'32654&/.54632QXbd T}<肰 a^gjKQ_=4598P2&Pۭ s7:<98<"&9`!;# /!j3-<肰AiN`);PۭV$"#"#5476767637$!!!264'&#N+0!K0DVpz==p$0 "MB?~m66'!2)#533264&#32654&+3}}}p_ančqv׽ İRPSff`wyjg53!!!3#! '&)265U''VVvyzwRrrڇ&=pp9 !!!icc!+!! !&5&+!#533264&#k ,!0Okٯ'Lyihz暶 _y+Ni_^{.#"!!!#53!>320M]& zcc%+w&6J`ind{ ,27654'&#"!67632!#32767# &@B.,Nt@@"6RTdր~44dhccj`pP**fL--XWq}J((ʵ^]1G%$:(&xY{5!!5#"763226&"5$/JKelkZHGlnna114/.DxY{%!!67632#"'&&"2$/KJeklZGHblnn_a11䘘/.ODw $9@h  hr!t2! 0%22991/04&#"326>32#"&'!463!#"Rl__nn__lh6Ze.ܵ3 !"&'32654&#"Jb$ZS@RUB'++*, 7;9:s/}-%32'&#"763!"'"''47&'&76!2&'&#"96SgfQ=ZSTS@MLR9Kr]G&A,7MM$26&"7!;#"'&=#"'&32nVXX"-5aHH%<;QdbɺG99D:Stnlka110/.FO#26&"%!5#"32776;#"mVXX%xQ˽H99+EHa4Dab40/.]gl75Xy{ 703267!57632!"&&'&#"ke vԒ <<;\}{?@ . )991@ yh hr9905!5.#">3 #"%3267\ekfv)'spex w x:? **'twzq{ 332767#"'&'&'&#"67632?;#"'&5_-HN10 w@A;hUYY^`___5!Hq; ?;:n,Q`ƟVv)?!"D07o[t(<@! h )#"h&r) "5)190467.54632.#";#"3267#"$jvyi;vvshydvn8yd"B6,@_B=T/$fk{(\@! yh#) y hr)& )190 KTX)@))848Y%%]!"&'532654&+532654&#"5>32)boXrq2uhzy]=p"#(YO56E1>W0{A#"&'5327654'&+5327654'&#"567632?;#"'&5'Cr?@wuJTCNOUH'NI/&XK1?@&[R>fZPOEi>  !HZ<0=JJp\["TO E.)  O.N7o[$ley. !27654'&+5327654'&%672'$*F(5:&J@*?;9Yon:8<:nmY%?H "J7#"-VWbLHIGgVV=LX`%#!53265!5!!5!3#RbRN+nV ;"32765#"'&54763!3676;#"#!"'&'32765Y1200\Z12;HG]lkkl'RW|*TONOKHHIABwRROPRR:Gb..⡠rv..!6MM|VU`*%#"'&54763)!"'&'3265"3265.=_mnnm.'VPQQMJJJ[f11_[fb\⡠!6OP:7_%%#"'&7632&'&#"32767'#5!7]edk򍍐`YXOQPQPQPMN'"!e2(1YXYY baoo ' !!3254g> CCN}V#L~||bo`%254'!!! 54f-,,CCW;HJ9SLJ*^!32765!!#"'&#"#NP--#jQQVy44GF}\]fij*!4'&#"!476;#">32*#"NP--[Zy'dRjQQ)y44GF}@kln]fijV*(%4'&#"!476;#">32+5326#"NP--[Zy'dRjQQSZy'dR)y44GF}@kln]fij)blnjh!!!3#!!5!5!5!!fDllܐ@`!5!#"'&5D&&fmnl.-vxjh` !!5!!5!llbXz*#"';!"'&=&#"567632!5!3276xJHHN6>(*bZ\NJNHJRNG=FDF+<77llu:?= le"&#";&;!"&=#&'&!5!!;2` 3RbѶ{@'N 028?pno)b̜rD^%!5!;!"'&N*(b\Z77llH. !;#"&5#5!!#"'&'32654'&+V$[=J휉l;S<5ssMNNMGEgm88n~Nn`L9 !qfst%)58jcf54P`"%#"'&326532653#5"'&!!fJ78&d((d&7788pGDdd<w1}TV{1{VT}tB&')(PV`"%#"'&326532653#"'&!!fJ78&d((d&7788pGDdd<w1}TV{1{VT}B&')(PV{367632+5327654'&#"#4'&#"#367632!32K77GJc R""133267DDpF#"dddl77~)*+-y1y-+*)~1`tD$'R@F{'!54'&'&'&#"+5326=!67632 &\55ZYzRB'CKK[Vo77#WVyv\pb./DF{"4&'&#"!!67632;#"'&5&'U<5'CKLZRSUuY_-HWK`b./wxj/.vxY` !!!!xI}`uubo{ !7632#"'"!3&'&!#3276bzi<. % .5!!5#"'&'0L^EE&$,XZv<::45,,! >&ln`45327676765!!5#"'&'0L^EE& $,XZv<::45,,! >#9/u`45MV`%327676765!;#"'&'#=#"'&'M0A@M]FE& %()d'yZS+YYw<:945,! >&66n\67lb)`45V{.#"!!676320N\FF&&*ZXx<994+,,! >&lnR `45V{!.#";#"'&5!676320N\FF&(*f&xZ`&*ZXx<994+,,! >&ln%77lp7`45J`35!476;#"!XZT%%QP! KJ`35!4'&+532!~$$VXZK !PQhh`+!&'&'&+!! 32?674'&'&'&#*9  4'$lwxHFX>W) 1 +B- m WF`MLtDC_(:% A` "#327654'&'32767!#!!OO['(('%K-+%k322?s99nnf HI Ga/0!GHoKJ`V({8&'&#"#"';#"'&532654'&/.54632QWXXb22 TKJrq?A)d'yZ[ade^gj&%Q_^_=48P2&TUVWS)7lk7<98"&[Xv!#!53276546;#"T\c()Ccl78ʜ@[Xv!##!53265!5!!5!546;#"3R[bR)B/JblnNʜ0A[Xv!&'&+532;!"'&B)(c[T@}87lc;Xv&(%;++5$!346;#""#"67638t K\24@5Hi@`(Yl ʜ@L56TJ`!5!4'&+532!!~$$VXZ>K !PQVJ!!;#"'&5!5!%%TZXDK !PQ>Xx`!5#"&=#53!!!3)32760hRR&:&HDNP,%J]f/yhF;U|`&327654'&'!##"54767#5!&@+99^_89+@LE86>OELQskXWWXksTH;l:pAT}`$#'&55#53!?27654'&/A;0}}o[Z%"%`D=/!D;lyx:yƩ 4aXksTHP`3!! Pdid`j` !# # #!!ӄ`:676;#"!! ;RSvwZ**+ٞIHo<)D`!!!DOQf`Yx(.V`!!;#"'&'!5!FN(*d&xZTN``77lbv[`%3276'&!367632+'47!5!R'& /62]N N9G`v[0@78nHc`! !5!!"'&'32654'&+7yNqMGghhfa``]KK9 !qfst%)6jcf54eHl` .&'&23676 !5!5#7#$!2&'&++ c4'xMqNFet ;K}A U4X9 !qfs.( #-[H*4+32765&'&#"67636!XTN33YHHFFDIIؽw[rLG`Br+*#Ӆ^+!&'&54762&'&#"3y[wأIIDFFHHY33N ^ׅ#+rB`GLr+!'"'&'327674'&#XB[wأIIDFFHHY33N3^ׅ#*+rB`GLrV+#%32767#'&54762&'&#"3YHHFFDIIؽwwأIIDFFHHY4`Br+#Ӆaׅ#+rC_7 *32654&#"47632 '&47632"'&EWUyxvyUW䦥.#"1<"Db"#6^^aa5'''/-6L&&wZ` ,@ hh  !21/9032654&#32654&#%!2#!^^^^HNLJSNdn33320203፛Rsjley.% !";#""'&54767&'&547676H F(5:&J@*?;9?Yon:8<:nmY-kl%?H "J7#"-VWbLHIGgVV=t=+%#"'&7632676;#".#"32767'#5!^ddk򍎐VZy'dRQPQQ'!"e2eln10YX xY` !!!!!!xFUF`k1^Wt$!!3+5$)3!5!&#"?676%8tFZҽ4Ni]kb)+VSk ]L@6hVh` !! !7!h@D4d$V'``!!!D`FVM'%#"325#476;#"!26&"*;9HɶR<;&IHa6,+XX+[1/0411aml76e}\\D\?&327654'&#"67632;#!5+53lTN34YHHFFDIIټw[ rLG`Br+*#҅^p¾?&5&'&547632&'&#";3+!5#53#[w٣IIDFFHHY43NTp^ׅ#+rB`GLr¾3 %(26&"73!!!+5#"'&7632 !rBBr!կa,-=s?@A@w6++R\\D\SL`ۦa11/.~@ 4>!#5#"76323!#"'&'32654'&+26&"Z>?=;9:7SX--Xe! rBBr {ab4/.]SL9 !qfst%)6jcf54\\D\9<%3276'&26&"73!367632+'47!+5#"'&7632! rBBr ԯ 8/yz-,=s@?@Aw6++R9Gi\\D\SL`v[0@78a11/.~B/I&'&#"#"'&'#"'&5#533367632532654'&/&'&547#3b1455;2`-,DD<@?@56(G998U:=<8>@R0e/.3=48H:'SUVW"(PQ>½K3ZH7<98"%RQ0*K !XH&.35476;#"+532767#"'&5#53#323(17~;563NNN0Ab+bl7.bPQ>CK !Qs3<G367632&'&#"763#"'"''47&'&'#"'&5#5332'&#"#;&54Y6222'.-2U]>e=x1$65"!XN23-)#,7Gr]G&A#(PQ>J9K,K !JX3;>32+5327654'&"####535476;#"ϮZ?`0176~H<^ϰ32(b]fikTkl77z45GG~NNN04?&'&#"#"'&'#"&5#5!;5327654'&/&'&54632o0545;2`--DD=?@A}nb;:x9> Q1d/.999=48H:'SUVW#)`77H7:98"%RQ.#5!;!5!!!5#"&b:aaٯ~n`77`9 333# #333# #9xyyy޼yxxyyy޼yxsPTQ PTQ %#!#!#!#!>)>)8J"332765!!#"'&54'&+5328""NO/,$LJjPR**d&xZV^Vy44GG|\]33ij:7lcQV)3327653;#"'&=#"'&54'&+5328>@$$BPaJHxUA@#!PaHE^VyhGF}˄nlk}]fij:7leI#4&#"#367632,2300De443i~D:'(Fh5;<I#4&#"#476;#"67632,23;9M@400Deh3i~D:'(Fa<=~>JL4vjK+532765#5!5#53Ktʖ?zox~J~rU|&'&#"#367632|)*1>*/ 99L, $"b !<>tb6 U|32767>53#5#"'&'U)*1>*/ 99L, $"J !<>!b6 "32767>53#;#"'&'=#"&'))2;-, @N:5rM&J!I #<>!8~=7{J6: "#327654'&'3276?3#!322:l0]j! (I%$FG()|)7((?Y**t 333# #ޜUNLWWV=F+5326?33&45LL96ŤYY((}">!bitfLL2764'4#7&&'&6vTSSTvt'&l&'STTS0"'&4763"vTSSTv6&'&&tSTTS'&l&'H327654'&#"567632#6}1!!9g..--,/.iyLY;m8@+'6%@  vJyQ5H#&'&547632&'&#"3m;YLyi./,--..g9!!1}8d5QyJv  @%6'+@f5@ 91290K TKT[X@878Y3#'#Ʋff5@ 91<90K TKT[X@878Y 373Dzx @1@0#ǽ+-X [@1@0#ǽ+-j&6`!!he|6`!h|&`cL&`dL.j 533##5#5J!5!J>մF;1L R@ ] ]10K TK T[X@878YK TX@878Y4632#"&732654&#"LvwwvM67MN66Mvvvv7LM66MMoj@  ] 1/90!33267#"&546-5%=2&M(6_)r|7GF'1 \V5m 9@$    999919999990K TK T[X@878YK TK T[X@878Y@M           &]'&'&#"#4632326=3#"&j7 +$%g^$H)>%$'g^$BT%>;+?:)=f2%7;!"'&7r&&iNBs87o[ 5&73733254؜}~ztoق2SFEE['#5!;#"'&z4?ʆ:;~J>~=<J..#"#"'&'532654'&/&'&547632V4p8?@6d20IHADDE>.8(.Fhb[sTr..> Lu)=f4@@o]991<2055]3#3#Vfxfh #ȾD'f###£׮fxx 'tsF >32#." cd FJKE2@10!32!525#xxz#3$5zyz;f!#!fN'CN'vyo#5353#&M33##ٽSC~n!5!#+1532654&'3#"&CI'1 \V5mR2&;1'M(7^)s{6  "&463"3\\%22% 2%&2V53533W;fN#5!##+` #53533##SG~5!S+G/X/ %+53276=!/[Zy'b+)#+ml75}V 75!;#"'&#)+b'yZ[)}}57lm2(1000]!!-2(@ 1<203#%3#u D  1  0#"&546324&#"326D]\\]3%%22%%3\\\\$32%&23t:@10!#oo3!@   ]1/90!#"&'532654&'97{0e5-T$:A*.>j/_[ .(R<o?v j&#ǽD-&#5##v@#"'#"'&'3276732767y0@k>=k. MPMG@FF0:m@ 91<90 373Dzx@ 912903#'#Ʋm> @  ]]1<0332673#"& dSSc EKJF@ #."#>32 dc EKJF 7@$    999919999990@M           &]'&'&#"#4632326=3#"&j7 +$%g^$H)>%$'g^$BR%>;+?:-j&10!!-wڼBXy+ax5!x5!'uwy')?rs 0526544%22%\ 2&%2-n3353.vJ&!5#=n@67632632#&'"#&'"y0@k=>k. NONF@FF0:map '7'77h)!#532654'&/&'&5463#"$ 9?, ;? 'RRz .!RSz#: &33;1t $#&'& #&%6iy z 3OG$%%$GN(tf3AntVH%#AnHV #"=3;X3Vhs' fv-; &jf'yJf'yHf'%f'Uuf'f';wf' &!$}%XJ!)! qi0/=J(s=H+\u3"326&! ! qhhqrhh  \  xx%,u.!)!!Hi+V{0wX1H )!!!!!HAA!\u2HV{3bx !! !!o>nZw7<\u&.67654'&'3!35&'&547675#!QQ[\ww]\wb#QQ Uk?  HJllKFFKllJH=?kU;P3!35&'&!!6765!wwb'@'@'a+~[##[ą+ZwF@$    OON N 9991/<299073&5323!>54&#"!Zxllx)QNpgfqPR'm8nȿm O֦M%k' uk' u6f&f&V/f& f&L& 6y(7!;#"'&''&'&7676'&'"7A$DRfQ1*5ʵjpq$A;::6GElT =$,9xУ[[RMBVo!%!! 5456'&E,rbbEI8    8V`!!&'&+532w)n*&*Pn]o(vVH JYbo!%/&547632&'&'&#"32#"76"326&G~}cTy `&ZQh:#?;ixxijxx)?NM ",f=KJtV##52764'&# !5!3T][TF!;)l MX[TL&DV/{!4&#"!!>32/ENO[#jTzi~`]fao(&'&"2767  G -<<- -99- $wppvvRQ`;#"'&5#5%%T:NZX` J!!PN`qP ! !'&'&+532)''^p_o<q-wJJXT`w/x`67676'&'!!!@|8M6P3E.*{2'Z|u4@bY|rNįt`V#$! %$47#5! #52764'&sl3S][TF!0A*R LX[TL&bo{R`#3267#"&'.=!!#51F",c7Or`? " VX#sVw{  #"&'!&"2Z6lnn{]^SDV*}$%#52764'&# '&!2.#"R^\SF";w&ZS@RM?LX[TL&9*,7;WGb`#"476)327654'&z(C$=8\]^r'J`!!;#"'&5!%%T:NZX`K !PNL`!"'&5#5!327676'&'!ZXJ9g>  L#7&$PN JB7gLGةAVj!2!$76676'&&`rkjs+*Z6j{4V@BsSV}dxaM>HV`'!&'&+532!;#"'&Cz#%&OudgRPC)#%&PvgdSr6LJ<mLJ>EV`%6!!$!!n$nn$n$d 3V@ O@`3676!#"'#"'&7!2H WlrU11UslW H86*CqSSq61&jL1& jbof&Lf& @f&p$ # 76'&!"'&7607676"B ws6Fqn r96$(-k<`zt ~xim$ $6'&'&'&%6#"'&3676g2DTGjЈ ۞Q;^{/G/z xGCcX}վUjNk"!4''&676'&\+**SJtxLGkeJZ vV "40z7/cT$f'O"k& u:V'67!!&'&4%67654'&W$dTܢe)FF6$&EEj#U mi$a mE+_q!v+\u!`#5!#'$'%!76''iKJ#  9$G@F=#- t 4`/'5&'&7'&7676'&#"5677632y&͟2!/65F\8R~ 0=2C\gO\ 20R[Fk{ZsB="20BPZPa|bl\Vu "326&!&'&! hqhhqrhh!ܛ\  Y  `*~xƇbVo{ "326&!&'&32hixxijxx)܋^[Jha#j=i`9 $2'527654'&#"76)!8b^\TF!;Ÿ|DRfa\T&(%xyǬjZVt`""#52764'&# '&76)AMM=nR^\SF";wxM[\ILX[TL&X) V"%&7632.#"!!#"&'53676y~fs7+P'R4F7czc<;7W RS; 4`+6'&#"56776327'&'&7'&765F\8R~!0=2C\8R~!/6sB= 20BPZPa|blB= 20R[Fk{Kw{!3!! ! #"&'$&"2 "Z6lnn(S]^D%}FXRM\u!3276!&'&#"! ! @Q )4qr4(_ 4rq4  _xx]ΔKyyHxx}$%# '&76!2&'&#"!!32767FYPgZTSS?MOPL.7LXGIC9*,6[5XfAZ;%}%7032767!5!&'&#"6763 !"'&CIGXM7.LPOM?SSTZgPY9 ;ZAfX5[6 ,흜{Vw9&V{ !!###V`b'+%TVV{ !!!#!V{}'%.Vw{%!!!5#53! #"&&"2GhhZblnnppS]OD9703254#">3 !"&LLLLDU.>U+HFAAFH$$rr$9'y09&4yEJk&L uJk&L uX,%+532654'&/"!#5!!6?67632ڈbR#`)GG" -ݾ+/5^)Q+nz3%  D1C0$ 5iXk&J u9:@!pop op nr! 2210%# !2.#"!!32679AZ>.]?6`<IJ+"&xy'!2UCDV6%,%k&= u+K SKQZX@88Y1m-%3264&+#+532>!32#6%tt%% rFhn laaZѰ+o'%3264&+!#3!332#6%tt%Bn laaZh9'!!670767632!4'&'0'"0!#"+/5^)Q#`)GG" -ݾ0$ 5iz3%  D1Cuk&Q uwXk&O uk&Z H 3!!!!!'q'/+B!${3@pp p d!   2<99991/0%32654&+!!3 )yuuyn5kbyyb'}%X@ pd1/0!!/(m@p dp  1/2<<0 KTX@8788Y@  ]!32645!3#!# 0d*P~ `3Uc5/BJ( u@   <<91@ Bd /<<2290KSX Y@ I:I:I:I:I:I: <<<<3! ##'# !MM dv_}LwX M@  %d && 991/<2990@  ]KSXY"!!!X^+==wXk&O u.H1@1@ ppd/<0@ ]+3267!! \ _c fѰ+ aoM+V{0H+\u2H@pd"1/<0!!!!+/{39&Zw7+326?!!>uP:a(71<1/jl#|@:  :: <<<<1@p dp/<2<20 KTKT[KT[KT[KT[X!  @878Y@ 00 0 ???]!5&5475!!>54&'sZUxsZVwssffpҏэ;P G@ pd " 1/220 KTKT[X @ 878Y3!!!3!P'q'//Beg"@p d  21/290!#"&5!3265g%¯#|GbVI3VN @ dp:= : =: 1/2<<0 KTX @ 848 @8Y KTKT[KT[KT[X @848Y@ ////])33333..Nw@ d p:=: : =:1/<<<<0 KTX@848 @8Y@   //// ])333333#zB000%32654&+#5!3 !5)tt)!5avaZ(%32654&+!3 )!'tt  yavaZ+|U,@p p d  !  299991/0%32654&+!3 !ytty'n5avaZ9!"63 !"'3 !(횒.>Ҩ(k|HHrqHH{&9@p"np r%pd:$ :$%:'<91/990"36766'&!367632#"'&#2:**),4"(,"'shdWgwoeloyxwx y"uSA@ ! 291@ B ppd /<90KSX  Y;#" .54$)!#tnntFK٢m_^#˱+N^T{DAoK ,5@* .$)-21@ 'h!h r-0632#"4/&4767676%6"32654&n&0u6$ZyU3ixxijxxK #of=4 JIX@ RwZ` ,@ hh  !21/9032654&#32654&#%!2#!}vv}NN@SNdn=FF;0119ۍSlj`@ 1/0!!{`?`l@     1/2<<0@  ]KTX@878Y!265!3#!#(5O{XF^VU{\}{H`u@   <<91@ B /<<2290KSX Y@ I:I:I:I:I:I: <<<<3!##'#!HH`WVJz1zU7{&>@ $ '1@yh!' y hr'90#"&'532654&+532654!"5>32YOsuc{rFTUs=w,1(TT56E|4}qC` ]@  74 221@% /<<990 KTXA @88YKSX@ Y!!!C#e`5`5CF&o`Ei`/@ 1@h/<0@ ]+532>5!!; _ dPQPIVz` @  !  91@  /<290@  %KSXY"K TX 88Y KTX @ 848Y!!# ##V```q7s/` %@  74 221/<20!!!!!/#=``}bo{R/`@741/<0!!!/`{`Vw{S%}F/`@ 1/20!!!5/`{;X`\OV>@ : :: <<<<1@t h hr <2<20&733>54&'  FjjFHjjHV..gs77`[a` %@    7 1/220%#!!!!#=#`{{$a!@  7 421/20;!!#"&5!\k"޾+[;3N` @ := : =: 1/2<<0 KTX @ 848 @8Y KTKT[KT[KT[X @848Y@ ////])33333`{{:`w@  :=: : =:1/<<<<0 KTX@848 @8Y@   //// ])333333#QV`{{{(`3264&#'32#!#5!FInjjnI^=;۬`3264&#32#!!B2jj2m%=;Z``$`$@h h 7 4291/032654&#32#!{njjn{,=FF;Z`%{"'3 7!5!&#"63 ő.#5駄$V ur T8{ 2@hhr  ::: !291/0"32654&33676#"'&'# ,^c+8T\k"BnmVf½›t`Q[ڔu `T`@@  291@ B  /<90KSX  Y;#" .546)!#nPPn)+y F=<S\}f&lC'\}1&lj'Xw53!!!>325654&#"!#K~R[i\L]fn2Ƭg f&jv%}<@" yh yh r 752210%# !2.#"!!3267%Jb&ZS@R|_UB9++89*,7;Ug9:+{VL1&jXRM` 3264&##+532>5!32+.2jj2v , dPV =;PIZ#`3264&#!#3!332#.2jj2+ =;!`}ZB53!!!>32!4&"!#KjE\L]fJyhf&qvCf&oC';XF&z/` !!!!#!#>#`{%32654&+535!!!3 )5)tt)'C5avaѠd(32#!#53!!!3264&#F^#nInjjn})=;\uabo{X@pd1/0!!!/2@ 1/0!!3N{`:/X !#53!!!هhM\` !#53!!!݂h6X!2+532654'&+!!!5~d+QڈbR#X5inz31RX/`32+53276=4'&+!!!QQY[qb)("%LxihӶjl77|14k` 3 !3!3###' L MMv_  Bd`3!3!3###'S'HHWWV%z1zU}oL&No7{&nu3!#!!!=#'N)BN`%3!#!!!)#d%`cX `T`}^%!#!!!!!3!'q'h9/`)!!!!!!!#=#!`}qN 33!!!#!N;DB"]N` 33!!!#!NC8`zro9&uXo%}&HxZw )!!!!!%1/` )!5!!!!!B<9V`!!!944b`)7! !3#!#35>"#>3Xw@@9V`!!3#!5#535944b`)>۰>!# ! ! !  V11XB%!7`%3!# ! !!5VVyHNeg!3!670767072!4'&'&#"e#*/79^)Q#`GG.-f2$ 5i1z3% C/K%, k&M F&muX !!!2+5327654'&#/'NOUW.QZ[ڈe&)#1FN5ool77~/1X`!!!32+53276=4'&#%`c QQY[qb)("%Lk`}%ihӶjl77|14XH%+53265!!!!!H\[ڈbR'q'+nlnmh9X/`%+53265!!!!!/Y[qbR#=#+jln`}eg#"'&'&5!3276765!!!D+/59^)Q##`GG.-#0$ 5i3z3% CF$a%5#"'&'&5!;!!!k+# 7sk"e[3=! '!!k&G ^TF&g!k&G u   +@  p? 0/   ]1^T1&jg{Jk&L \}F&l\uQ\}{\uk' u\}&j k' uM&jm}Lk' uN7&jn ynHc`8wXN& OC&owXk' uOC&oj\uk&U u +1bo&ju +1\u+bo{\uk' ubo&j9k' ud%&jN& Z;X&zk' uZ;X&jzk&Z ;Xf&zegk' u^$&j~X )!!!!%3` )!!!!!`B(k' (ub&j}LRt\u4ZV;{T:`ZG%!327.'7>5!.'#".G'qf 4.'7%F"k|;'p(L" 9RZ< +2Cbo!54&"!!!4> ^qq;| |;CppbۍCC!!#!#".> 4&";炻x9;| |;qq8O3bGΉECۙVpp`8[@#!4> 3#!4&"!!;| |;qqۍCCۙbppb| .5!!!26=!^;||;' qq''ۍCCۙppM"3!>54&#"!5!2!k'QPsy:[?! ʇC$P^KG^苵%NyT<1@WE4pa 3!!!!p'6?bo!54&"!!4> ^qq;| |;Cpp ۍCC+(53#".4>;54."#4> 32>=#"OWW3^[01\QkBjiBA DŽAA;( k?=Ckp:2ks8CiF%%FiCۍCCViZ/J5`!!!3# .>;"265';||;9x3O8qqbۍCEЍG#@[8`ppVjf!54&#"!!>32fqfdp'.j?|;1CpykzpC73!!'w/N2>53#".5##3 $0! KjkO`58K..K8_|t77t|#+<33>32.#"#".5467#2>54.'#.6~Q*`XGKX^,0W(yD8X}gҀ8*(f 1ac4W*,?eF&  4&dֈWsS-]o^f_[,0^\Zg<Tjf!#".5!3267f.k>|;'qfcq`Cۙ1pxl@%.5467>7!#z5`I+qvaTaͻ-#F4@M/BdROD\/oqh( -G <%267.#".'#".54>32>54."!54>21X0/\."('XH1V& ? ReO]33]OXO!)!>WlZA$UĒU /#()H)v8z@0Y)KB*RyPO}V-;5NckX% Qh?C!4> 3!4&"!!;| |;GqqۍCCۙVppG&>3"!".5467>7 3!?T&Xi}KCqS.TQ'N);5B;,EpP+?s_AeHNB}< a(#2!!# .5!265;||;'qqVۍCCۙpp28#".=!32>54&#!!2>54&"!4>2>NJŅE*:U8?X7wp[0C*[XD|Ү}FRH5O4bML\ )L:#$=S0_q1@#TOTUcm:;n_m&I^m! .5#!26=!;||;qq''ۍCCۙppK32>=!".54>7.+32%.#" =Wl[@$'RƒSBfx6:90 ]BIJ! %&8\C%^gV%#ThBF|Η]    ~ Jjf!4&"!4> fqq;| |;RppۍCCM$.#"!5!232>Tot:[?! ɈE<\o2%L&8]C%%NyT<1@S⏃қeBY6| Q4##54>32#4&'bU@֖ك8X`AxhݞXX݄iHA2>54."267>54&#".54>32!!!>nS04\c;.'3X( SL]S-4Uyh}4*TV%'Y%Ea<  'XWQ!Fg?7Sc,*!!)J!FS 7GNN#9~znT1eT2K!3#!4&"!4> qq;| |;bppۍCCjf!265! .j'qq';||;'ppRۍCC!3!#".=!3267G.k>|;'qfcq/CۙCpxlig?32>54'.54>32!4.#"#".5&=O*&F6!?ax|v[8*FgZy:&9F >1;a||a;A~w{ĈHK\1*G8.?2,5Dc^+`]VA&@nQ3=! $;-4H7.6Gge`yDE}kjf!54&"!4> fqq;| |;CppۍCC2G"32>54.2#".=!32>54.#!3.54>3D''D40C)(C4pz@KO:P2>NJŅE*:U8?X7:V8@y/="#>//>#">.=p_^(Sah.bML\ )L:#$=S0*K8!*Y)^o>pa3!!!p'6 -!>54.'.>75!#IR) )RHIS( (SIg)*gi**iw+Zdeb23aedZ+]ܜVzzV]3.4> #"&'!!!5#"32>54.9wz;;z+f1cْtK+.L98I,,d{EG|cd|FQ6L./M86N0.M7\u2 -7%>4.'#.54>;#".53"3IS( (SI)\nꇱi*,PO, )RH$. .$,VzN% NrK$M˂WiJ''JiWWV, " dI6!#9cf !fxH%#>7>73)PvH1WxHSb9Bn#2(L`?"+9'A~lPf !fxC@)#>32#".'332>54.#"7\{Qgs=*SzP1bS:6& ":K)SsIYgH&7`K8hR19^F %"0 7`3!#zP`+%#"'&32>532>53#5#".!!eK78 !2" "2! mE!@4%pHCde;w1@Q//Q>1>Q//Q@tBM%3V<{%!!!>32!54&#"<|#jENO[V ^ezi`AV{(%#!#".54>325!32>4.#"mldi76h`_$D4K0/L55L/0K4VHMێۗOèZ/0YY0/ZgVj{%#!4&#"!!>32jENO[#jVTzi`^e B!26=!!5#"&5!BrDY%j%`5yh0^eLAV{&!#".54>325!32>4.#"oldi76h`_$D4K0/L55L/0K4HMێۗOèKZ/0YY0/Z )!!!!%CLbV<{!!>32!4&#"323##".54>332>=#"(1_Q'PA(#G^l5OvIEMQm>HsR,5Wq<"5&#(f2(/=}d@%Ec? .I2:⨏f)'JiANsK%5)BkM)=A&#"2>5#".4>;!39N00NrO0$3PnYw77x$m/W~[14\KRMmP,a̚ZLV*!4&#"!!>32*ENO[#j)zi^eHV`!!# OV3#3>322>53#5#".54."< &/6551& "2! mE451& "2" V'7_d>Q//Q@tBM7_d>>Q/.Q@bo,!##".54>72>54./#"aQ[-9D~}D"GnM7UtV7)<'*msXG^؛VV؃Ze P[16^I>`L<V+!265!!#"&%DY%jyhP^e+K7.@!5#".4>7.546?!3:7672>=4.'75I[5aR$.XQLP,G4EvR&%0K]-V3lcRdgVj{!4&#"!!>32j]ENO[#jTzi`^e`F*=!5#".5467#53>32.#"326=4.'Fj^T&'$i2}\ "$! +% )KD<fyC$&#OY#CdA##K^eL]umXi:  0C(cE_>#%SI6h+`XX`%#!532>5!,^i1E+%+~p39]B5gj#5!3265!!5#"&ENO[#jzi^eV3y323!!".54>7>54.#"'>Ul~D'AaV_Q|0P; 1Rm +{QV-!".54>7.546?32673*J8!2EPY.1\G+39xB#/?9#T2cF~kW=!;U53yz2)E_:?:{@"G1?8xf$*#PV`+#".'#"'&32>532>53_6!@4%!eK78 !2" "2! V6:%3HCde;w1@Q//Q>1>Q//Q@VGy,D3!!".54>7>54.54>32%">54.G9ni)C/&|1P: -=$!)>H>)MahGF<(','O_2/H`c'G=1"9J'$RVT$!G&1J??OfGSl?7jX/>")@5/18#(1RsZM,%G7"u\{&"!!>32!!5>54.B-@)# 4I^7\X*5F'+<&*F+H`5{`*G5FuRPl+0enzF8gN.+`XaVp!#"&5!265!pQj%DY%P^eTyh7#Q{+!#4.#"#5#"&5332>73>32 "  mEo "! mEp>Q/)H8tBM1>Q/)G8tBMV*{!4&#"!!>32*ENO[#j)zi ^euX[}J`%!!%`QV+!#4.#"##"&5332>53>32 "a5o " b6p>Q/.Q@D?1>Q/)G8ACAV{*%!!!5#53!632#"4."2>inn$\`h67id,4K`L55L`K4DDOۋۖNZ/0YY0/Zbo{R*V#)4#.'7".4>;"34.'>-hByh' @CH(]c34ii~i-QLGHK.N;9N/A|Θ[Y'?V45I/2Z~Z2FNr.x)?fI- ;[w.%!5#"&5!265!j%DY%^eyh'!!!!MM'-%'-/HŞߎ;<;m$/#4''3767653653#"''##53+#*L&   %,s$ۻ44`OpR3&#]|0q<* S3E{;m*532767#"'&54767&'&5476'##53B4V53Y31*"@GC1.N@?G?2,2&!e*ۻ44`m|&z-,#-/ F?n=@;#v{ =TBV\L98FGDC+P=YNc-%57&'&54767632&767h8@}SnZdFG+9:H:Z?LWaKaKQ6&Xs%#4FD7CARXL%5,iGec50$ $*  ,$ QB]WEfnf`-Sm\daّ֔w<$6!`]]T'&c   %327654'&#"!#53!632YM,>5M(N365%65!"'&'&7!3276M 20yl$.0`UZkFOIij﷯\HK\ssj1+-3<,276'&'&'&#"&!4763&547632p1& 04Y2xAw~M`b|_'7&':&4Of7*g?'H-8XD&  %"327654'&'2'"'&5476).N(@A5/VǎQ%T=1V9>( <6-T|PQ#JQL  &"34'&!5 767"'&'&5476326LG($L:0 ,*F94D\0('3g'm8h"(uA~b7S:(05%5%0ppV05%0p*%654#"&'&547633"'&'5676 &$ 50?4M_/?%M(=tVF,*#$!4D\0(%3g1+k 05%0p(#'&54737676537654'3'&x=Es7" ,$ 4G +@&#^2$NQ2omb}8>#&!RY$6,It5\ ;! 4&"2>"&462FhEEhF2GF34FG732767#"'&'>|*631N:B6^f:lq=& )L0-Av84373^^F!!Jldx )!d&..l> 1<>}x!'!767!!"* %IiB=t/)ut_ptmTTPf>x%!!7676537653#"' D7Cx<,2/< F(9y-8 {)[D >2%,*1lL6 x*32767#"'&54767&'&5476`iST0PQNB6fpkMH|eepdOFO=4x!6E'5 PN>Q!S#!'%}pmrh/>5lwUflx "27654'&' '&47hP5<<6RNLZtrZQ[[Q꿻܆&zBx!'# '53 JT[10xN|EeF22Zzx!!6767!Ym,fJ Nc/n?5&<eNRa{oZzx!&'&'!6ZlfKMcЯo>4xhfNSa{z|x&'&'&7676!&'&#"ЈZDgvi\6/1vIF1>3#""@:#GlocyG O%(H'%s'J 3#3#!!@V ^,,6767654'3is$Kpm\erPO``Nov_E9!# VI@L !!%goqgpoUU 57&'&54767632&#"7T "$C5H@&_ |H&    ]l-%2767654/&'&54767#"'$47!&xH^'45!"1K79 ~xhWohpl,&y1l".BX6]E+//193J<01)|n]30 b,@^n@ _' )7G%63&'&54767&'5#"'&'#"'&547!%27654'&'67654'&#"9"*%D(3?=bf^E=SP8C!N'-_"x>fy(&547632&'327#"'!! x\$Jk jܤx3767&'&547676&'&32704trzV`jXX>+a?,Vv)krm!RA@!JYZzxZzxz|x>s{%#"547"54263 !4#"游ʑ,Lۖf7pQQuYl)"2546632&# '%7654t$P{R~8:tqu;;=MkČG:.j)y}47&76$!4&$'632y*, N~j+8Hu]6OA$,Ll^ȨH 463 &'%327'&32'4:NJ*!y @?Aquxs  A@@?K &"32654& 76'#5432!"!54n$ % *+C{aKKG 5"32654&"76767$326765! 54%7654$ % #t}Q҆em0o %"%Ƀ@<@BDkQcC;;eo">!fkz 3265!$'&7&5!2#"'3jj~$,*Xf,_b2O6hKM{V{$"24#"54$76Ȭ)kyx5 *%"32654&"&#"2#"5!23263!4#" $ & *|H, <8@>"&Y %"%ӎPW0n{$"272#"5"547663 !4#"ՀӨ9y6ܸu3I jdmbJS~ +"32654&4323254#4%$7" $ % fz3%:?( %"%Hھ|XsEhf\[K24#"5765&5476325!!"'@>]J*uP]#8Zs@0[2JFfHMM}T|"247&76325!%$ԛ% &xLB}T"247&76325!%$ԛ% &xhBT0 A"32654&!"32654&"' &54323253325&74453$ % $ & TeTXXTМЀ %"% %"%JJbߩ44ˆ+,@T} +"32654&#"' &5432325332536$ & 'BCTeTXXT %"%3^^JJbߩ44ˆH} ,"32654&2?33253%&'# 47&76D$ % LRZLĒ>%  %"%㩪,-=%ۂPH ,"32654&2?33253%&'# 47&76D$ % LRZLĒ>%  %"%㩪,-=%ۂP`!325!!"'#"543225""T)3Ƅu`,888fk  32765!$'&7&5!2#"'3jj??$,*Xf,_bZYO6hKMd )"32654&4%$5! &#%$&763232,$ $ /(8. <h $ $ ,^$RE?`{<> +%"32654&! '&'&7 3654#"%6#% $ 2,L/-(9X!$ $UzƜ1o5Ox{"326! %,'4323254#"0@@<) =;TYr>0!-553! '&'&7 365# "32654& 54!"CЀ,L/-(')% $ L@Uz!.!$ $6/'= 4"327$"327$7&76365&5632676#4#"%A?A?pƾ6\w5@W?:@@@L@@@E>/>icbX #+"32654&%$%&'&54327654=$ % 骅 AcS %"$36H9Gt6caf5 1"32654&$'&'$6!2327#"&#"%$&76$ %   ph@\†bjC %"%ŇD0tHG_)Rq}]  ''&'$! #"'"32?6+(xsx 8@7YCt N*(mEsJR'^R, '&76'&'07o V@P5)C21skf!4#"! fܺN=  Au f&/ %$ 5 '& [_0~9e}%$ 3!5 54 [`f G~9fP3} %$ 5 '&#'57 [_&&&&0~9e}#### _%$ 3!5 54#'57 \`f &&&&G~9fP3}####i&7'6'&762'6C3217*9{[l僐,.--Tbڂf xm&7'5667&763253% 2AKA |ۺ f@TwdD:0;QF, &'676'&76Lk`W r,sWf-2G)4G^&/'&767%254Gb*gW/A67 *ut7.,U 3533##5#zۺPhi%&767'6&j=DAA566bq ! $546732654&'3.'!'q-(xikvYK@,6\,"H%]x8ڡWedn'6.#32! 6756&'&5!94P54S;9R35C8,Tf[SZ00ZSPyP(@3i*3 !_#bsBPn*-%' 7! %>54.#".5! d}~'3B$8, /6O   /D2# "/--/"Kt/~m V4.+"32>5>7.54632632#".5.+"!4.%' a+%!4" 'AT3]KKcd30a`@eO8$C =cc="8HKI;&m8S89kS2"=U3_.,#CiEE3mxr4&@SZ[' )giI;6BY@-'+#anp*'%! 46732654&#".5 p UQ sghfdXf #fJG_*)*jd_`mjQW8F"Z"76;(Sbnp*:! $'4>7326=4&+532>54.#".5 p ! xgmvcX??)E16K.`q '_^d`r=A?S&P#llUX\XU/H23M5KN'4b09@ m``4#mbp 34&'&>4.#"326%! 467.7! |54&+"#"5!263  D) E2432R<6:!_ IwWPIpl0@$HHLBNy}IBfbmIS ~~? EELbp*'#>54.#"#.5&!2p0UD;?7O18X= B732>=4&+532654.#5 o*/ ~q1N7IXUL=ri_onq$RK<|'su/H3/nwDK-E/n_|#*bnp*6"!.'.#"'>7&546! .5467>54.^֐KX;$ Ji$Q5qj ,#   #?WUdUgn3>~*?l- ! 7*&=0H'Q>DdA dp/4.#"326%# 4>3254&#!5463 J:T45S;qaq&?~}AFn*yoU` WtFEnOkl{Bw6*6^]46bbbp 4&#"76%! $5&63!!">3Jtmiwsjmw& ((0gF 4A37 6!>=4&+"#4&/"!.=432>32(C36A$ 3-?7*2 'G:3D(3a$#W2:uuu;:mhb/Y[lY[0ein:73326&+532674&'$5!#p&#  }rar}l{Nar%PUjk349 !524!vrML6B 55 54%5%q&rd<_C$?> 7C% BhZkj5P6I CRc9Q{R*]FA!>54.+"#4.+"!.=4%2>32 "B9=G"  @  E "HB9I*%^?m$(hL73! 717ds reGuwDL 12iDhqmp@D%# 4>73254.#"#'4.#"#".'32>32>32o*8 p "' $!#-#!$3+*< #'*"*5#5I#&rA/)WRGm3^5sp9T76T:$,% )0( ' C77Cn*S4&/"32>5+".5;2>=#57##54632632o #/&A}v"jH".")8")7"'5AiZ(.dp(;B!(:&lm7,$!E6`RD:AD"bno7%! 4>732>54.#".5!2<645!o 3%~v1R:!:aICN   KF9f#@?A#A3c1sp"GkIwx=8;,(&W"<:@'H 657!9m*F>7$5432>32>54.+"#4&+"%.'&#"5>d&'kF+C0%8% WB%*+4)"^?&2 L$##$B>me_13UKF%J(1^1(ߧGV؅%=/" 9<dnp ,! !>=#".54>7!3265!p 7dl"847!@8aJ7=T73L3qu%u]_' /k{Brjg6=_L?:9!GY3Z]v@4.#"326%! 4>34&#"#54.#"!!2>3 P V^'@Cl-17%*#%Z%#{RlSk?>iQPwN'y  r430RQ np 5Q6UOQSbp/4.#">3!654.#"!4632.B(BH18F-8Ĉ"A[9ch2)4]F*0;! US% pOqJ#z5f?Zu9oP4# 54>7!2>56+532'6&'&'!{PHY[;Ә2P:*8#Qh;sa97!+)]hFy1@X~?ca\NbyAC$O|X·+=8BR"vbp 4.#"32>%! !2J 54.#"4>32>3 !4.#"#54.#">72!".5 6I+>^>9Q46U< 2XzH)PG:&Z%#*%++xC@xy<NuO'!GqPQoF?kJqL')=)QSZ6Q5 pn 1)+8:zM{bnnG32>54.#"! 472>'4&'.'" !!3#},69.,59/  uh5V; `P4=   "1)UU&C11C&%@//@\l $$c]5O5Dn*0V*\bE# F4/T9ao 4&#"326%!".5!>3Jyhluxlgw%ÂA$0oGLц<5anp(%! 46772656&'.'7%!%p@Aqfm~`Nbcd xed`dQU2]3suioU'CH5e)OM>HbpA! 5!32654.#!5!2>54.#!5!2>54.'.53p%ofl      !.'!32'4'#53>54.+532654.+5325&'&'3p0%! F"$ "# F$8F##`/-$ >GbbEJp=@Nbp* #>'6&#"267.5% 7$'okk}mftHj }{ &mC[$#aXXQNv+anp*"% 7326=4&#5326'!%p+*/vgi|roz}|`{jdtjypû d=|anp8%! %32>=4.+53>7#.54$"32$7p&wg4U< !>V6.S@(9=<A!!kZ}}3O55.G/0QsH 4c]ʽdbkoB)sm'#"325;54#"! !35#$! 3#3ʽy&kkk4c{L~fLo* 34&+"265%!"'#52>'4&'#&3$5!2>3##P#3d!&<(+I4bz_[R(yQ\@<<5>:<~C%&z{7Pn8654#"!6=!! 47.x]~hq!":b}^ooNc5=T+(4&'.#"'67.5%'>54'"3SNIm2R:͡86okC/XC.L|n#,m|I'bzDwwLu*.T*kP1 3#3#3#<л~R4#"322"43!%"\PUX]' *Cw;l5{ @32654&#"26=%!4&#"5>32>32+3267#"&'#"&IWY@=?=&2~2=d_A=B/fk{2#"'&'5327654'&+5327654'&#"56763 )HI]hjyTlz2US>hu2@`g#qriiXohib'(@BW  &1E OY"[\pigd %!!5!!!!!%bV{ (3!4&#"5>32>32#"&'#"&26=7326&#"c_uAHv&DX&h+0)5!264&#!5!264&#!5!#GD}TV{1{VT}1`tBMQ!fJo&d((d&np 33#'##hX:8I#3#3!5##3и6p\Dc"3264&#32654&#%!2#!|XHLT||F<>D0\\ln6<F-43/jiQZ nbym,32654&#'32+2n\\nK{zDD !!!!!!JpjDD 5!5!5!5!5!DjpJۑ*5#5!#"&54632.#"326~"6JȾ8n0(dJD<14AGș  33 ## "4RJx]D3!DDN 33###ppޠddonb0 33##0``0 ##3Ƥ`D` "265446  &BB@.H[232654&#%32+#LZJJZĪDU7DC7{{#'&'&+#!232654&#(r2D: `VLDBN'%4 ^DrVf$5>=4##5!#ĺ(32653#"&(FH?GG?9񫚚 333# #DP^4ljdsfI) %"326=7#5#"&546;54&#"5>32gR81IQ"h?yA?B}C=~G/:*3bX ?F)-qeni() |) ,27654'&#"367632+32767#"&*g))1J(("35?xQO! ?B>?B<~G9*20X @fF*qen54( |053#5#"&547632264&"鸸//@yDC~8.,DxEExY]6UUggg @32654&#"26=%!4&#"5>32>32+3267#"&'#"&5.78('(& O ?<&K& O4A^R>em010$U..Y(>NR:p\..+*(*01 F55F vPU! /-0,g_hg45"%"%04&"2>32#"&'#3DxEEx"Z8~y@^yggg4476]g03#5#"&54632264&"鸸^@y~8ZDxEExM]674ggg#"&54632!3267'.#"@J"a`@{DHG?LCJH #ABE?>32#"'&=!.#"327679@J^]WVa`@{D$$G?&&TTRSCJH #@"!#"?$2&'&54632&'&#";#"32767#"'&546X-.:BCLV5DM64'BJt)bek$%1aX &z ,2 33?HS$2#"'&'5327654'&+5327654'&#"567632X-.:BCLV5DM64'BJt)bek$%1aX &z ,2 33?HS. &4&"26#"&'5326=#"&5463253HtGGtH:r:4l:NJYAxw=^TeeTUeeAFC,,2.P'm !#5!#3#53y浵~~Q& 373##&?7w%5>32#4&"#4&"#3>2A.\E??FUG(&pF/0EmE0/FmsA%+-M+532654'&#"#3>32:8L?312_Be333<<~=JfD('Gs^49:<" "3264&632#"hBLLBCLLwhhhh0бO053264&#"567632#"'&O*./6ZaaZ41/)4558\]>65!ef WXʮ " 4632#4&#""LCBL՘\hh\" #"&533265LCBL՘\hh\0#3>32#"&64&"2踸^@y~8ZDxEExb^674ggg93#;#"&5#535.6p~*$~Zz!~M32653#5#"&M+d8^BefwD:OFh]49v!5!264&#!5!#5MAXOkh:@&Y2S;Y\"#"'&5326532653#5"'&;A.\"#??##U$#(&88anF/0EnE0/FA%#3໖s&<dU|YdMs8ds;dbXH} (%32654&#"6!2.#">32#"&'!r]\qq\]r\]S[|v+f`+ۓW .,u|yPNZR:jh`53!5!!3#!!5!5llZXF!5!;#+532767#"&NRbSZy'b+"Ѷ`n9}+dl7,d0#367632#"&64&"2踸0.@yCD~8ZDxEExYs]6UU4gggO#"&632.#"3267/k>8j4)`4ZaaZ6]*6!fe !JM,;2'&#"763#*''47&'&7632&'&#"$"]R4AkA~3'\]]8554)004Z01 * e@4'$W6XW 33^V% (.#"32654&7#"&54632''7'37>BMKAEI J?6(e#c:*FQFXdea7MUzBA=qAHEB$(#"&'532654&+532654&#"5>32`kǺ>F8HGWetJB[UMyVL:[ SH?f2,z&"  Xa1IJ3###53546;#"ٸi*F7~ ~,qW~t]33#+532767#53 ppADp'@v?CB2(#"'&5476;#"'&'53265"326='[ v\/<~g+#, #5!;#"&t4=ǃs~J>~y,#5!;+53267#"&t4=57L>0s~J>~x;:~78yxY3!!y03>32+5327654'&#"#4'&#"#367632@/\#"-.n>3   "#+*$#(&87z8=~J|FDmDFmsA&"#"&533265332653##"&;A.\E  F+*G(&panF/0EnE0/F0%+- (#=4'&'.#"+53276=367632ż &:!"98kM4*0/9e45>R10U{ rCCK{\7BC"4&'&#"#367632;#"'&56&!*/09e445Jj8<0( 1*[s\7BD~KCGn0 33##0lm" !7632#"'"!3&'&+3276"XYXYYXYB&&E &BC&=0XYYXXYY4(??(4-44#+7675#5!#3!535&'76764'&fHm/mHffHmѻmH////=0X@q~~q@XX@h~~h@A^ 44R 44O:&'&#"#"';#"'&=327654&/&'&547632S3787> 5e/.GH()?L99=?@;A!"0Y3i10IJ;<;- /0Pa10.~=< " -.L[22 +532765476;#"5:Ȕ>55*y8<~ Iq,+~ $93#;+532767&'&5#535.659L>28~*$~x9<~8)-z!~"#5#"'&=#533333+32760/Ae4344ƹ-12]4;;ummD'!b$327654'&'5!##"&54767#5!?)H;<#$) /,#"'1%q+-AG%n$#'&=#5;?27654'&/L)%OO|Fh99ee <+&+l!J=XYXDC~D~¯_71P<@/(f3# ໖sG !!!5!V4st{y!!;#"'&'!5!4t?L95ttdJ~=7{y.]3276'&!367632+'47!5!?3;1t (B3_$y)7!5!#"&'532654&+4[1G^AA>x;W\_\jz yP|<79;" .#"3267"&54632BCBBADEAll~hh! &$^ T{&D}k&% w1&Ed}2&%2w&E}j&%jw&EoAk' u&u&opf&vf&HFuk&' Z;1&G2u&'Z2;&Gju&'Zj;&Gou'H'Zo;&Gu&'Z;&GJ&(\}{&H'J&(\}{&H'oJk&2&( \o}F&2&H'Xk&) ;k&I ujN&* 2bXH&JHk&+ /k&K 2H&+2/&KHk' u+/y&KjH oH&+Co/&KH&+/&K%&,&Luk' u.k' uNu2&.2&N2uj&.j&N22&/2Z2F&O2N& Z2FN& j&/2ZjF&O&/2ZF&OV{k' u0Rf&vPV{k&0 R1&PV2{&0R2{&PwXk&1 /1&Qw2X&12/{&QwjX&1j/{&QwX&1/{&Q\u' 2' 2bo&w&R{r&3 w|Vwf&S{k&3 Vw1&Sk&5 #1&U2&5#2{&U2N& #2&j&5#j{&UVk&6 +1&V2V&62+{&V2Vk&&6 2+1&&VZwk&7 o1k&W Z2w&7o21&WZjw&7oj1&WZw&7o1&Wj2f&82%`&Xjf&8%`&Xjf&8%`&Xjf&8' 2 %&X&w9T&9 \P&Yw92&9P2`&Yr' |:m&CZr' |:m&v1Z`'j/:&jZk&: 1&Z2&:2`&Zk&; 71&[k' u;7&[jk&< ;X1&\sr&= .|9m&]gs2&=29`&]sj&=j9`&]j/&Ko1&Wj/&Zu;X/&\u ;k&A bo!!2&$^2T{&D!2r& |^2Tm&g!' &$ ^T&<!2k& ^2T&s2J&(\2}{&H'Jm' 1u(\}&wH2Jr& |\2}m&g"2%&,2&L\2u&2b2o{&R\2ur& |b2om&gk&b u f&cvk&b u f&cCm&b u 9&cw2&b 2&cj2f&82%`&Xk&q vuf&rvdk&q vuf&rCdm&q vu9&rwd2&qv2&rdr' |<;Xm&C\2&<;2`&\,m' u<;X&w\6r&6r&6r&6r&6r&6r&6&6&r'Hr'r'r'pr'{4r'gT'H"'r&r&2r&<r&xr&r&Jr'IJr'IJr';Jr';gJr'r5Jr'hV/r&V/r&V2r&V<r&Vxr&Vr&V/&V/&Hr'Hr'Hr'Hr'+Hr'6Hr'6(H'(H'r&r&2r&<r&xr&r& & &%r'I%r'I%r'T%r'@q%r'|?%r'rU%'IU%'Ibor&bor&bor&bor&bxr&br&[ur'ur'Iur'Our'O%ur'0ur'&Lr& Lr& Lr& Lr& Lr& Lr& L& L& zr'mr'r''@r&@r&@r&@r&@r&@r&@&@&owr')wr'bwr'Owr'O>wr'Iwr'Iw'}w'q6f&C6ff&CfV/f&CV/ff&C fbof&CbofLf&C Lf@f&C@f6Vr&˜6Vr&˜6Vr&˜ 6Vr&˜!6Vr&"˜6Vr&#˜6V&˜$6V&˜%Vr&&Vr&'Vr&(Vr&)pVr&*4Vr&+TV&,"V&-V/r':V/r';V2r'<V<r'=Vxr&>Vr&?V/'@V/'AVHr&BVHr&CVHr&DVHr&E+VHr&FVHr&G(VH&H(VH&I@Vr&r@Vr&s@Vr&t@Vr&u@Vr&v@Vr&w@V&x@V&yoVwr&z)Vwr&{Vwr&|Vwr&}>Vwr&~Vwr&Vw&}Vw&6F&6&6Vf&˜6Vy&˜6Vf&˜69&6V9&˜!k& !N& f'*f!V& rV r!55#5! Ax 9w ;'TjV/f'V/{'V/f'V/9&V/9' Jf'DJfHf'&yHfVH&2r'6xr&n. 'F&&&  9& &%k& %N& )%f'b%f<r'@r' 'LF& L& L& LVwr&Vwr&L9& L& k& N& f'f{r'5;'rj-; fC@Vf&@V`&@Vf&@9&@V9&8uf'qUufBwf'{wfZVw& fv r5!#7 @ïx-10!!-w-/10!!//10!!//10!!//10!!/&BBL@ tW10!3c~L@ tW10!#:bjo@ wW10!#9coL#b9 &@ t WW 1<20!3!3b`~~9 &@ t WW 1<20!#!#9b9b9o &@ w WW 1<20!#!#9b9bo9 #!#bAb;3 $@f d ?  <<1<20!!!!!5!LK}8;39@ff  d ?  <<2<<212<220!!!!!!!5!!5!LLKK}+}` 10467>32#"&'.736HI256743IH426I235624HI447743?!!?qH9o #@w VV V 1/<<220!!!!!!q'd'd'ooo $0<HLp@?J%K+I"L 71= +"%j4C: L(I1KJ\\F\7@\1.\1(\/99991/<2229999904632#"&5%"32654&4632#"&%"32654&4632#"&%"32654&%wwwu2IH33JJxvvw4GH33JKޥxwvw3HH33JK'!xwxwK24KK42IxwxG45LM41J+xwxH35LM41J^\1<HWg"2654&46367636762#"/#"/#"&"2654&4632#"&"2765454'&!"2654&#'-@@ZBCגkjIIjiKIӕjhIKihJKhj4GHfJKxvvwh,A?[! #?,  [AB^\H35LM41J}xTSTSwxSTSTNG45LM41J}xwxK24K&$13$&& )+ &K42I`J!`u`'))D`')w'))`J#!J`u`',,B`',w&,,Z/#@ U29190 5/+#y#@ U<9190 5-+#wv"'4)#'!6763267654'&![`bede""^XD#.  S#F 8LAB\VC)*= %(+C" M B0 !#3#3!/+ !53#53#5+޾tt&'  4&3' D' 4; 2###ͼ農 '47632"'"2764632#"&$"H !! (TUUT <XFFHFFE4llll&@ !3!53#3#Xn|洴 ~~w E@    %   ^ <<91<290KSXY"333##5!5mmw)%#!!>32#"&'5327654'&#"G+y8 `_gfAF3B;=j8777\6677ߑON}LL &%GA('  ("264&'&#">32#"'&54632"##"F081.nr dDKLUTUT033?'&JI''Nݕ xv%$GHMMff !#!MUu2 )7"327654'%.5476  '&547672764'&#"**TDC**)NPRSRS('LT1/VWWW11k"#x"##"<;##"#tF$#9:#i^Cj;<<=hC/.86Mz@AABxK971b  .532767#"'&547632#"'&27654'&#"J081.m99!22DKKTTUTgh033A"##"ABF## <;w$HGLMffkl ''JI''N'(8 !!#5!5!58G!!LGA!!!!ffƅ #.67SQQSdabc~| 3#>54&dbbdSPP|~I#4'&#"#3676322300Df433i~D('Gs^4;;T;d9C{dTtdTudC=d%C>dT?dC@dTAd TBd4Cd%Dd)EdifFdifGd)#d)d"2dt}d*dIhVd&g/d'h{d0dIHd06dJ|d9%7d$!!!!!!#"32.#"3267$Ii8w@@w88l!Fi+Eh,6767# !2.#"3>32.#"!(%PQQ_M_QQXZ(30zW !@!_f "I77ef87ID: ̦c[[!!!!3#!#5/Ѳz[X"XnS#67632.#"!!!!!!35#535#56kpQN=Dh1.IIȫw|'&>:r 'B0#4'&#"#3676323632#754'&#"#m4C#$+66CJ97wn8 |2@% ۾75UU``/085cs VH)v75TR#5#535#53!3!3#3#!#!3'"aMBBBnAAAARlbMxJJJJP3264&##532654&/.54%+#!2333632.#"#"'&'#"&5\1551: %S2-./hT,C{n{C2 ,-B+^71]*,+!*wSsr04}]M_ml%N9"/:90/ # U~^> 0035+. #F2654&+#.+#!232654&/.54632.#"#"'&9C;;CZf?5<HPP,E$ 8;?@;;)=#k7yH@x597*6#i>A?ZgfXspR֔-v/:90/  0035+. #"&)-1'#53'33733733#3#####5373'!37TE11DThprJrrh&;;x;;#vvVVBvvBvvvv !2#4&+!%"3;!տQ~PտQ~Y*5=YAHJB @@=kbq~Es/4 %UAB`_KBjoD7=AiK}f^[@ )5'32327&547632#527654'#"'&#"654'&#"ؿlCm(IQXMboB0?iqK:(/$3!%DlX``S㫣lm@.jVBthP#lH+990C<#(/! 3#3#!#!#535#53!327676'!%&'&+5\cVVb\nffffy;*\ ;ynKg((gKntgPgM1 w((P 1K,[x$-#5!#5&'&76753&'&'6767uOrz^b፭]Xl`bPgh"'N;]=mi@^TKF#eiϥ"7?& 6#PU"53!3#3#!!!#537!!/3':?+}EhgxhhF~+$++P(UÕeeÕÊ63267# $545#536767!5!654&#">323#!xmlvho O9+ rk`_eg|P=5YcRT45 2 f@PVFC ./_T. )%#&'$%6753&'&'6767_h8:Ьݢ:8g_Q\DGHB[T%;/+R7r~or 7N'(OQ"z!Zw !!!!!!Z-Ygz%#!#!'&+532767!7!&'&+7zV  V5K,!0Oky4 /Xy 4zX8J_@[ _y,>**;1 %3267# !2."_|dT ȄE}=[~oi 7@,L]r4*:0N̾ '1.#"3267#"&632#3"32654&'2#"&65::@@:!;2.M''M#+//+)00)@!vv#B5qtsrrstqT3!3'!!!!!!+aa#]x}+ ;`!>54&#"!!>32Nۅ:7Sty/#t0e (5:=\gL;`4#"!'7!7>2!6+qStyқ7?#&B00ۅ w6;N>;}N,7?2#".5467>"3267>54&'.!2+#3264&#hZZ\[~}ڶ[\ZZ~cIGHHcdFHHHHHss|AGGANZZ[~}ڶ[[}~[ZZGIGebIIHHbeGIGwuu`7e5J3%!2+!327&+67654'&J m5//5=*__*]x~#g)ZZ)'%!# ! %27&"676'&|F"Cobbbbn%lkh4334a%-13&'&7!.+!!2!27&#676'&%3{THvu2M;o.a4w5}fcd)1ED)m=s@1F~h|hb)tLVy't& >Vy'u& >Vy{'=& >V/{'{& ?V#{'>& ?V/~{'{& AV~'u& AV#~{'>& AV~{'@& AV/w{'{ B 5!!B#x4̂x#xxM '#'"xx$Mx4x#B '7!5!'7x4x#xxM !737"xx#x4̂xB5!'7'7[x#xx#xx##xxݎxM'3'7#7x$"xxގxx#xx#x- 7!##v<Ļ vĜ<7 #5'#5!7Ĝ< dĜ<6t %!537536vdĜ #!$5>+;6-0$(v# *%;(#8MX!GL!!+Im6#"'&'&'&'&'&#"'67676327676'#5!#O$0-6;+>4!# >&.&=A"?,.!" v([+!!O7!XM8#(H. % #vdB!!'#537xZx#xZxxx##xxM'75'3''#xx$"xxxZx#xZxB'73'7'7#'7!5,xZx#xZxxxݎxxM77#75'73؂xxގxxcxZx#xZxB'!5!7w>x#xx1xx##xxB'7!'7'7!'4x‚x#x>w1xxݎxxB 53#5!5x#x,x##xM %'3'3!5x$"x,rx#xB !'7'7!#3rx#xxݎxM 7#7#5!؂xގxx#xrM%7'3'7!!5"xx$"xx",#xYx#xxB(276767654'&'&'4#!5g    @16T)+51@x#x  Q87;=49(*x##xB(!'7'7!"'&'&'&547>763"j x#x@15+)T61@   xݎx*(94=;78Q  B$=+#5#53547>76"3276767654&'&'&g@16**)+50AGx#xT61@  G    ))87;=49(*x##xH;78R  H  B$=23'7'7##5#"'&'&'&54767676";54'&'&'&j@16Tx#xGA05+)**61@    G R87;Hxݎx*(94=;78))  H  BF26767676763226767'7'7#"'&'&'&'&'&"#"'5[ #$! x#x,"    "/x#x   %$   xݎx "  ! x##xB#'7#533'7'v81x#x81x#x'x##x'xݎx9~ 7'7ckn"[ kOcuP%8 5!#Ђx"xhx##xw !#!'7'zfx$xWxݎx8e !3!5x"xwx##xe '7'7!3x$xxݎxW5p !5!7#7[_xݎxwx#x?^ !3!5Xxx"xx##x2X '5476767632#4'&'&'&7#7,#!A=PNZ]KS;>#"!*#13#'Dxݎxq!TPA>! #;SK]ZNP=A!#qx#x%'C "()/ZOR?<# !>APT2V 5!7!##2lv<ĻʌvĜ<B  !!#33#'7!5!'7xpxxpx xx,xx742#"'&'.5476732767>54/#7!&>((*MGgZsn_aMOP(%R.-'<0CA57---0v\bon_cMG.(()LNkoaZU-8:>=96/(-,r=ZHv74#5!#53276767654'&'7#"'&'&'&5476v0---75AC0<'-.R%(POMa_nsZgGM*((=\vdHZ=r,-(/69=>:8-UZaokNL)((.GMc_nmdB5!B#x4#xB!!BM̂xx#M3'#"xMxM#'x$M4x#B!5!'74x#xB'7!5xxM!37xM̂xM!#73ضx#x4B  '7!5!'7 5!!x4x##x4̂xxx#xx M  '#' #737"xx$rxxMx4x##x4̂xB 5!!'7!5!'7B#x4̂x*x4x##xxxxB'5!!!!5#x4̂4̂x>#xx# M73'#'#'3i"xx$x44x#B'7!5!'7!5!'7x44x#>xxݎ M%#73737#hxxގ#x4̂4̂xB '7!55!x#x4x#xB !! !5!'7BM̂xM4x#x#xB!73!!!'7#5!!{Va6PEV`6Dx#x\HHVߞ;cff:bDx##xHHB!7'#53533'7'7##5'35#HHDx#xDDx#xDHHHfDx##xDDxݎxDfHB!'7#5!7!5!73'7/!7'!8Va68PxV`6Dx#xHH;cff:bDxݎxHHB!!5!3HH\ Dx#xDyHHfDx##xDfM#'3'#' fDx$"xDfI\Dx#xD \HB!5!'7'7!5!7Dx#xD \HyfDxݎxDfHM%37#73fDxގxDfH\ Dx#xDHB5!'7'7%!7'!Dx#xDDx#xDkHHHDx##xDDxݎxDfHHHM'3'7#77'Dx$"xDDxގxHIIHWDx#xDaDx#xHHkHH}6##7!#V`JvnJVJpvJT '#5!#5'5CJnvJ`JvdpJ^VT%753!5373JvdpJ^V^JndvJ}6%33!'38V^JpdvJV`JvnJB!!!!5!!qYNx#xdfYfx##xfB'!5!7'!5!7!5Yx#xNYdYfxݎxfYfB3773#''#5[KLnDvvx#xPP~~x##xB'7'7#''#5377vx#xvvDnLKxݎx~~PPM%#5#535#535'3'3#3x$"xV¼x#xVM3#3#7#75#535#5353xގxV»x#xVŸB #553353!Ƃx#xC{x##xM 5'3'#7#7x$"xnƂx#x}ʻB 3'7'7+53#53Ƃx#x}ʻxݎxM 7#757'3'3؂xގxƂx#x}6B !!#3xpxxxB 3#'7!5!'7xpx,xx 5!5! !!5cm 깹] 333'#!#\^Z A !!75!!5 ]]YY ###3!3"^\ZX0 m 3'335%!!# #^\znnZgm %3'3#!5%# #3!^\^dddZm! #!5#7'# #3! ^^dd9cm! # #3!3#!!5#3f\ F ^m!וddkn'33%# ##!#'37"\`\\\~$ym?TT %3'3#!5'3!3#7# ##'37^\^p\\\@ddZm?TTB 5#35!7'!!!5 5ddm]]YY'd!#7!##gv<ĻdgdvĜ<,x!5!!53753dĜ<cg<Ļd 3'3#7## #3 3^\\^XZZ֤mmB676323'7'7##"'&'#58X)O$A?x#x:[V6NJ9\ 63SxݎxH9ZY8J M 3'#'737"xxxxMx4x#x4̂xB'7!5!'7!5!'7!5!'7x444x#?xxݎB#5!5!53!hZיrrh4_+__|z|?X!0?"'&''7&'&54767>2"&'2767>54'&ww&'''OO_:3www('''OO_;4AA565  AAA565 ww49_pm__ONP(&www4;_mp__OOP((D56MJ@  56JM@K@&%h91/90KSXY"% !!{!5q!!{qdmCq?3 3ް2ް22013!!"&63!!"!Q )SS) PH{__{HPd;w +mq?33%ް 222ް2222*+/"/01#"!#73#!!3!!"'&'7&'&63S) W J+!y 53W ^c>H{HPPHC4|Pu_`P`_cnB / 3 3ް2ް22fff01!!3!!".>3!!"U*^_*&,bމ`+dmCq?33ް2ް22 01&'.#!5!2#!5!26767!5 )SS) mPH{b{HPd;w +kq?33*3+3ް222 ް222%2/"/01%326767!73&'&''7#53!5!&#!5!2'#܃S) V SK,"xk 52X ^c>H{HPPHC4PuP`bcnB/33fffް2ް22 01!5!&'&#!5!2#!5!276|*]^+&,bމ`+L9@__120!!!9_^-LP *@     <91990 5!! !!5my=/9) ӇB v10!!BKXy !!5!3!!!5!ff!bbqB`yTU etrX)#1 ,@     9190'%3##q@`44{$1v&!u)1e&!==  %.#"326"&'#"&54632>3"36J032#"&'#"&546320P2;JC88b6J0#@ a/<<1@h h//047632&#"#"'732UpsXlNGUnsXlNGD"McoHޕMcoH^#+4632&#"#"'732%4632&#"#"'732ronQ.A&ronQ.A&ronQ.A&ronQ.A&D"K&ޑK&"K&ޑK&^#+A4632&#"#"'732%4632&#"#"'732%4632&#"#"'732aroKnR&roKnR&rnLnR&rpKmR&roKnR&roKnR&D"KzޑKz"KzޑKz"KzޑKziB'4W'W$i>'0$'$Wi'$WiB'0$'4W'$WB &tJi !!!!!!PMM:>/6'n$'oT'$'TXy''~7Xy0#"'&'.#"5>323326yKOZq Mg3NJNS`u_G0;73 ":?<776<Xy032?3632.#"#"&'XJG_u`SNJN3gM qZOK0A<677323267#"''43NJNSFXɉ;5GJKOK[C :?<7Dj323326!!yKOZq Mg3NJNS`t_F(!Z<73 ":?=676=X'y#"'&'.#"5>323326!!yKOZq Mg3NJNS`u_G)!;73 ":?<776323326!!!!yKOZq Mg3NJNS`u_G)!!;73 ":?<7763233263!!!'7#5!7!5!7yKOZq Mg3NJNS`u_G`aG#z`;73 ":?<7763236767!!!!'\UQ:43NJNS`jNDJKHEL=a?]R<# :?<77 Y A;XXy7Z@110+5.*'   '.5  810*8<29199999990#"'&'.#"5>323326#"'&'.#"5>323326yKOZq Mg3NJNS` t_FIKOZq Mg3NJNS` u_GZ<73 ":?=67 6=;73 ":?<77 6<X=y4&'&#"5>3223267#"'3267#"'&''75>3243NJNS`f]GJKO)-D\NFIKOZq gZpNS#(  :?<76Z323326#"'&'.#"5>323326!!yKOZq Mg3NJNS` u_GJKOZq Mg3NJNS`u_G)!;73 ":?<77 6<;73 ":?<776323326#"'&'.#"5>323326#"'&'.#"5>323326yKOZq Mg3NJNS` t_FIKOZq Mg3NJNS` u_GJKOZq Mg3NJNS`u_Gu<73 ":?=67 6=};73 ":?<77 6<;73 ":?<776<X<y"32?3632.#"#"&'!5!5XJG_u`SNJN3gM qZOK!!A<67732#6454&#"#4&#"#3>32V""V!Z6^b"%25'26 R28W35uo :5SN[5SM%Q//7V'x:%)!!!!#546?>54&#"5>323#V""J 6 0*)^3@"   v v  <291<2<2.990!3!!!'7#5!7!NŮmA1}X<y !!!!!!X!!!Xy{!5!73#!!!!'7#537!5!~P$fZ=e(lN"ebSbKXy !!!!!!!!X!!!!Xy &@v  <2291/90 5!5y!PNFXy &@v  <<291/9055%!!X!! Xy 3!! 5!5X!!! PNFXy 3!!55%!5X!!> X}y#5!7!5!73!!!' 5ZYM{~ X!i i행PNX}y#5!7!5!73!!!'55%ZYM{~ X!i i행Wyq&%5767$'567Rȳ}v֜P ij~wԞP(Fd%EP7(Ge#Xy%5%7%'bk8jyx!:_|:XCXy'75%%57'xk8Zj<y:_|B:[XCX<yD7%!!'7!5!7%5%ykSnKAD*ZWOzd#@4=PhLx"LX<y@7'#5375%7%5!!' z0VFdjeG'C0'Pau6LvLXy!#"'&'.#"5>323326 5yKOZq Mg3NJNS`u_GJ!;73 ":?<776<PNXy!#"'&'.#"5>32332655%yKOZq Mg3NJNS`u_G)!;73 ":?<776<X<yD+.7%3267#"'&'&''75>327%5%RmKKt`GJKOZq G:GAFJNSMOyel!PNX yy  5 55%y!!PNXTy1!7%'757%57%5%77'4PGx0e4P0GkB$$1F4F\}F5F\|t?t?XTy15%%''5%75%7XZuu: &uvk;Z&vFIs}FyJs}Vwa%&'567$wSh"/_D$#Q_Vwa%$VhSbQ#$DbO/"X[y5%$X{qQ_#pOKI4 &X[y%%$ynq{_Q#yBpR&4 RIKX2y%%#"'&'.#"5>323326%$yKOZq Mg3NJNS`u_GJQ_;73 ":?<776<TKI4 &X2y%%#"'&'.#"5>323326%$yKOZq Mg3NJNS`u_G)_Q;73 ":?<776<TR&4 RIKVw67&%'&'567677\RN@E߾\SiIRa_bIGE#"R!+Vw'76?&'67&qRN@E߾\SiIRab_bb*IE#"R!+DXyx!!"3!!"'&5476?:nLMm:׃x|~KM᎚ِXyx2#!5!27654&#!5̍:mMLnxϚMK~|Xyy %&'&5476;3!!!"''#"T=1̆cHcw.nL!5ِEFǚ|~K Xyy +'7#5!!5!232654'&'}=1̆cH>9c.nLA!5ِE,F9|~K Xy)%!5!!"3!!"'&5476y:nLMm:׃c|~KM᎚ِXy)7!!2#!5!27654&#!5X!:̍:mMLnQϚMK~|Xy(#"3!!!"#!!'7#537&'&5476;7OnL!-u/K.~=M=̦CH|~KuLx#BِEXy(!5!27+!!'7#537!5!327654/:*'EE/&`-u/K.~-/ mMLL ,E(ϚuLxuMK~|M Xy)!%!'7!5!7#"'&54763!!"3!!y(6^N׃:nLNl:=6ؔDuِ|~KM1DXy) 2!!'7!5!7!5!27654&#!5̍Kh]6(6^:lNLn)ϚR"KDؔDuMK~|XVy!!!!X!6XVy!5!!5y6Xy 5!!!!!X!!6*@Xy !5!!5!!5y!6?j33?2"&'&'&547676"2767>54&'&'3!!#!5!WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;93372"&'&'&547676"2767>54&'&'!!WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FMXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;933?2"&'&'&547676"2767>54&'&'77''7WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F8xxyxXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;9yxxx3372"&'&'&547676"2767>54&'&''WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FxxXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;9_xx37!!2"&'&'&547676"2767>54&'&'M.WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F/XVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;933BL2"&'&'&547676"2767>54&'&'2#"&546"32654WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F7b%&'qqX>=,-?XVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;9d)'%`8nqq>Z<=,,3!)/7?E2"&'&'&547676&'&'&'75676767'%654'WV,+++WWWW+++,VW:F!#!E: ֈ :E!#!F: & XVWih{xihWVXXVWhix{hiWV9  9{18@9p 9 9 w:A92t3!;!!!!2"&'&'&547676"2767>54&'&'+{{WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F@XVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;93372"&'&'&547676"2767>54&'&'!!WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FMXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;92K 3!!#!5!!!!o2K !!!!!Moת2K 77''7!!! yxxxoixxyxs2K !!!!!M3o/B!!#/^B!!5!3^  B!#!5RPB35!3!Bw 10!!Mv\ !!'*]])]\@寯X'y32?3632.#"#"&'!5XJG_u`SNJN3gM qZOK!A<677F =0|bF=&N@NU@Xy&3!!!'7#537!5!!5!3>F =0|bF=&NjN@Xy5!7!!!!!!!'7Xw!6_H4%H@CffXy!!!'7!5!7!5!!5y_H4%Hw6jCffXy" %3267#"''&#"5>327%5yL;5GJKOK[_b43NJNSFXH!e3275%X!V_;5GJKOK[_b43NJNSFXJ,323267#"''yQ_k43NJNSFX]_;5GJKOK[_KI4 & :?<7D323267#"''X_Q{43NJNSFX]_;5GJKOK[_R&4 RIK :?<7D # #hGf> 3 3h*f> # #!!h gGf> # #!!!!h ggGfj!#!/+!#5+d!3 "/+!53!+ dR!!3#CܐRL  R!!3# C RL ~3#!!C~ L ~!!3# C R Xjy!#yG,$%%$nn!"nn 8ʸ !%6 !&'&";112Q2qp`XXV@@VY  67"7,J5PP5JkX*77*I=P"2642#"''7&546xtyΞi56؝wYe:vuvo3N=eXt#O'+6@KV#"&46235462+32"&=#"&46;5#'54&#"3!3264&#"32654&#!#"3265k֘֗kk֗֗kL65LL56LM56LM56LM55LL56LJחkkחחkkט5LL56LLkLL55KK65LL65KK5Xjy!3!y3j !#! 5!# 5; p;!!3Xp;)3!X;5f477632#"&'&'&#"jkbwL=.> \bP \bP]Y"\I\\\\I`LLMK\y>>(I !!3#!#%33'((d&sd4^7 h\\D?cc!!!OZW !!!!5!5!![[[ZWT !!!!!!!!!55[[˰f W5 ! 7 !!-.O|c;5[Z.Ґ|_W=&   #276'&"! '&'676 !%!pqqpB`Ϙ_BB_1`B[5;@@@@EI7XX7H"H7XX7IÐW3#3#&'$%67%676'&ff2ffff1ff=>>=E>>>3;@;$;;#p#3##tD&  k4 !%!!,5m5A4 !!!!;rԵ5mY5A !! ![[#ZCoW^pK  !!! ;[5;Ő[#Z !%!!%!ex[xWx x !!'7!!'7!;Őxex[xÂx'3273632#'#"'$%65'&#"pp./NRR2]PQ pqp/@ XX`H>@!3 33!#!3!3POJ3+^/4 57!!#xr;rw/Krm-4 % !%!5 !53!]UPmrZ[z !! '!'!;xcx~Wxwzxw!3 3!###!##mPOmʀʀ3+^4!5 !! 3!xS;ZKmy4 !!!!5!#];r~U[ZZ, !%!!7!!7x&xWxpwx&  & >' g &  D' k &  Z^& & v7& -i&  8t' k Xyi&a 8Xmyi&! 8!%!567!6!&'&'&'yKH FJ97;6L2J!/,3 4(`<?-joo   lXy(8#"'&56776326"&'&&'&'&'&'3276y m{{d m{{cf/ %#2J!UI*/YKX M3 +/fg/  Do}R ( p}I ( " q}}RXy3#"'#&'&#"5>323326yKO32 7 ]J3,AI^9lys:932M#%]J3,AI^9lys:932"326=7#5#"&546;54&#"5>320;0]J3,AI^9lys:932'''']J3,AI^9lys:92".33&'."#67>76#FVʓVVʓ= mm &#Km % mK$ʓVVʓVVj,(Km@@mK( mK,K/,Km F^%.!4>2".7!&'."67>54h4.#52".ut&F@mm@@mSoʓVVʓVnt]&sْFUSm@@mm@VʓVV!!![ZWm !5! ݠr0p !!#v !&5 ƠT_Cm 5!p g!0v  !!#ƚΐ,Um!!! # q !!#v!!  rm!!#N#!0v !!!##m !4763!!"{zf+!u0%y!4'&/32765!-9+en:==@ne( =F|AEuH<y 3!!"'&5!+f}{y%0Wy!! m 4'&#!!2!+fz{}%0y&'&!;!76<(en@==:ne+!< TuEA|R=y !#!!2765{}f+!y0%! z% !!!#!55!mw ¤]]]! !!;bc;$<n$c 1/<03!3n$CV( $! ! !!!!#!#(!(F(!Z((!((!(h(!|((!(*(!>((3(i( ,} F( #'+/3!33!33!33!33!33!3䟟䟟䟟mnmnm(%8K#!1!!!!!!!#!1!!!!!!!#!1!!!!!!!#!1!!!!!!F????">>>>#>>>>">>>>(((((!%)-13#3#3!3!##!#3#3#3#3#3#3#ޟޟ#|ŸŸ|Ÿm#( !#E( /Zh!|i 6}h( 6(& 6& 7 8(& 7 8(& 6& 8 =(& 7& 8 =i( 6}(& 6 =(& 6& 7 =w!N<w7!!!xr$<w 3!254#!") ) xrVVVw& J Aw !%!5!5!5!5!5!5!5!5!5!N54&'.#"!624HI347652IH637J347744IH426532<( 67672"'327$%&#"!zzzzzzzz12Ι4FG΍FG@XXXX(( !#%&#")7632ΙK/zzzz`XXGG 3327$3!#"'&12zzzzXX`GG7| %63"71{y`X{G7| 2#'&#82{{x|XG7 527638x{{ΚT{GZ7 "'$33{y{TX`G|0#'&"#%632ˡqppp12A@_XX32763#"'$pppqΙ@@XX`wN<wN<w!Nw!v<`/3267>54&'.#"467>32#"&'.H+(*h9;i)*,++(i::f+),H736HI256743IH426?:IILII޸[["[[w !!!!!!IIN< w !%!!5!!!I) NjBZ7="-?33 #&'&+"'&#"/573;2?"#'57#&'#"#567635a)8)kOkaKA-'= //G),Y=  !H$ /+HDH)+) $., fYYx !=Z Lx73&'37&'67&'67&'67'32654'&'7654&#"3672 $54767&'&47'&27632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5430'&327632#"/#"?#"54?'&5432&5432&56327&5432'&327632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5432PO~ )*+')+(@&'$||e?/A}]\B-71SLoWj\vLLr%%,* #$ )*n$ % +) $ #*+    ? '+&()&(+&p   % % +) $ $*+*EC*Z*,)-)-*,%&%&fБfU 5HhfeefhH2pu^QFs棥sKQG4 4  22044 22 9       L%('*%)(*%)(*t     144 22 0r!2CTev+&'&54?6?6/&2#"/547672#"/547672#"'=47672#"/54762#"/54762#"'=4762#"/547672#"'=47672#"/54762#"/547672#"/547672#"/547672#"/5476l=.%G\&#- Lj.N 0d&K4i    }    g    s            &                            H5-V"*2-.T<:U'EGE'DN-ֈU]\`CDcbF]WWZA@ZZ@AZZA@[[@AZKPrqqrPGeޝdMP䠠P }2ٛk A4&#"26%4&#"326#"547&'&4632 $54'&'&4632XP79NNqO.N97OO79N']EacDC_\n\U>DbcEXFDbbDEaaEEaaEDaa+G詄UUSj멏i LVV6 "32654&7#"32?ɏǾ/`TcȐɎ;P12Y.1"264&"3264#"54327&5432#"'&'@KjjjiOiiLKirqrtPssrqQܩZTdIU )5AMYdp{3/&76'!'47653!476=332654&#"#"&54632'#"&54632#"&54632&'&676&'&676'.7>'.76$6&'&%6&'&6>'.>'.f<;.=+,>/;Kyz~LZ|WX{{XX{IE11EE11ET    m       ;   R       s@dd@s}>}=/NnN/=}>@MllMNkk& % I% % "!$# "! "!! & % % & %-5AMYdp|5#!4'&'5#2#"&546"264&"264"2647>'.7>'.%676&'&>&'&7>'.%7>'.676&'&676&'&753!476=3''676%27/&76'77&'&/#?6'&7liilYz{XW|{bEEbEd      8    @     .HxttxH%?%5E$6  6$D5%?%-5!!1(~(1 5,4t4(4N4(4t4;hhh%%#%% $ %_ $ $!"!$!/!!!" $ $ $ %:-,GtG,-: XLRqqRLX ![$n[ii[n$[!ob !!'!tKZGkcn "!!'!##&+572367676hNn_5, S Grj3#-EmDJ~o.(*!4\tR~UL !!'!  ##' CI3Z  > << 5DCX << ; YD36273 ##'5&< +Z@\\DC ZY\5#,5>~3+&=4%3+&=4%3+&=43+&=4%3+&=43+&=43+&=4%33 #&'&+"'&#"/573;2?"#'57#&'#"#567635@)A({@(@){A)A(@A(^)4 'iOj_J@,&< //F(0'&&'ܐ'J&(lN5  >! )&V?<?$&$ '& ZN N />Eqw!674#!!6?676'4#'323276767654#3#&'&'&6%67!672!&=75$/563&43!32+'!67#>54&53 *,  3)="(&)09$) L&TE` MPA[MH Y $ ;&&e=O%/ N ,8(.7L1Rf~H8SQ,zH%9D6 )jGP@4Rjd_*KfsDIR 9! O  -]&C+/3#"'43727&'#"$472776725676&5&U8)$ tJ .; d3f,"3' VD ( GL/7;;,g t^F$< LD&?>X4R !/# I ? P?D!)Mv>/z2!"&54676737#&'&54>;7#"&546767!7!"&54>3!6763!2h!.)g$'30!/&j ! /:(/  )/ 9)/  9)0:*0z2463!2!2#!!+32#3#i9/ ! j&/!03&$g).!);0)9  /)9 /*  /(:!!C4&#!"!&3!!"3!#";#"3&'6737#&'6737!"'67!7!"'63!67!2e;'+pCCo CCCC2CCKK<LLKK%JJ60"2=2).=<==<@=:>=;TT USUT UT83$QE!D72654'6#"'4#"'54#"'54#"'675674767#%$4:JILLHOKHLKIhghgighgD>-sJ1 b6'SS cRR SS?SS\\K\\;\\]]!A*>K !C!254+'3254+'!254#!'!254!&#!"0!463!!2!!#!3#3lCC2CCCC oCCp+'q=2"06KJ%LKLL:=@<==<=.)g%27TT TUST E!C32=732=7325732'654&#'%2&'&5&'5&'IKLHKOHLLIJ:4$N->DghgighghSS=SS SSb SS'6a!0J)K>*B \\]]:]]J]] O!%)-1523656;2#'7+"/#"'+"5&54775'"'5476;25'7&567635&56;374765'75'76=4'&+ '"'4!#"'&36365&5&#%#754'&5&&547'5367&7+&'&'735&2?"5775537'7'3533553535'32767&5%2?&#%55'575775775uo,Mz"060D/5I:2'5:6&" *:D:S46$.e QN5  u4MDa 6bUP+ ,H;`I23N5( (#I0M '^5%#!:X+ "*  6W}W:uW4 5vT & /H3V XD9\SL+&31.d+%X!Q $2``KPPPG[6%# Qy- 6[[3GK[O`_A[-)$t7 L-$ L6=" (CJ#R"0 :~GB{~Eoj<4S[Za LC5 ) .U%+Z&)͢ 7e<ILAaMoK33K@G6 $$(& (''&1/----2)( (-((d.'-T?OK8T$ !T3(-<((')))())( &2%2#"'&=477654'#"'5473t\*e O@UCXq P S. P ӍMOb>YaYƮ58l7P P@ $0<FX + &=%6&#"3 6=%&#"';27!54767%!&'&'2+"'&=476^7\Pg㑵Hr'.)%sM M#fC-7!%A.; ӎw:kKqz +H*G;M tu/&((AA&:+C;."/ 8Pi>'67&&&'6.7#"'&'#"'676'773.#'6'5676&&5476'&'67&&07 ^< 1x,B5@2 JVMv!#uA+UBDX[f*;-10)..C,sB#HKU P]12<0VQ }%'H6-T}^$k7 R2'7f!A\;y?1!50BEt"!zkQ;0qu0\oi:5oPZjsXFaPJGl;4ejN^1F[q7&&'7'6&'$#&7'&#"'5&767#&''5$'67'6'6'5$'67'656&'67&'6'&'''5$7676'&&'6'63&7"7&'7&'7&'7&'6'6%676767&77&77&''5&"'6%35&'.54>23#67#&8 p +WDTc'H @XO`= ;*)8 kDv/Pk-J KDhGa D`gBD6DDD =3dTDW, :g j)Yi#'WtI-9w18$^8;./7-I)jS)'#i\-IM91D;8%a7/.D=uRNBR&'%QBNRq d2 D s98C ["|44&3, '2^3R T(B?#'9C- !y ~#Z10>N?$%Y4 )%FN? ({ usis< 3(&^T05<>7;,#4[:O(vAfGEtYB z^~4j #,;b:['~Av@~EQ Bak4~_H#T2 $$$$ 2T"`q$&'6&'67327&#!65#&3jjdnh wWVݱqZre[c7 7 cyX ,35'533#3!'#'5!5!5#53!5!5#!!ʶ~~ blvF F A<<3ffX苜qXGccGap 3264&#!2+73 #'#5# 3m`hh`2`Ĉѳh|;vvʷ}f33#!!#'!'57!5#'5735 64pzp7d+!#!573#'5!3!'573!#'73!#'5IxOOTxSVVdY\yvVPPvIyY',32#' 37+ &5%6323'#57'53mJl{~m@+ݼh4144'0>,_ vNknmmnObs32732753"'#"'432364'5;+"'#"'53275'&'&54?5572'#&'&547634%476='4&#68$$B )Z>&A_;i88u-o1bFGfQ_M5mwLbkjI,K=''8 0##Rm4 ڹ+ܴ5!PP"4\=ѻ"8Qý32#"&546324&"26%#"5432itvxsq1"00" 0/B//B/#a`ir|H!//!"00""00"!/0 _b #>DJPV\bhn27654'&#"&7367'67675673#''5&'&'7&'%67'7&'67'%7&'&'%6767%&'&$h%$%%34$&1++XSA N@`==k>P CRX++XYC P>k==l?L ?Q oL+ Nn;P?;@  nMNn3%%%%34%&&%s==`?J >PW,,WW? K?_==f?H?PW,,WU?H?^<=Ke+cL mCP`k<<!4(0847632#"'&7327654#"&#%#&7&'67&'67!󫪪vӤ=6 5N'V[S.U[R󫬬񫪪񿉊 ʯX[V[X[V[!4(0847632#"'&7327654#"73$3&'67&'67!󫪪vѦ=63QNV[S.U[R󫬬񫪪񿉊w  'X[V[X[V[!4!)47632#"'&%#$''&'6%&'6!󫪪4>;D@KDzcngk?dnhk󫬬񫪪I kpinipi !4 "*2:AIX3#''%#&'52#"'&5476!!'5%!!'53'5%3'5%3#'32765'&#"M==,/0#H 8&O6 |7iY06./==e6a&i1r4z012+KN2HQ>>>>f^2"/1]8`1"Y 4f2y5+ +"'5$76%&'547327676=&#; hz0/O{[(*TQ~`NO =tR[\ 8d<+% &56;2'5$%75#"3vh0.P~N^(8P,VRZycOpO >S\^ f`1B7#5#53'&'&54767&'&=33676=3#327654'&O&"}|fzg}}"&&"}UQn$mQU}"$nQUVV{xVVUQ<"{u^^\ _u{"#| zUOOUz |#YOT{zQPPQz{TO@>)4'&#"3276&5476327#'#53'&`____`oŠqk]^^]YYňÁhgf@> '"3276'&'7#5373'#"'&5476j___``_ߓqŊqYX]]XYfhhĈÁj0 '&'&376&+"'&5'476%7!Z{z[ZZ[~\YWmpN#ZX[[YZ[PQmp#TG*52764'&#"#463233#!5sPQPPtrQPyzg֏LQQQPPQr{{t|g*#"#53533#632#47654&#"#ddiqqCBigIIugzyUr}ppDtPQs_CS 7"27654'&7#"&54767##53#533333#3##h. @\ ! 2(>>?ZW~>'3|}}! -/@ /- !^'?XY??~YX?(F}R}hh}}hLS<#5#535&'&'5'73'3#'73'676=35'73'13|e{vw}wwUATwx|xxS@Wwx}vv|d|re{Eus~~suE|VAKtrrt@X{Ius~~suI{dr|*! #!!!'!27674'&#_82V)3{D#MHZW{s{?zK8! %#"#&5463 67!2#6#";z\)MaBuh __ itBaM(]y tt[+##+tt\5."264&'67>3"#"&54767&'&#52hq៝rd:BJ|^d#!p⡠q $c]7A;{26XY "zz" YX62 &'5 %$ 56?6'.j拈|*xIIz'&|JJx, F42$8"3264,'5'&54632264&" &$#"&547>ȜmmNMm} lyzU<Mnnnm+}7 lyzU<|||,&(uO#eaHG||||Q'(sO#e‹`IH=! <>'.463227#"&5454&#"&'&5476766&D9BB8Ğv?W:pbW~tp) "-ff)-gtpQ@3AA:ACj›GmN?ijbvr56WGe((Wi0154d)-?/6?2>32>32#&'567'6'#4&&#4'3>64&"-S5,9"\0+Fgv!4u|W")^,k ikdS!eb[_[H|NYC:RHB=G`SnU|#!!!53&54632!!5#67654&"U't00Z =yy= :]ZssZ JjkkjJ 2f4%353'5#"'&''#&&#4'3>32>32YE;<<-!&Y*dx cf_Oz.*O2)7Ze``b<`WuALh`8!5!1##'!5!_drrPk^K{U_W{'/27632#"'#576&#"4'5267>327&'"2XCZd}uud$gq~dV)40tlx!&%"dLk}:Uwma4 sOHK{wY@x A63276327632&"'&#"'&#'6327627632&#"'&#"'&#'YR #{=('%{XNCEz>O&z>'(#&R #{=O&{YNCEz>'(%{=('#&ee22ee$l66kd23dEPdd33dd$l76kd34eE^s#!5!37!!'  L34((C $Td67&'&"!3!67>54.#"!5&'.54>325467675#53533#63232>54.#"3'8xpA?9l9>@q<;9'D} 5RTP=: SSPSS ;r>>p  p>>r> !A% )RSQ1 )6BB6) 1QSR) p  ""V{zHNRh|&'4>32"'4>32&'4>32&54>32&54>32#!5!'!567>54.#"32367>4.#"323732>4.#"327>54.#"732>54.#"I )),(?)(#!3()3$))BG!(( K{mg,;h IXI L$  P   H''1|G''#s%'')7$ ''A  ''HTݬ9.%~~ rF)~ wpa!'-23353#3!53573#'5#5335!75!!5'57!ePPeeQQeDpH>H@A~}}~00mrTTreppe-!7CQ^&54767&'&'5676767&'&54>32!535#5##3654."!2>4.#" 1""#@%@#!@% ?$##0 ܍a1%?E?%4,/--+D,/1+ 4;AB<>"  "#>"">#"  ">#10$ITNnVB, n ?%#Naji-/4^t&AYcgb3%' + ((NV8OQĿ>:<uyg**5 k5h P[32>4.#"732>54.#"!5&546767&'&546767&'&4>32'&'.#"+L)+L*+M)(LH     > |n @: !:;! 8An} E04`a30TL**LTM((     ++x: 8>>q ?9 9? q>>8 :x++c^UZbbZU^jg% $Tdhy47&'&";67>54.#"!5&'.54>325467675#53533#63232>54.#"!57#&'.54>3234'67632#7$5oh<:5d4:;i865%1MNJ96 MMJMM 68JNM0v    +0 +/0U-,,+,.T1/, 9j9:h  h:9j9a &LMK- '2==2' -KML& 1  V//X//X//V6HLP&'4>32"'4>32&'4>32&54>32&54>32#!5!5!M ,,.*C,+%#7+,7%,+ FK#++ PDNAM**4d;K))$'**,dY&"**E #**L:ƥ??@@=%)5!5!3353#3!53573#'5#5335!mD^JJ^W^KK^׋LLZZ,}}uz%yuu{{u}--4@4767&'&'5676767&'&54>32!&7535#5##3 1!!#?%?#!?% >$""/ _1+ 4:AA<="  !#=""=#!  "=![1=%T e >6.HC'L"'G 12h[FH`[$%ok+*8d .Ncv[.7&546767&'&546767&'&4>32 w "E> #@!!?% =E!w ./@ =CDz" E>"">E "zDC= @/.QO##"'##565'##"/547?kM ,4N"DF &Fi?JO/FB!O {|Im<&=M2227632#&547636=4'&#"#4'&#"=` ]d2 cBU;/G;SXMB:@B ս;7hf% #>|\@9@O &&5 iC n:^O G  %2O7236;2"'##'65##"'&5476;235&'&=476jS c1=EO ;SCFRʝT6*F@E1;O+.`162V Yi8/D ;8[B VRP"<B+"'##565#+"'&575477;2732;276=4'&3&'"ih;F(wQ"DG".FWCNfBy" bODUq5u4  Pro@ |S`64 '4'&'~ v '  w (  w ' $k=F F>jG3~Pjb^*IerN{̑?qJAe}Ωv6\~x(ONPPNO(!8?|EE|?8!r!_3#"/4?23D-!]UF+}{<!/3#'654'&'#"547326Rs9W5[S%3;B[/OBC'*|<j_g#"=4?2%#"=4?23ɧ%QM?ˠ)TK7(w7џ5s ?|O"'4723!#"5472!5YA>RHIOq 1 ӫg 4D% 3363'$6'"I+4 puoS^*  3%#'#3%#';&2 IʗHj7*(,377#'#'547#5773%%,ppsr,'zzxz'984?/99e5>:_`qE#&#"'5654'5673;54'56732733273+&##&"#&'565*G1 VV2Is3'{'3sI1VV 0Gs3'{'3sP3+1='3sH1WW1Hs3'=1+3PH2WW2H. ;G7567&'&'3#6737'#&'7#&'6735'67#3335#5*)SR))&*&';((:'&)'ȶkkn\\[[nȶ kk n[[\\n`ff/ee.((&(;((:(&((@))SS**n\][[o jj |o[\\\n jj e(P( /N#.6CMhw!2732!'5675&'&=32#&'567637&/7&+"+&'532?4/%32#'#&'&=4?#'57335'3!273+#&='#"/547354;2?!&=35-,;K> #WU* y "њHV ηz/;@"q=o )we)$IY'L ALaXwH >X%CII$PC/DN6g+  b% #  jnN :3 O+5{bQ< ,d-  X] f '^ JJA!< 8 2E35733!&54?'7'7!!"'&%#'73676'77'7'&'676}]} =--HW(7*! >y*1c{F=.,H-.'d(#Y+GC8957jN})%%tGl5nm3(,H:0/(_kiN}!N920K 1DW3!5>7>54&#"5>32&54?'7'7!!"'&%#'73676/77'7'&'676@.#5*"I?6O"[m" c<,+GU 5) <|w)/ayD<,+G,,&a(!>B<#q'%NG91 M7835hL{'$$qEh3kj2'+G8/.&HghL{ L8*/D *(=Pc#"&'532654&+532654&#"5>32&54?'7'7!!"'&%#'73676/77'7'&'676D|q%N24H'CB=9PS3464E>6O#]o 32EX!#"632#"&'532654&#"&54?'7'7!!"'&%#'73676'77'7'&'676H$evyn$L27C'y*1c{F=.,H-.&c)"ERUHHS S /(*. 8956jN~(%%tGk5nm3(,H:00'\jiN}!N91/K "7J]"3264&7.#"632#"&54632&54?'7'7!!"'&%#'73676/77'7'&'676]'00'*//l+2>AB(S`dT^dyg7<,+GU 5) <|w)/ayD<,+G,,&a(!.T--T.H D&RECSukf{7835hL{(#$qEi4lj2'+G8/.&HhgLz L8*.- X.A!#!&54?'7'7!!"'&%#'73676'77'7'&'676n!?/.JY08+"(@},2fH?/-J.0'f*#&K:;68nP*%'wIn7rp5).J<21(vmmQ"P;:1-K':7&54?'7'7!!"'&%#'73676'77'7'&'676N!?/.JY08+"(@},2fH?/-J.0'f*#:<68mP*&&wHn7qp5 ).J;11)wnlP!P;;199'9HR!273!567&#2&'676+&'67'#'6765'533!273+#/#"/47$,7Jv I MO $p%|I ^ [T<K"(~GW$?8?])( EAs#L, T 0 ` +WVۄ`$$a.|%2<J\e3 + &=762367#&'&#367&#&#"3274/"34?3'35732?5#+'535^-J|@h'\-e@<r2&H); uZJM =9jl:jgb.Qi2Q|酝:*}( dpR!h j `]_i$x:-(^%,3"ؿEa HMP E7g /:BR`j # &5%6; 65%&# 327#57&/#2#&'676+'%3#'#&/47'3327##'%3#"/6j1M{ǮG&z v$ExݨE(+=R:n:D!s Y!gQKum;} uA;>e=g¯Cy??ԢB|*>w4I ' 5@` bC$ j$H?iM!%.|7H27&' # &5%6367&#'.7&67263'#%; 65%&# mJB|e6O}°I+o|BJn^jaygwaaygxaj^w$FyتFG퇢D{C?` B]ww]B JХC}.?yP%.232#!7&!"4#".54767267p   {u*_ Jcllm8*#I%<($|ʀX#{Nwt7mnld4)5:IIIB,<_4767632#"'&'&!%!!  >W$`4 Z|b<_/374767632#"'&'&4767632#"'&'&!%!!    UW$`H    Z|b<_/GKO4767632#"'&'&4767632#"'&'&4767632#"'&'&!%!!      UW$`H      Z|b<[/G_cg4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&!%!!    /    UW$`  K     Z|b<_/G_w{4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&4767632#"'&'&!%!!    /      >W$`  L      Z|b<V/G_w4767632#"'&'&%4767632#"'&'&4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&!%!!  0      /    UW$`+    .      @  Z|b.t )2 $$ >54.#"4>32#"&h..--t*Ƅ2..2/.y )62 $$ >54.#"4>32#"&$2#".46h..--1.-.y*Ƅ2..2//2..2/.t 2 $$2>4.#"h-..-t*f/2..2/.j '2 $$2>4.#"$32>4."h-..-q.-.1-j*f/2..2/y2..2/R7!!R-ӖR7!!%!!RMzM; 67'&/#'3#67$#%ׯP==Ͱ̼bN+#!f"K++!|o554.#"##"'5##"&'&'0!5!5&'.4>32!!676767'7' :!9!"9 :! F GF;kY_1278e56d:81)RLk<GG E~^ : : ; ;NG 5 e4G( Li) enf77fne )i (G4e5 G( Pm 9Y%&'%67&673&/'67'&'"&'4?&'37' '7 &/7&'#>7$%88EFu/- 6uNDL22LENu/80uFD8jU45B%y\A@Yy$F 0=/0 ,-X70 ;~*2 %% 2*~697X-,oo  +F9d1 ) ( 1d9C1*CT'&#"'5&767#&$'&%'6'&'''$'676'&5$'6%'.54>32D$ "@F,NNNvF8p^Lb2  N**+ B@0"AR/0?wA%od/D&3.YaQ/5#3$"uI' @3/u= =#n- .... w3% % 32+#".7!"&'&'#&=4;73737D*$#GFHH%#Ι+(&aa'm99m9 3.055_4i4_550.3k  #tttk"632&'.'#####֊v)%8 _^>:k{ZG_?g@`H,>|:=+,j,,<6O/233<bbJ 132>4.#"367#&7&$735&'.4>2,P*+P,.N+)PƗd"/%(MM~95DLMNMD2)WN,,NWP**g!ʇw֜s~  &JJ&?GO277''"/&'&'7&'&'7&47'6767'676?  6"&462EG#96\>42(p __ p(24>\69#G#:5\>42(p __ p(24>\5:'NmNNm U%4m+3 EJ5:6JE 3+m4%T  T%4m+3 EJ6:5JE 3,l4%T '\nMMnM* ? !&+05:?DP3&7"7&'7&'7&'7&'6'6%676767&77&77&'"32654&'5&'.4>323#67#&#"'5&'&547&"'6%6761a$O` "NiB*4l,4"U47),3($aM#"aT*BF 4,=44#Y3,)0BB0/CBO"!-$F$FJF1.#- -#-2MJF$G# 8<g7*!2U6J%n=_CBnT> rYw0d "*7]6U$u=n;wBLz >\e0wZ3C.1BB1.C(N "%""%" M#p.PA.$;QW$.AP-{ "R &.FR2#".54>&'767&%76'&''67&'&'&'67676547676'&7>3263'##"'&'&'&54767&'&547676&'&#"6&%6767&'&'&676&5467&'&6732767&h@9h),)RP|  |PR-*g:>/**Y&()((')&&)')(()% @9f+.TR"`33`\_ .np, 00441/ ,pn, ]]&&()&&EEEJ032WyQT.,d9@.**..1230IDE%&**%&F+.SEFE.IMMI."#FES. !  ";-0.--.0IM+.REF$$1.%2S_`Q2%-1OQQO2-$3Q`_R3&.>GIIG"" 7447#.$$FER/+L"  !75/57%"IJJI* )p~67&'67&'4&6%67.'4'6&&'6767&54?67&'&#&'#&'5&'"'67&'&47632>4.#"72#".4>"0'-, )*#'05%"*%%, ),,"GNYI'+""$(JYNO21, 9,4=SM:7,: -12-[[Z[]WXIOMKLMN2 Y{\ bCWDJgABcp7L^BML0b \u]! @R%KlhhO+ww+O hhlK$PZX'@D 0:)ww*;0 EA&XZw[[[[GJMMJ"( %3!'#!52#"62#".54>o:5(67%'$(n H0L*I"33'554#$/* PR 6h"&>I > >A>!!ua!&5476'#5!+{h_a66mHHm.rZy'#"'&#"'&'&'&547676763232767676'&'&'&/&'&'&547676762!2!%3276767654'&'&'&#"&#"3276767654'&'&˗Pz  ,D@   7;+  23  M98G ):               r         0   L:5U        .\ r26767654'&'."#"'%"'&'&'&54767676;27>764'.'&+"'&'&'&547676762%632$"26767654'&'&#  #  @!R763276;%326767654'&'&'&#"6767654'&'&'&#"32ɓ E79E"  21 +96  >B+  # zOo              49D   /    "    :           =JZx-4H67&'&'&+"'&'&'&4767676327632 #"/#"'&'&'&54767676;276276767654'&'&'&"276767654'&'&'&""'&'&'&547676762"'&'&'&547676762'&'&'&547654'&'&'&";276-&#"+"276767654'&5476%327%&"'&'&4767628?.  !  !a=?^'_)\?=a! !# "!.8?""  "  "  "   f  2 .?E S@6f G=. 2  ŕ6@  B   )_>9 9>_)  % I        ? *        ;d.      ?P   !-  @( ,#%>  NpNM&_*# (! &) ,,f&  ! (K_  Z0-  Yi D   cp-)L &gK1 [N3$ n/ "!0{I"H#fmt2>,7HBI.;/8[, Q[z)  .)S9L *E   '+(4%(4  *X >  7A) 0'-570+I;-% *#%(0  ]'5.  U-9Lp{7654'"'&#"+"'7&54?67676763276323273#5%6767'&#"6%"/67#"27632327654'73654'676547& t!M#l5G;@\ 2BX-0%-m * '?,N ?'!&R;-> <\-R5-6E!"$b$6$!q",; t@P"#C  *FS "DX@! %z$(`]jMP   &O/+@ p_u<  3  DMKZRdYL6D_YBI5.!!''kGWz")3SZ67654/##3276?7%754'654'36767632#"'&54767632'0,,; (| w| ki5.U,\\    %g .  ;,-0j{w {w3V. T, \[^     -5& '-EL4'&'&/767675'7! !'7!654'!4'!!$4767>2"&'&'!654'$$CCC||]V|V#u    9Z(f(Y7%$66%"'&'&'&47676762%'b&I    )^tN/  /dIW?    @ViDV /  V%&%$64'%%&'&'&"27676@))< "  " ]NO]    9|23277632#"'&'&5476"# 6v>? (-=%P8j?  #j<  y"$"JrB23277632#"'&'&5476"" YTo k%,02?=V8jiA{C {u+'qP?  ' 7 sssssstsXrsrtsssr@Q  ' 7 5NB2632#"'&'#"'&547677&'&54763267632676   Bt  ah>) c!  ,Hs *ܡ   },"2A "  {3+Q26#"'#"'&'#'&'#"'&547&'&54767&'&54763267632676  ΂    NjM  rkW* & \ *3 #ﳎ*3 Tv! ( 5+" , @V #!!!!!%!!!!!!!!#!5!3 ;E;JEJJJ<;E;EJK!IKV{ !!!!!!||uv9f35#7!!#!5!3*+մܳ*ִ0r!!%!!!!!!/0``1/`1) !!#!5!3^^^~S3!!'#'!!#!!3!5LDʃDMA #5!#3!3'3#!#35!3###5353;9nj#5AI##0vQ#"#3;54'&'&'!"3276767653#4'&'&'&+3!52767>5/]LED73!&&54GBO]63H>SkS>H388]OBG45&&!35FEL]63H>SS>H38882I32367675&'&'.5467676236767>32#"&'&'&'#"'&'.546767675&% >#"? ?"#>    G   >#"? ?"#>  G     F  >##> >##>   F    ?#"> >"#?4'&'&'&'.54767676322767676767632#"'&'&'&'&'&#"'&'&'&5476767676765"#"'&'&'&5476767632B ,#,+%) 3!, &&*-#''#-*&& &$0 )$W$) 0$' L+,$&&$,/"&&$b3") M*,%&&%,."'%%0 )$W#) 4!, &&+,$''$,+&& &$1 ,#,+$)0267632#"'&'&'3&'&'&54676763267632#"'&'#"'&'&'&5476767#6767632#"'&'"'&'&'&54767#"'&'&'&54767676325##"'&'&'&54767#"'&'&'&476767632&'&547676763235#"'.'&5476767632&'&54767676h             -  (                  '    *             .         +j276767653"4'&'&'&+sidUS+*+'WPihtthiPW'+*+SUdi),)URhexuhbXR,,,,RYaitwfgSU),%t?247676763"'&'&'&5!276767653"4'&'&'&LEA86:4DDMMDD4:68AEtjdVT,*+(XQjhvvhjQX(+*,TVdj-76DCOME@:66:?FLOCC67-*UShgyvjbYS,-,-RZbjvyghTU*,(8 %%! !)ttJHcdecH]F]~]^C5 )!%%!2#"'&'&'&54767676hzt@z@Az@t{ne_RP)((&SNcdome_RP)((&SMdd0x}*(QObbrle]TP)**(QObbooe]TN+*(.'"276767654'&'&'! !_)(""""()_)(""""(Y$(*/.*(#  #(*./*($]^#< '1%%2"'&'&'&5476767! !#xxa)(#""#()a)(#""#(YDgghgD^I^W $(*0.+($  $(+.0*($ YZ(8 3'7'3!%%!! !hE۱CCDe g  g f ҁссi:]^= 3'7'3!%%!7!7'7!hTDEDDTNPPIQ2P11P2#mm(? -5%7'%!! !] P  gfeer­696ƌ]^^. /'%!!%!77!yrryyqm"_^^l%%tu%ߴ߳!63% %#'-7:|:||9|kֵֵkֶWz`37'%7% %#'ZZZZZ]^Z^ZZ˛ʜm˜˜mʜ0o #'!5!73!P6M6P$6PMP66R#6QLR6$Q6L$z     - h<_K <; L_zK <; J`;<_  '!'/7'?!7% % -[9^[[ZG^ZZz'}*}zy}*}'q^\\ZG^ZZ:\O}zy}*}'yz(}2 % %  h_y(_^(zFGs% % -hVHzVUzHrVU{HUVH% % -hhhႁhhhႂhhh$h7% %' 7-'hX5 5XV6 6g5VW6 6WV5 0t/37%!!%'#''7'%!5!%7'77;[TA:#T8#AT[TA#9T#8AT T8#AT[U@#7S#9@U[TA8#154'&5476276767632#"#"#"327232#"'&'&/"'&5476=&'&'#"'&'&54767632332?&547'&#"#"#"'&'&54767632676?>$,.c.,$> ]5 71+: H3> kR  Sk >3H :+17 7Z  >$,.c.,$? Z7 71+: H3> lR  Rk >3H :+17 9X  ib9@R'))'R@9dg  8d< +$;)01):$* \570+9 F3= kQ  Sj  =3F 9+077Y  >$,.a.,$? Y770+9 G3= kR  Qk =3G 9+079W     > h`9@Q'(('Q@9bf  7c<+$:)/0(:$+HH#:.'W4,CEH@,4W'*>&DL:Z##KGW,f ',;[;;+*Q--}KOW*AA*WSGu5-U&+;;[;,)  '+;[<>**Q--}KNW+@@-USFu5-S(+;>Y;+* !67654'&"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"#"'&#"327676%32767654'&'&#"#"i/)F)/,UK:M $\/8E(5>H6-EFJA-5H;8)D7.\# L;KU,*UK;K #\.7F'5>H5-DE-6H<7*C8/\$ M:K U+:6-21 4 $:<;$ 4 22-6 O;(A7##7A(; !*:#.#;&Rm!CcJMU)??,RMJcCoS%9#.#;)!  );#-$:'Qn!DcIMU*??*UMIcD oS%;#.$:* f /D;;D/ $i"276767654'&'&'767632#"'#"'&'&'&'#"'&'&'&5476767#"'&'&'&5476767632&'&5476767632o00'))'00o00'))'0]0+)*+%# #+%0%##&&.0%+%   #%'.0$,#0%-# #%'.0$.  #%'-1$,#$%*/0961/*%%*/1690/*%) "*&0-(%$$$)-0&*!&"*!$$)-0&-#%(-0&*"" (-0&*"$$(./& n%#"'&'&'&5476767#"'&'&'&5476767632&'&54767676267632#"'#"'&'&'&27654'&'&'&"67&'&'&'276767&54767'&'&#"276767654'&/?676767654'&'&'&#"h &,&1/(&#!$&1%-$!&$/'.)2$-%c%-$2-*++&$!$-%1&$!#&(/1&,& =s0 9 55%R 9 !_  , 9 R%5s  _!#'"+'0/)&$%%).2'+$ * '1.*%%%%*.1' * "+'2.)%%$&)/0'+"'#L% %L %#M L:2(&6  _ M#%   6&(2: -[3b &'#"'&'&'&547676763267'&#"327%327676764'&'.#"7632#"'%&'&54767676324676762676322##"'&'"'&'.5#"'&'&'&54767"'&'&'&54767676&'&'&'&'&'67676?&'32767677676765&'&'.#"7676767&'&'&/326767674'&'&'67'&'&'&#"67'&'&'&'67676767"276767654'&'&'"'&'&'&54?&'276767654'7654'&'&'&"67'&547676762    (  b  (       #!"G"!# * " ' ## G!""  '  Y m    ( y   ( O k  w  m Q (  O (     ? ?   + / L* / *   +. M+ .*   !!!! '? ?' "#& #'"!!  '? ?'  !"!  $&  m P    O        m     y    O k         b       %j<\l"276767654'&'&/2#"'&'&'&47676762#"'&'&'&54767676% %-[''!  !''[&( !! (TB39)+,+76?A3:(+,+76>tjeVT,++(XRiiuskdVT,**(XQijtuz"z!uv!z"z#&(,-''""''-,(&#e)*:6?;97,+)*97z88,+,*UThgyricYT+,,*USigtvjbZR-,zvvz"z vv!z29"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"27654'&%&'&#"327676%327632 654'&'&#"#"i"(-+S I9K #Y.6C&4<F4,CDH?,4F96(B5-Y" K8IS*)RI8J "Y-5D&3<F4,CC,4F:6(A6.Y# K9I R*"(-62 #9~3 #9; 01+56 00,5`%;G,A $.?'!3&@!*Yx$ ImPT]-EE0ZTPmI "zZ)!?&3!'@-$ #,A'!2'?!*Yx$ImQT\.EE.\TQmI#yZ)!A&2"'?.#~&41%%14>3t-3>41%%14>3f^CC^B%@#@@%@#-4>41%%14>4-3>41%%14>3+  V  ++  V  !r +?Sg"&46277''"'&'&476762"'&'&4767622"'&'&4767$2"'&'&4767eeeBABA#U##U##U##U#V%**%V&**KV&**&V%**~ffeAA$AAV%**%V&**V&**&V%**#U##U##U##U#  &3@MYam+%5%32476;#"'&'?632&54?#"632/&54#"/72#547"&462"'&=3?_?6 6  6] 6'?&M&C_CC_?&M&< 'L&&L'!6 6^6!6 >_CC_D<>"l267632%632#"'%3#"'&'"'&547#"'&54727%#"'&47632%&'&54763&5476h!#;'&1'h 9##8)'1!, ;#A#; '&1')8##9 h'12;# 4%.&! 6 = 6%".% 3 3 G%.56 = 6 %".G 4 $8 ! 54."#"54$32632#"_ ɀ~~a>E  %!#!3!p EE?p9E=V %!%!35!cE:d FF8 %!!![:F:\;[0q %!!7!N]<N;)G+t  ,o9; #q !rQk!k`!733}b>v!#7#)iC~'  #'  #g]jOS2#"327676765#"'&546;57!##"'&'&'&54767676%#     42;%-n`Ԯrr#26A@:V7:$)&7.Yq   % $.277g[(dVDQ49%*,04?()-#52&'&547676762"'&'&'&5476767hc"$njln(Lfe*+$$$$+*e*+$%%$+! #'(*dRjjSc*('!"%*,20,+%""%+,02,*%"%C&'&547676762476767622"'&'&'&5476767hcn(%X%&&&W%(nؖe*+$$$$+*e*+$%%$+,Dj*('(&,,&('(*kC"&*,11,*%##%*,11,*&".i%%&%&54767676247676762hhÔ*(42u24)(()42u24(*i\=97,*+*96@@69*+*,79=Zr_'#"'&'&'&547676763"'&'&'&5476767632_dA=;0-/.=:DD:=./-0;=Abx1.=8DC9() 1F="%".4"tNa5&$4! /.r<@6B2L_0>Q#kI|"rz7&)?),%=^K=.C26F@13.!9+cM313N676 547&'&327#"'#536767&'&'&5432&5476323254'&543253%5@26`', =NR6#!vWR>4 2:O t51"".1&X.RO A5ȏ )T/186,FAS :#(=:tA0 9SD 'A#5}11BO9 "'&'&'&547676763"3ᗊpm8884qlYTN! !C@RP]e:6pltm9:'62~~jf77"05276767654'.'4]PR@C! !NTYlq4888mpe'67fj~~27&:9mtlo7:fkR !&547jljjlyyxzQqpnc$0!!676n wu;;vi43f$lcC}U# 3tD}U 3 DutV.! !JV. ! JA! !m^\GHB ! ^^HHv!'7DWWWW|'7'7WWbW>W^$#"&=4&+5326=46;#"3xMe,,fLx1d=AOOA=dƂ׈ihDŽOߍOi(326=467&'&=4&+532;#"+5nCFVU$#Cn5BB*)p//oTBB¥P⎁AAPDBۇ45iDCS/~ #!5!3}t]} 7%d^=]d>S~5 /%0~##t] '-f\=]d]>-!'7!. (``I)=2"&'&'&5476?!".'&47>3!'&'&54767>2 '!  `!!  !' ,&   &   S~&!5! F78-x!5!5 V(Mr6u #3#3#3!!5 鴴ZZ---I(,,,,S~  55!#3#3#3F9UU**b]^bUUUS~!!5 F7.`tq!%  qR{V$%! S%@{V t%226=3!5 5!"'&'&'&6  $hI$  h$   6<47676763!5 5!"6  $hI$  $   $O!! e 6n55!lMlTwccwekl!5!!53 ' !_[y"kd""e/l5!!53 ' !_["/d""5 !73#57!%!6UcGjbzbdǩ""ap 5!'53#'!!!7%acߎA[؁(ZqZ{{{ĒҒ}TM %'!'!53 !;qKRnKa26wwIw22wT}> 3#5!7!!! ZQtZQ0L>ssjLK2Nu '!53#'5!'7! !pSn%R&%Ua2wKJ,Lw22w)1 '7!573#5!7! !r&j&St&SpWl6qM,LLyy77y@!6767632#"'&'&'!  6IYZgb^UMI%&&"LF\Zfc^UM3!t:6I&&&#LHZZhc\UMH'&&#L2<tt XNy "&*.37#37#37#37#5'!!55!!3'#3'#3'#3'#r+qr*rr+rr+rV{{*q+*r*+r++r+9Ɔ\]t] 7&#"7'7 #%5#t69.wZY96t".*X/S~k 55!5!!7'!nnUVGG8:ȏu\j '327'' #395t".Y/Y"u69.xXXN2%&#"6767&'&"67632&'&547676767}:"  s %*&*(&"!#!"O>>;*E/4767!"!47676763"'&'&'&5!3!&'&5v  5 $ %% $  H vgMME %!#"!% EMu\2&'&'&'&54767#"'&'276?&'&'32\":  #'$'$#Y@I:86s6::I  #&'#'" X  :5*+B67"'&'&'&547676$47676762"'&'&'%&'&'&547676762$[ /  H =a=   / ZI=X  q> d(*c     XJn.676767632#"'&'&'&%&'&54767&'&54765 #&+*1)F-Y)) .EOO/3S>>S&/ #$))%#]]#%))$#&e"'&'.54?654'&'&'&+"#!".4?64/&4676763!2;276767654/&54676762 I ]]I    Q      Q  %eg"'&'.54?654'&'&'&+"#!".4?64/&4676763!2;276767654/&54676762 GKa u~iKG E     2 +#76767&'&/3#6767!5!!5!&'&'g?j7R=y66y=R6k?VO S+ +Sd _8=eyu'&utj{J<p1 GM+ SR-PAdmR &567$'&76&#""+MG 1p7 mdAP-KS u#\u ! !h i/u-1Q ! !RppQ ! !pu#\u h u-B\33##!##533RPPRB\333333#######5ُُPPُُRRRRPPPwXsw7!!!xr$<w!!!xr<w7!!xr$<w%!!Yr$<w% h5bcJ6bw bc66bw ! bc6bw! 5bc6b w #)-17%#535#5#5#5##5'3#5#5#5#5#5##5##5###5ZssssVrrrrsZVr~rZHNrrrrZZrHNbVrrrrrrVV%%;#"'&5! !&'&+3264&#Tb'~V^ ,A/ Ok^yihz)lhh暶 A^yi_^`~!5!!5#"'&3227654'&"Z$3QRmtsaOOr<<>==><<6+b22rv/0YVV{##!!+53265}`byMdRsTqen! !!!!!i@m'\Z'cq`~!%!!67632#"'&4'&"276$3QRmtsaOO<<>==><<թb22p/0H!!!!'hh/`!!!/#`Aj2!$76676'&&`rkjs+*Z6j{4@BsSV}dxaM> !5#"'&'3276767654&+5!2,XZv<::40L^EE& Sb<ҵ@`45,,! >&64pnbo{&"26$  6 54&"OsPOsPp% ]`xxiPPsOO*=CWW K_Xd3!3h&D6.54$32.#";#"'%&'&'&'32654&'ߞge_`kr*(SU@aS@G:dLohvlmxPLU/.CFVP>((20Ejmp6GxY41TRcYCes%;#"/&'5!!FdSwkwF3Gx#'!767#"&5476?67654747! [`bede""^XD#. D#F8LAB\VC)*= %(+C" :!#!/:+#5!!!V&3!! P/+!53!+ N#5331мtt#5331t#5331ht#53314t!#533#1t53#3м053#3d53#3h53#34!53#t!!#03!!#̼d3!!#3!!#d!3!0E 3'#'32+53265 ENPZ#j[Zy'dRyh}]fTkln'!J=!5!5!5!J>>!!# !#qed10!+eg^lDe"&'&#"3;!"&5#"'&!5!! ;2RbѶ{@'N ?28Ԅn+)b̜P !!3!#!P=g}^=/B=a{3##4&#"!!>32ENO[#jzi~`]fbo#2#"67.5463%""326&rv.6 VHrPixxijxx|=k)m9%QJ JG@& t ZYX  X<2<<991/<2<2990!!!!!!#53546;#"%%\۽PDB/-cNʜ0 J?@" t ZYX X<2991/<229903#!#535463!!#"۽B/cNʜ30L'  EL'  hX'  S'  TL' a EL' D lX' @ Sl' 8 T0' S E`' . d]X'  S`' D T'  E'  ' 2 S4' S T' G E&  5z<' ;[ S_' @~ T'L$ E& L mD'L S?'L T|!' 0 N&  4q&  j&  V|1' 8P N &  ,(q&  &  B"X ' [475353;#"'&'&5327'&54767&5$02a@M(1l\>mF|iɖ{1'OZ$^>B/7*_lH>^OpbĘDh '@5~\c&  c&   8X ' [473#%3#;#"'&'&5327'&54767&5$02a@M(1l\>mF|iɖ{1'OZ$>>B/7*_lH>^OpbĘDh '@5\c&  lc&  @X ' [48<5353&'&5327'&54767&5$';#"'&'!53JE>>mF|iɖ{1'OZ& 2a@M(1N.1]_lH>^OpbĘDh '@5>B/7 n\c&  c&  @X ' &[49=5335353&'&5327'&54767&5$';'!53MZ >mF|iɖ{1'OZ& 2a@M(.#._lH>^OpbĘDh '@5>B/?\c&  c&  T' _` '  &_LU` & L ]lnl1%!"'$47!32767654/&'&54767;#"' `pl,&y1[xH](45[ "G(79 -RTHLF0 b,@^n@",DX6,vB"C@,\ 04G?!+  c' n & F T+& G & H   &%#"'$47!3276765'!;#"E EzjF/?I,6gb`-d t8@<"0@%3!"'&547!763&'&54767&'5%376'&'&'%67654'&#"kB)!F'-p 77/&9 A53 <$4#50!&'&54767&'53376'&'&'%67654'&#"&<*%D(E+k/61"  3]Q=ERH' >B*)!F'-p 77/&9 A5X %!#53276=!Hw,1VV,1j%#!53276=!3!!"^@,1"1,HH,1jj1,ur* rX' 3 S' ? T0t&itku ;#"'&=!1,cK\W#u71,\W+0v0w&wi*x&xi0y&yiz&zi;!{!&i{-O'|U& v|Q'}UQ& v}'L '}`fq & }b&~U& v~V'};rw& }& S}p& T}pU !;! '&1,PWxj0,\e'  E&  X'  Sp' p T%i' pV&  k' C Ek&  Ls3& S 4R3& T >R' > E&  0q' 2R Sk' 7, TX ' [X &  (\c&   c&   X [X /;#"'&'&'327'&54767&5$& na`(1l;)'I|iɖ{1'OZ$B(-s}n}c`D^OpbĘDh '@5\c&'&'&5672+5327676G+9RHK07hdw}[;  #4f5uTD4FD7< 33M! '_%5,iGec50$ $*   CARX,$ QB  8-Sm\daّ֔w<$6!`]]T'3WEfnf' -%&'+53276=3676+"7327654'&#"V<%&:WE*((0HNRXLگ]>4FD7$(/`rNN`Jfnf`-,$ QB 9"327654'&#"'&'+53276=3676;#"/EC8\?hڢ8Q%VE*(FOQX $3H3 &_QB*,$ b$/drNN`Jfnf' 5-' Oc&  &  &   e %%327654'&#"%;#"/#!#53!632M,>5M5M5M(N365,K+)5!27654'&54767Z)6r u^Jf>^\$$(B;G&m16%*%Xy}qe$%)5!27654'&54767;#"'0V>)6r u^J',8.RP"\$$(B;G&m16%*$YYm#;#"''&'&7!3276765@390gL(5n$.0`UZkFOkK',7bCsL\ssj1+-3dij )5!2765!VP,0e\,1jl%#!532765!;!"^@,1"1,HH,2ilj1,<n$5;#"''&!4763&547632276'&'&'&#"D5("V  ntY2xAw~M`bp1& 04!+ 3DP&4OKf7*g?'J 7&':'*%276'&'&'&#+53767632#"31?1& 04"J@ZZ-aA`b|a7&'9`@(H.8XD/%276'&'&'&##"'+532767632;#"1?1& 04ui"IAkZZ+SA`b ,Dd7&'9Pl}P@(JG7'  & N O-3' R S3' R T%pV| %&'&'2&'!;#"'0'#"'$5476 f=ef^E=S*%"*% '   P8C!N'-_ ;49rFq#+CIQ=ERHf\DM`L(22(L`MD\Y>GE:8E&uHg>MOufU`p\`wxxw`\p`UfuOM>gHL q  )"34'&!5 767"'&'&54763236Lur*8$4'&'&5476762;#"'&#"'&&7!2767#/1/N,`V@I #,+3 *L>Koz$m<49f,; 2$0bjCA2:Zᬪ *Ll,& }#,lpS& ~lU& ~lS#"'5327!65!S;QjK`UrH{/!B_M PSgl%"'5327!65!;#"hjK`UrH{/!a=Mi PSg..j (  !46?>54&#">32!5h: &DX^Deb`T\\-?ZP> j: =TBV\L98FGDC+P=YNc{;f@10!#!f-1@ 1<20KTX@878Y3#%3#u810K TX@878Y@ //]!# !@   " "999991<<99990K TX"""@878YK TX"""@878Y@]             ! !.]'&'&#"#5463232653#"&j6-(mX$K&<'$'kX%G"<2j'<9j810K TX@878Y@ //]#7b@ 91<90K TK T[X@878YK TX@878Y@/// ]!#'#5߲Ʋh@ 91290K TK T[X@878YK TX@878Y@//// ]373ݲDz/wqB@ 9910KTKT[KT[X@878Y78]'T%#%vwck 3>323.#"bLM`c<=<=ck @  ]]120332673#"&bLM`k<=<=uk10!!kcbk/@ @O?/]1_j2^k$l"$:?02\[3#*2-~^xf $!$#1/GB  iuw^h68@6`azRK@mzdQLF6f_fj.<UjVo+\7/2C!Fx)Ab"$ +*"N? 5353!532^5353^? 5335353!53}^ I-  5dl %4'&"27>"'&47623J22J?@?@@?@$2%&2@@@@@??lXd@@%d p & &91/2990KSXY!!+53265!l\\y'dR; gnln!"'&547!32764'pg3|nuZqX4;TfnLH0DI0("6654&#322765#"'&54767632!&'&47!i)@5,!,)'j$i+TcQ_V.Nݢ057D(K#N$.=*,̖"Fc8(R&HTsu~]zJ\k' ' @ &  9X &[}8X 1&[ PX &[ B'>_|:654'&32#"'$&7!3276767#"'&54767632,,;!%&,?Mﲥ{# $xZ;re73 C\|-wqb`YM]#&-#*B|^6()[:\^-"')Hac)s52(#4&m#"'$47!3276765'!TfBZZzXXXX l}>>LZZz>lyKGf{5SK}}TTHHfd>O>X5RRk i 3hbdPabbbbbdbbaabdb9b^baabcbaamMcP~5fiba)(+",DD*:PN D002())000$$.'&M"O""09M&UMb:jZ0OJ%$Jt2M'?'',,x 0"O9G.)"!^}}}ZZZZZ\\\ub CuuuZZZZVRVRVRwwww\b####ZoZoZoZojjjj9P9P77;ssso;b!^!^!^!^\\\\b\b    j;;;66666666q5T"h6,((  r@UUbbbbbb\&LLLLLLLL{n@@@@@@@@p*?}66bbLL@@66666666q5T",((@@@@@@@@p*?}6666666!!!  y   ) LLLLLL-@@@@@8UBZ--j9Z/&&&% I9% )"I&'I0J9YEn'<2 <,.Zg;;2J Zu!///#/#/BBBBB;IBBBBBBBBBBBB9?222B77BBBBB BB B BBBBBBBB}}BBBBBBBBAB',B BBBBBBBB!w?ddcddcBXqy111 XXBJ/XXXXXXXXXXXXXXXWXXXXXXQQXXXXXXXXVVNXXXXXXXXXWXXXXXXXXXXXXVVXXXXVVXXXXXXXXXXXXXX2222BBBBXXXXXXXXXXXXXXXXX9?rn//  XG#X  C@_24!!-XX!XX@64HFFFniEiiDDu777777aa"5"Y::;+6.ocN8/&//0 "N`ya7g!!!!GCL54=/U28Y^CVpj6gv2 1.rjD7`./<KD$>K--9.7.P<<<<<<....RRs'J*R*"..:=4%=?D-W&W;%J@3@V90~0G+%(C(#(=(.6W0$2$0174!$%2 02)!"$E=80+Qg.rA1fnCDStSt-IS-6SS`{{66O6ee5a}T2)XtSuN*u5&%2SuQQuBBw`V!`Abs//EEGelPab EEEEEEOOXXXX`` n n000*0;LEVEEXXXXXX``<< HTHTO<VLvlllll.- /oZ-P-EOlXXXLDDDDxXH0d  \  ` $p||,D d` x h !!P!!""H#@#$`$%& &'`'(@()**++,<,-.@./H0@112823(34t4t456778$9X9:;<<<P>?D@@@@AHAxAABlBBCC<D0DTDxDDE FG4GLGdG|GGGGHH8HHI IDIpIIJ4K8K\KKKKLhMXMpMMMMMOdO|OOOOPPP0PHQ`QxQQQQRRpSxSSSSTTTUU4ULUdU|UUUUUV V$V<VTVlVVWhWWWWWXXX0XHXlXXXXXXYY8Y\YZ(ZZ[[@[X[p[[[[\\]]]]]^h^^^^^__,_P_`\`t`````aab0bdbbbbbcdddddee,eDe\eteeeeefff@ftfgpggghh0hHhlhhhhhii(iLitiiijjj0jHjkkkl@llmxmnXnhno4op(pq,qr0rs spstDttu<uuv0v|vw<wTwlwx8xyyzzxz{ {t{|(|`|x|}}\}~~H~~ltX0,D\t ,Dd|<Tl,D\t0H`x 8Ph(@Xp0Ld|\(@Xx0H`\4`X`<,,x Dd lt8t$`8H\XHd HP<lhT|0|TP 4lL0x8 hd4tÈ0pĀĐĠİ(ń<Ƙ@\pDŽǰȌ,hʼˀ˸4\̬̄ ,Xh͸HXθ,DxϨ(<`ЀР8\шѤ<|xӈӤ `Ԝ DTdt֐֨8`׸Tl|ؐ8HXpوٰ٘(@P`pڜڬڼ<L\ۈۘۨL\l|,D\tތޤ޼\$l\l0@`hP(@Xp,(4pPTDTdT4p4Ld|4,<xl|$ 0@l|\h0d0t,X ,l|4D  `   h  X     @P`xHx4xDTl H TDHXhhpPhx 8Phx ,<Ld| $<Tl$4DT  ,  ! !""\"|"##$8$%4%&&&''(D()L))*4*+ +, ,-$-4--..$.l.//$//0p01 111233\3|44445566670778X89: ::d:;(;8;X;D>?8??@@8@AA,ADA\AtAAAAABB BBBCCXCpD,DDDEEpEFF F8FPFhG$GHH(HI IIIJxJJK8KTKLL`LpLLLLMXMN\NO O\OP$P`PPPQLQdQ|QQQQQR R$R<RTRlRSS,TTT$T4TDTTTUXUUUUV@VW8WX$XY8YZ ZhZ[T[\ \]H]^h^_ _`<`aTabc4ccddTdldeelef,ffgLglghdhhhiXijpk\klm,mnHnop pxqqr$rsTstupvvw`x xyPyz{d{|T}}~|~ ,`8pH$($\|\(d D0t|4t`XhT,\X<,PP,XXLHX(@Xp $<Tl4Ld| $<Tl,D\t4Ld| $<Tt $<Tl0Ld| $<Tl $<Tl,D\t4Ld|4Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t$<Ldt4Ld| $<Tl,D\t4Ld|”¬ $<TlÄÜô ,Td|ĔĬ 4D\xŐŨ0H`xƈƤ 0H`xǐǨ(8PhȀȘȰ(PPPPPPPPPPPP|Ɍɴ,Dʀʼ|<tPʹ00τд0LlѬ ҔҨ$@\ӔӔL8մֈHd׌DXl؀ؔبؼ  4H\pل٘٬$8L`tۜ,lhߔ<t$(T|h0tX(8HX @` @`x@0` xPTXh($hH<d@h,d0pH,ld$`,x  `   8 t   , h   L   \ \TH4$XPxDH\l4pHp , P t   !!@!`!""p"#@#$8$%\&\&'())`*****++L++,`,,-,-|-./4/0@0t011l12 2L233x34 445d56t77\78$8899t9:T:;D;;<@<= =t=>H>?0???@@PAABCPDDEFGHGGH(HdHHHHI,I`IJJlJJK0KLLLMXMMNhNOpOPP PLPPPQQHQhQQQQRR@RhRRRS0SpSTTTUU4UTUVV<VV[\\0\\\\]T]l]^x^^^_H__`4`a aPaabDbbc,cDc\ctcccccddd8dPdhdeff\ffffg g$g<gTglg|ghXhijklXlm|mmnn8npnnnoo,oPopoop$p\pxpqq@q\qqrr,rHrdrrrrrs s(sDs`s|sssstuv0vDvXvtvvvvvwww4wPwlwwxx\xyztz|||}},}H}t}}}~~$~P~l~~~~$@l<X\THL(P(x(T (D\t4`8d0l`L<hP``((\8$|lxl4(8<$d T@ÀT\Ƽ l<,ˠdtͼTϔмxԔըָD<ټڀHH0ޔ8\|pTL0@hTd` $T,TLl$4\pdTpp< |  L t  8d L|x !""l"##X$ %%%&&H&x&'H(0()|*,001H12@234 445`56 6p67x78 8\89p;$=$>d@B0DJLTNOdPPR`SU0VWX<XpXXYY\YxYYYYZZ0[([\]$]^_````a$aLataaabb<bcc@clcccddde8eeeff4ffgg4gxgh hXhhiDij jkkHklHlmn`np(q\qr$rrs(sPsxsst0t@tltttuuHuxuvLvw4wwx,xTx|xyXyyyzz{p{{{{||(|P|x|||}}@}h}}}~~(~H~t~~~T<$40L4Ld| $<Tl,D\t 04L\l4D\l(8P`x0H`x 8Ph(@XpD0H`x(@PH4L\4hL 8PhP@Xp0H`p< 0 0X0HX0@,D\t00@Lx<TxX (`xt <Tl|  +e@$_B] 5m  4     S *b *  &  "I : & hCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain DejaVu Sans MonoDejaVu Sans MonoBoldBoldDejaVu Sans Mono BoldDejaVu Sans Mono BoldDejaVu Sans Mono BoldDejaVu Sans Mono BoldVersion 2.33Version 2.33DejaVuSansMono-BoldDejaVuSansMono-BoldDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/License~Z   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                          sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F4uni01F5uni01F6uni01F8uni01F9AEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0221uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0243uni0244uni0245uni024Cuni024Duni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C8uni02C9uni02CCuni02CDuni02D0uni02D1uni02D2uni02D3uni02D6uni02D7uni02DEuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EEuni02F3 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0343uni0358uni0361uni0374uni0375uni037Auni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0462uni0463uni0472uni0473uni0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni04A2uni04A3uni04A4uni04A5uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04BAuni04BBuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04CBuni04CCuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni0510uni0511uni051Auni051Buni051Cuni051Duni0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564uni0565uni0566uni0567uni0568uni0569uni056Auni056Buni056Cuni056Duni056Euni056Funi0570uni0571uni0572uni0573uni0574uni0575uni0576uni0577uni0578uni0579uni057Auni057Buni057Cuni057Duni057Euni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058Auni0606uni0607uni0609uni060Auni060Cuni0615uni061Buni061Funi0621uni0622uni0623uni0624uni0625uni0626uni0627uni0628uni0629uni062Auni062Buni062Cuni062Duni062Euni062Funi0630uni0631uni0632uni0633uni0634uni0635uni0636uni0637uni0638uni0639uni063Auni0640uni0641uni0642uni0643uni0644uni0645uni0646uni0647uni0648uni0649uni064Auni064Buni064Cuni064Duni064Euni064Funi0650uni0651uni0652uni0653uni0654uni0655uni065Auni0660uni0661uni0662uni0663uni0664uni0665uni0666uni0667uni0668uni0669uni066Auni066Buni066Cuni066Duni0674uni0679uni067Auni067Buni067Euni067Funi0680uni0683uni0684uni0686uni0687uni0691uni0698uni06A4uni06A9uni06AFuni06BEuni06CCuni06F0uni06F1uni06F2uni06F3uni06F4uni06F5uni06F6uni06F7uni06F8uni06F9uni0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1D02uni1D08uni1D09uni1D14uni1D16uni1D17uni1D1Duni1D1Euni1D1Funi1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D62uni1D63uni1D64uni1D65uni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Buni1E9Funi1EA0uni1EA1uni1EACuni1EADuni1EB0uni1EB1uni1EB6uni1EB7uni1EB8uni1EB9uni1EBCuni1EBDuni1EC6uni1EC7uni1ECAuni1ECBuni1ECCuni1ECDuni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE8uni1EE9uni1EEAuni1EEBuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni2010uni2011 figuredashuni2015 underscoredbl quotereverseduni201Funi2023uni202Funi2031minuteseconduni2034uni2035uni2036uni2037 exclamdbluni203Duni203Euni2045uni2046uni2047uni2048uni2049uni204Buni205Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B8uni20B9uni2102uni2105uni210Duni210Euni210Funi2115uni2116uni2117uni2119uni211Auni211Duni2124uni2126uni212Auni212B estimatedonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215F arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni2213uni2215 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangle logicaland logicalor intersectionunionuni222Cuni222D thereforeuni2235uni2236uni2237uni2238uni2239uni223Auni223Bsimilaruni223Duni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Funi2290uni2291uni2292 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1uni22A2uni22A3uni22A4 perpendiculardotmathuni22C6uni22CDuni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2312uni2313uni2314uni2315uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2325uni2326uni2327uni2328uni232Buni2335uni2337uni2338uni2339uni233Auni233Buni233Cuni233Duni233Euni2341uni2342uni2343uni2344uni2347uni2348uni2349uni234Buni234Cuni234Duni2350uni2352uni2353uni2354uni2357uni2358uni2359uni235Auni235Buni235Cuni235Euni235Funi2360uni2363uni2364uni2365uni2368uni2369uni236Buni236Cuni236Duni236Euni236Funi2370uni2373uni2374uni2375uni2376uni2377uni2378uni2379uni237Auni237Duni2380uni2381uni2382uni2383uni2388uni2389uni238Auni238Buni2395uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni2423upblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2601uni2602uni2603uni2604uni2605uni2606uni2607uni2608uni2609uni260Auni260Buni260Cuni260Duni260Euni260Funi2610uni2611uni2612uni2613uni2614uni2615uni2616uni2617uni2618uni2619uni261Auni261Buni261Cuni261Duni261Euni261Funi2620uni2621uni2622uni2623uni2624uni2625uni2626uni2627uni2628uni2629uni262Auni262Buni262Cuni262Duni262Euni262Funi2638uni2639 smileface invsmilefacesununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2654uni2655uni2656uni2657uni2658uni2659uni265Auni265Buni265Cuni265Duni265Euni265Fspadeuni2661uni2662clubuni2664heartdiamonduni2667uni2668uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2670uni2671uni2672uni2673uni2674uni2675uni2676uni2677uni2678uni2679uni267Auni267Buni267Cuni267Duni267Euni267Funi2680uni2681uni2682uni2683uni2684uni2685uni2686uni2687uni2688uni2689uni268Auni268Buni2690uni2691uni2692uni2693uni2694uni2695uni2696uni2697uni2698uni2699uni269Auni269Buni269Cuni26A0uni26A1uni26B0uni26B1uni2701uni2702uni2703uni2704uni2706uni2707uni2708uni2709uni270Cuni270Duni270Euni270Funi2710uni2711uni2712uni2713uni2714uni2715uni2716uni2717uni2718uni2719uni271Auni271Buni271Cuni271Duni271Euni271Funi2720uni2721uni2722uni2723uni2724uni2725uni2726uni2727uni2729uni272Auni272Buni272Cuni272Duni272Euni272Funi2730uni2731uni2732uni2733uni2734uni2735uni2736uni2737uni2738uni2739uni273Auni273Buni273Cuni273Duni273Euni273Funi2740uni2741uni2742uni2743uni2744uni2745uni2746uni2747uni2748uni2749uni274Auni274Buni274Duni274Funi2750uni2751uni2752uni2756uni2758uni2759uni275Auni275Buni275Cuni275Duni275Euni2761uni2762uni2763uni2764uni2765uni2766uni2767uni2768uni2769uni276Auni276Buni276Cuni276Duni276Euni276Funi2770uni2771uni2772uni2773uni2774uni2775uni2794uni2798uni2799uni279Auni279Buni279Cuni279Duni279Euni279Funi27A0uni27A1uni27A2uni27A3uni27A4uni27A5uni27A6uni27A7uni27A8uni27A9uni27AAuni27ABuni27ACuni27ADuni27AEuni27AFuni27B1uni27B2uni27B3uni27B4uni27B5uni27B6uni27B7uni27B8uni27B9uni27BAuni27BBuni27BCuni27BDuni27BEuni27BFuni27C5uni27C6uni27E0uni27E8uni27E9uni29EBuni29FAuni29FBuni2A2Funi2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2C64uni2C6Duni2C6Euni2C6Funi2C70uni2C75uni2C76uni2C77uni2C79uni2C7Auni2C7Cuni2C7Duni2C7Euni2C7Funi2E18uni2E22uni2E23uni2E24uni2E25uni2E2EuniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716uniA71BuniA71CuniA71DuniA71EuniA71FuniA722uniA723uniA724uniA725uniA726uniA727uniA789uniA78AuniA78BuniA78CuniA78DuniA78EuniA790uniA791uniF6C5uniFB52uniFB53uniFB54uniFB55uniFB56uniFB57uniFB58uniFB59uniFB5AuniFB5BuniFB5CuniFB5DuniFB5EuniFB5FuniFB60uniFB61uniFB62uniFB63uniFB64uniFB65uniFB66uniFB67uniFB68uniFB69uniFB6AuniFB6BuniFB6CuniFB6DuniFB6EuniFB6FuniFB70uniFB71uniFB72uniFB73uniFB74uniFB75uniFB76uniFB77uniFB78uniFB79uniFB7AuniFB7BuniFB7CuniFB7DuniFB7EuniFB7FuniFB80uniFB81uniFB8AuniFB8BuniFB8CuniFB8DuniFB8EuniFB8FuniFB90uniFB91uniFB92uniFB93uniFB94uniFB95uniFB9EuniFB9FuniFBAAuniFBABuniFBACuniFBADuniFBE8uniFBE9uniFBFCuniFBFDuniFBFEuniFBFFuniFE70uniFE71uniFE72uniFE73uniFE74uniFE76uniFE77uniFE78uniFE79uniFE7AuniFE7BuniFE7CuniFE7DuniFE7EuniFE7FuniFE80uniFE81uniFE82uniFE83uniFE84uniFE85uniFE86uniFE87uniFE88uniFE89uniFE8AuniFE8BuniFE8CuniFE8DuniFE8EuniFE8FuniFE90uniFE91uniFE92uniFE93uniFE94uniFE95uniFE96uniFE97uniFE98uniFE99uniFE9AuniFE9BuniFE9CuniFE9DuniFE9EuniFE9FuniFEA0uniFEA1uniFEA2uniFEA3uniFEA4uniFEA5uniFEA6uniFEA7uniFEA8uniFEA9uniFEAAuniFEABuniFEACuniFEADuniFEAEuniFEAFuniFEB0uniFEB1uniFEB2uniFEB3uniFEB4uniFEB5uniFEB6uniFEB7uniFEB8uniFEB9uniFEBAuniFEBBuniFEBCuniFEBDuniFEBEuniFEBFuniFEC0uniFEC1uniFEC2uniFEC3uniFEC4uniFEC5uniFEC6uniFEC7uniFEC8uniFEC9uniFECAuniFECBuniFECCuniFECDuniFECEuniFECFuniFED0uniFED1uniFED2uniFED3uniFED4uniFED5uniFED6uniFED7uniFED8uniFED9uniFEDAuniFEDBuniFEDCuniFEDDuniFEDEuniFEDFuniFEE0uniFEE1uniFEE2uniFEE3uniFEE4uniFEE5uniFEE6uniFEE7uniFEE8uniFEE9uniFEEAuniFEEBuniFEECuniFEEDuniFEEEuniFEEFuniFEF0uniFEF1uniFEF2uniFEF3uniFEF4uniFEF5uniFEF6uniFEF7uniFEF8uniFEF9uniFEFAuniFEFBuniFEFCuniFEFFuniFFF9uniFFFAuniFFFBuniFFFCuniFFFD dlLtcaronDieresisAcuteTildeGrave CircumflexCaron fractionslash uni0311.case uni0306.case uni0307.case uni030B.case uni030F.case thinquestion uni0304.caseunderbar underbar.wideunderbar.smalljotdiaeresis.symbols arabic_dot arabic_2dots arabic_3dots uni066E.fina uni06A1.init uni06A1.medi uni066F.fina uni06A1.finaarabic_3dots_aarabic_2dots_a arabic_4dotsarabic_gaf_bararabic_gaf_bar_a arabic_ringEng.altuni066Euni066Funi067Cuni067Duni0681uni0682uni0685uni0692uni06A1uni06B5uni06BAuni06C6uni06CEuni06D5@tGG22dk  Y&Y@&2G@AGA2k2%e}f2 ]%]@%e2d~}d|S{f{2zez2yxfxdwvts s rq.rq.pfp}onmnml]mmli%l]l@kkji%ji@%hfhdgfgdfefedcba}`^d\[\[Z2Y-YXWWV2UTS TS RQRQPQPONMdNMdLKJIJIHGF}EDE}DCA?2>=<=<; <; :9:98 998 8@76776-6545K4343212d1-10/@0D/.-.-,--@@ ,+,,@ +*++@ * **@W)K('K&$&$%$%K$$#""2! 4!>  42@ @@ @WK77222X}X       @   @   @ @  d+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++pyzo-4.4.3/pyzo/resources/fonts/DejaVuSansMono-BoldOblique.ttf0000666000000000000000000071451012662716363022617 0ustar 00000000000000 FFTMYR,GDEF.$HDGPOSGSUBD`TOS/26TVcmapcvt >|8fpgmQgasp@ glyf6Lheadf06hhea h$hmtxތloca_T'maxp B name4!post):ZprepԱ.ɐ!ɐ!<abbcders    >Lcyrllao latn*mark ntz "(.4:@FLRX^djpv|cIgMaH+u^D,vdJdJ`F-waGbHcI;oQQ ;obI;ofM<paGaGdJQQ `GaGW=E_bber>DJPV\bhntz`|| cyrllao &latn0SRB 4ISM I K _ q !!!!!!!"!$!&!+!.!_" """" "-"="i"""""""####!#(#+#5#>#D#I#M#P#T#\#`#e#i#p#z#}#####$#&&<&G&g&o'''))*/+,d,p,w,z,..%..' !$CLPCXatz~br1Ya,0>bw{0Th| HPY[]_   & / 9 < E K _ p t !!! !!!!"!$!&!*!.!S!"""""'"4"A"m""""""#####%#+#5#7#A#G#K#P#R#W#^#c#h#k#s#}#####$#%&8&?&`&i'''))*/+,d,m,u,y,|..".."meSOLGFEDC53% }|{udbYDCvfdc`^ lfZUEDB@=;21/-,*)'&$"! ;{tse2PaXPbwbsbqbb    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpk#vjsgwl|cnTm}b :  yeuqqrszvtf#3#N#'#\ ##3##/%%1/;sqoV#d\H```{\{`o7L'5%={D=!/s, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-hh@ 7/10!%!!hsr)R ]@.    49    99991/0KSX9Y"!!! # 7# f!qe@ 91<20!!!++N@3      91/<2<<22<<220#3333#3#####73#73J^^^6H6^_^_5H5^qrvuu%v'D /m@;)%%$(<54&#.'.54$?3.';Rb95HS49Ua3YWD0/GL22 ZD*9-.,=BZ(|',.! '3V@-(  (?? ?>.?%1"+1""499991/999904632#"&"32654& 4632#"&"32654&!?9PP9:OP)˹=8ON9:QRXO::OO:9PP`^P::OP99Q&0@O$%$&%%$()'/.040'$ % -<;C<BE%%!&!0$ ' !* 199999991/99990KSX999Y"%#"&547.54$32.#">73!3267VWź FA16BX^&(*0 xlsZ^^,Z.H23հi:d/#&GB"tW :][4\c910!+R7 @ G   99910 #&5789RPۏͦJ"F!qV @G  999104'3q89QQ8[%3y9TJ@(   B   <2<2991<22990 %#'-73%TJLLLLKLXXB\ #@ H   <<1<<0!!#!5!RPRPo4@4I10KSX9Y"!#:6׬o9910!!+u8\o/@4I91/0KSXY"!!NFoB3@ 49/9910KSXY"3#TmZw /@ J$J-B$E0 '010@////////// / / ?????????? ? ? D@@@@@E UPPPPPU ookkkkk k o   C////////// / /  ]]4632#"&3267654&#"#"&547>32I45JJ54IjDG@k$6ABGCj$6AfUIeSJ5HI43IGqljc\rkheI+ O@(4JJ9J   991/20KSXY"!%!!!^J3F53bL J/oc@24LK JBJ 9999991/9990KSX9Y"!!7%6754&#">32bi3K13^pcRv3me:(.RbHQ;: (*{"`(M@,M LK#M L K MBE) ) &  )999190#32654&#">32!"&'32654&4|sjSn6hXfz6`loUFL*( Ÿ$&$.0Rc` o@<      4 J9    991/<290KSXY" !!3#!!u51>?:jBj@84JLKJ J9 E  999190KSX9Y"!!>32!"&'32654&#"T+35!H)__3F_ǎzNO  (*hy(&` '8@M M L KM%BE(  (91904&#"326.#">32#"&5476$32#[Mi[Kln3?654&#">32Z 7XX]kJEYt5fhbui~`N]~C9>IH 7:_bVis ?a@4-0)? OO P,)O0P O09@? < ,- <&<3@999991999902654&#"#7#"&5432>54&#"3267# 476$32hQGgP+r<Hh |i|X[`OQ?k_XTtP-MXS]R1531/#B[kv{؛0095~D<ͨ@$ n@:      4<Q9    91/<90KSXY" !!!!!hqpq+q#@I###  # #4T T9T S"  "#$9991/90KSX9999Y"] ]32654&# 32654&#%!2#!VŜjoTDy{VZK׾LEKFVRFmjD@릤 l?C<J@UCVUCV BE  99910K TX @ 878Y%#"476$32.#"3267MUyaRM@2XP6EU~vQ^+$$nr$$DEaUkӇCI ]@+   4V 9V    9991/0KSX99Y" ]]3267654&#! #! `p+4A};%hH^?XXj%~ WqE\P U@/  4VV9 VS   91/0KSXY")!!!!!_ 3??1L{y/ P@+4VV9S   91/0KSXY"!!!!!>B3#uf&x@3#&& !"&4"##V%VUCV BE'%$#"&'991990KSX9Y"K TX'@''878Y%#"476$32.#"3267#7!FsxbQMB3QG4)E "t$?91^;@"os$$CF;9/~MbN# @?     4VS9   91/<20KSXY"P]@ PPPPP]!!!!!!'oqn'ف9+h J@&  4V9 V    991/220KSXY"!!!!! 3y3׼)43)3R@+  4 UCVV 9E  991990KSX9Y"'3267!!#"&ANgtq4!RCCqoNX`_l 7785X w@B      49   91/<290KSXY"!! !!'l3\R_- P!6@4V991/0KSXY"3!!P#'w3/  @I     4 9   991/<290KSXY"K TK T[X @ 878Y@>  /////   '//)/;8]]!!## #a1)h/q+qR @:  49  9991/<2990KSXY"(]@*5(*'%300005QWPPWPWPW]]!!! ! /=+==)P@V'VB'E*! *10@+0044553300000000 0!0"0#0$0%]4&#"3267>7>47>32#"&dPQ;d$+PQ;c%-dUMbTNxxGE'\sKxvGE,Ss ck@;    4TT 9   9991/0KSX999Y"32654&#%!2+!)Th}I:N蹤mJ[MrB[Q=-|@%VV BE." " ".99991990@A000 0 0 00000000005553$3%3&0'0(0)0*0+0,0- ]#"&547>324&#"3267>7>cTNaU#iIӛPQ;d$+PQ;c%-]Bn/xxGE'\sKxvGE,Ss@Q       4T T9    999991/<9990KSX999Y"(](]!&'&+!!232654&#=Dj5ius#ZP{\\ HWx կi|NM'@;'''4 ' UC!V U C VBE('$ $(99991990KSX99Y"   ]@    ( ]]@() ( ( 9 9 9 I I I Y Y Y h h h y y y ( ]].54!2.#"!"&'32654&'|Cg[9Dovtn|;akFEP~))>?qXTS3A451QUud>[}@4V9991/20KSXY"K TKT[KT[KT[KT[KT[X@878Y@ ])!!!j1#3-s@=   4  VE9 9991299990KSX99Y"!3267!#"&546D'\Tl'#P?L} '/RZz|R?JNƷ*d-F@#4991/290KSXY"%!!!-{dA#+)Z @A      4 9    91/<290KSXY"]@**5888]33!!!l )3 ;H+q? u@?    4 9   91/<290KSXY")! !!/pf#hA1N`@149 91/290KSXY"!!!%BsswwL  7@4V9V   91/0KSXY"!!!7!13/Z}#?F@!4WWG9910KSXY"!#3!D&%ZTB.@49910KSXY"#mjB@4WWG9910KSXY"!73#7!d%%9@ 991290 # #--W/10!5/۾s`f/910K TKT[X@878Y#fx/u{ *@O      4!  ]]!<"\]%[E_ (  !"(+99999991/9990KSX999Y"K TK T[KT[KT[KT[KT[KT[X+++@878Y@23 0!0"3#vv x  ]]"3267%!7#"&54$!37>54&#"7>32Q?v7{Cn7%faRv/q[hm?Rm}MM1 9<98'''j/ @J    4 <<E[G    999991/990KSX9999Y"K TKT[KT[KT[X  @878Y%254&#"!!>32#"&XoSJrZ)/%w?_=7Euoephw`c۽i}{W@ ` <K< K [E  99910K TKT[KT[X@878Y%#"$5!2.#"3267MSo(bL5=Nz|F[%!!=)+:8ƀ~44B @J    4 <<E[ G    999991/990KSX9999Y"K TK T[KT[KT[X  @878Y"3254&7!!7#"&547>32yoSIrZ}%B_>8Eupephw`cܾkL}&w@#`d<Kc#< [E'& '999910K TK T[X'''@878Y@ & ]%#"$547>32!3267>54&#"_nXPT  l]fYd(7**afm-vSce>;h Ubvv@=    4 ddG e    99991/22990KSX9Y"K TX@878YK TKT[KT[X@878Y!!!!7!7>;#"XX+ۮ+*+A:LNӓ7X +@\+ + + + +* +'(&)+ +4)* `<<K<&[*ef,* )+ + ,9999199990KSX99999Y"K TK T[KT[X,,,@878Y]%254&#"!"&'326?#"&547>327!sRHrP5bW6KW}6`TIFjh#% dofo --u|SUѲlgtng;`@L   4  <[G   99991/<99990KSX999Y"K TK T[KT[KT[KT[KT[X@878Y@    7 ]!>54&#"!!>32Nۅ:7Sty/#t0e (5:=\gL7 @5   4 i hded    991/20KSXY"K TK T[KT[KT[KT[KT[X@878Y!!!7!!!!5Dl++m%C`X@?   4 id dhef  .991990KSX9Y"K TX@878YK TK T[KT[KT[X@878Y]%#!73267!7!7!!!=/;-df+N)B%+.;4kTVR @D      4eG   991/<90KSXY"K TX  @878Y!! !!%r9tRZ^B `T @/ 4dGd  999991/990KSX9Y"K TKT[KT[KT[X@878Y@ ]3!!"&5467!7!q>N-ͪ +Vm!-%pF,{+@k !    $!"!#""!4&$!  <)[$e" $,"%  &!"% # % " ,.999999999991/<<<29990KSX99Y"KTK T[KT[KT[KT[X,,,@878Y@? ? ? ?????7???????@@@@@@@ @ GOOOOOOOOOO O!O"O#O$O%O&O'O(PPPPPP P __________ _!_"_#_$_%_&_'_( !"$%&'L]>32#>54&#"#>54&#"#3>32 L\l  #"09 $"/7)yEEW@Kpa/aK13=(*Zw1*B(*Wz1`tDKK;`{@L   4  <[e   9991/<99990KSX999Y"K TK T[KT[KT[KT[KT[X@878Y@ ]!>54&#"!!>32Nۅ:7Ruy% 0e (5:=`\gLXy} !@ <<[E 104&#"32!2!"&R]U{aSyM mvjy6BpV{@M4<<[Efe    999991990KSX9999Y"K TX @ 878Y%!!>32#"&7254&#"{{-%!G]>8EupoSJrZ ba۽j|rephwDV}@N4<<[Efe  999991990KSX9999Y"K TK T[KT[X  @878Y7!!#"&547>32"3254&V'%s?_>8FtooTJrZP_dܾk}sdqhw{}@5       4 <[ e   91/990KSX99Y"KTX@878Y.#"!!>320vJ(d%#>w>o,/+)``iZF{'@<    4 ``<K<K%[E( " "(9999190KSX99Y"K TK T[X(((@878Y@(-/// / / ,/. / :::JJJYYY ]].#"#"&'32654&/.54$32F2K]^nIJvhc3V]n{=Xzr]=56F;-7%%r##8;KC.4'$p@>  4 de d  9991/<2990KSX9Y"K TX@878YK TKT[KT[X@878Y!!;#"&5467!7!P>+hj`@O   4  <E e  9991/299990KSX999Y"K TK T[X@878YKTX@878Y@      ]!3267!!7#"&546{#:6Sqz#!,eT4:?wZiH`@#4e91/290KSXY"K TK T[X@878YKTK T[K T[KT[KT[KT[X@878Y@    ] !!P``nZ-` @B      4 e    91/<290KSXY"]@.&))::KL   **-<<<IM[\]]333! !`!#^`5ZN` @@    4 e   91/<290KSXY"K TX @ 878YKTKT[KT[KT[KT[KT[KT[KT[X  @878Y@ )8HYY]] !! !#ժ- `dR=X`@A     4  df e  99129990KSX9Y"K TX@878YK TK T[KT[KT[X@878Y+7326?!!'p~-uWd<')5yi54&+7326?>!3#"3o%Ĩ + `w>%=)+${hY& }_S) DPm}880=L=nVѧWLE'.8;3k10#0@i+,+,,+ 4+1''%,%W'W/W'G1/, 1'&%(&01.991999999990KSX9999Y"326?>7.546?>54&+732;#"+{gX' ~^T) BQy%ͤ)_t>%>)+UͤTOG*06<1`r#m :UFtޓXy++@ ll  19990#"'&'.#"5>32326yKOZq Mg3NJNS5dJ t]F+<73 ":?=6 7=R ]@. 49 99991/0KSX9Y"!!!3H8 f#+ehN$R@1` `J K JK[ E%  ""%912<220.'>7#.547>73N5-n@@@3>A77mfP{775rh5(/L.)" țjSY"j@F  4 dLK<B J  9991/222990KSX9Y".#"!!!!3#737$325i!P+?34@-%3J))N ^T /c@8  *( -'! ) -0)'!$ * ( $02299912299904&#"3267'#"&''7.5467'7>32;dJIeeIJd$P0'T-)S.'QIccIJffq)S,/Q$*S)/Q&?@K  4 u u9   .9991/2<2<290KSXY"!!3!!!!!7!'!73==%s$dVTd%:%X𻗽J@ <210##  h=#G@N7829 %8?/;.2;+;;EB+H785 (" 5%(9B 8?"B5("B54&'.#"#"&'732654/.5467.54632P&:>#&N:)9>&"J-D?U^=micUor-+Rb.JKV^dmPzr30G7,X&':6&'X(#@"!D?)HECAzLTC+e?!"D?Ee DHsK]2)b?;H1a@99991<20K TK T[X@878Y@////????]]3#%3#Z111}N1ID@%  q po>qpo2n>n&JD  8 ,/210.#"3267#"&546322#"&'.5467>"3267>54&'.P4[0akjb5`*7j2ʩ7iZZ\[[[~}[[[\ZZ~cIGHHGGcdFHHHHHh__g$ZZ[~}[[[[[[}~[ZZGIGebHHIIHHbeGIG *.t@A! '~!~ -~+ +'~}~|B/! *$-,.+/  $ $ /999999919990#7#"&546;7>54&#"7>32"3267!!X.{OkЇIF9S!PAzu:,Up#P@X65zh#(*(&i^ EHK,:xNN# 1@    // 991<299077N--v\/-w#Xjy@ H 10!#!X!,9910!!+u8}N 4L\@3-*+'0!o5n2+oAn M*',$0-!1"3+"$ G3 ;/29991<99902#"&'.5467>#32654&'2#'.'##%"3267>54&'.hZZ\[[[~}[[[\ZZb@@998(NG&7O?9)cIGHHGHccGHHHHHNZZ[~}[[[[[[}~[ZZb((+)oXZAU 81:/qGIGebJGHHGJbeGIGXB19910K TK T[X@878Y!!w%V@m mB102#"&546"32654&hCz//12.0zDHdbHHdc30/xDBz./3dHHbcGHdXy *@H  H   <2<21/<<0!!#!5!!!fff!bbL@&4 3999999190KSXY"!!76754&#"7>32h3[HC?MLIfD-%A[-1$#lc/(K@+ # )  &33)999190#732654&#"7>32#"&'732654&omX^PF0PIBypScKE>O`oW73(.fYZo cJQF-59f-10KTK T[X@878Y!#wAFfT{`&@V   &#$"% 4 % <"Ef e'&'%  '9912<2990KSX999Y"!3267!3267#"&'#"&'7-!D=Rf! +.P$HL 5{F:LaT X0?Fps#! KSOO0/f; `@-  49  <9991290KSX9Y"!###.54-վEfN'-/@4I910KSXY"!!NHo|! S@+334       99120KSXY"3?33!;jŇo-+)N+ 4@~~ }~B  999910432#"&4&#"326!!3%D=WxEIH7:_bVik&$ 3uwk&$ 3urk&$ 3um&$ 3unk&$ 3um !@J    !  !4 < = Q  " !  "99991/<9990KSXY"32654&#"!!!.54632!oM66MN56MEku$*vt/P6MM66MMq$j5uu;lj@L    4V <V9VQS  91/<20KSXY"!!!!!!!33<3N64H}nj{o&&uk&( Xuk&( Xuk&( Xuk&( Xuk&, /uk&, /uk&, /uk&, /u"}@F!" 4<V9V #" #99991/220KSX99Y"! #!#733#3267654&#;%hH^/f?-N`p+4A}WqE\PFrXXj%~m&1 3u=k&2 3u=k&2 3u=k&2 3u=m&2 3u=k&2 3uwXs 0@   <291<290  ' 7 XJJGJHJHHsL'.l@8+'/.(+%&"V"+VB"E/ .(% '& &/999999919999999032.547>327#"&''.#"wM8}4 bUNi= L5Nr9K4y6b<=2^"J'BAs8a,>ObJHrN47-k&8 9u-k&8 9u-k&8 9u-k&8 9uNk&< /u@L  4TT9 9991/0KSX9999Y"!!32# 32654&#w@#'/I:NVg|L㸯qB\RJ[M3@e )*+*&'%(++*4("!%3.+%`< %<.G E))*31! (+*1!  ! 1*499999999991/99990KSX999Y"KTKT[KT[KT[K T[X444@878Y@lllllllllll ]#"&'732654/.546754&#"!6$32y`L;:B@1:k.ZhoE,*ZR]r) #dS9`L;}D ODBoG-g;%@Gfefݮ+d/uf&DC/f&Dv/uf&De/9&Du/u1&Dj/u&Ds{ L3@. .8765493.01/2.43 <1!C=("L !`"<`= ]]1]"K%c9]=\F@[+%E1_MLI 32!"<=.(16C.I..M9999999912<22999990KSX9999Y"KTXMMM@878Y@,3;3<3=/012 L0000;0<0=]]7#"3267>54&#"!3267#"&'#"&546;7>54&#"7>32>32Jbu:1CP% (,8G=UF8A/9Q^ =Yl DA4V/OERt4T~NgU332#"&ZoSJrZ{%uG]>8Eupephwba۽j|X1&\jN&$ /u&Dk& $/uF&Do'tc$/ou{'t Dk&& uf&FvVk' u&f&eVFk& d&1&KF4k&& uf&FfVk&' IuBv' wGBs'@h'&% #"$      4 "  <"<E[G %      (999991/<2990KSX9999Y"!7!7!3#!7#"&547>32"3254&VB%/%%B_>8EupoSIrZTtt`cܾkqephwN&( L&H'k& (LF&'Hk& (L1&'Ho't(Lo}'tHk&( muLf&HfBfk' u*Xf&eJfk& 2*XF&Jfk& 2*X1&Jf' *X+'ZrJk' Lu+;`k' -uK4!!7!3#!!!#73!7'+q+&+ ځ׆ #q#hQ8#!67654&#"!#737!!!>32%݄ :7St| # >0e),:=zz\g&m' /u,;9&uN&, B&k& ,`F&Bo&,(tio>&Lt}k& ,7` j@"4ded   991/20KSXY"KTKT[KT[K T[X  @878Y!!!7!!5Dl++m`%3267#!#"&!#3!3{>,lBJO1>]wIG8/9/00JX`_l n853 u"%+73267#7!7#3!!!7!#3#_#6%/z]0IR.+G{ .. qGH.;4kTVk' u-X f&e0X'(.R0'(NA` !! !!%Jc7wS`}^B `TPsl' /v/rm' .wOP0!'(/5 &L-OP' n/!' "OP'y/l'y?O! d@8   4  V9   .9991/90KSXY"3'%!7!P`s+'_s^\w3c#%@J 4dGd  99991/990KSX9Y"KTKT[K T[X@878Y3!!"&5467'!7!%=N-Ω 5Zb+V^"gEl -%pF,Ò:ˊl' Bv1;o&v Q0&C(1;0`{&j(Qk&1 u;f&QfS'_QV6&#"!!>32 +73267IjmIM#'/>~84qqy,%dgoKN4nlnfX{#+732767654'&#"!!>32v1poy,'d43| NOv|# 1j<$Tkl77=+,5~`]fi?eC=N&2 Xy&R=k& 2XyF&R=Ck& 2Xf&RHc@84V V 9VS    91/220KSXY"!"&547>3!!!! ";b3C54&#"!3267#"&'#"&547>32>32267>7>54&#" (,7G> VH9@/9R^2\@68DŽSx%0Rr4D 394D3B'40UYF54O]72((QEKK<=<;>1XZ@JvaZxHA@I*|HQz"IAl' 0v5]o'v U0'(5!0{&(Uk&5 7uf&UfVl' Bv6Zo&v Vk' Nu6ZFf&eVo&6ZoF{&Vk&6 /uZf&Vfo&7o&Wk&7 Our&W s)#73!!!3#kh&b22b&!!!3#;#"'&54?#737!7!>>,&&U,I/#&,>)!P3k=P>-m' 9u8j9&uX-N&8 j&X-k& 8jF&X-m&8s8Rj &Xs-Ck& 8jf&X-_&8tjo`&Xt)Zr' G|:Z-o&e ZNr' :|<Xo&e \Nk&< /u l' Bv=1o&v ] k& 2=11&] k&= /u1f&]f)!7!7>;#"oۮ+*+A:Nӓ7P/'%!#737!3#>32#"&7254&#"}) % ??_=7EuooSJrZzz`c۽i}qephw! (1!2)"#7676763232>&# 32676&#J%#*,& <&j{hD-po#PnU-b׽ İ%/!MALPSF`wyjFP"'!!!632&"2I6 .)KNK76?H?Kæ~D;p%32>&+'3 !y/byTmn5`bbR.}&"2>32#"&'!'7?H?KHZǛ76e ܵ|D]]ba9703276#">3 !"&@?L11sLY@KU.IItU+HFAAFH$$rr$k %27# %6326;#"&#"@Ije/-p`+];2 x1]Hx#@7.8"#%27# !2676;#"&#"456c:Xoy,'_8,"4ndD4/sV89Vl7+rU?!"#767676; !#26&s>_nMMz33j&4MFmts?;#";! &767676?6%3!]Ĉ_Y)%W4Dejyw`+ Q2[n"7632!7!!7 26&"67ʳGK) ?I?J4DT={%/%#"'&'&'732?676#"32%26&#"PcRs* Y%WQhA( jV53Mi@Tij@T5?NM ",f==KJ 3!!!!!2{L3??2B+[u6'&#">3 ! 732767SLY@MUJJJqrK2(HbyAFH$$xx~_PxxP<'"267# &7>7.76$32.#";vg5kq*Ϧx$0\a4_Tnhn2}s^o1/$&٢!(*]SOWX !!!+7276?!?3^ 2foV,h3/B}+dl72}-X7!!#!732767!7!7>;#&X,0gq+c34,'+obcl78TNʜNk(%27#7!# %632676;#"'&# #]170̅Hia<: nb,];2]jP+"TsC  l7.R:dOT^ '!!%3276VBbCK34B%v ]u~t*'&76'&#"#3>32?67673.Y,/)3 ?@21}.u+U-,)3 [;CI9y} V*4GF}]fij 4 T^x !!!#"'&2y2אf2VW.-vx!!3#!!!#73 2y2b&6)22)6&b 1!!!676&98ߣc#'s@؋x6@G:$wGq3= 676;#"3! !!1poy,'b53Dc8wSۻAml75}^B `T3+;!"&?#73!7!% =b,ь1%p,N^n^B!!''%!H׋&E P)+UTEW %#"&326732673#7"&/sJH>d9d6&}`pGD<}TV{D{VT}D+tBMQcVn!+732767!!! m@_0,<::=Z4K=+=eVw{!6&#"!!>32v0NOw}#!1jyTzi~`]f>u  &7%63 6'&#"!3276"+ztTaJ}N3*&UVyQqrK;txxpOD2P_xx]I&2&RE &7%6327!#327&#"_ ,|[GPWvݒD@!CgFgU%q+pi5x yh8Vf{327!##""327&iWgmi@Ti+Nd* =|erП]]Q32>&#"#7676763! !#!U/b>_/5`mJbb'&4MFmVv$&"2"'!#776;#"36322?I?J?>76&#">32ǛRd eml;ro, c ak`m8ng'7eCYcRT45B01Q>PVFC ./˞|[{'>323267#"&7>?676&#"^__UV ^g^l2se"X  YbX\=&"<89<:7##&2P8954 X&(;#"&7# %53232#%4'&'32'3p ?+'͇24/2F:t8)oa@ iHY@ lY265LX  !!;#+732767&'&7!7!>,g=U$1hoy+'b6+BJ+d,>KA+fl7,eHQ>R"#76767632'!!,!<&j{h/2!H!MAL-t67676;#"!!;#"&7!7+pey,'b53+g=U,*e+`mb 75gKAV!!!!;# 4?22 ',~V=, >&8<'XhY##"67#7!32676&'!c7%==%)4a{ *Cgf* "C4mn8mO֦M >N'73!"'&7!276@ c7%=G,"2'f^!H9mȷn.-XT !!776&rr./8i52JL@\MXF` +7326?!76&Q,wZ`@"%f^_1eW?-=o<A)_;;& #O9 !3!!!7#73!/0&2/]&w&0`!3!!!?#7!!"-&M+i-o% ` "# '&7!327676'&+7!!6lw+>V=bF #!'+30!Je6;m?tk9)qo dn %2767!! &76767677!!#  X<>+69[euS03-+(8N5J߀tPcfq! ϋ..H`!#"32767# '&76767677!!+UU}]fel:mllgڂ+[cwS-+45fcj6%tsޏfq! V`932767#"'&547676%76767654'&+!7!lEOie=m#*&.dnoom+itjqYhY>f*$.Y('"08g!CA &%+ K;jC:$ $"i$!!?#7!7>76&#">323+Qu3L1%UG\[ coOv5s^%Y[u%6UtAVAdm?<')ݿX^H"#!!!2! '&7!32676'&+33DsF:3+lw+>D@oAAfO@tfstt_95jcf54H` 2!"'&'32676'&+!!na33+gdd_9U[AAY)Y0,fgst%)58jcf54v58%#"'&'327676'&+#7;7!3 Ip-*٣EF=:=BDHoA%7T8%!B!%61^ׅ#*+rB`GLr!¦V{!!676;27676'&#"@<<)"^O_,% c& !iI7 o%,p\])(UN& A,AQ!!t +le'!!!!!!!7!7!7!t C.f+.fXXf.+f.:Rk&$ /u/f&Dfk&, /uf&f=k&2 /uXf& f-k&8 Dujf&Xf-'h^' <8jO&q#;-' N' 68j0&x-' 6&8 kj&r-&8' 6 j&rSz{'h^' <$/uO&q#;'h^&$ /uO'q#;&DjN& &f,k' u*Xf&fJXk' Pu.Rk' uN=_&2tX_y}&Rt=_N& X_y&k' /uyHf&6ffk' u*Xf&vJ333327673#"7##oo 8E! WH;;ށ9RJ%2:8}0qhk&1 [u;`h&QjHk' uf&vsLk& 3uf&vvk& $/uf&Dfk& $/uF&Dk& (Lf&'Hk& (LF&'Hk& ,7f&k& ,7F&=k& 2Xyf&R=k& 2XyF&Rk& 5f&dUk& 5F&dU-k& 8jf&X-k& 8jF&X&6ZF{&V&7'WRj: >54.#"7%>54.#"$32j4Q[y}< +poZ97O-9D6ZO1)DF$6hhE"Sslr*g""hv@ "69LOa13I U_"SLT>*> ,F^]O{37>54&#"?>54.#"7632B^0V1@{usa6bB4<,R|D) '?? ,YZ<gyA8C@#h&2>DR*=EEME=C/#4 ~υ 4ILk' Lu+;sk' uKt%-%726#"'632#"'#67&'#"32!26&"^-g*3}-HtC2%?%3+JL67='g$0?!wi?!x yaGyl$>40/-D X6!!#+732767!7!C/31foy+'c5+0_w#dl7,d^X`!!1+732767!7!P,0M+2foy,'b5+-``dl7,dk& $/u1&Do&2(Lo}&2H='h^' <2XyO&q#;='h^' &2XyO&q";=k& 2Xy1&R='h^&2 XyO&q";NN&< X&\tZ %726#"7632#"'#67&'&7!7!5-g*3`}.HtB%9F1,N y1BXl}7&t{/%726#"7632#"5#67&'&?6'&#"!!67632$S!)9tc{.H^9uz*41/ ?-2}#%9T(8!`j y1BXl+5GQt`l3!3ߨt$%726#"'7632#"5#67&'&7!7!!!!$S")!-:sd{.H^9t {*41Y,=%=,^j y1BXl>X`%#!73267!7!!=/;-df+N+.;4kT-26&"&"2>32 #"&'#7#"323?(q?*Z?(q?)@8v?\77ІGV ! 4{GY77ҋ?RsDD]]baab40]]S5J{-&"226&"#"3273>32 #"&'#?(q?*?(q?)@8v?\77ІGV ! 4{GY77ҋ?RsDD]]04baab]]rM!!!#'#7'7'3'EiNUx9M! &lP{s[q^??y?RrM( &#"# ''&7!27&'32672MUy͋,J.UK"Fj9LZ_$$r1y Ms~"&AF''7&7!27&'3267#"'&#"!tcZOt &;UN5Rbu !uw9 ~y!Z9:++~~V !3#!!#7'%6w3bi%_!'!!7#7#DDFi2'&#%N+& =&W1D-.eٛVp'32p$0 "MB?~m66+32654&#%!2#!#7332654&+3Dy{VZK׾LEKB~.~Ŝjo.mjD@릤 l?C<RfVRg47#73!!!3# &%32?!-R&R''Z&Z"NF''\`2 R`TfDRZ=@ !!!A+!#!#73!2!&'&'32654&+Jss0pĵ=Dj7Q\\\NN HWx |NM{.#"!!!#73!>32(xM]2z&UUc&c_%">w&6J`i^{ ,27676'&#"!67632!#32767# & IJ%#NtQQ}"EZ[d#& --dhhhu0hpP**fL--XWq}J((ʵ^]1G%$:(&x{7!!7#"763226&"$!BTTe67Z?>]?H?Ja114/.D Y{%!!67632#"'&&"2N$!BUSe67Z>??H?J_a11䘘/.OD2$67632#"&'!676;#"3&"2IQPZNN86e 1poy,'dg?H?K]./ba@klnKDH'}70326&#"6763 !"'&H56EDUAvRSRJ4[WXZuu66bQQ9 :P7 ,s}-%3276#"763!"'"''67&'&76!2&'&#"a46 S*:fJ0t65ZOQJ46GFR 9Kr]G&A,7B$26&"7!;#"'&?#"'&32?2|?5s#6+a3418EEQEE77G00D:Stnlka110/.#26&"%!7#"32776;#"d?3{?4> 8Q77H006Y]a,4Dab40/.]gl75%y{ 703267!77632!"&6'&#"%4_e 457v 00epEE7 ?:w 喗*q=><;Sz{>3 #"'&7!6&#"32767nvwx75mm4ew-/peHH!'**“ w:?t;<>=q{ 332767#"'&'&'&#"67632?;#"'&7[!HN<;="7]eht.Riz7] M9hu+2Dfb qrlm^.tklbً=@BW  &1E OY"[\px{2!"'&'7327676'&+7327676'&#"767632+bfej.Refrq&lZ<2+uhCY M-zoV.~lk]"UU=p\["YO E1&  WB@{A#"&'7327676'&+7327676'&#"767632?;#"'&7'En02JO.=JLUH*SC+&+XK6E 8![U@l._RREY5 !) !#HZ& 7=JJp\["TO E.)  O.N7o[$ly. !27676'&+7327676'&%672'$HG*F.< 4!J*@/E 42b_]IF~.,~~M@M%?H "J7#"-VWbLHIGgVV=LXT`%#!73267!7!!7!3#1,bgL&3,N_&+nV ;"32767#"'&76763!3676;#"#!"'&'327675YAB !\ZAB=NQP]LL1+'-hn|.*'(+1TLJI3ACBIPQ wRROPRR:Gb..⡠rv..!6MMbV`*%#"'&76763)!"'&'3267"3267P_NO1,.'2VMNK4BEEJ +["!_[=b\⡠!6OP:r_%%#"'&7632&'&#"3276?#7!gjikop23`UTG.GKMPba <='#"+e2(1YXYY Hao 7!!3276DI,f=.B\CCcN}V#L~||`%276' !!! 76- ; /CC=h'.1*W;HJ9SLJ^!32767!!#"'&#NP;:|#u1j<=Vy44GF}\]fij!!6'&#"!676;#">32 ݄NP;:|1poy,'dg1j<=)y44GF}@kln]fij9VJ(%6'&#"!676;#">32+7326}NP;;}1poy,'dh1j=<(2goy,'dh)y44GF}@kln]fij)bln!!!3#!!7!7+7!!-f9D^&)l,,l)@&3ܐ`!7!#"'&7,De,VW,l.-vx` !!7!!7!=l,,l,,b*#"';!"'&?&#"767632!7!3276-VNMN6: b,EG1NV,ZMORe,NB>FIL+<77llu:?= m"&#";4;!"&?#&76!7!!; 2`=b,ь1{&+@D,N 028?pno)b̜ D%!7!;!"'&4+N b+GF77llH. !;#"&7#7!!#"'&'32676'&+A|ZT)I,^0+U,k6M%"+MKJF:=Dh..n~Nn`L9 !qfst%)58jcf54+`"%#"'&326732673#7"'&/sJ$%>zd9d6&>>10pGDdd<w1}TV{1{VT}tB&')(RV`"%#"'&326732673#"'&.tJ#%>zd9d6i&>?00pGDdd<w1}TV{1{VT}B&')( V{367632 +7327676'&#"#6'&#"#367632./99K#$={/Z^c, R-, 22 31'=?DD`F#"dddl77~)*+-y1y-+*)~1`tD$'RF{'!7676'&'&#"+7326?!67632хP &\FF=+'qpz-RT+' VTT[xVo77#WVyv\pb./F&{"6&'&#"!!67632;#"'&7 'UMDl' VTUZ;<,U-uBG&-HWK`b./wxj/.v ` !!! !I}`uubo{ !7632#"'"!36'&!#3276-jmojniN7!!7#"'&''zL^KK2 )d$"?bdv<87.5,,! >&ln`45+327676767!!7#"'&'b'zL^KK2$!>bev<77/5,,! >#9/u`45V@`%327676767!;#"'&71?#"'&'F'=&66n\67lb)`45!V{.#"!!67632n(wN\ML2'-&"&lnR `45{V{!.#";#"'&7!67632n(wN\ML2'\g&,xEJ0&"&ln%77lp7`45`37!676;#"!,d+gj,T+,g,QP! K `37!6'&+732!,~gV,HK+d,K !PQ`+!&'&'&+!! 32?676'&'&'&#(0s$"$Vlhi"US0>W/ 9  %A- m WF`MLtDC_(:% ` "#327676'&'32767!#!!O4O[-."!%K313kF;;Bm++|}f HI Ga/0!GHoKJ`|Vi{8&'&#"#"';#"'&732676'&/.76$32i2GRSXb78  P;:"?@d',yEF12V^`^gu Nz  _[\=48P2&TUVWS)7lk7<98"&X7!#!732767>;#"0gq+c34'+Ccl78ʜ@X7!##!73267!7!!7!7>;#"3U1fo+bhM&3,)'+B9r&JblnNʜ0AX!6'&+732;!"'&E B+'c+FA@}87lcX7&(%;++5$!3>;#""#"6763̋)8t,\q2;93'+@ 5Hi >a2Yl ʜ@L56T:(`!7!6'&+732!!z>,~gV,HJ*d,>>K !PQV!!;#"'&7!7!>+T,KH++>DK !PQ>C`!7#"&?#73!!!3)3276U 0hz( R&R_&_:_&_H&0NP:0J]f/yhF;V`&327676'&'!##"76767#7!fO.A((^_IJ27&@+#20 KW\&ELQskXWWXksTH;l:pA`$#'&77#73! ?27676'&'7,6!1oCC,=,%C=!&`VO6!;lyx:yƩ 4aXksTH`#!!>iEf`jn` !###!!d`676;#"!!fZ`av+wZ00A!s~ٞIHo<)`!!!OQ\\`Yx(V]`!!;#"'&7!7!,/N"d&+xEB$,``77lb`%3276'&!367632+'67!7!'& --1.CM1; -9G`v[0@78H`! !7!!"'&'32676'&+M+-!Ik73+gdd_9W[Z]AA9 !qfst%)6jcf54 H` .&'&23676!7!7#7#$!26'&+j c8*+- Hk82+ i=9tm3A}A U4X9 !qfs.( #-[H*4V327676'&#"67636!PTdB&CHLJN:JMLؔ])-n`rLG`Br+*#Ӆ^!&'&76762&'&#"3q`Hp-)أFE>:>BDHo@$8 ^ׅ#+rB`GLr!'"'&'327676'&#`B`Hp-)أFE>:>BDHo@$83^ׅ#*+rB`GLrQV#%32767#'&76762&'&#"&CHMJN:KLMؔ]**أEF=:>BCHoA`Br+#Ӆaׅ#+rC_6 *32676&#"67632 '&67632"'&EDCyxvyhj1䂁13. +)1< Rb6^^aa5'''/-6L&& ]` &27676'&+27>&+#!!2vH,+ !!J&R^44T^( ^)) cc4520Lf2QHIjOP`GFR:97ey.% !";#""'&76767&'&767676 HGF.< 4!J*@/E 42b_]IF~.,~~M@Mkl%?H "J7#"-VWbLHIGgVV=t,+%#"'&7632676;#".#"3276?#7!Xhiikpq322ioy,'dg7HPbc y'##+e2eln10YX  ` !!!!!!FOUOFZZ`k1W$!!3+5$)3!7!&#"?676UB%ب(9t0Voҽ;93,Ni ^kb3+VSk ]L@6:>BDHpA$8TD&%%&p^ׅ#+rB`GLr¾ %(26&"73!!!+7#"'&7632?re?r3tU,a* 066=s!"67_^w6!"}\\D\SL`ۦa11/.~@ 4> !#7#"76323!#"'&'32676'&+26&"B /m+rs>:<6:1357Sm##XeR?re?r2{ab4/.]SL9 !qfst%)6jcf54\\D\9<%3276'&26&"73!367632+'67!+7#"'&7632?re?r2tU,$0Y/y4< z  /66=s"!68]_w6!#}9Gi\\D\SL`v[0@78a11/.~g/I&'&#"#"'&'#"'&7#733367632732676'&/&'&767#32'.05;$# u.Y"UU<<<9%'+d,>>% 2X965/778>K L-^ g3=48H:'SUVW"(PQ>½K3ZH7<98"%RQ0*K !X &.37676;#"+732767#"'&7#73#3>'AB+(& ,/DL~+;#%'+d,>g3NNN0Ab+bl7.bPQ>CK !s3<G367632&'&#"763#"'"''67&'&'#"'&7#733276#"#;&76F=(w6..)4)(1V ae,<>x) ''%+d,= X N2g3'&)#,7Gr]G&A#(PQ>J9K,K !~X3;>32+7327676'&"####737676;#"Ϯ!%n?`)1KK~H+<$#} ^*)}Ϯ,'BA+(&b]fikTkl77z45GG~NNN0?&'&#"#"'&'#"&7#7!;7327676'&/&'&7>321&0/5;#$ u.Y!UU=<<:}D0,b ;0l9>%& K-] 966=48H:'SUVW#)`77H7:98"%RQ#7!;!7!!!7#"&,b ;*,a*}D`77`3 333# #333# #3$Ϳ&%C$Ϳ%&sPTQ PTQ" %#!#!#!#!?WW9LWW:>)>)J"332767!!#"'&76'&+732MP<:}$v1UTj;=(d&,xEB^Vy44GG|\]33ij:7lcV;)3327673;#"'&?#"'&76'&+732V#>@22},P,a531,U-+(P,a31^VyhGF}˄nlk}]fij:7le#6&#"#367632OK !23%$FB65De)(3i~D:'(Fh5;<#6&#"#676;#"67632OK !23$%FwGEM@@65DeQ3i~D:'(Fa<=~>JL4vj+732767#7!7#73 ? ]z&ox~J~r&'&#"#367632'(1>-3 8z%?>L,"b !<>tb6 32767>73#7#"'&';'(1>-3 8z%?>L,"J !<>!b6 "32767>73#;#"'&?#"&' '&2;1/  8z@N.+%~M&GI #<>!8~=7{J6: "#327676'&'3276?3#!322:S0  {+%&)E NOz()|)7((?Y**t1 333# #=F"+7326?33q7<=LL9=(UYY((}">!bitCf8@4G910KSX9Y"!35׬~q!#R94׭~$Y27>'H#x7-.6vDC.cdvt'&l&'STTSr0"'&6763"!vDB.cdv6..tSTTS'&l&'e327676'&#"767632#6}>) -g.001 010ib=iEq68@+'6%@  vJyQ5_#&'&767632&'&#"3P6i1I[i,-( )*,.gF( $}8d5QyJv  @%6'+@PFf6@ 991290K TKT[X@878Y3#'#󷰘ff6@ 991<90K TKT[X@878Y#373F򶮙x@1@0#ll+XB[@1@0#ll+Mj&.`!!e|[`!C|&Y`aL&`bLj 733##7#7,,",,"w[!7!9>"մ`FR;1 :@ 2 210K TK T[X@878Y4632#"&732654&#"ϦwvvwM76MN57Mvvvv7LM66MMo#@   2 91/90!33267#"&546K04*%P-1\+ctNPJ!) MB<yh9@     99991999999990K TK T[X@878YK TX@878Y@'           ]'&'&#"#>3232673#"&1*"4 _"=.3$&4 }e#AT%!E<$'C>uf2%7;!"'&7""i#:/ ls87o[ 7673733276r)$١Ya toق2SFEE[cn#7!;#"'&^zw(?ʆ./~J>~=<'.#"#"&'732676&/.7>32.j8?F4`OAA8z sufL@ 1<20@/,,,,&87663146LLJJBB]!#3#fxff#VVD';f###yfxx` &qr$+F>32#654'&""˜H7(+rL;b"@#%KEa!32$!727#03xyz$#3$7#0yy30z9f!#Af'C1'vy6#7373#%#lo-33##۾$%$dCn!7!#%+lE7326764'3#"&:A'= tV5bR2&;1'M(7^)s{6- 9 "&54763"]XbL]%Fo?wS?:%PK73733%#$%N#7!##n%+%#2 #73733##%#$%$@G7!A$+$GXi %+73276?!P0qoy,'b63#+ml75}V ?!;#"'&# b',yEE)}}57lm28(!9910@ //??]!!$0B2(E@99991<20@////????]]3#%3#00  1  0#"&5476322654#"cL\]XbL\]Y%FI%EwS@o?wS?o;#Q:%P6:!#o|%@    291/90!#"&'732654&'4$$4f0+R#DI0\,cv 62E7otj&#VVD &#7##I$$Iv@#"'#"'&73276732767/5@k0Kj  LP(LG*@FF0:mI;@ 991<90#373񷲚x@ 9912903#'#J񷲜mb> @  1<990332673#"&5bXLQuƘ =HME(@#454'&"#>32ȍ(+q"˝G7@$%JFL;b 7n@     99991999999990@'           ]'&'&#"#>3232673#"&1*"4 _"=.3$&4 }e#AR%!E<$'C>Mj&9910!!rw%ڼB6+#"'&'.#"7>32326-WOZg Gb3NV,ZS5`D j]F+<73 ":?=6 7=x7!&&x7!&&'!ttuwyrM'N?rs  2654#72#!%FI]YcL];#Qo>wS@ n3373 I$$Iv&!7#ppD,,=n@67632632#6'"#6'"/5@k0Kj  LP(LG*@FF0:mH '7'77Nlhilh),!#732676'&/&'&7>3#"9F " ;D'RRz .!RSz#: &$;1rj $#4'& #6%6k9  3OG$%%$GN(tCf3ġtVH%# H{Vs #"?3;cX$3VhL'9fv;('pjf&-yf&f&f&f&tNf&;xf&O(&$%zH)! qi)_/=( =+[u3 "326&! ! 22 q449qr449JVJJ\  xx,X.3!!!iq3+ 01 )!!!!!A2A2o!2=2jT3  !! !!22>3227N<[u%-67676/3!37&'&7676?#!aE5Mp))jx32wNo))jx32U'^A  Uk? HJllKFFKllJH=?kUq?;3!37&'&!!6767!x32wHkKC'C1% '[1C'CK|+~[##[ą+x'3&7323!>76&#"! c7%=^=%))4a{ *Cgf* "C4'm8nȿm O֦Mk' /uNk' /u5f&df&eVf&Of&(& 5y(7!;#"'&''&'&767676'"79DR,fA<1=˴NR6=AMK!"*'GD#lT =$+9xУ[[RMBV!%!! 7676'&Ea,Y7Yo-Da.++9(-EI8    AV`!!&'&+732)})SSW $ *.PmO^(vVH JY8|!%/&767632&'&'&#"32#"76"326&cRs* Y%WQhA( j53Mi@Tij@T)?NM ",f=KJdRtB"3276767#"'&76767&'&767632&'&'&#";xM9 -;rthf+ k4nhtt _^<<W^s* Z$WQi@((4g+/ )A!!*%VVyKK@?dNM "* !V#727>'&# !7!:1DMmd,F;g[1),l,T MX[TL&DeVw{!6&#"!!>32v0NOw}#!1jyTzi~`]fao(6'&"2767  ]YB2,VD0v$ƐlƐwppvvRQO`;#"'&7#7hT:,NJI+d,` J!!PNA`oIz!!'&'&+732Ȳ`k !!^p-P_<q-wJJXT{`w`67676'&'!!! Jf <34&ޑW2'Z|u4@bY|rNįt`pV$! %$67#7! #727>'&xP;4,l,1%+.!0CMmd,F0A*R LX[TL&Xy}R`#3267#"&'.?!!#7,h%F"%.f7Ok z㮢,`? " VX#sV{  #"&'!&"2"68Z}#rܾt*?H?K{]^SDV}$%#727>'&# '&!2.#"ҌDLmd,E;_s56cZJ45R! <1LX[TL&9*,7;WG,`#"676)327676'&ș(551(,G/N TikMN 6=8\]^r'`!!;#"'&7!,gT:,NJI+d`K !PN`!"'&7#7!32767654'!2JI+d,ɓ9gZ"7=#) :PN J!!7gLGةHVj!2!$76676'&HU:caRRw`?,;;.wL^&7)L4 j{4V@BsSV}dxaM>V`'!&'&+732!;#"'&L&i !O-ueXF.&:)h !P-vgVGr6LJ<mLJ>sV$`%6!!$!!4f$ghdSSzfg$f8^$d 3V@ O@`3676!#"'#"'&7! 2UH%g;eTE\gH[86*CqSSq6OH1&j1&j Xf&f& @f& oV$ # 7676!"'&7607676"&WxsI]5cLC@n)l!?F-k<`zt ~x^m$ $6'&'&'&%6#"'&3676 #T A@CN^XSTujH*<,_zEf/z xGCcX}վUjNk!6''&676'&wJK'hh-Cj}3Qk@1qS "40z7/cT.f&k' u8V'67!!&'&6%67676'&#jS$SPw5/iQQPx50,%Y* 2z)&W * j#U mi$a mE+_q!v+\u!`#7!#'$7%!767'K,,J.Q 7e9YD5%F?JMF=$3- t `/'7&76776767676#"7677632gᯪ-iDNKF]D,]{ BUGC]gO\ 20R[Fk{ZsB="20BPZPa|blV "326& !&'&! q54:qr459QQA_JKUKIt  `*~xƇV{ "326& !&'&32i@Tij@TQQHniW45pJha#j=iZ9 %2#727>'&#"76)!"@oNK!md,F;IIƪ|3Yk11sfb\SL%xyǬkIV`""#727>'&# '&76)ݛP^! ;/mCMnd,F<^t66,M[\ILX[TL&/)UV"%67632.#"!!#"&'73276yn2.&J'QAU7czc<;7=P 3 '&7 6'&# A2^N5]/:1H]H3 !"&@?L11sLY@KU.IItU+HFAAFH$$rr$&.y9'yE2k&J Xuk&J XuX -%+732676'&/"!#7!!6?676321oo+bhZ])GM% :Z((d15< T$=)+klnz3%  D1C0$ 5izk' uH| 7# 547!2& !d)@I.z@y.<2gs͇HCuyH6,k&; Xu-a"%32>4&+# +732>!32#% Q%#Fw%0 r\E^Shqn;lacQZѰ+o9 %326764&+!#3!332#%Q%"nBnqn .la|)cQZh9:D $!!6?67632!6'&/"!# (d15< T$=)ob])GM% :Z0$ 5iz3%  D1CXk' uOk&M u(Fk& X 3!!!!!"'q'>>/+B$%32>&+!!3 )gy/by2:n5`kbb'%z!!"2/x!!3267!3#! qd; Pq>~> ZFU1K/B(|V#!3!##'yffEV5pEE*v_  d` !!!"s+==k& MX.u +3267!!h#Fw\3 _vN2SfѰ+ aoM+ 0+=2j!!!!+/3&7(F+3276?!!\u2P:K=S1s1/jl#476?!#7&$>4&'.C̕/͔tAw* r\KQsff͒LS4w(xq?; #!!!3!""'q'q>//B !#"&7!3267ݕ6‡)g#YkG{QVI3V )33333"..2)333333#bz"Bq000|l %326764&+#7!3 !) Q)U)!q5-av,fQZD~ ,0sbOn#i08+N֐^m!/u{D$K!,46?6767676%67632 $26764&"$12~X3&E)0w==5%(BӜ BӜL ,IX@  #ofLX G b` !3!2!'327654&+7327654&+ ^E?"}AQ`}+,7<`|jGy_#&Y15>#3qj`!!C+{``3267!3#!!!JcLEO{c8X8 (VU{FL}H-`#!3!##'_CC wI`;;0WWVJz1zU4M{-?3267654&+7327654#"7>32#"&4.Z{rF+TN]랤+Z|eKKaѭs,1(T =0 Th4O@p}qL\L+` 3!!!+##݋`55+F&mA``+732>7!!}&(>_/ d^FRQݯPI` !!## #`31`/`q7s?` !!!!!ee#J=J``}Xy}R?`!!!ݯï`{`V{S{F`!!!7+̯ݯ+`{X`\=V%.547733>764&'0M5GOO4MJ!9CK!9DVMZ.gN[s7P}P}`[m` 3!!!3##=#c8`{{(a;!!#"'&'&7!M  0rk_"Qkv'<+=! 'e[3` )33333𯲯𯲯`{{`#333333#𯲯𯲯Vc8`{{{ik`3#7!32#'327654&+,RԷ"TIn;KTnIZt&+&\/4` 327654&#32#!!222;KT2bRhO"%&\/4ZVAt&+``` 327654&#32#!2{n;KTnRԷ",&\/4Zt&+`F6{"'3 7!7!&#"63 Ł5n^#+駚4tO5V ur TkKX>{:"32767676765&'&'&!36767632#"'&'&7#)N-  #*- " %Tj7!32+22;KT2v&(>,/ d^FRVR "&\/4PIZt&+` 327654&#!#3!332#22;KT2eeJ+JR "&\/4!`}Zt&+(3#73!!!>3 !654#"(+U#U+K:1jI@ rNw8L]fCTJ;,zAf&vo+f&C<mXF&xZ` 3!!!!#Z#>#88`{]^%32676&+737!!!3 ))a)5)'C))5/-avaѠdtc32#!#73!!! 32>&#aD^+J#J+n\2Inu_n})=;>uabo{!!!"<n/2S!!3%N=g{`:z )#73!!!!ف.s2@/WMHj` !#73!!!YY+V++h+6X{!2+732676'&+!!!5~Z&=)_1+bhRW"25inz31RX`32+73276?6'&+!!!~==)$1no+qb43KxO)ihӶjl77|14k`|V#!3!3###'zeeD=Lq> 4qEE)v_  Bd-`#!3!3###'_CC RSc8'I`;;0WWV%z1zUo`&L4oM{&ltX3!#!!!門M _ 'l3\p -p`%3!#!!!Qh?#wS%JcD `T`}^%t!#!!!!!3!"'nqn'h9/p`)!!!!!!!gg#J=J#!h`}qN #3!!!#!C"p;pD0ŃB"]&` #3!!!#!LCL8)ee`zro&Vuo{&vHJt )!!!!!22%1pU ` )!7!!!!ݱ))̈!hBN<V`!!!4n4YY`)7R!!3#!#37>> 3333 Xw@@ZV`!!3#!7#7374n4 +""+ `)>۰>t3!#! !!SM I1m1p%!7S`%3!#! !!0h?5VyVDyHNe!3!670767072!6'&'&#"e.#/5?:T$=)zm]GM2:ef2$ 5i1z3% C;`K,|Vk& K-F&kXT !!!2+7327676'&#-b"'r@NUM(<(_0po+e14R(EN5ool77~/1Xp`!!!32+73276?6'&#"O%Jc ==)$1no+qb43Kk`}%ihӶjl77|14X%+73267!!!!!0ro+bhy"'nqn'+nlnmh9X `%+73267!!!!!71no+qbh_g#J=J#+jln`},t#"'&'&7!3276767!!!wb15<:T$=)f#Y]GM2:Q#Mۀ0$ 5i3z3% Ct(a%7#"'&'&7!;!!!+(kv'<+B  0rk_"?he[3=! 'j !j.k& E/uF&enk' 3uE/u1&jej{k& JLF&j[uQSz{[uk' 3uSz1&j|Vk' uK-1&jbk`k' uL41&j_lyH`6N&M +&mk&M u+&jm=k' 3uSXy1&js[u)bo{[uk' 3ubo1&jk' ubF1&j9(FN&X X&x(Fk' 9uXX1&jx(Fk& XXf&xk' u\(1'j|t{ )!!!!"2%3p` )!!!!)!h`B:k' Mu`1&j<RdRt=4DV}T)Z:Z-`Z#!327.'7>7!&'#".''Yf% $'#@2B:va'p(L" 9Rx "32C!76&"!!!>  X̊B3Pڶe aCppbۍCC7!#!#".>  6&";3PP炭]6b a4X̊ +I3bGΉECۙVpp`8[@#7> 3#!6&"!De a43PX̊ۍCCۙbpp) .7!!!26?!:ea'H"JX̊''ۍCCۙpp"#!>76&#"!7!2!(?'R}#Oy:bN: ;Gg Frh2KG^苵%NyT<1@WE4 #!!!!!"'I26u?3!76&"!!>  X̊2e aCpp ۍCC(53#".>;76."#> 32>?#"uW2W'StyF.SqQk 4cpP1 k i0;,# 'k?PCkp:2ks8CiF%%FiCۍCCViZ/J5`!!3# .>;"267'P34eb6aȂ3VD. X̊BbۍCEЍG#@[8`ppVW!76&#"!!>32A Yfd~"'N3n?a1CpykzpC3!!#'w3/2>73#".67##3  0% >ajk}9T!H`58K..K8_|t77t|$,):33>32.#"#".7>7#2>76.'0.BQ*]TB>APZ,0[,udhꚛ` R: XmF)C:T?eF&  4&dֈd]o^f_[,0^\Zg<Tx!#".7!3267N3o>am'|Yfc5`Cۙ1pxl%.7>76$7!#=1V< q {TG{f#*%F4@M/BdROD\GJLHA - ?%267.#".'#".7>32>76."!7>2T1^9(T."0 j4EU/&>/aeOzLKnOXE0L,Pl`Q?"޷u% /#()$$)M:8z@0Y)KB*RyPO}V-;5NckX% Qh?C7V> 3!6&"!De a2GX̊ۍCCۙVpp%>3"!".7>7673!6jLT3&\u^*CkF)@W5eiNP=;,EpP+?s_AeH'ctDy a(#B!# .7!2672e`'X̊VۍCCۙpp!8#".?!32>76&#!!2>76&"!>2e`׊l$*.O8?_B) bp3[0H4 LhcҢi'mP1@"bML\ )L:#$=S0_q1@#TOTUcm:;n_m&I^my .7#!26?!ea2X̊''ۍCCۙpp#32>?!".7>7.+32%.#"f,PlaQ?'"t$!j877/ ]3AFG< $%8j\F^gV%#ThBF|Η]    ~ J!6&"!> X̊ڶe aRppۍCCM$6&#"!7!232>}Qt:bN9 ;Gid{7; < J&8l^I%NyT<1@S⏃қeBY6| Q>##7>32#6&'evk疡d 7]AxhݞXX݄i@2>7>."267676&#".67>2!!!>gE -#PnK+ *3^-"CL]zH   0KjИh< Lu`3&?'Y%Ea<'XWQ!Fg?7Sc,*!!PDFS 7GNN#9~znT1.SsUT2K3#!6&"!> @43PX̊ڶe abppۍCC1!267! .F'X̊'ea'ppRۍCC/3!#".?!32673GN3o>` 'Yfc~/CۙCpxl-=32>76.7>32!6.#"#".7a4L*8V;!3WorhI%=Vs[c 3D A8* -WrwnMaw{p&K\1"=V3.?2,5Dc^+`]VA&@nQ3=! $;-4H7.6Gge`yDE}k!76&"!>  X̊ڶe aCppۍCC!G"32>76.2#".?!32>76.#!3.7>3I0>40H3>pd!gX6@ `׊l$*.O8?_C( 0O83_/="#>//>#">.=p_^(Sah.bML\ )L:#$=S0*K8!*Y)^o>#!!!!"'I26 -!>7>.'.6>?!#4J[:%HHJ\;%II=H6UK6WÈw+Zdeb23aedZ+]ܜVzzV].> #"&'!!!7#"32>76.}Zb\Ȁ+a-,2c2YtP6 #F98O6! "d{EG|cd|FQ6L./M86N0.M7=2 .8%>.'#.7>; .673"3J[8""KIYP?jnHN-Eet:IH$0,$,VzN%)JmHNrK$M˂WiJ''JiWWV, " rbZIu!#<95حf !GdfxQ~%#>7>73S|RKiKVkC 4W~#2(L`?"+9'A~lP5f!fx@)#>32#".7332>76.#"UuQg`$ >bP1^G(0*4H)ScYgH&7`K8hR19^F %"0 7`3!9!#z+`+%#"'&32>732>73#7#"./rK$$={  2&  2% &|E!<-pHCde;w1@Q//Q>1>Q//Q@tBM%3V_{%!!!>32!76&#"!,|R+#!2jy)6-1NOvQV ^ezi`QV{*%#!#".7>327!32>76.#"t,mRqdL a`9!$"B0/VF6$B/0TF5VHMێۗOèPQZ/0YQQY0/Z VO{%#!6&#"!!>32O,R1NOv}#!2jy)bVTzi`^e F!26?!!7#"&7!,rY/t/% 1jy)%U`5yh0^eLQV{(!#".7>327! 32>76.#"!+oqdL a`9!$"B0/VF6$B/0TF5HMێۗOèQZ/0YQQY0/Z^ )!!!!//%UC,LbV_{!!>32!6&#"+Z+#!2jy)݅1NOv ^e)ziV{,=>.#"!!>23##".7>332>?#"N SQ'WN< +#!UgqjcS?!EM(r>HlC Kfx<1&#."f60$/=}d@%Ec? .I28Zpf)'JiANsK%5)BkM)=(#"2>7".67>;!39WA-DrYB-fB4Me}^:`΅U$Um,/W~[14\KRMmP,,PmLs̚ZLVI!6&#"!!>32I݄1NOv#u1jx)zi^eV`!!,-# V1#3>322>73#7#".67>."~b*f63/&<  2% &|E3/&<  2% V9.7_d>Q//Q@tBM7_d>>Q/.Q@., !##".7>72>76./#":|Q,[n~}eAaSc&Kt`J4 0"*mXG^؛VV؃Ze P[16^I>`L<V!267!!#"&%0t}%s1jyyhP^e7\KD-?)7#".>7.7>?!326?2>?>.' CSa5ax:"R|b%0K]-V3lcRd V1{!6&#"!!>32+]1NOv}#!2jy)Tzi`^e`\*=!7#".7>7#73>32.#"326?6.''r 1j^u;T9i,C\ !# 1'$ )PMI"b_"e ## Ot6Y@7LK^eL]umXi:  0C(cE_>#%SI6hp`XpX``%#!732>7!Oti,1J6% %+~p39]B5#7!3267!!7#"&+1NOv}#!2jyzi^e6Vyy523!!".7>7>76.#"'>lh& ":Twf8Q5,|0J/ GiGbb8  )@+%KD9*qy 7\{QoV-!".7>7.7>?32673~,*D- 4L_kq71T: LDO)= 39#Z7AQpT5;U53yz2)E_:?:{@"G1?8xf$*#RV`+#".5#"'&32>732>73d&j6!<-0qK$$={  2&  2% V6:%3HCde;w1@Q//Q>1>Q//Q@IVy.H3!!".7>7>76.7>32%">76.X|1Q:#',|1J/*=M+(* 2;/ 7Qfu@hk),LE4 #?^C, %A`c'G=1"9J'$RVT$!G&1J??OfG7gYI47jX/>")@5/18#(17WG;64%G7"B{&"!!>32!!7>7>.-H8' }#!BTc7\y@ .#"#7#"&7332>73>32  $ &|EE*  $ &|EF+>Q/)H8tBM1>Q/)G8tBMVq{!6&#"!!>32q݅1NOv,#!2jy)zi ^e%XJO`%!!+%`)V+!#>.#"##"&7332>73>32  #" g(l6E*   "# i%o6F+>Q/.Q@E>1>Q/)G8ACV{+%!!!7#73!632#"6."2>8,i m,m$!`J ad "B`UG5$B`TFDDOۋۖNZ/0YQQY0/ZXy}RV#*5#.'7".>;"3>.'>URRA{lX2:C'}]QMziV}NLP @KP  E9|;YA+A|Θ[Y'?V45I/2Z~Z2FNr.<<)?fI- ;[w:%!7#"&7!267!:, 1jx)%/t}%^eyhZv'!!!!)MG@MH'%%Pc8ߎvv{%#"767"76263 !6#"(('H&&ʑ\?ۚfHpQQuY6)"276$632&# 7%7676;  @2 P{iS028A%tqu;;=MkČG:.j)N~67&76$ !6&$'632NB!/6N:pp"Zk/&':KG' p]6OA$,Ll^Ȩc >3 &7%327'&32769fy3e`,x12 @ ?A quxs  A@@?W &"32676&  76'#7632#"!76r$# %!E+:|08Mt$&ڸ"=y!$` %"%:~6Yh>{aKKO 5"32676&"76767$326767! 76%7676$" %"S*=+e |Q/FK#e01m7$o %"%Ƀ@<@BDkQcC;;eo">!Hz 3267! &767&7!2#"'3Qk"n%n9! 6FW f9Ob2O6hKM0{$"26#"76$76gv! J!(''*(kyx *7"32676&"&#"2#"7!23263 !6#"$" %"|-T&+)C= o,[\4@(+*gP]##A)is@0[2JFfHMMM|"267&76327!%$g0%'%%!)%V &xLB&"267&76327!%$׀h0%'$% *%W &xhB0 A"32676&!"32676&"' &76323273327&76673$" %"$" %!HEtzv,"'YTX<<XTY!)М $1(a %"% %"%JJbߩ44ˆ+,@ *"32676&#"' &763232733273g$# %!Et{v,"'YTX<<XT %"%3JJbߩ44ˆ} ,"32676&2?33273%&'# 67&76$" %"HR!--"ZL!>Œ6.=/ 3 -  %"%㩪,-=%ۂP! ,"32676&2?33273%&'# 67&76m$# %!IS!-.!ZL!>Ē5.>. 2 .! %"%㩪,-=%ۂP`!327!!"'#"763227"%%"?T>u `,888   32767! &767&7!2#"'3)jPP#%:! 6FW f:ObZYO6hKM- )"32676&6%$7! &#%$&763232$" $")19(ϱϼ ,:C.)80h $ $ ,^$RE?`{< +7"32676& ! '&'&7 3676#"%6%" $"Il1'K %,/M$-)S&W !$ $UzƜ1o5{"326! %,76323276#"@@$ƾ$Cq7#e""w!TV@?:u@@@@LBE>/>icb$ #+"32676&%$ %&'&76327676$" %!<<<;z1z"):3c4VS %"$36H9Gt6cafI 1"32676&$76'$6!2327#"&#"%$&76$# %! %?4ph@ͺbj ;O,+= %"%ŇD0tHG_)Rq}] ? ''&'$! #"'"32?6T ɍ@Ex "$837 YQs,%N*(mEsJ?'bb}^, '&767&'07r$63 l Mӭ8@P5)C21s!6#"! ܎/N?tF6T  Au 'r/c%$ 7 76 $?IZ_E:$ 0~9e}%$ 3!7 76 %?I[`G%f% #G~9fP3}%$ 7 76#'? $?IZ_E:$ $-$-0~9e}####A%$ 3!7 76#'? $?H[`F$f$ #$-$-G~9fP3}####-)&776'&762'62 30 .9&){KYj,.--TbڂfxY&775667&763273% ><XM|@@*ۺ f@TwdD:0;QF, &'6767676;`n ,,,sWf-2G)4G^&/'&7767%276Te/'E~QFGI! v*EW}u"E#IK|X!767&76$7673( "Ij1k%.Z?^)47676367673!#7676'&'&'&3&76'$B?2! UfYGgy% ="!BEam%!9>TT WK{;PH9;Ed>Gb*gW/A67 *ut7.,U 3733##7#$**%**zۺULllPjj%&76776&)-,. >C B@566>k,! &547>73267654&'3.547!'b2 ?0" Lik6FU #)CōnZ*9%654' ;2! 47%>76.#".67! 3 5 R{N^6\-% .@$:0!9xF?O.&-%Hn]=G /D2# "/--/"$(Kt/e6m e654.+"32>7>7.5467>32632#".5476&+"!6.#"" a9 !%!;-# K !)-47 .]=YcO Sx`@]C(Q CH OqxuV$0BH&"=/ m$889\ 52"=U3_.,#C0'iEE3mnEWr4&@SX,/) )9/'jiI;6BI$-'+#n*2%! 47>73267654&#".67!3 1R - n_ ]sg{}DdXu   L f<07JG_*)*BV_`%;XQW8'Z"76;(4=n*K! &547>7326?654&+732>7654.#".547! /N #-TgmGX?)?)I:*  ,F.` G {i^7r1:=A?S&P# @\UX\4I/H275KN b09+ m2;`4#q;#*!N&D654&'#">654.#"326%! &547>7.547! %B?D%?9T\ *M63[J7 .N5iC3~eI8DX ;/Z!&9SR$%A\?4 IU,-UzM7.IN(ޑGU3*t8!%d*1?n-*bA >6.+"3267%#>76&+"#"&54?!263 H D.$<.24B/IjKCN.B_ :DaWzaBWp'0@$(7654.#"#.54676!2r/JmOJZ6 'G18`M6 '-<@0< f!Tz9LB90 FQ,+QuJAq3QL8xo0bTE%Fn=!# &547>732>?654&+73267654.#7 0) ,8;  Xq1R@+ &X+UY)ii,wvfB Ź4=$RK<|'#Ec/H3/3(.\DK 7.5476$! .7>7>7654.4CxD8# J~m.]7X"-, A,    +  2QUZP\d֚Ugn3>~B]f͏>H?l- ! # &=0H'Q>+#EA $9654.#"326%# 47>327654&#!76$3  ,O45XI4 Pqa;a}AéFf,YoUj  F3 FFEnO@2Nxkl{BwIWw6*63!!">3 Bmi @ikFf# +(2 +9rFE6IE7HґBP4A37H >!>?654&+"#6&/"!.546?632>32 '>Z?BV8 0 -?LM7*C/ 2.'- 03Z*^2}:uuu;:mhb/-"!DlY[0A$Zn:! 47>733267654&+73267654&'$547!#8^6  1)Wra Ql{'Npro%Si]_9L6?349 !524!$E`0(IrML,= 5C'"])77 7654%7%1T &OdG!>76.+"#6.+"!.546?6%2>32y 9XDH\6 8@NP  E$2 27./  0 t?e.oLp7DfE' Ipg%1%/./:")5Q. qGU ?2K{(O247NR~)-0 /9+m?'3 654&'#>73! 547e$iQl|uy0Ot_ HӺLb r[>12iDh"`r+qHm@L%# 47>73276.#"#7>54.#"#".'32>32>326-H *:F$# \p."- , !#4+($2&"n %(1<#5<1Am/C5?)WRGm3^5"E`9T76G &$,% )0( ' C77ClAO9n*a6&/"32>7+".547;2>?#?##"# 4?>32632zo!6,*#@]v"jl 6Q1.VE.-Y$ZY 6Aa[ )Dgbc /@J&3! ")8" &"'5AiZ(.du/19(1!(:&lm7ENa,$!EgDS6`RD:A3n*=%! 47>732>7>54.#".67!2>7!b( ,?,"% Yv1XH66UICY  @G@3  =fI3<#@?A#A3c1#D_"GkIwn@k=8;,(&W"<:@'H 657!9-m*O>7&54?632>32>7654.+"#6&+"%654.'&#"d!? 1>].rFl )?U9/I3 @WQQ B& + Y!.&"iK&2 35L$##$tRfB>me_13UKF%J'^1(>8]GV؅ :/" 9<$n 4! 47!>?#".547>7!3267!1M 5 7 d~1(?99!k ,LuU7IfF+ )H3q]%uP7A+#]_' /kw'=KBrjg6=_L?:9!8' 63Z]+J654.#"326%! &547>3654&#"#764.#"!!2>3  0Q64UE2 7\>V|?`dˁCb$37%, "* RZq3R2 >-C?>iQ7.NN'yGSr430( = np 5Q6UOQS.6N7654.#">3!67654.#"!>32(@(BX+8732>7654+7327654&'&547!gRM2`ߘ8TtM7Q=* QoJ.Y(a'3!&VEFy1@}6~?p\NbyAC`$O|X&e· 4-'. >"e7#"654.#"32>%! 47!2,N6A7]J552Sk$D=0nO; LR&F5!HpKjHU$ -!: (X32>7654.#">32>32!>54.#"#76.#">72!".547T &B+>dL4 ,K46YH5HgH)L?. 6Z  $ (%,":3Ch4x]<0HO'!GqPB0 BF?kJqL')=)QSJ`6=( pn 1)+8:z%GVM!JUnV32>7654.#"! 47672>7654&'.'" 547!!3# "1>8* #0>8)YRD  Uh5[E0 AH+,  E Oc#-7" xJ+$211C&0//@\83;l $$=P5O5/`*0V*-4bE# F4.P9}6'*U#654&#"326%!".547!>3 Hhl GlgF5f$m7;267654&'.54?%!%!(K `R$# Kcm;FU?92Q% Wi2VXR3B6?QU2]3& @aio5l'Cv45e)OM>H@.N! 4?!3267654.#!7!2>76.#!7!2>76.'.5473#%? %Lfl  +  +8Q2` )J_2 MA7(h}A3<#@YJR(  P$*( =^!Q&vFRenf! 4767>54&'!327654'#73>764.+732676.+732754'&5473A0R4   &L d1)",  )", )Fw 2A!OH<"^<[cOd#`*$(/$ 5GbP&Jp1m@>| *->7654&#"267.547% 323$' ?dr Jmf}Hn$k> 73!a7 9zj5-D$#C5LaXM8=JYXQNv+n*+% 4?326?654&+73267!%3RH G$GTgil~&r &N,qH`:2;{@Vtj!K`û d=I(,nG%! 47%32>?654.+73>7#"#".5476$!3"32$7RH ' Og4ZF/ 4R6,/\P>;>>{m 'D*kD!~XZN9D*$Dh3O556/0QsH 3cn*).ʼdb~koB)H# 3#"327;7654#"! 47!37#$47! 3#3ʽ E&UJ\ F k*kk0s4xp',{L,3f(.L!o*=654&+"267%!"'#72>76&'#&3$4?!2>3#P# Y d- 7R<$-)+N<* b,:`* ,Q[@1Q!-<5/<M mR*3%! &547%$7654#"'>32327>.'7K, IrB=5&!} --0R&ݿ4<vzU`!Q *9!Hh1;ޅB(!,($u'mE0!3267654.'%!#"'323267!! &547&pZud +SC0ER& \Qd&Z  3>y~,+ Bhe0b>~E=?'& z{706~n8 +67654#"!6?!! 54767.54(V 7eI~=h %aq"(!7Ab}Q^oo)1c5Z-N=+2654&'.#"'67.547%'>7654'"3/Sh^t*<" W #O[o  3'S3'!,|n#,m|N &'  'X3$Yӻ,6/<Cw;l { L32654&#"326?%!>54&#"7>32>32+3267#"&'#"&5467 Jbu:1CP (,8GfUF8A/9Q^ =Yl DA4V/OERt4T~NgU354&#"7>32>32#"&'#"&5467"3267>7>54& (,7Gg VH9@/9R^2\@68DŽSx%0R4D 394D3B'40UYF54O]72((QEKKĀ=<;>1X@JvaZxHA@I*|HQz"IA/{ 32!6&#"4W4 Tji /=񥹹/ #"!326755% Tji /=)9)!2>&#!!#'9yu{~}9`9WI&DX&hB-7373!!2>&#!!#-...|9TVQX>99t;*v&DX&h*\0)7!2>&#!7!2>&#!7!#uA0.}\N{1.{^L}1.`)t=8f!fJo&d((d&np 33#'##j\`I!#3#3!7##3!",']TEDc (3267654&#3267654&#%!2#!1|XS 7T=&|FE .D0zn]jK6C(3F-4 )^GQZ ]<ym 3267654&#'32+h2nt 5n+K{C1:^v>L !!!!!!=Jp#j+D 7!7!7!7!7!+j#pJۑ!7#7!#"&547>32.#"326~"J>J)8i+$\>HH?t 7!#3!730jjN 73267#7!#"&&,p>JQ Ro<|14AGș  33##]@bЕd7Jx]W3!󢺆DN 33## #k)ޢonb 33# #ȐvȐv`` ##3vƢv|`D`  "267654>  &54\Z*Hc*c[Y<)_[=(ۯgEWۯfF 3267654&#'32+#"/LZT7ZD=U7D(/kW${!#'&'&+#!23267654&# pI D:@ vt,VLN 0N'%4 ^DcP#Vf$5>$,Q##7!#s32673#"&54go0V og!e';7654'&#"7>32g\ 1IdF*q?y8)?BJAG9&/: bX ?F)-8*Bni >(R,42767654'&#"367632+32767#"&54.g-. 1J21F*89?xb[Y?B@BI@G_9 20X @fF*bBn54 fR,$ 73#7#"&547>322>54&"z'54@yQ ~8)'&xY&xY]6P2< +Qg +Q I32676&#"26?%!654&#"7>32>32+3267#"&'#"&47 .7A '(0 O+ %<&R,$T4AU#[>eL00$[42](>GY:p1..+*(*01 '&5F v (D! /-0,X9hg,"%"%dvU 654&"2>32#"&'#3&xY'xl-d8~S y@Ty +Qg+R44Q2<76]g3#7#"&547>322>54&"A(h@yQ ~8P&xY'xM]67P2<4 +Qg+R'#"'&547>32!3267'654'&#"aDJM7 כF2 "*`@JtG?YT32#"'&54?!654'&#"32767pDJN6 כG1  )`@JtG?-,T32&'&#";#"32767#"'&547>U&˜:@BIS4CM!:+$BJt+@5GHDC<ICD>XP{$% aX z ,'  3-5HS:#"'&'732767654'&+732767654'&#"767632U&˜:@BIS4CM!:+$BJt+@5GHDC<ICD>XP{$% aX z ,'  3-5HS( ,654&"26#"&'7326?#"&547>3273+tZ)t\:o7/g:NW #bAx[ w=T -ReT"+QeAFC,,R)12.Pim !#7!#3#73ZaI&~~Q 3%3##{ŸI/7w%5%>32#654&"#654&"#3>2H.\"DN ?# NN ?!Ny NU?(&O+>i. 0Em,!/FmsA%+-&#+73267654'&#"#3>32KFCL??F12%$EzjBe(3<<~=Jf ('Gs^49:&>##"3267654'&>32#"'&54B`BC`QӕH2 ӖG3h\$. 4h\$. 4ĘX?^'-X?^'"0732>54&#"767632#"'&#+,6ZuAZ442/9778K6ڣ>42!e|6V W?b&, >32#654&#"ӕz -CB`՘^'-$.Th\9#"&54733267ӕz -CB`՘^'-$.Th\#3>32#"&$654&"2@(i@yP ~8P&xY'xb^67Q1;4 *Qg+QL3#;#"&547#737":'6O8"~*$~K@ )!~432673#7#"'&5454&#!7!#5 MH GOk  h7(&4!.E$Y\(#"'&547326732673#7"'& H.\ DN ?" NN ?! Ny ''U (&8+>ian, 0En+!/FA%T #3f7sdWd4sdTsdV} +"3254&%$!2.#">32#"&'!rRHrP4bV6JV~6`THFjh"do fo--u|SUѲlgtngjA`73!7!!3#!!7!7;%3,,3%*l++l*\Xr!7!;#+732767#"&ި+N=b 1foy+'b6+ь`n9}+dl7,d #367632#"&$654&"2z'63@yQ SU~8P&xY&xYs]6P232.#"3267K3p> ۤ8f/#Z4ZuAZ6c1a',!f^7U !GM 4;27654#"763#*''67&'&5467632&'&#"g""]L4Uk A ~ / 6'nn833/#--4Z:; $ ee@4'$>a'XW 33^%& ..#"3267>7#"&547>32''7'37;B] -AE]u0 ͙y‰'MM-FQF"-Pea7Mf"+/[&*zBA=qAHEB.#"&'73267654&+732676&#"7>32^P ۺ>C4HG[ XtJBaOM{YO: i I2?f2"z&"  PG1IW3###737>;#" bb z*#F7~ ~,qW~T33#+732767#73`^ppMQp'@v?CB,-#"'&547676;#"'&'73267"326?2e v\/<s~g+Q#7!;#"&54t =ǃJ~!1~bF)#7!;+73267#"&54^tw =ABL>:J~!1~x;:~78bF);<3!![ 08>32+732767654'&#"#654'&#"#367632H/\ D8:n>3J   NN Nz &(+*>(&8)?kz8=~J|-Dm* FmsA&.(#"&54733267332673##"&"I.\!DN  # NN !N;O+*>(&N+>jan. 0En,!/F0%+-*#?676'.#"+73276?367632J ,":*,"FEkM4`4549e(>R10U{ rCCK{\7B)@((6&'&#"#367632;#"'&5476/*^'-XYY>](4(? -4-44)3676?#7!#3!737&'&567>54'&%wTq/iD9D9X@q~~q@Ae#X@h~~h@Add^ 4z5#R 4z5#-D&'&#"#"';#"'&54?327676&/&'&54767632-467>"#z3`&QQ() ?L- 7<=;A$%*U1e(ST;:9) /"1a10 !~=&E)5 " -"2[22  +732767676;#"@F> ~=>*y8<~ Iq,+~ $c 3#;+732767&'&547#737 ":'6@DL>!* 8"~*$~x9<~8)@ )!~&#7#"'&54?#733333+3276|/64Ae)445555-12#]4;%=#+mm '!b-3267654'&'7!##"&5476767#7!c1( ;G%n>+##"'&54?#7;?2767654'&'7# a`|Fc+"ee&"( <60 #k!J'$+YXB+D&,D~¯Q#71P$&/(f}3# Zf7s  !!!7!4Ds{y!!;#"'&547!7!e4Ct ?L-td!~=&G&y]!3276'0#"!367632+'67!7! 3D)M1i! (B3_i$y %!7!#"&'73267654&+'4Z/C<ڹA= 9r;Wg K\jz f3|<7 )5!654&#"3267"&547>32CB_DE_ؕi(i(-&7ul~,'5vhkCNlBO &$/ u{&Dk& %/1&dE2&%/2&Ej&%/j&Eok&&&u uof&vV&FHk& 'B1&G2&'B2&Gj&'Bj&Go&'Ho&G&'&G&('}&'H&(C}&'Hok& &(2LoF&'&H2/k& )k& IfN&* 2X&Jk& +;`k& K2&+;2`&KR&+ \;`y&KjHHo'+ko`'K&+;`&K&,7&LXk&. uRk&N u2X&.R2&2NjX&.Rj&2NP2!&2/2 &OP2N& 32N& 4Pj!&2/Mj &O2!&2/ &O k&0 ?uf&Pv k& 01&P2 &02{&Pk& 1;`1&Q2&1;2`{&Qj&1;j`{&Q&1`{&Q=&2'  2X'H&uRr&3 w|Vf&Sk& 3V1&Sk& 51&U2&52{&U2N& Q2&Rj&5Mj{&Uk& 6ZF1&V2&6Z2F{&V2k& &6Z2F1&&Vk& 7k& W2&72&WMj&7Mj&W&7&W-2&8B2`&X&8`&X&8`&X-&8'  2j'H&Xu-T&9 \&Yu2-&92`&Y)Zr&: |Z-o&C Z)Zr&: |Z-o&vH Z)Z`'j5/:Z-&jZ)Zk& :Z-1&Z)2Z&:Z2-`&Zq?k& ;1&[q?k&; u&j[Nk& <X1&\ r&= .|1m&]e2 &=12`&']j &=1j`&];j`&K&WjZ-/&ZsX/&\s k& A8|!2&$/2u{&D2?r& |/2um&e& ' l$/u'B<2k& /2u&q2&(L2}&'Hm&( puL&Hu2r& |L2m&e"2&,27&L=2&2X2y}&R=2r& |X2ym&eIk&b uf&cvIk&b uf&cCIm&b u9&cu2I&b2&c-2&8j2`&Xk&q vuf&rvdk&q vuf&rCdm&q vu9&rud2&qv2&rdNr&< |Xo&C \2N&<2`',\Nm&< 1uX&\u5r&i5r&5r&v5r&5&r&w5:r&5&x5&r&iHr&r&vr&r&w{r&g&xH&dRr&idRr&dr&vdr&d&r&wd:r&cr&iIr&Ir&v;Gr&;r&wrr&heVwr&ieVwr&eVr&veVr&eV&r&weV:r&eV&xeV&1r&icr&r&vr&~r&w6r&6&x&Or&iOr&r&v r&H&r&wO:r&O&xO&cr&iIr&I8r&vTLr&@r&w|r&r&xI&IXyr&iXyr&Xr&vXr&X&r&wX:r&r&ir&I3r&vO[r&Oxr&w0xr&&r& ir& r& vr& &r& w:r& & x& Nr&Nr&Nr&oN&@r& i@r& @r& v@r& @&r& w@:r& @& x@& xr&ixr&b3xr&vO[xr&Oxr&wIxr&Ix&x7x&q5f&C5fdRf&CdfeVwf&CeVfOf&COfXyf&CXff& Cf@f& C@f5Vr&ɜ5Vr&ɜ5Vr&ɜ5Vr&ɜ5V&r&ɜ5V:r&ɜ5V&ɜ5V&ɜVr&hVr&hVr&hVr&hVr&hVr&hV&hV&hCVwr&CVwr&CVr&CVr&CV&r'CV:r'CV&CV&1Vr&hcVr&hVr&hVr&h~Vr&hVr&hV&hV&h@Vr& @Vr&@Vr&@Vr&@V&r&@V:r&@V&@V&Vxr&hVxr&h3Vxr&h[Vxr&hVxr&hVxr&hVx&h7Vx&h5F&5&5Vf&ɜ5Vy&ɜ5Vf&ɜ59&j5V9&`ɜk& N& f&*fV&hri{Vsr!77#7!2"Axyh9u;&jjTTCVwf&!CVw{&CVf&eVw9&jCVw9&of&Dff&&fV&hr'i6H&r'i.n&ijMO`F&OB&OH&O(Oh9&jO&kk& N& f&bf r'@R:r'&jMF& & & (Vr&iVr&9& j& kNk& NN& Nf&tNfr&5s;H&jr;(s`fC@Vf&)@V`& @Vf&@9& j@V9&f&qfxf&{xfVx&h9fvLr7!#7 M"A"ïx9910!!+u8@ 9910!! 1/@ 9910!! 1/@ 9910!! 1/@ 9910!! 1/&BB8@4G910KSX9Y"!35׬~4@4G10KSX9Y"!#95׬o4@4I10KSX9Y"!#:6׬oa#a8s8 d@2     4 G    991<20KSX99Y"!3!35׬5׬~~ `@0     4 G   91<20KSX99Y"!#!#}95׬-95׬o `@0     4 I   91<20KSX99Y"!#!#93լ+:3լoX #!#P8s8A7t8;y Z@/4; 9    9991<20KSXY"!!!!!7!yLL++L}81;{@J   4;;  9      999912<220KSXY"!!!!!!!7!!7!yLN-ZL+JJ+LZ-J}+}` 10467>32#"&'.736HI256743IH426I235624HI447743?!!?qHLo u@;    4I   999991/<<220KSXY"!!!!!!:F'E;Hooo $0<HLp@?J%K+I"L 71= +"%>4C: L(I1KJ11F17@11.11(1/99991/<2229999904632#"&5%"32654&4632#"&%"32654&4632#"&%"32654&%wwwu2IH33JJxvvw4GH33JKޥxwvw3HH33JK'!xwxwK24KK42IxwxG45LM41J+xwxH35LM41J^\1<HWg"2654&46367636762#"/#"/#"&"2654&4632#"&"2765454'&!"2654&#'-@@ZBCגkjIIjiKIӕjhIKihJKhj4GHfJKxvvwh,A?[! #?,  [AB^\H35LM41J}xTSTSwxSTSTNG45LM41J}xwxK24K&$13$&& )+ &K42Ia`l!a`u`('D`&'w`$#!$`u`'B1`'&w1m#@ /91907m2)w#=w#@ /9190 7%'-B#wv'4$x$(!6763267676'&!}bm4mjje`a ./piQ 3* ( 7 7S#F 8^^LAB\VC)*= %(+C" M B0? !#3#3!D&z%z%j !73#73#7%z%z%޾tt'  4' 4' ; 2###T%#Ҥ,ԿH農/ #647632"326767654'&#"#"&4767>32J%#J2.L'3/&%&H=4UUg$%95e <e?<7UabH>8TbaxYL+*|RMV@ !3!73#3#mn|R* ~~w _@3 3  3 334      991<290KSXY" 333##7!7Reml#${)%!!67632#"'&'7327654'&#"@%k&SS~DDK<$2=?o^_yvtSL&& !#!o ?u2  -=4&#"326'&'&547632#"'&54767327654'&#"L@P23('AMdJ&&iiPP?@jN++ijRRFG !2F/. !;A-,1:+*B0T'(=gHI78YR99./G}KL78d_CC)"!4,#"!$3732767#"'&547632#"'&327654&#"!$+223oKK"+66?uEDlmOP++LEcb012 7I54@5M33 <=u$>>l``FGytsTL%%188R1<988 !!#5!5!58G!!LGA!!!!ffƅR #.547R#$42P]`ZFR 654&'3#$33QP]`[ #6'&#"#367632OJ 23%%Ey65Df)'3i~D('Gs^4;;/Td!C{dTtdTudCdCd!TdCd Td!Td4d%d)diRfdiRfddddt{ddhTdgdcnhydd ddzdL%dL %!!!!!!#"32.#"3267B#b9=Aú$%As2 1e*E & B/6767#"476$32.#"!>32.#"'*]u7r^^\l1ad3O[Ps 4a#.C&g $N12J=78IDbǭ`e!!!!3#!#741/5!l!l./u"ݹ#!!37#737#737632.#"!!!H!./ )(0SL25~@q?<>(A)¸ Wz|%(=;zWB/#6'&#"#3676323632#?6'&#"#fk" 4AZC6;:AI00lL yaTH1?."C־75``/085cs VH)v75TR!#7#737#73!3!3#3#!#!3' -a&A-B&AVB/-H*!  $ 2644.YK&)qUu.&l=75I5g30`+05+dP~57KN*_zQV& .(D) 64$( (91iOUMkc> %2473$ . @!F2676&+#.+#!232676&/.7>32.#"#"'&v@@ .@U(:31::˙6}_Q(5059=7>"8 {U5p@8l269#2 S;<?ZgfXspR֔-v/:90/  0035+. #T"&)-1'#73'33733733#3#!#!#7373'!37kI4;;:4HZnOoH r1 r'vvVVBvvBvvvv= !2 #6&+#%"3;3ɐ6:: 1tksʐ7:: /uY*5=YBjs&j/,v@@#$u& uUCJ UC JB E*-,&+*'## %+#-999999999122<290632.#"!!!!3267#"=#73>7#7]VPI@8Oi9RZRhMs?LUTT$$KMg|>Z$$ +K!!!!!!!#7j[KHcw_`u!8LȘ !'?'7!!!77oDo o^~8*83n!n<^:MS:MS9MR9M7.i'6767632#"'&'32767654'&#"67'6767632#"'&'&/#"'&54767632327676767654&'&&#"032Ho$*(0.;' '#-/29"f  ,#'  !% l*%$GmoOL'$  0@&++6)!  5SK# 0*G(o&4+-' 1  7+Уc]97#96O3AHJB @@=kb3JMq~Es/4 %UA4G_KBjoD7=AiK}f^<={@  1@'32327&54767632#72767654'#"'&#"6764'&#"Ϝq:])| \fK* *m**tQ;)v;.*/qI"'" Dl26X``7a0<㫂&'lm@.j?3thP#l$:990C#i"',!23#3#+!#737#73!!6'&+!323~|TG HL%:N蹤mٱVUYY yG[ZH--HpB[QHZHHZ(zfffE[x&1#7!#7&'&54776?3&'&'6767o#@xy{]^jW>ˢXPaS+RG]b!&LA^I-//STKF#dtiϥ"7?& 6#PaQYU"73!3#3#!!!#737!!/3'# CL!E--tؤF!~D(DjQUÕeeÕÊ63267# &767#736767!7!676&#">323#!seml;ro,&' c8&+ak`m8ng'|&bA5&YcRT45 2 f@PVFC ./_T6 )%#&'&%6?3&'&'67679jm;;65͗KGE$%95bTFIHcc >610%R7r~or 7N'(OQ"z!o !!!!!!2٦33-Yr &#!#!'&+732767!7!&'&+7 | |"A]*4k .k0y@ /~y)z~2@_@[ @_y,>**;t%%3267# '&547!2."U|d] ք{OH}8Sryx A< 7@,Lԉk|r4*:0Nuev t ,>.#"3267#"&5467>32#3"32654&'2#"&5467>8+1=N.*:4*G$jt)',qI!Cj<-@.?du$")oDdt$"(o9"QT#EiPYV5IFID˫cL]]ɬaM\^3!3'!!!!!!+aa#]x}+ ;`K;`4#"!'7!7>2!6+qStyқ7?#&B00ۅ w63"!#7267 8 7  b%4)/*W- -*W-  4D4E:F9G=>;N>;}N,6>2#".5467>"3267>54&'.33264&#32+hZZ\[~}ڶ[\ZZ~cIGHHcdFHHHHHsߔ|AGGA|NZZ[~}ڶ[[}~[ZZGIGebIIHHbeGIGw5e7J3%!2+!327&+67654'&J m5//5=*__*]x~#g)ZZ)'%!# ! %27&"676'&|F"Cobbbbn%lkh4334a%-13&'&7!.+!!2!27&#676'&%3{THvu2M;o.a4w5}fcd)1ED)m=s@1F~h|hb)tL #!$5>+;6-0$(v# *%;(#8MX!GL!!+Im6#"'&'&'&'&'&#"'67676327676'#5!#O$0-6;+>4!# >&.&=A"?,.!" v([+!!O7!XM8#(H. % #vdB!!'#537xZx#xZxxx##xxM'75'3''#xx$"xxxZx#xZxB'73'7'7#'7!5,xZx#xZxxxݎxxM77#75'73؂xxގxxcxZx#xZxB'!5!7w>x#xx1xx##xxB'7!'7'7!'4x‚x#x>w1xxݎxxB 53#5!5x#x,x##xM %'3'3!5x$"x,rx#xB !'7'7!#3rx#xxݎxM 7#7#5!؂xގxx#xrM%7'3'7!!5"xx$"xx",#xYx#xxB(276767654'&'&'4#!5g    @16T)+51@x#x  Q87;=49(*x##xB(!'7'7!"'&'&'&547>763"j x#x@15+)T61@   xݎx*(94=;78Q  B$=+#5#53547>76"3276767654&'&'&g@16**)+50AGx#xT61@  G    ))87;=49(*x##xH;78R  H  B$=23'7'7##5#"'&'&'&54767676";54'&'&'&j@16Tx#xGA05+)**61@    G R87;Hxݎx*(94=;78))  H  BF26767676763226767'7'7#"'&'&'&'&'&"#"'5[ #$! x#x,"    "/x#x   %$   xݎx "  ! x##xB#'7#533'7'v81x#x81x#x'x##x'xݎx9~ 7'7ckn"[ kOcuP%8 5!#Ђx"xhx##xw !#!'7'zfx$xWxݎx8e !3!5x"xwx##xe '7'7!3x$xxݎxW5p !5!7#7[_xݎxwx#x?^ !3!5Xxx"xx##x2X '5476767632#4'&'&'&7#7,#!A=PNZ]KS;>#"!*#13#'Dxݎxq!TPA>! #;SK]ZNP=A!#qx#x%'C "()/ZOR?<# !>APT2V 5!7!##2lv<ĻʌvĜ<B  !!#33#'7!5!'7xpxxpx xx,xx742#"'&'.5476732767>54/#7!%?((*MGgZsn_aMOP(%R.-'<0CA57---0v\apn_cMG.(()LNkoaZU-8:>=96/(-,r=ZHv74#5!#53276767654'&'7#"'&'&'&5476v0---75AC0<'-.R%(POMa_nsZgGM*((=\vdHZ=r,-(/69=>:8-UZaokNL)((.GMc_nmdB5!B#x4#xB!!BM̂xx#M3'#"xMxM#'x$M4x#B!5!'74x#xB'7!5xxM!37xM̂xM!#73ضx#x4B  '7!5!'7 5!!x4x##x4̂xxx#xx M  '#' #737"xx$rxxMx4x##x4̂xB 5!!'7!5!'7B#x4̂x*x4x##xxxxB'5!!!!5#x4̂4̂x>#xx# M73'#'#'3i"xx$x44x#B'7!5!'7!5!'7x44x#>xxݎ M%#73737#hxxގ#x4̂4̂xB '7!55!x#x4x#xB !! !5!'7BM̂xM4x#x#xB!73!!!'7#5!!{Va6PEV`6Dx#x\HHVߞ;cff:bDx##xHHB!7'#53533'7'7##5'35#HHDx#xDDx#xDHHHfDx##xDDxݎxDfHB!'7#5!7!5!73'7/!7'!8Va68PxV`6Dx#xHH;cff:bDxݎxHHB!!5!3HH\ Dx#xDyHHfDx##xDfM#'3'#' fDx$"xDfI\Dx#xD \HB!5!'7'7!5!7Dx#xD \HyfDxݎxDfHM%37#73fDxގxDfH\ Dx#xDHB5!'7'7%!7'!Dx#xDDx#xDkHHHDx##xDDxݎxDfHHHM'3'7#77'Dx$"xDDxގxHIIHWDx#xDaDx#xHHkHH}6##7!#V`JvnJVJpvJT '#5!#5'5CJnvJ`JvdpJ^VT%753!5373JvdpJ^V^JndvJ}6%33!'38V^JpdvJV`JvnJB!!!!5!!qYNx#xdfYfx##xfB'!5!7'!5!7!5Yx#xNYdYfxݎxfYfB3773#''#5[KLnDvvx#xPP~~x##xB'7'7#''#5377vx#xvvDnLKxݎx~~PPM%#5#535#535'3'3#3x$"xV¼x#xVM3#3#7#75#535#5353xގxV»x#xVŸB #553353!Ƃx#xC{x##xM 5'3'#7#7x$"xnƂx#x}ʻB 3'7'7+53#53Ƃx#x}ʻxݎxM 7#757'3'3؂xގxƂx#x}6B !!#3xpxxxB 3#'7!5!'7xpx,xx 5!5! !!5cm 깹] 333'#!#\^Z A !!75!!5 ]]YY ###3!3"^\ZX0 m 3'335%!!# #^\znnZgm %3'3#!5%# #3!^\^dddZm! #!5#7'# #3! ^^dd9cm! # #3!3#!!5#3f\ F ^m!וddkn'33%# ##!#'37"\`\\\~$ym?TT %3'3#!5'3!3#7# ##'37^\^p\\\@ddZm?TTB 5#35!7'!!!5 5ddm]]YY'd!#7!##gv<ĻdgdvĜ<,x!5!!53753dĜ<cg<Ļd 3'3#7## #3 3^\\^XZZ֤mmB676323'7'7##"'&'#58X)O$A?x#x:[V6NJ9\ 63SxݎxH9ZY8J M 3'#'737"xxxxMx4x#x4̂xB'7!5!'7!5!'7!5!'7x444x#?xxݎB#5!5!53!hZיrrh4_+__|z|?X!0?"'&''7&'&54767>2"&'2767>54'&ww&'''OO_:3www('''OO_;4AA565  AAA565 ww49_pm__ONP(&www4;_mp__OOP((D56MJ@  56JM@K@&4<91/90KSXY"% !!{!5q!!{qdm3!!"&63!!"!Q )SS) PH{__{HPd;w +#"!#73#!!3!!"'&'7&'&63S) W J+!y 53W ^c>H{HPPHC4|Pu_`P`_cn!!3!!".>3!!"U*^_*&,bމ`+dm&'.#!5!2#!5!26767!5 )SS) mPH{b{HPd;w +%326767!73&'&''7#53!5!&#!5!2'#܃S) V SK,"xk 52X ^c>H{HPPHC4PuP`bcn!5!&'&#!5!2#!5!276|*]^+&,bމ`+L9@44120!!!9_^-LP *@     <91990 5!! !!5my=/9) ӇB H 10!!BKXy !!5!3!!!5!ff!bbB3yTU etrX)1 ,@     9190'%3##q@`44{$1v(3#532654&#"5>32#"&'532654&'%3##oooJUOFEDECibouKJCPU_b@`442,-3teI^p\y@8=D{$1e 333##5!5'%3##mmw>@`44&{$  %.#"326"&'#"&54632>3"36J032#"&'#"&546320P2;JC88b6J0#47632&#"#"'732UpsXlNGUnsXlNGD"McoHޕMcoH^#+4632&#"#"'732%4632&#"#"'732ronQ.A&ronQ.A&ronQ.A&ronQ.A&D"K&ޑK&"K&ޑK&^#+A4632&#"#"'732%4632&#"#"'732%4632&#"#"'732aroKnR&roKnR&rnLnR&rpKmR&roKnR&roKnR&D"KzޑKz"KzޑKz"KzޑKziB'1$'1W14Wi>'1W'1$10$i'1W1$iB'1W'1$'14W10$B '1tJi !!!!!!RMM/6&'1T'1$'1oT1n$Xy&'1~1Xy0#"'&'.#"5>323326yKOZq Mg3NJNS`u_G0;73 ":?<776<Xy032?3632.#"#"&'XJG_u`SNJN3gM qZOK0A<677323267#"''43NJNSFXɉ;5GJKOK[C :?<7Dj323326!!yKOZq Mg3NJNS`t_F(!Z<73 ":?=676=X'y#"'&'.#"5>323326!!yKOZq Mg3NJNS`u_G)!;73 ":?<776323326!!!!yKOZq Mg3NJNS`u_G)!!;73 ":?<7763233263!!!'7#5!7!5!7yKOZq Mg3NJNS`u_G`aG#z`;73 ":?<7763236767!!!!'\UQ:43NJNS`jNDJKHEL=a?]R<# :?<77 Y A;XXy7Z@110+5.*'   'l.5l l l810* 8<29199999990#"'&'.#"5>323326#"'&'.#"5>323326yKOZq Mg3NJNS` t_FIKOZq Mg3NJNS` u_GZ<73 ":?=67 6=;73 ":?<77 6<X=y4&'&#"5>3223267#"'3267#"'&''75>3243NJNS`f]GJKO)-D\NFIKOZq gZpNS#(  :?<76Z323326#"'&'.#"5>323326!!yKOZq Mg3NJNS` u_GJKOZq Mg3NJNS`u_G)!;73 ":?<77 6<;73 ":?<776323326#"'&'.#"5>323326#"'&'.#"5>323326yKOZq Mg3NJNS` t_FIKOZq Mg3NJNS` u_GJKOZq Mg3NJNS`u_Gu<73 ":?=67 6=};73 ":?<77 6<;73 ":?<776<X<y"32?3632.#"#"&'!5!5XJG_u`SNJN3gM qZOK!!A<67732#6454&#"#4&#"#3>32V""V!Z6^b"%25'26 R28W35uo :5SN[5SM%Q//7V'x:%)!!!!#546?>54&#"5>323#V""J 6 0*)^3@"   H H   <291<2<2.990!3!!!'7#5!7!NŮmA1}X<y !!!!!!X!!!Xy{!5!73#!!!!'7#537!5!~P$fZ=e(lN"ebSbKXy !!!!!!!!X!!!!Xy &@H  <2291/90 5!5y!PNFXy &@H  <<291/9055%!!X!! Xy 3!! 5!5X!!! PNFXy 3!!55%!5X!!> X}y#5!7!5!73!!!' 5ZYM{~ X!i i행PNX}y#5!7!5!73!!!'55%ZYM{~ X!i i행Wyq&%5767$'567Rȳ}v֜P ij~wԞP(Fd%EP7(Ge#Xy%5%7%'bk8jyx!:_|:XCXmy55X!`aX<yD7%!!'7!5!7%5%ykSnKAD*ZWOzd#@4=PhLx"LX<y@7'#5375%7%5!!' z0VFdjeG'C0'Pau6LvLXy!#"'&'.#"5>323326 5yKOZq Mg3NJNS`u_GJ!;73 ":?<776<PNXy!#"'&'.#"5>32332655%yKOZq Mg3NJNS`u_G)!;73 ":?<776<X<yD+.7%3267#"'&'&''75>327%5%RmKKt`GJKOZq G:GAFJNSMOyel!PNX yy  5 55%y!!PNXTy1!7%'757%57%5%77'4PGx0e4P0GkB$$1F4F\}F5F\|t?t?XTy15%%''5%75%7XZuu: &uvk;Z&vFIs}FyJs}Vwa%&'567$wSh"/_D$#Q_Vwa%$VhSbQ#$DbO/"X[y5%$X{qQ_#pOKI4 &X[y%%$ynq{_Q#yBpR&4 RIKX2y%%#"'&'.#"5>323326%$yKOZq Mg3NJNS`u_GJQ_;73 ":?<776<TKI4 &X2y%%#"'&'.#"5>323326%$yKOZq Mg3NJNS`u_G)_Q;73 ":?<776<TR&4 RIKVw67&%'&'567677\RN@E߾\SiIRa_bIGE#"R!+Vw'76?&'67&qRN@E߾\SiIRab_bb*IE#"R!+DXyx!!"3!!"'&5476?:nLMm:׃x|~KM᎚ِXyx2#!5!27654&#!5̍:mMLnxϚMK~|Xyy %&'&5476;3!!!"''#"T=1̆cHcw.nL!5ِEFǚ|~K Xyy +'7#5!!5!232654'&'}=1̆cH>9c.nLA!5ِE,F9|~K Xy)%!5!!"3!!"'&5476y:nLMm:׃c|~KM᎚ِXy)7!!2#!5!27654&#!5X!:̍:mMLnQϚMK~|Xy(#"3!!!"#!!'7#537&'&5476;7OnL!-u/K.~=M=̦CH|~KuLx#BِEXy(!5!27+!!'7#537!5!327654/:*'EE/&`-u/K.~-/ mMLL ,E(ϚuLxuMK~|M Xy)!%!'7!5!7#"'&54763!!"3!!y(6^N׃:nLNl:=6ؔDuِ|~KM1DXy) 2!!'7!5!7!5!27654&#!5̍Kh]6(6^:lNLn)ϚR"KDؔDuMK~|XVy1/3ް2/301!!!!X!6XVy1/3ް2/301!5!!5y6Xy I?3 ް 2 ް2/33 3015!!!!!X!!6*@Xy K?3ް2 ް 2 / 301!5!!5!!5y!6?j33?2"&'&'&547676"2767>54&'&'3!!#!5!WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;93372"&'&'&547676"2767>54&'&'!!WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FMXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;933?2"&'&'&547676"2767>54&'&'77''7WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F8xxyxXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;9yxxx3372"&'&'&547676"2767>54&'&''WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FxxXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;9_xx37!!2"&'&'&547676"2767>54&'&'M.WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F/XVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;933BL2"&'&'&547676"2767>54&'&'2#"&546"32654WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F7b%&'qqX>=,-?XVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;9d)'%`8nqq>Z<=,,3!)/7?E2"&'&'&547676&'&'&'75676767'%654'WV,+++WWWW+++,VW:F!#!E: ֈ :E!#!F: & XVWih{xihWVXXVWhix{hiWV9  9{18@9p 9 9 w:A92t3!;!!!!2"&'&'&547676"2767>54&'&'+{{WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:F@XVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;93372"&'&'&547676"2767>54&'&'!!WV,+++WWWW+++,VWGE:;99;:EGFF:;99;:FMXVWih{xihWVXXVWhix{hiWVj9;SP;99;PS;92K 3!!#!5!!!!o2K !!!!!Moת2K 77''7!!! yxxxoixxyxs2K !!!!!M3o/B!!#/^B!!5!3^  B!#!5RPB35!3!BI10!!Mv\ !!'*]])]\@寯X'y32?3632.#"#"&'!5XJG_u`SNJN3gM qZOK!A<677F =0|bF=&N@NU@Xy&3!!!'7#537!5!!5!3>F =0|bF=&NjN@Xy5!7!!!!!!!'7Xw!6_H4%H@CffXy!!!'7!5!7!5!!5y_H4%Hw6jCffXy" %3267#"''&#"5>327%5yL;5GJKOK[_b43NJNSFXH!e3275%X!V_;5GJKOK[_b43NJNSFXJ,323267#"''yQ_k43NJNSFX]_;5GJKOK[_KI4 & :?<7D323267#"''X_Q{43NJNSFX]_;5GJKOK[_R&4 RIK :?<7D # #hGf> 3 3h*f> # #!!h gGf> # #!!!!h ggGfjT!#!W$o !#7>$db!3b $"}!73!z$> dR!!3#CܐRL  R!!3# C RL ~3#!!C~ L ~!!3# C R Xjy!#yG,$%%$nn!"nn 8ʸ !%6 !&'&";112Q2qp`XXV@@VY  67"7,J5PP5JkX*77*I=P"2642#"''7&546xtyΞi56؝wYe:vuvo3N=eXt#O'+6@KV#"&46235462+32"&=#"&46;5#'54&#"3!3264&#"32654&#!#"3265k֘֗kk֗֗kL65LL56LM56LM56LM55LL56LJחkkחחkkט5LL56LLkLL55KK65LL65KK5Xjy!3!y3j !#! 5!# 5; p;!!3Xp;)3!X;5f477632#"&'&'&#"jkbwL=.> \bP \bP]Y"\I\\\\I`LLMK\y>>(I !!3#!#%33'((d&sd4^7 h\\D?cc!!!OZW !!!!5!5!![[[ZWT !!!!!!!!!55[[˰f W5 ! 7 !!-.O|c;5[Z.Ґ|_W=&  #276'&"! '&'676 !%!pqqpB`Ϙ_BB_1`B[5;@@@@EI7XX7H"H7XX7IÐW3#3#&'$%67%676'&ff2ffff1ff=>>=E>>>3;@;$;;#p#3##tD'  4 !%!!,5m5A4 !!!!;rԵ5mY5A !! ![[#ZCoW^pK  !!! ;[5;Ő[#Z !%!!%!ex[xWx x !!'7!!'7!;Őxex[xÂx'3273632#'#"'$%65'&#"pp./NRR2]PQ pqp/@ XX`H>@!3 33!#!3!3POJ3+^/4 57!!#xr;rw/Krm-4 % !%!5 !53!]UPmrZ[z !! '!'!;xcx~Wxwzxw!3 3!###!##mPOmʀʀ3+^4!5 !! 3!xS;ZKmy4 !!!!5!#];r~U[ZZ, !%!!7!!7x&xWxpwx&   !5!'!! ;qi0Z/=>&  &  D&  & Z^' *2 !!!!!!!MM['ZWv7' 2-i' 8 t'  Xyi' 8aXmyi' 8!!%!567!6!&'&'&'yKH FJ97;6L2J!/,3 4(`<?-joo   lXy(8#"'&56776326"&'&&'&'&'&'3276y m{{d m{{cf/ %#2J!UI*/YKX M3 +/fg/  Do}R ( p}I ( " q}}RXy3#"'#&'&#"5>323326yKO54&#">32!!!  >PZ?-\\T`beD^XD&[cNY=P+CDGF 89L\VBT= W`;#"'&5#5%%T:NZX` J!!PNVw{  #"&'!&"2Z6lnn{]^SD@`3676!#"'#"'&7!2H WlrU11UslW H86*CqSSq6y& N}(!5!'# '&76!2&'&#"!!32767N51FYPgZTSS?MOPL.7LXGICꐓ*,6[5XfAZ;`& `& 6y(7!;#"'&''&'&7676'&'"7A$DRfQ1*5ʵjpq$A;::6GElT =$,9xУ[[RMB$ !!#!#5! C4: %+"326=7#5#"&546;54&#"5>32 7 ]J3,AI^9lys:932M#%]J3,AI^9lys:932"326=7#5#"&546;54&#"5>320;0]J3,AI^9lys:932'''']J3,AI^9lys:92".33&'."#67>76#FVʓVVʓ= mm &#Km % mK$ʓVVʓVVj,(Km@@mK( mK,K/,Km F^%.!4>2".7!&'."67>54h4.#52".ut&F@mm@@mSoʓVVʓVnt]&sْFUSm@@mm@VʓVV!!![ZWm !5! ݠr0p !!#v !&5 ƠT_Cm 5!p g!0v  !!#ƚΐ,Um!!! # q !!#v!!  rm!!#N#!0v !!!##m !4763!!"{zf+!u0%y!4'&/32765!-9+en:==@ne( =F|AEuH<y 3!!"'&5!+f}{y%0Wy!! m 4'&#!!2!+fz{}%0y&'&!;!76<(en@==:ne+!< TuEA|R=y !#!!2765{}f+!y0%! z% !!!#!55!mw ¤]]]! !!;bc;$<$!3!39W V(! ! !!!!#!#(!(F(!Z((!((!(h(!|((!(*(!>((3(i(} F( #'+/3!33!33!33!33!33!3䟟䟟䟟mnmnm(%8K#!1!!!!!!!#!1!!!!!!!#!1!!!!!!!#!1!!!!!!F????">>>>#>>>>">>>>(((((!%)-13#3#3!3!##!#3#3#3#3#3#3#ޟޟ#|ŸŸ|Ÿm#(#E(Zh!|i}h((&&(&(&&(&&i(}(&(&&w!N<w7!!!xr$<w 3!254#!") ) xrVVVw&w !%!5!5!5!5!5!5!5!5!5!N54&'.#"!624HI347652IH637J347744IH426532<( 67632#"'327$%&#"!zzzzzzzz12Ι4GG΍GG@XXXX(( !#%&#")7632ΙK/zzzz`XXGG 3327$3!#"'&12zzzzXX`GG7| %63"71{y`X{G7| 2#'&#82{{x|XG7 527638x{{ΚT{GZ7 "'$33{y{TX`G|0#'&"#%632ˡqppp12A@_XX32763#"'$pppqΙ@@XX`wN<wN<w!Nw!v<`/3267>54&'.#"467>32#"&'.H+(*h9;i)*,++(i::f+),H736HI256743IH426?:IILII޸[["[[w !!!!!!IIN< w !%!!5!!!I) NDJPV\bhn27654'&#"&7367'67675673#''5&'&'7&'%67'7&'67'%7&'&'%6767%&'&$h%$%%34$&1++XSA N@`==k>P CRX++XYC P>k==l?L ?Q oL+ Nn;P?;@  nMNn3%%%%34%&&%s==`?J >PW,,WW? K?_==f?H?PW,,WU?H?^<=Ke+cL mCP`k<<!4(0847632#"'&7327654#"&#%#&7&'67&'67!󫪪vӤ=6 5N'V[S.U[R󫬬񫪪񿉊 ʯX[V[X[V[!4(0847632#"'&7327654#"73$3&'67&'67!󫪪vѦ=63QNV[S.U[R󫬬񫪪񿉊w  'X[V[X[V[!4!)47632#"'&%#$''&'6%&'6!󫪪4>;D@KDzcngk?dnhk󫬬񫪪I kpinipi !4 "*2:AIX3#''%#&'52#"'&5476!!'5%!!'53'5%3'5%3#'32765'&#"M==,/0#H 8&O6 |7iY06./==e6a&i1r4z012+KN2HQ>>>>f^2"/1]8`1"Y 4f2y`1B7#5#53'&'&54767&'&=33676=3#327654'&O&"}|fzg}}"&&"}UQn$mQU}"$nQUVV{xVVUQ<"{u^^\ _u{"#| zUOOUz |#YOT{zQPPQz{TO@>)4'&#"3276&5476327#'#53'&`____`oŠqk]^^]YYňÁhgf@> '"3276'&'7#5373'#"'&5476j___``_ߓqŊqYX]]XYfhhĈÁj0 '&'&376&+"'&5'476%7!Z{z[ZZ[~\YWmpN#ZX[[YZ[PQmp#TG*52764'&#"#463233#!5sPQPPtrQPyzg֏LQQQPPQr{{t|g*#"#53533#632#47654&#"#ddiqqCBigIIugzyUr}ppDtPQs_CS 7"27654'&7#"&54767##53#533333#3##h. @\ ! 2(>>?ZW~>'3|}}! -/@ /- !^'?XY??~YX?(F}R}hh}}hLS<#5#535&'&'5'73'3#'73'676=35'73'13|e{vw}wwUATwx|xxS@Wwx}vv|d|re{Eus~~suE|VAKtrrt@X{Ius~~suI{dr|*! #!!!'!27674'&#_82V)3{D#MHZW{s{?zK8QO##"'##565'##"/547?kM ,4N"DF &Fi?JO/FB!O {|Im<&=M2227632#&547636=4'&#"#4'&#"=` ]d2 cBU;/G;SXMB:@B ս;7hf% #>|\@9@O &&5 iC n:^O G  %2O7236;2"'##'65##"'&5476;235&'&=476jS c1=EO ;SCFRʝT6*F@E1;O+.`162V Yi8/D ;8[B VRP"<B+"'##565#+"'&575477;2732;276=4'&3&'"ih;F(wQ"DG".FWCNfBy" bODUq5u4  Pro@ |S`64 'RHIOq 1 ӫg 4D% 3363'$6'"I+4 puoS^*  3%#'#3%#';&2 IʗHj7*(,377#'#'547#5773%%,ppsr,'zzxz'984?/99e5>:_&*'$676#"'6767 5N@#3 J<*Ĕ $8FJ, SR-PAdmR Ga&767$7676&#""G,*PH$/YL-;;/xL^&7)L5 j{4@BsSV}dxaM> !7#"'&'3276767676&+7!2"?bdv<78.6(yL^KL2^=b+<ҋ@`45,,! >&64pnbo{&"26$  6 76&"@s_@s_xjVj%|nO TӜ iPPsOO*=CWW _Vd, 3!3-~>&D4&'&54!2.#";#"/&'&'&'32654&'=>Cg[9Dov{^rE@G9:p.#$"r|;akFEQJK~))>?qXTS6AP!zGx251QUud>[<%;#"/&'7!!H76&/.76$32.#"77 7GL+  eW4RT\i 3IA"v{ =TBV\L98FGDC+P=YNcwI#7337%мtt<I#733$YtI#733$htI#733$4tI!#733#H% 켼tI73#3d%% м0 73#3)$$[d73#3$$h73#3$$Ӽ4Z!73#u%%tI!!#%0 3!!#<$̼d3!!#x$x3!!#$<dZ33!%0l 3'#'YkgKQovoQe #'737CYkgKQovoQ+ 3#73#G>P>&+ 73 73>N >h&&A}#D ~d37 !7 !7 1^81^%1:$-HXcmZ `!7 6!7 6!7  *6*6*N(6aY 7!$*.P)jc@N1 V{ 7!$3>P)jc8^N1 V%+73267!!!!!3rqy,%dgx"'nqn':nlnlh9;X`!>54&#"!!>32+73267!:7Sty/#t0e 1y+'dh5:=\gL/Sn1L'U!}=!7!7!7!>"">"!! #u f!qe910!ll+\nD";!"&7#"76!7!!%&#";4l=b,ь1l{',@D,N ; 2`CԄn+)b̜28? !!3!# !/iq>h=/B=Z{ 3##67654&#"!!>32#cc8:7R;:y% 0e:=HH`\g&')*2 54767.54763%"&"26765%5"b6(V)/\BӜ BӜ |LX LXkbMB@KGGB@]       4 ddGe       999991/<2<2990KSX9Y"!!!!!!#737>;#"%9Zۮɮۮ-'P+CB:-cNɝ1B@M    4 ddG e      99991/<22990KSX9Y"3#!#737>3!!#"d+ۮ-'B:cNɝ31j%!?6?>54&#">32  @7 7 iubƥhf5tYEJs]XW5v{iVb_:7HI>9C~dN`:9f!#Af;\@99991<20KTX@878Y@////????]]3#%3#P/15Ds10K TX@878YK TX@878YKTX@878Y@++//]]!#+s`@  99991<<99990K TX@878YK TX@878YKTX@878Y@%         ]'.#"#>3232673#"&1!#5 `%A#5%'4 ^%@!% @8) =:Xy} !@ <<[E 104&#"32!2!"&R]U{aSyM mvjy6BpX910#ɏX?@ 991<90!#'#L\s@ 991290!373/wo@ 9910'T%#%vucfk>32#."(ǓJ@V76 cC:m=<=ck@  1290332673#"&5 ROGj(Ɠk<7=6}uk9910!!0kcCk!#!#&}kOcvk#!#kk,!!!767676767654'&#"67632I 7Y01O9!B\8Q A|RY>0.8QlR/=X-j-j-j-j-jS//jfR=X=Xf;js//LL=X=X-j-jZ; ^/L=X=X=X=X5ssd[^x HB%S-lbH9m +R bK@U+!{|:CV Q6 7 <~Eg~3qYe_PM.wRyuc`w<s9PyRu;96od-@B Ib(M6s H{9tO[=[q5deO5A8deaOAIpX,Hs@OX@o^.8ZI/VUkP[F|a(x|u=(q|/$ qL4++A?X?=(iFLLQqZ(A+Z]t>bSH|4%JUZ7e;|,(//jL[S[S|4++=X[b[bF((((<d=D)Z77)$x7!y#M>1/-!=ZQ5+Q FQ.7D \pp67oRIp?*%O)XZ6NcWOH0$M& -$?-)Uj>$!K9$ #H9-$+# * ! ~N] ,)B* Q$(i&9L4T4Tj\FGW,Kd;-c> ////BBB'CL/f;;;Hk;RRRPPPM2;;;=XMZZZMM-B-j)Z)Z)Z)Z)Zqq111;Z8////LLL=X=X-j55555555ddddddd Heeeeeeee2dOO HOOOd9MXXXXXX4\yy o@@@@@@@@4\755ddeeOOXX@@55555555CCCCCCCC2d@@@@@@@@4\75555555{yCCCeCHOOOOOO Rtss@@@@@9L1a11=$j/! ! /!! !c LLCBBE6ortt;;2oJ /* / BBBBB;IBBBBBBBBBBBB9?222B77BBBBB BB B BBBBBBBB}}BBBBBBBBAB',B BBBBBBBB!w?ddcddcBXy111 XXBJ/XXXXXXXXXXXXXXXWXXXXXXQQXXXXXXXXVVNXXXXXXXXXWXXXXXXXXXXXXVVXXXXVVXXXXXXXXXXXXXX2222BBBBXXXXXXXXXXXXXXXXX9?rno}  XG#X  C@_24!!-XX!XX@64HFFFiEiiDDu777777aa"!!!!GCL2 1rjD7GuQuBBw=C?@b,Wjw<l# ;1Un)BB95sXX/OP-DDDDHL  P 4D P\`d(|d p!|"#\$0$%p&$&'$''(L(() *+,`-.t/h0223457894:H;l<0=h>h?@lAXBtClDE\EFGTGTGHIJKKM(MNOPHPPQRHRS0STTUVVVWxX XXXXYYZZ(Z@ZX[`\@\X\p\\\\\]]^ ^$^<^T^l^^_``4`L`d`|`atcc,cDc\ctcceeefff0fHf`fxfgggghhh0hiiiiijkkk4kLkdk|kkkkkl l$l<lTlllllmnnn4nLndn|nnnnno o$o<oToloooop(ppppqqq0qHq`qxrrss s8sPshsssstt t8tPthu vv,vDv\vtvvvw,wwwwxxx0yzzzz{{ {8{P{h{{{{{{||(|@|X|}}}4}L}d}|}}}}}~ ~$~<~T~l~~~~~~,hPP( t`XLH X|PHL\TXpd\ 8 ,H 8Ph0HXx0H`x 0H`x 8Ph(@Xpd(h 8Ph@T4<\THtLH044dlx@ XX4$Ll|(X d¨@üp4ń\ư Ǥ$LȌX<@˨̈͠@όxpӜԄ֠h(lhټ$lی<|܌$`ݠh P`ߐߠ <Xhxddt Hd ,<L\l|$dtP`$@l,PHd xdtL ,<<d ,Ht4Ld|$4DTXhTlP0| 0x\l,`LH`<(8@    0 @  4      X  , D \ t $<`Ph4Dl,<t HXhxDTPd8@(8H`p0@l|Td$d t    !$!4!D!\!l!"T"""##<#$$$ $P$|$$%`%&(&&&''P''((T(l((())8))**\***++ +,,X,-,-----. ..,.D.\.l.|...../ //,/D/\/t//////0 0$0<0T0l000000101`1x11111112l238334\44556P6t6778T99h9:@:;<;<$<<=>>d>?h?@@APAABBBBC@C`CCDDElEFFFG$GH\HHIpJJLJ\K KtL,L<LxLMhMxNNOhOOPHPQ$Q4QXQRpRS,S|SST,TU8UV<VWdWXPXYPYZ(Z[`[\](]|]^_ _`D`ab<bbccdc|cd$deeXeffffgggh$hiXjk klmdn no`p,pqdr rsptHtu`v<wPxxyzp{{|X}T~T~t\t|@t\DHd<p DD0|LX0tTP@\Lx80H,x4Pt (\$ DxD4Ld|0H`x 8Ph0H`x 8Ph(@Xp0H`x0H`x 8Ph0H`x(H`x 8Ph(@Xp0@Xp 8Ph(@Xp0H`x 8Ph(@Xp0H`x 8Ph(@Xpˆ ¸0H`xÐè 8PhĀĘİ(@XpňŠŸ0H`xƐƨ 8Phǀǘǰ0@XhȀȐȨȸ(@XpɈɠɸ0H`xʐʨ 8Phˀ˘˰(@Xp̸̠̈0H`x͐ͨ 8P`xΈΘ4Ld|όϤϴ0H`pЈРи0H`xѐѠѸ0HXp҈ҘҨ 8H`pӈӘ<pԤPլ0tPټ <Dtݔݰ(lްdx߬4ttXl(x$\,@Th|0DXlh($D|$x < ,<D|x8Xx@t h(pdX0p(dx| `   8 p    P  X   <`(LplDlDD<|8x`D H<8`8@$|    !@!|!""##$T$$%t%&$&' 'T''( (0(D(X(h()h)*<++ +D+l++,,d,---..0.H.../,//0L01812<234p5$6667888<8\8|899d9::`::;4<<=,==>D>>?<??@$@@A@AhAB8BC CD`DDElEF FdFGGH HHI@IIJhJKKLLxLM8MN NOTPPQR@S(TTUUVVdVVVWW8WhWXXTXXYYlYZPZ[0[[\,\]]^8^t^^^__8_l___``@`h```aaTadaab@c$cHclcccd$d|deetj jjjkktkklhlmm@mtmn n\no8ooppppqqTqqqr r$r<rTrlrrrsss0st|tu$uv$v\vw w8wwwxtxy0yz{||}0}~T~~~Hh(Hl4P4 <Xt8TpTD0DXt (Tt0Xt0P (Tp(Dl<\`xXl p4<0Dl H(h$@l 8xDdL4TTTL8X,X8h X,XDp4\ 8\(X\ˆ˜(àPPpȄɈ0pʰ(|ˬ̠$l͔ +k@_B m H  ^4 \     S :z : . 4U " : &6 hCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain DejaVu Sans MonoDejaVu Sans MonoBold ObliqueBold ObliqueDejaVu Sans Mono Bold ObliqueDejaVu Sans Mono Bold ObliqueDejaVu Sans Mono Bold ObliqueDejaVu Sans Mono Bold ObliqueVersion 2.33Version 2.33DejaVuSansMono-BoldObliqueDejaVuSansMono-BoldObliqueDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/License~Z   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F4uni01F5uni01F6uni01F8uni01F9AEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0221uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0243uni0244uni0245uni024Cuni024Duni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C8uni02C9uni02CCuni02CDuni02D0uni02D1uni02D2uni02D3uni02D6uni02D7uni02DEuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EEuni02F3 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0343uni0358uni0361uni0374uni0375uni037Auni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0462uni0463uni0472uni0473uni0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni04A2uni04A3uni04A4uni04A5uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04BAuni04BBuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04CBuni04CCuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni0510uni0511uni051Auni051Buni051Cuni051Duni0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564uni0565uni0566uni0567uni0568uni0569uni056Auni056Buni056Cuni056Duni056Euni056Funi0570uni0571uni0572uni0573uni0574uni0575uni0576uni0577uni0578uni0579uni057Auni057Buni057Cuni057Duni057Euni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058Auni0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1D02uni1D08uni1D09uni1D14uni1D16uni1D17uni1D1Duni1D1Euni1D1Funi1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D62uni1D63uni1D64uni1D65uni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Buni1E9Funi1EA0uni1EA1uni1EACuni1EADuni1EB0uni1EB1uni1EB6uni1EB7uni1EB8uni1EB9uni1EBCuni1EBDuni1EC6uni1EC7uni1ECAuni1ECBuni1ECCuni1ECDuni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE8uni1EE9uni1EEAuni1EEBuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni2010uni2011 figuredashuni2015 underscoredbl quotereverseduni201Funi2023uni202Funi2031minuteseconduni2034uni2035uni2036uni2037 exclamdbluni203Duni203Euni2045uni2046uni2047uni2048uni2049uni204Buni205Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B8uni20B9uni2102uni2105uni210Duni210Euni210Funi2115uni2116uni2117uni2119uni211Auni211Duni2124uni2126uni212Auni212B estimatedonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215F arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni2213uni2215 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangle logicaland logicalor intersectionunionuni222Cuni222D thereforeuni2235uni2236uni2237uni2238uni2239uni223Auni223Bsimilaruni223Duni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Funi2290uni2291uni2292 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1uni22A2uni22A3uni22A4 perpendiculardotmathuni22C6uni22CDuni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2312uni2313uni2314uni2315uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2325uni2326uni2327uni2328uni232Buni2335uni2337uni2338uni2339uni233Auni233Buni233Cuni233Duni233Euni2341uni2342uni2343uni2344uni2347uni2348uni2349uni234Buni234Cuni234Duni2350uni2352uni2353uni2354uni2357uni2358uni2359uni235Auni235Buni235Cuni235Euni235Funi2360uni2363uni2364uni2365uni2368uni2369uni236Buni236Cuni236Duni236Euni236Funi2370uni2373uni2374uni2375uni2376uni2377uni2378uni2379uni237Auni237Duni2380uni2381uni2382uni2383uni2388uni2389uni238Auni238Buni2395uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni2423upblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2638uni2639 smileface invsmilefacesununi263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647spadeuni2661uni2662clubuni2664heartdiamonduni2667uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi27C5uni27C6uni27E0uni27E8uni27E9uni29EBuni29FAuni29FBuni2A2Funi2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2C64uni2C6Duni2C6Euni2C6Funi2C70uni2C75uni2C76uni2C77uni2C79uni2C7Auni2C7Cuni2C7Duni2C7Euni2C7Funi2E18uni2E22uni2E23uni2E24uni2E25uni2E2EuniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716uniA71BuniA71CuniA71DuniA71EuniA71FuniA722uniA723uniA724uniA725uniA726uniA727uniA789uniA78AuniA78BuniA78CuniA78DuniA78EuniA790uniA791uniF6C5uniFFF9uniFFFAuniFFFBuniFFFCuniFFFD dlLtcaronDieresisAcuteTildec6462Grave CircumflexCaron fractionslash uni0311.case uni0306.case uni0307.case uni030B.case uni030F.case thinquestion uni0304.caseunderbar underbar.wideunderbar.smalljotdiaeresis.symbolsEng.alt@tGG22dk  Y&Y@&~2}|{zG{{@zyAzGyAxw2xkw2vutsrqp%olkj j iihghhgg@fed:d}cbcbaba`;`2_^_^];]d\ [Z[ZY]ZZYX%Y]Y@X%WV;V}U:U2T;T'SRSdRQPdOSM;M2L:L2KJ;JdIHGF F F@ED.ED@.CBABA@]AA@=%@]@@?k>=%>=%<;4K7 7   @6 2   2X}Xd++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++pyzo-4.4.3/pyzo/resources/fonts/DejaVuSansMono-Oblique.ttf0000666000000000000000000073046412662716363022024 0ustar 00000000000000 FFTMYR,GDEF0h%HDGPOSTGSUBEb7TOS/2TVcmapycvt &|fpgm8gasp glyf head6hhea B/$hmtxAz68loca^d(maxpC* namen.:*!fpostYL@`prep8D.ɐ!ɐ!<`aabcdqr ! " # $ = >Lcyrllao latn*mark ntz "(.4:@FLRX^djpv|cIcIaG/u^D0v`FdJ`F1wdJbHcI;jS9;jbI;jfM;iaGaGdJG-`GaGZ@D^aadq>DJPV\bhntzt|w|w|w|wt`~~`~` cyrllao &latn0SRB 4ISM I K _ q !!!!!!!"!$!&!+!.!_" """" "-"="i"""""""####!#(#+#5#>#D#I#M#P#T#\#`#e#i#p#z#}#####$#&&<&G&g&o'''))*/+,d,p,w,z,..%..' !$CLPCXatz~br1Ya,0bw{0Th| HPY[]_   & / 9 < E K _ p t !!! !!!!"!$!&!*!.!S!"""""'"4"A"m""""""#####%#+#5#7#A#G#K#P#R#W#^#c#h#k#s#}#####$#%&8&?&`&i'''))*/+,d,m,u,y,|..".."ldRNKFEDCB42$|{udbYDCvfdc`^ lfZUEDB@=;21/-,*)'&$"! ji߲݇{zybbbbb\! +   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`ardeixpk#vjsgwl|cnTm}b :  " #ydtqpqrzuse##X!#/{{RfoZ!7={fs +bb#3N{T\q#w!`j#fy```{w`{{RNNfffHF?{L'5oojo{-{{=foD7f}s, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-hh@ O/10!%!!hsr)s` Y@.   :TSQ 991/0KSX9Y"3 #3#ʁX2s2qeR@UQ1<20###Ѯ++P@5 X X    91/<2<<22<<220#3 33!!!!###!7!!7! Siih'T'hhii'T'haabbN; 0l@@()%/0%W$ZXWZX!0 !()/ %$, 19991/29999902654&'#.'7.54$?3.'+uV^PwTe;__#QhZ 0d/UH AYQ LEtNUcEJ-.+73#'#"&547.54632.#"326773>J zk{;_hǼA|9#*zLq,_AGB͋fqGG֯!g4l7&&s^/Iuw96Q10#+u @hg   99910 #&5;32267654&#"P77ON87PsTTߔürUUݓþP/E]bkP0F\g7PP78NOEEg`f__{ T@* :ccQc   99991/20KSX2Y"7!7%3!!9#`5!ɪuLJժf\@99991@ q cac/29990@9%!!7754&#"7>32  n!7'r\~)paGR!#]jCD12›`g*N(J@+c W q cW qc#ae) )  & )99190!"&'732654&+732654&#"7>32rl^&[b {Rq%{Tس~%%45uy|d_((!? s@>      : cQ     991/<290KSXY" !33##!7JٜFE$3dT j@8  : cWbccQe!   !999190KSX9Y"!!>32#"&'732654&#"`!H/Y,i`XlH)T^OKժ|VPP!32ꮀ(&\h *;@ ccr W bc(ae+ "+9104&#"32.#">32#"&5476$32\tkzk $:K7DqPDNzNDb1KRqzy '%'fiǸwSa`צf5@:cQ91/0KSXY"!#! <^+)f #/?@" c* c$cae*t0 '-!09919904&#"326.5432#"&546"32654&5vpykl(ֲ ~uf{xtŚi~&i)̳`|cr}[l?L *3@cr c( ca(e+"+91032654&#"732#"&5467>32#"&Jtkxk#:K7DqPCNzNDbKqzzٺ%'fiȹvS`a֦X#%O@):nun91/0KSXY"3#3#'?>54&#"7>323#`MqkUGpcOi$meaiT<1[^[Ho8JRGB98~^lVFfVu @o@5.1*@ xx -*x1!x1:A@= -. ='=4A9991999990@ ]2654&#"#7#"&54327>54&#"3267# 476$32}eX{c3QT_TX9xC-BS|n& Ә`nїap}o?D9H=+#4~}՝{=ѰM/ v@B      :cyQ    91/<90KSXY" !3#!##-+{ u@@       :c cQc z   !9991/90KSX99Y"32654&# 32654&#%!2)mkÈ{Xo}bи}<ݮkff>^T|s2@|{c|{c ae  99910%#"5476$32.#"3267T[rkPK)@P_GhUX5)) |*(?>QS{@=j P@(   : c Qc   99991/0KSX99Y"%267654&+ )DD6ErgTGh#}s?w/5 X@0    :ccQcz   991/0KSXY"!!!!!!Xu!TV rh!wժF\ S@,    :ccQz  991/0KSXY"!!!!#o!\Vd ժH7N)a@3&))#$"%):%&"&c("c|{cae*('&%)*9991990KSX9Y"%#"5467>76$32.#"3267#7!Wu! #^8kPM)AQbD,N"#Dl%L`>? ZouC|*(?>SQ4arQ%# {@B     :czQ   9991/<20KSXY"3!3#!#v)vʋՉd+99 ?@"  : cQc   91/220KSXY"!!!!7!!Z>!:!!9ժNU@.  : {cc Qe  991990KSX9Y"'73267!7!#"&-Hg(!H8a=OSD.J j@;      :}   991/<290KSXY"33 ##ApyN 8@:cQ9991/0KSXY"3!!q!eժ  @D     : }   99991/<290KSXY"]@&  ]]!!# ## nݺNs +/ @<  :}  9991/<2990KSXY"]@  )]!3!#3+3R)!@c c'ae**10%267654&#"#"&47>7>32U/J\inU/L\i-+C'Xڑ++C'XڑÇf_f_hun\;yo\;y3h@6    :cc Q   99991/0KSX999Y"32654&#%!2!##m|mu/ϿlgR-3@c%c ae.+"+.991990#"&47>7>32267654&#" ++C'Xڑ@%6OU/J\inU/L\io\;ySotC_!\f_f_h @R       :c c Q      9999991/<9990KSX999Y"#.+#!232654&#HW6yj,mn{!)fۣ~jhybճWpi{'@:    : |qc|qc%ae(  "(9999190KSX99Y"8]@<'''))))777  (((%%%%88VVVV]].#"!"&'732654&/.54!2{'Q]byhd)bpXeJ P;<GX('2--EBRZ)!:y(5@@:cQ99991/20KSXY"!!#!t!++ժ+P~@K   : ceQ991290KSX99Y"33267>73#"&546s˳mnY|--˲*_NGj=h ;\\9>h?8;%~+J@':}91/290KSXY"%3!3V1ƾ+R` @J        : }    91/<<90KSXY" ]@\  ;u      )%& 86736 FFCE ZVTU kfch e zvvy u   %]]333##ZIyF +f9 ~@H      : }    91/<290KSXY"33 ##D1dDf@5:} 9991/290KSXY"33#σub 9@:cQc   91/0KSXY"!!!7!1-" )՚oG@":~~g9910KSXY"!#3!mVdB0@:Q910KSXY"#3ժǪC@ :~~g9910KSXY" !73#7X+ޏH@ Q91290 # #Ȳu-m/10!5/mPPPf2@ 910K TKT[X@878Y#ŏfxH?{ .@a! "   : %  &W%")e,% &,/99991/99990KSX99999Y"']@65$0%0&5'  ' ]]"326?#7#"&54$!37>54&#"7>32wt+DRgX&} Hn5 ~uVo#lY 3{IScԹ)L^e= U[33'' h;T%@L$#  $#"$# !#$#$#%$$#:! e$""#%!#$ $#&999991/990KSX9999Y"%267>54&#">32#"&'#3T.19eeS30:k>fRHL|b"/ZV^l\XVo{RTVϺlsVTq{4@WZWZ e   99910%#"&547>32.#"3267IZk[]߅YO%BUn<8?`S3')ܕbcb++:6YYRh>?w%@L  $%#: # e# &999991/990KSX9999Y""3267>54&3#7#"&547>32X.16feU018msѸAdPEK|aWWZsZXWrxCTVҼ!lvxWbf} &\@" W&Y#  e'  &  '999910@h hooooo ooooo ]]>54&#"#"&547>32!3267t{,uigxjSm Xr#x++֜,mVZ"eN 498j@9     :       999991/22990KSXY"#"!!#!7!7>3_`)&Sec/яN;Hy{.@[ ... ..-.)*+(,..: , W  ) -/-,. .#/999199990KSX99999Y"%267>54&#"#"&'7326?#"&547>3273O.17pdr5RK%IP%:nPHJoe"ZTZjwwI,.uW[׸jp|aUTH@R    :   99991/<990KSX999Y"#67654&#"#3>323]W!{/tKb J G'QWab ^= h@7   :      99991/20KSXY"!!!7!!3#f׾mnm-`/BCV r@@  :     91990KSX9Y"!7!+73263#)ʵ_nJ-wZ {@:!!     :   991/<90KSXY"w]v}p]]33 ##%uͨXZFF?7]@/:    99999991/990KSX9Y";#"&5467!7!XY[ϭ #6IHP.{+@e    $%"%#""%:!  &$ )$" $"%  &!"% # %" ,.999999999991/<<<299990KSX99Y"K TX,,,@878Y>32#>54&#"#>54&#"#3>32'}MVg}.3KT+}}/1JQ-}٨,o@K\DIm\6kw\u?8aq=:w``323]W!{۸ Kb J G'QW`ab ^uZ{ !@ e  10"324&'2#"&547>ppp_RJO⓻RKOVΊmvvˇqtxVh{%@N%$#" ! :e&&# &.99991990KSX9999Y"%#3>32#"&7267>54&#"%o-=dOFKzdQ-18gfU018p RXлnvzTHYU\qZXXr{bV{{%s@=  : e&# &999991990KSX99Y"%#"&547>3273#"3267>54&=fRHKтd#ոU.19efR20:kTVϻ"pvxUUXV^l[WUp{{h@7       : c    991/990KSX99Y".#"#3>32-wL&kٸ(CՇGs(y,,`x~%#s5{)@;  !    ! : !WZWZ'e* ! $*9999190KSX99Y"]@`++''99IIDDYYYY)iiii)yyytty)) ' ' //55GGv]].#"#"&'732654'&/.54$325#GMHutQc%^S CyX9-/f\b8{\##55w\c4!g!dp@@  :   9991/<2990KSX9Y"!!;#"&5467!7!=^wNfߧ w+>3B5ovD/`>}m`@Q     :  e   99991/2990KSX999Y"332673#7#"&546]U!{ڸ!Ii J%DOUyba~']`J@'!!!!:91/290KSXY"33#`D\#` @J      !!! ! :     91/<<90KSXY" ]@f   * J       *$ :4 GCD ZZ T kfh c yxy u   !]]333##\ 1j<צ`wBo` ~@H!! ! !  !  !!!:    91/<290KSXY" ## 3>_4q`DT uV`~@F!  !!!!!:     99129990KSX9Y"+7326?33>g6:`lQu<+ٷhkILJ]qTN^Nm` 7@!!:  91/0KSXY"!!!7!?.!u!`ۓ%4@)(54.  $-59991@: 5'- )'.)~)~'~g599999990KSX@* -.-..-    9999Y#";#"&546?67654&+7326?>7>3N^3!xoIP/VaNH- bq>=~2H6/OgK@&!3?9m{D% 2-UKn{'"10#4@e -.-..-    :-5)  )'.)~'~)~g54.  5)('*(5.99199999990KSX9999Y"7326?>7.546?>54&+732;#"#N^3!xpJP/UbMJ- bp>= /H6/OfLA&"3@9l{ D% 1-VJn|'"Xy &@ ll 1<2990#"'&'.#"5>32326yKOZq Mg3OINS5dJ t]F ;73 !;?<6 7=s ^@/ :SQ 99991/0KSX9Y"#73#3B1ˁX1+e{?$V@3WY WY  e %  "" %912<990.'>7#.547>73? 1{HAK!K?7f7̢FV8f7<}u5&,-(!úD`16"+`႙@F  :  Wqa c  9991/2<2990KSX9Y".#"!!!!73#737$32%6y?&+rZ 3!Z/3<+-ُ/яLB /}@B (-  * -'! ) -0)$ !'$* EFE( $02299999999912299999999904&#"3267'#"&''7.5467'7>32d|[Z}}Z[|Z^.[20`0\^.[3.^Z{{Z\}~t]1]02[-^Z3].2]-_/@]  :    Q    9991/2<2<290KSXY"!#!7!7'!73333!! wbbs7ln   o#o1ho#@ <210##  d7-4Bv@E$%B<;5& %, XX2aC$%5 "<%&,/B;"%/"%8%/?%)C9999919990.#"#"&'732654/.5467.54632>54&/-!G:gV|Ur))FS!J=p J}Pv)%GTXkPRVU~&(bHOZ ;VuEe5#Z7$'^Nb| 3UoI`?%P53o75p^34p73`SF-n@99991<20@@@@@PPPP]K TX@878YK TX@878Y3#%3#''}N1ID@'  &>>2J- /,(8 (8*D/210.#"3267#"&54632'"3267>54&'.'2#"&'.5467>`:o:u8g24r=г=rjKKMMKLijLLKLKKkZZ\[[[~}[[[\ZZ/lhȬJKKjhKLLLLLijKKJgZZ[~}[[[[[[}~[ZZ  +/o@?  .,#", &a0 .- )/,0 #"6)6099999919990"326?#7#"&546;47654&#"7>32!!ٔI"3267>54&'.XXP:&rk1=-7ffZJJDZZ\[[[~}[[[\ZZ~jKKMMKLijLLKLKKLbeG]C;P*T6?>5VZZ[~}[[[[[[}~[ZZgJKKjhKLLLLLijKKJb+@ 9910!!V+u @ a#$# 102#"&546"32654&hAu,-/OomOPqp1.-rBoPPlnNOpXy.@ l l   <2<21/<<07!!!!#!5!X!dCDLI3j@5:  aK999999199990KSX99Y"!!?$54&#"7>32!9J?3zST}5vrni3=%)o^(H@*  #a)   KK& )99190#"&'732654&+732654&#"7>32HNЯB;Ch2pUYHHavVV*e<Gz7yr\ [Gy\NC@jP@33sd[Ot?ff/10K TKT[X@878Y3#ufV`&@\   &#$"% :%   c"e '&'  %'99912<99999990KSX999Y"3326733267#"&5#"&''-mf! (J!CO5b\| mV H6[a  UIRLSS};u g@0  :Q  99991290KSX9Y"!###.54/эZfN/`/@:n910KSXY"3#;`us\ X@. JJ:  `     99120KSX2Y"3?33!qu c)t'+n)!7@  a"  !"6 6"999910"&5467>3232654&#"!!yA8=Z@7=cWjcWk\WCJNXBJNTixؗix{RZ# 3@  u   CC991<2990?777R+^#T+^# ^R^ ^R/m{'V'{ 0/y{'{& 0tV/m'V'u 0!@J !!!:S WY ce Q"  ! ! "99919990KSX9Y"33267#"&546?>7#73PojUGodOj%lebhT=1D]]YHo8JRGB98~^lVFfVk&$ -3uEk&$ +3u3m&$ .3u^&$ ,3uLN&$ *3um !@Q   !! !!  !:  c  [ y   " ""!"99991/<9990KSXY"4&#"326!.54632#!#Y?@WW@?Y33srPF#Z?YWA?XX`#kErrQ {o@P   :c ccQcz  91/<20KSXY"!!!!!!#!!V3!jd!K\5ժFժsu&&d5k&( -`u5k&( +`u5m&( .`u5N&( *`u9k&, -9u9k&, +9u9m&, .9u9N&, *9ujk@;  : cQc    99991/220KSXY"%267654&+!! )#73DD6Er[ kgTGhщ{}s?+/ŕ{^&1 ,/uRk&2 -+uRk&2 ++uRm&2 .+uR^&2 ,+uRN&2 *+u;T .@     <91<290 7   ^t^_t\t%\^u^uw^ 8o@;)9$863 '$*73(c$c3a$e9)9(*-7  '-6 8 -999999999199999990%3267654&' .#"#"&''7.547>327B^Bp=26yf[8q<2:$iSXQ6^cwhTXM6Td,2'{#)u'+l <[Z..@:V++}@Pk&8 -7uPk&8 +7uPm&8 .7uPN&8 *7uDk&< +9u3 @I   :c cQ   999991/0KSX9999Y"#32! 32654&#FH#3`i|m縘gb7f5@[#$# $$#45512035:3*$ #0W0e445$'# 35'*-  '' '-56999999991/990KSX999Y">32#"&'732654&/.5467>54&#"#*˰ )<3sQGA;G6U=Z8˲h\yۻqϠ!F$`&=,'UW !}l4R?/D\?$OYH?f&DCHjf&DvH?f&DdHZ7&DtH?&DjH?N&Dr{ OP@'O'OO'  ';:987<6'5':+ $!F@6?$O4%W$!(@W?<IC.(ePOL?965F$%+4@L'1P999999912<2<29999990KSX9999Y"@`>#>$>%0?0@Z#Z$Z%Z&ooollloooooj#j$j%j2o3o4o5oNoO{#{${%{&#$%&%9#9$9%2?2@I#I$I%I&j3 ]]7#"3267>54&#"!3267#"&'#"&546;7>54&#"7>32>321}i##N;Ze=D;SjaUF5";Hj/]uUM1L#O8Rv2PyH'!b3C'\uq{&Fhbff&HCbff&Hvbff&Hdbf&Hj=f&C=If&v=f&d=&jwj!0@L:! %%+ e1( .(!. ( 19999199990KSX9Y"#"&5467>32.''%'3%.#"324&f{qKFMLGL0 5)h%P,sh/֗htx~io{&V:^\VȑX\ xA@eTV7&QtuZf&RCuff&RvuZf&RduZ7&RtuZ&RjXyo '@l D <<103#3#!!j!/ '1m@:'2"1( +%&""+"e2'2& 1(.%.  . 299999999199999990 324&.547>327#"&''.#"N4PKQߓNx.p]{ RJOJ{3w\%I49W.`0Pnwu$$M2Tmvv%%L'}mf&XC}mf&Xv}mf&Xd}m&XjVf&\vVh%@M%$#" ! :e&& &.9991990KSX9999Y"%#3>32#"&7267>54&#"%or=dOFKzdQ-18gfU018pRXлnvzTHYU\qZXXr{V&\ji0&$ 7H?&Dm&$ 2HJH&Du.'s$Hu?{'snDsk&& +uf&FvZsm' .u&qf&dZFsP&& 3Kq&FKsm&& /uf&FeZjm&' /*uwg )9Q?}Gjw-@|  "! #+,-+,-*: + "e+ %.999991/<2990KSX99999Y""3267>54&!7!733##7#"&547>32X.16feU018m>IAdPEK|aWWZsZXWrx5yyTVҼ!lvxW50&( 7bf&H#5m&( 2bfH&H5P&( 3bf&H5u'sh(buf}'sH5m&( /ubf&He7Nm' .9u*;Hyf&dJNm&* 22;HyH&JNP&* 32;Hy&JN'*;HyN'Q.Jm' .Ku+THm' .,uK43!733##!##73!7+*++ ʊ֊׆ ,*,9Q,##654'&#"##7373!!67632 (p~ZX!{} }a >C^^r>%JA1B)G[Xzz`21qBiK9^' ,9u,=97&t90&, 7=+&9m&, 2=JH&]u&,$slAu&Lsp9P&, 3=` C@":   991/20KSXY"!!!7!!f׾mnm`/B%73267#7!#"&!#3!73#w)+tD]d%}4@fC >OSD.êS !!!7!!3#!7!+73263#-׾mnm-)ʵ_nJ-a/BCwm' .u-Vf&dJ&h.Z'N` 33 ##Y[>vҢW`/ZFB?NIl' +7v/7Im' +7wON &l/7'ONR' )n/7 ' ))ONb'yc/79'y;O e@8    : Qc     .99991/90KSXY"3%!!'%sr)Iqq dqJdϨ3hdXZ@K:   99991/990KSX9Y";#"&5467'!7!%XY[ϭ ;Jss;JX%6IH#O(c$)br' +I|1T_m&vQ&(1TH{&QQm&1 /uT~f&Qe0'^IQ{(V+7327676&#"#3>32&nnZ98#T|'#'L~3ij>>Wot}Vn{$+73267654'&#"#367632\&moZr 'qXW!{ڸ!D]^s>%6ij|~A2B)G[\``21qBjJR0&2 7uZ&RRm&2 2uZH&RRk&2 4uf&R#H"b@7!"!"!"!""!:c c Q c"!    #91/220KSXY"%!"&5476$3!!!! ";P!B5VR!WH!k!v/1}{>᪪ђlêFlm7~j=xz{,8P@\-'@?6758'@@?  '@? '?@?:8/-59 - E5- W 5*eQB8/.- N N2'N'!Q9999912<99999990KSX99Y"o.]@oo o o jjo-i/o8 ]>32!3267#"&'#"&5467>7>32!7>54&#"267>7>54&#":Pz aUF6#;G_":Q;'8uP|4IB=Sk7<`(HCA_(J?@+TZ[nNX:2)+E@AD6NyGfkDVL8GNGB,jl MTEF5`V$QW l' +7v56m'vU &}53{&U m&5 /Buf&UeZ{r' +I|6s_m&vV{m' .ou6sJ&dEVu{&6su5{&V{m&6 /9usNf&Veu5&7ud&Wy5m&7 /`u&W )W*3!!!!#!7!!s!-p !pp! p+ժA@V!!!3#;#"'&54?#737!7!>^-- &uE+--+>1%91S5e:J>P^' ,7u8}m7&tXP0&8 7}m&XPm&8 2}mH&XP9&8r }m&XryPk&8 4}f&XPe&8s}sm`&XsR`t&: .J|\#m&dZDt&< .R|Vm&d\DN&< *9ur' +I|=Nmm&v]P&= 32Nm&]m&= /9uNmf&]e!#!7!7>;#"-)&_`яNSe;T/>32#"&'##7373!!267>54&#">f))HMmm|b" Q 0T.19eeS30:kTVϺms@?VTzzZV^l\XVo{T!*32676H#!"#7>332676&#Wt|0"}'5E8 #~2j/>orqp ˘//&r1F݇E,L>32#"&'#!!&#"32=DEdgh=in !&#"32>32#"'&'#'S^[Y*)/0;fUV56dDCYmnnnRRX랝+,S|170732#"7>3 !"&)=X<5F=@@=C!%# !2676;".#"3267DSRkbF@)JoZ98<])(`M9++8(8rGj>>~A:;>jH3 !#"#7> 6&!#VJKv6E8 #a@@//&r1FHKwd#";7!! &7676%3器~' F(H}Xɉ{+ˡdA#";7!! &7676%3|器~' F(H}Xɉ{+ˡdv 32676&#"7!#7#"32aS])*`Y$Ҹ>dˬ65%fLT}SW;7XR3 !7!!7!!7w!ir!VT!+t\7!'&#"7>32 !3276u,#(VV)U[LLK'!(_PS{@=))yz~#vuH(.76$32.#";#"3267#"&7>|i#0Is$`Y bp'ng("{ (({smz45$&ΕVM!!!!+7327677"U !%noZ98ժH#ij>>~GV$+"!!+732767!7!767676;b..)deF1i/0(#i,AJb)(gc``01N\''@g(%# !2676;".#"3267#7!`vHG^QoT?6B_;=Ck0N {KMonj>5MI!GRcI ! 7 3 3327P1ch8=?un+J&#767673#"'&76&#"#36763288C=PP-Ú77*8CBH"{.vhUUum14IBe``  19!7!!;#"'&7r!=!ǯ !Z ٪HE'+z=>jf7!!3!!#!!7!!7!!Z=!p !O9!!9O!pժi@676'&0##3lT%-WEq"ˁ r3r%BshT7676;#"33 ##&nnZ98`=wҢWij>>~ZFB?;#"&?#73!7!;+6BY饌&&ߠ  |~ĎɎd% ##''3:\.Ze:dn@Jo |%"%#"&33267332673#7#"&0wJF8Ǩ.FPS-Ũ/#JIQ-ŧާ,p?LWrHE! s{ p{+`>~}R]{#6&#"#3>32\ڹOq"{ڸ!Ds~d``ct\  32'&#"32767\LLLB,#(_S;#'!(_P:z~{{vu&2 {&R*6%63273# &#"327-_8>Pݯӱ90!@@CXY+͠v~^}R4{ %63273##"'&7327&#"}UWeRްeI:MV~,`/s%+n+͝u"mmBx +#"#7>3! 27676&+D[m^t6E8 #~'^!]mM//&r1FE8D V_*3267'"#"67232#"&'#76;#"I"*Q b@{˪65fo:DN5/2FIWS.OZ"#.+#3323 654'&#JX3{h.icG"4nPcEg3->nhy]oQك^Y% P0EI'>323267#"&7>?>76&#"hqV'qhہ*ti)$o^g''Ƞ/ vp{DI--յ1#hcq<;3{'>323267#"&7>?>76&#"sWZʸHszTq%pS Mjl}SX9!!1Yc55##}#JKSQ.. V>!6'&#"3;#"'&7# 76!23 n| wZdAE#)*/@)-0A3=g)(V\`@oV !!;+73276?#"&7!7!>^wKu&nnZ98(ϊ'w+>|bij>>~`>o4&#7>3!!#6R8 #!-/7&r1F+j!!;#"&7!7!7676;#"^wKuϊ'w*&nnZ98`|b`ij>>~V5;#"'&47!7!!0ߥE-+!s!- ujDE?78'8vq'Xd|M##"67#7! 76'7"a6$==$""+-4-+Fj"n8!L ILX(!"'&733276'3hHE& !Z-+Fia6%> ILɸC #367632'&#"u2-P?b("8t*b7m*9 VZ`+7326?3676'&xSU|lLbN1ôJY]j,QN$h:=HTN~)+) =  !3!#!!7#7!!C!P"! e!"՚i@Xyb!!!#!!7!7!!K-!8Z! dbҖg 7676'&+7!7!2! $7UU{rsNO!! 2ivZA=++KKKKIJG8+lh3! $76767637!!#" 76++fjki !!kjUU{rs݊hl+8GJIKKKKLn` 7!!#"3267# &767676Aj9e@zy38GFmgLxkj^V5zrܨ JKKK21%݊hm*8VC`'2767# 5476%$7654#!!7! TQSZ#^RYaQ.$Sej!NG6&, 5%b f8d1a w[ 6323#!!7!7!67676'&#"$$?1SP!Ex!u!!/" $4Dc``JU?T<>< % 7676'&#!!!!2! &735 66 W^#$!zFrO_}nw~FVrAV{#3676327654'&+";-!DZҸ7% S0 *vBz FN1pJ[05W2(E(Ar_3#+)'6vp3!!!!#!7!7!7!O_!._!dd!_.!_ls`nm&$ /9uHlf&De9m&, /\u=Nf&eRm&2 /\uuf&Re2Pm&8 /Nu}mf&XeP' *2'gH8}m2&q%<P' +`' *28}&H<P&8' *2 /h}&T<P' *2' -n8}m&B<pW{Hx2&qM<s'gH&$ 3Hx2'qM<o0& 7&Nm' /9u*;Hyf&eJJm' /9u.Zm' /9uNRe&2sueZ{&RsRe0& 7ueZ&m' /9uyLf&e5Nk' +9u*;Hyf&vJ%2763#"'&7!#3!36)44:>HH8hEՊ#w+w57Of'MSb9du'k&1 -FuTHf&QCo<k' +*uf&vk' ++u/f&v'k&$ 5H?f&DWm&$ 1H?H&D5k&( 5bff&H5m&( 1bfH&H9k&, 5=f&9m&, 1=H&Rk&2 5uZf&RRm&2 1uZH&R k&5 5f&U m&5 1H&UPk&8 5}mf&XPm&8 1}mH&X{&6s5{&6V5&7d'W"R+276$>54.7%>54.#"7$32+..;.9!)Fd2";͈:_$m`0*GL*(Ϋ/\KJ99&(?SP,Fu3~}=Uc 6Hj+v~}<2I%RO{3 7,54&#"?>54.#"7$32p/M1! ]P;E%AE(D!&c_'!5!:3%3?=a.pFTVU@l)=;Z:tO#C8;+2)!m' /Ku+THm' /uKlN(1%7276"'676#"'#7&/'&'&3232676&"C{!h+>65PjqSBԚ))DԐ>>t#O9 Y%Z5H7WSCT4V6!!3+73276?!7!m"%noZ98՚o*ij>>~Vb!!#+73276?!7!y- %nnZ98|! bܖij>>~%P&$ 3H?&D5u&(2buf}&H2R'gH' *22uZ2&q%<R'gH' ,2uZ2&q(<RP&2 3uZ&RR'gH&2 3uZ2&q%<D0&< 7V&\*l%7276"#7&'&7!7!676#"jC{!-wi $h+=F>O9aJwt# l?{.%7276"'676"'#7&'&?6&#"#3>32Cz" g$>r.O9aJs{``>^>>t#O9aJ;>V` !7!+7326)ʵ_nwB@12676&"&#"32>32 #"&'#7#"323T(~*))(S'PP~))(PPN+n=yO56z32 #"&'#T(~*))S'PP~))(PPN+n=yO56zZBC {J8FFJ!@w@) '&#"# /'&!27&'3267COV<eU[wZ.3G[M74YV EXU$ ~)) F/ndJI ~' @=( &#"# /'&7!27&'3267wJ9H)OSRtM *$6aQI&%uNh G`M7S]++ L8 rMq;>L 3!!!!!7!ˑ!O!dp! iAY #!7!7##'6{-!;V2J\Z[|BJ8jF`{5.#"3#"'&/&/7632676/&'&7>32`#FS}NGo!x{z#L'Tf@G)^.%{hA>zB:: Z9..QSKJ#}^R ~$=&[5#`cY1!GJ!b!;#"'&/&#?!- .{#L4[Tf@G1Z b ~$=&[?%x"#7>3!2##326&#6E8 #~'*+uʕʌ4//&r1F '32654&#%!2)#7332654&+3Xo}bи}^T|nȮkfƕ67#733!33# &73276!P}!}ˑː!+_Jj۬V,3"={;ww@  #!#+ 32654&##.+##73!2#f~PHW6yj,mnv{z чV/pijhybwv{& !!##733>32Z+" XX b(CՋRyXЙ<Ĥx~Hoe{ )32676&#"3>32+3267#"&'.=dl%| Nƀ%o^e$mX-+qpepӺ)Ld_y64''RR2{73#7#"32 676& ٸ>dˬ65&eT^ ))`ыSW;7WT04{%#3>32#"&&  ٸ>dˬ65e(T^))` \SWW '&#"3267632#"&'#7676;#"!S^*)_<U{>3 !"&'732676&#"RRkQE%=])(`M%++*,A:;>lw,%3276#"67&'&76!2&'&#"63 !"8K e 2&u45UNME!FJJPyyPMv+/#R9K9DhT#3 #+qrfrVs$2676&"3;#"'&?#"32tSB՚)*EBpH833&5EEP56 QkTC~>>jiáS,+;7WQ$2676&"%7676;#"#7#"32:SB՚*)EF&[]8H0/߷5P55 PkT ij>>~SW;7WSCW{632!"&'7326?!706&#"1/45[a$bX#]^"++98ȷpW{7!76&#"7>3 #"73267q#Xx$q[54ֿp/Z89++"Ţ{ 4%676732#"'&7&'&7>3273;#"'&7i&  ,g6G3  'ROQT"SQY P!10-'֊>8E#Z`vg'#d4*,#)u10`Z{h{0&'&76$32&'&#";#"32767#"'&7>r53QXZd cTTL @?PPc_^T"f]^Vyx _A@^  VJ=+,nQb54"[\mBA{2#"&'7327676'&+7327676'&#"767632<< U["EXWdeeKIQP ACKYYm n]_PNM_JJm\["45bQ77,+=J++  ^@A{B#"&'7327676'&+7327676'&#"76763273;#"'&7'^)* zy?B"2AAKNO54qv|f>? /0h8DCS!SGGÊO 0/,'=_JJm\["45bQ77,+=J++  OAf10`ZȢAWy+ 27676'&+7327>'&%672#'&RWSvAM1&F3/]TSg]OuvJBN^B6@JN67=6s-VWggVW>XVy`!+73267!7!!7!y \%ݥZr\ Fbh(Ӝ}}ؤiJH&07676;#"#"&'7326?#"63"326Y&\]19)(1>>#Fy6y"6X3. SmPIng\"ij>>~.,}^\::bH\ &0"326#"&'7326?#"67630!RPd1LN$YC">mĭ3,9b.,}^\:㣝y!%#7!#"76!2&'&#"32b7AgN[R55>dep!ijSGyyOMM">KMrqfqrkR` 7 333276>-X.@uq|2232#6&#"#7676;#"Ds},Oq"{&moZ98 `cJij>>~NV.->32+73276?6&#"#7676;#"Cs},n&nnZ98nNq!{&nnZ98 `cʀij>>~6ij>>~3#!!!!!7!!7!!.bb <mnm< Fˏ5i`!#"'&70!7!;ͥFE'o׊BZjf;6~|2` !!!!7!! mnm`By&;#"&?&#"7>32!7!3267#"'I%;7-";#"&?67# 7632!7!3#'6#"3`CY饌&%=&06%Kߘ6M \|~¾󠠄K9YVz;#"'&7!7! BYLG'|~jdL.;#"&7#7!!2#"'&'7327676'&+7!0Gg%W![T\G/-+LKMM&9W_^;32 +73276?#6&#"#6&#"#3>320vJF8e%ffR54}.FPS,}}.#JIQ,}ڧ-o?LWHEjj>>~s{p{``32;#"'&]pSS {ٵ!EX?>-?4),OO__^edwxH10`` { !3!#.ү{yyH{  #"%"!6'&!3276ҽ8888]R*7,3 $3]C{-m__mOmmOE`!!!!!"'&763#";_9pFbb65oo**EE`ݛlklm0{73276?332767676'&#"#"&7#"'&76769% 2"9$#u#$ 09*&%P; -nMO/Mo',2) `pB?UO++OU=]pxeY􎃃BcW}!*3#7;#3+737&'&76767676'&1I^78v//L^87{J9]*)2eU>^)*3"ouu{{BnmBHImnI `7327673#7#"'&'##,gg%sڹ"Nfg efNf21J 7327673#7#"'&'"#,di$ŹԹ"Ngf eif21gV`#7327673;#"'&?#7#"'&'"$,fh%si1F>?)"Ngf efN 10``f21VG{&'&#"#367632#"#,gh%Ź,"Ngf ef f21VG{#&'&#"#;#"'&?3367632###,gg%si>@*"Nfg ee10``֔f210{676;#"!!7!))eej0-rmnib`020{!!7!6'&+732tinotkѴ??V00`b `!#&'&+#!2327676'&#4**#rj)44a\\\ FBwHH77w 66^q$%'`PQi++ST** `!#!3327673327676'&+eT z||ڹ\aBBUG>@wGI86wSiQP`'%$q^667**TS++rVf{=.#"#"/;#"'&?327676'&/&'&76$32f"FZLM **=f?((**T@%$!*ML[[ 10``=Wg5-,QK((%$JK)V676;#"+73276_xr#hbîc-/*ceF1i0/pR[V((h)``01V!+73267!7!!7!7>;#">\&ܥZr\ F=$˳c]uh(Ӝ}}ؤiNQgcVoe;#"'&76'&+732i1F@?+cAJ(10`a(h((VaYV!#"327673## 76!3676;#"w |n*'Be*)#ibîd-/3A0Ǜo@`\V()gG`!7!6&+732!! >^vKuω'v+>>`|b@W!!;#"&7!7!~>^Kuω'+>|b >`!733!33##7#"&?)32763 bb:bb X"Wcau-./|i^Ĥrq,HKEioabdo?ܤdqnܑkmhq` !+"'&7#7!?27676'&'&B^"!.F??*oSg4kk/8QDdqn``;612abdw7,`!##3è}T`U` !###33Uŭtjj`j >;#"##u|lLbM2ózHTl}33#ff}8 ~V`!!;#"'&?!7!k \  ;5*+)! `ۧ10``%D`!#67!7!7!332!703276#H !Pej!t.$  FY3<;4% 6[L` 2!"'&'7327676'&+7!7!{juZA=+^``c&KmqsMO!ej 8*mh%12KKKJ#Lk`$- 76676'&+7!7!2#6'07&#"327L'2) NO!ej iv[@=8Q 9tŃ qL;*1JJ$8+lgqUeR8y*K/K'L327676'&#"76763 #?lI,MPTRV!SUTUlT$!rLbAr+#}swt#&'&7676!2&'&#"3YV!$UNME!FKIPyF+@ws}#+rAbLr83!"'&'7327676'&#YV!$UNME!FJJPyF+@tws}#+rAbLr[J#676!2&'&#"32767# '&7)$UMNE!FKJPyF,MPRSV!STUUjV$>}#+rAAr+#}^-sB )67632 '&7 676'&#"67632"'&^*}{)*P}}RQQQxx ((/. O^5ԜИٍccƍffff.""""./B!!a } &327676'&# 327676&#%!2#!F]8< #$^6U32 CV==cn10yyc36?\13+*?.Vi?>NNuTTz#y+";#"3$''"'&7>7.767632tCL1&E3-uTWQaMBcSTg]Ovu6=76NJ@6sx>WVggWV0%#7!#"'&76325676;#"#&'&#"32Z6tg>\]443D *PON(&;7a`P452 ^`03j&Brqfqr { 3!3#!#VVgg{A4V3#3## 76!3!7#"3276Z-ٵ)Ad+(;w {i0p?`я3A0NL` ## 33¹} kL T#{3!!dʽ!{/"V<)%#"'&76327676;#"# 276'&"n?LL]ONg]99&*POO&&κO((VUO()Ud0110d``01jtssttssxL$327676'&#"76763 3###73?lI,MPTRV!SUTUlT$!*65rLbAr+#}swԤ$7&'&7676!2&'&#";3###7*YV!$UNME!FKIPyF+@>F66ws}#+rAbLr #26&"3!!!+7#"32O8O8 vnT !^!4EANbg߀N/1%j+^Lۓa31DD10ML C3276'&#"%#7#"'&76323!2#"'&'7327676'&+7!OXWJHOWXHƼo!7>>Jz-.gmkzJ,+voV !?B3+9789&)nArJK(*ch cqjtssttss_3d0110d^L$8*mh%12KKKJ ,326&"!7!332+#67'#7#"3233276#O8N7|#s !i\/$ \]!6}KyZfzJXvn h *5j 3.#"#"'&'#"&7#7333#;732676'&/.7>32^!'^6Rb -R#n"#6757qB)vQQ>o>vEk3m:Neb#`>{=g?((TT@I!,KL!&`>NM55YQK($)$V%.!7676;#"+73276?#"'&7#73!33>#SMJ$&)PP5%N&'+.)vee>5jvUNZV()g)_`02PM`> &'pf.6@3632&'&#"632#"'67&'#"&7#733276#"&77#3>y3-.&!&,*0lQRO%Uu0$1(\rC)vQQ> -W N=.03rvE‚#+qrfr9DhT"2`>9KiNVf3+7327676&#"####737>;#";67632])DCm? ,JY|ooji$qhj;>m"<~-x+&?)TT?&$!,KL\[&:MV3-+RK(#*$JAD7 3!!!+sU!]rvGLۓ 333# #333# #t12t͉4Lt22tΉ4U= !#!#!#!#sS3k3S3k3UXrXHJ#32673##"'&7?6'&+732?l>]p {ҋwFLMX33-mN%5**)֟ybe22wx0t 10`` .V/!7#"'&7?6'&+73232673;#"'&7"32JtI=N^EutB4|JycwYYk\g883>32#6&#"#7676;#"4|JycJtI=N^Eum>=rB98wYXj\1Sw66WUzW 3+732673#t||r,B;tsxlX6Vr[t.#"#3>32`,b@uzu0S  qksa97]|v32673#7#"&'q,b@uzu0S  qkJa97*C32673;#"'&?#7#"&'>,b@ulC,r()/T  qkJH VX66x a97!+33276?3327676'&+@7 LMzt3j>))4{,&'}K,- ##K}N;[--s?5/. 333# #t22t͈4+7326?33YGkN\D0=/{}<{RpEW(K/itf9@:og910KSX9Y"#73)~g3#j(~f672676ʠg @j G@ sA?,__=>X@?X=>POPPV"'&6763"33sA?,__s @i F@ PPOP>=X?@X>~327676'&#"767632#(yC, 2q24454555E8QSoG=@*7K$@ ` XFh_@C~#&'&767632&'&#"3WGg:8Sh512,-//2qK+ *y=AmC@_hFX ` @$K7*@fR@91290K TKT[X@878YK TX@878Y3#'#͓ufNf:@91<90K TKT[X@878Y#373wx@1@0#kk+b+@1@0%#kk+h/O#!!rerJ';#!HreJN#`=#a/U 733##7#7-++++uyYu!7!;>ߖ)JHwDm)N h@ " I"10K TK T[K T[X@878YKTKT[KT[KT[X@878Y4&#"3267#"&54632X@@WW@@X{ssss;@XWA@WX?sstuX"@    9991/90!33267#"&546w=@/-!B "G"alK:e&%& PG7xV7@!   91<99990K TK T[X@878Y@=     ]K TK T[X@878Y@           ]'.#"#>3232673#"&/#*/}rY+D&/ )1}qY,B`1KN&/NMf\%3;!"'&7iDA'wd10`ZȢ/ 7673733276͂#$忉s /MM8 ;#"'&547#7![ 8h- o."#W<(@":Q"/2&'&#"#"'&'732676'&/&'&767632-329V00 Y'x*,XW8<;A;;<=Rbk'h'(PP@77 a /$*+MW33 k2-*)*IX01  #'#37ΉRH+#&'&767632.#"3WGg:8Sh512,-^2qK+ *y[AmC@_hFX `@$K7*@*X!!7!yЈ[*X!!7!3<4*X3#!7*xxhh*X3#!7<4*X%3!7y PfC?ffvfdV7tb+@ 9910!!V )JH&@ H H91<90332673#"&546xWUPjw HADJLvwDm$@ 9910@ @@PP]3#)F-jD2#767676#"76w #?:G: w[F[ƪ@$C!Xl05^ )NrfH@ 1<20@)////2222BBBBVSUU]3#3#҇LfxNfe#VVD*'4f#!#2ufxx)J&pq0)H>32#6&#"'A3wRUWn)H9fLJJLe #3Y#77#732oo1{73#3"11op{?f3#Мf'0Cb0v73#7#7K"_#:L#33"_"걈p!7!#_@q732676&'3#"&4P.8  m]0_hw-+76 >&Dzs5. :"&54763"]XbL]3+61lbo?wS?b#.A#>lq3!7373":#7!##S#cv{ #73733##'"""fck7!fc5V %+73276?&moZ98ij>>~PVE 73;#"'&7m !ZͥEE&~>>ji c/"@ 9910@ @@PP]3#H'_d.>@99991<20@@@@@PPPP]3#%3#((  1  0#"&5476322654&#"cL\]XbL\]Y3b233`0wS@o?wS?oQA#>QB#=>;3#Ous&@    991/90!#"&'732654&'+%#w,^/(I CK@]$]m<6N6ubs s/#WWD4/#7!#@&&@J9X#"6533273273"]Lt&uhf,vie-vGtR@ 91<90#373ēx:@ 912903#'#zmu8&@ H H91<90332673#"&546xxWUPjw ADJLv@:#454'&#"#>32w%)WUow'A3B"%JLH9f05@!   91<99990@=     ]@           ]'.#"#>3232673#"&/#*/}rY+D&/ )1}qY,B^1KN&/NMh/@ 9910!!VєmB]< #"'&'.#"7>32326"VOZg Gb3OU"YS5`D i]F ;73 !;?<6 7=&h!5&hhh5!Ĥ' MN`LMw@'/ZsVFFJ 27654H#3+723]YcL]l$-A#>bo>wS@43!735@&&@J /!!jjN66&b9X632#6#"#6'"#72tLt&uhf,vie-vGtRr_ '7'77VMdreMdrI}`}}a~~a}}`).7>77>76'&'RN/RN0PQn +0PQn  &Dq7"#6%62#&%nv..vd<<tf3tVH%#HVY #"?3;LX$ 3Vh#'?ffvF'irjf'>/`yf'DQf'f'Df'Df'f'&$%EG%!3:ceyy5(=+t\!7!#"32 32)r!P@@PLLLBǪ/bz~9,J.3#3#es+# 01 !7!7!!7!A!\!)A!A!ժ9R2S33 !!7 7!!"! !6!!A@57D<tZ&/3!737&'&7676?#7!67676'&'=Oe-.s!!Oe.-s!!O3P6"InO3Q7"+v[ssZttZss[v*DD*7*DD*9;67633!737&'&3^O?g?CCL*!!*DVLCC?0B["WDWx ֪ WW"#73&7323!7676 D"a6$=g=$"1"+--+Fj" 7oȼ߅LI L9N' *9uDN' *9uEf&ff&|Vff&ff&&Ey +76'&32703;#"'&''&'&767aSh#+68Uq Yo^D Cm5UW6: oik*%1)0T*XmY*)Vl!7#76'$`+^F6 9Td697+!k;%8Eb6=q'0 AV`&+7323#^1FzSSD~S]V_H" 4"32676&%&'&76$32&'&#"76*)e)*fh!2 f0"=DQR JD,  %i\_8888&L (=\ $+.! -&Jm@0326?#"'&'&'&767676;7#"'&'&76?6?27.#"YE$U^b$6{ x@4 ' *hv~ hI)  ,BMQX#iB2("P7Kp-*/6 B6 .(\=2 R%#677676'&# !7!@EYaD3' 9YM$f$%#KOxsPWKL,#%5,*3eaZi|V\{#6&#"#3>32\ڹNq!{ڸ!Ds}```cH6'&#"!3276 #"%%*fZ2%%*f[ҪHIIH<⇙8wyvs`;#"'&7#71lXn@?*n`+.0`b;`n8{ ##'&#7Ù#1Ee<2V`wa`!367676'&'31]|je P4&ޑ`T|p5dwY|rNįtR&%#677676'&# 6%$67#7! @EYaD3' 9\-c3&$$9%!*<KOxsPWKL,#%5,*p$Rݿ &uZ{RH`!#3267#"&7!##1$%7-%M%qB"T`PH? X%Vz{6#"&'#76&#"32VU65fn=HS\)*`{WS<R{$%#677676'&# !2.#"@EYbD3& 9laQF&<]()HIKOxsPWKL,#%5,*8(8*,A:no{` 32676'&%#"763!;^$)e)&@,884$ Gos*◗- `%;#"'&7!7!!lXn@?*g##j0`b`%"'&7!337676'&'1#%lrn*?@,4P4R}0.Տb`s{ZwfvtVh )"27676'&'2##"'&7673~A^i`*)84 Y`53NN^_4: G>d$(69g;ˑRh]c[斘n,mJseg.V`#&+73 3;# zE X1F EvX1Fz ~,~V`%67673#&'&733CJe!|~*NN_f*~|!C2;@WxؑoW@2`&#"&'#"'&376763767OpJoUn"Y&p8,@V8)II V?E+8H`{gLHk{A>oRyRo>-&j&juff&f&2f& 5$ # 7654'$'&763 '7676q ZiMKNCEBM \I,!2TpA{z Ʋ iR$ $6'&'&'&7!2#"'&3276(*f>5{B_NwR^YTTuomM9T^yhpw{A K~}Sj~9#6''&7676'&F ~~#*Tj}!Qu6hU0rROv~*+40r51_Tf'E9N' *uqVi'#&'&7673767676'&' OPg85tEERh85v [Bh)$7][Bf*$6VyvaxxGnռFCImֹD` !3237667%&# 67#7!C4FTW_QA1*\ct3NB$_$+T3 `/%76#"'&767676676'&632767vp9 yN @{],p9 yN , ZMf lh|TDY|lp"=lh|TDY| lcV #"32&'&32pP@?POCVLLBLK|O"y|•"jVo{ "32676& #&'& ㌻)*f*)eNNI^871Ҽ78w mw.vR#"32#677676'&#"'&76)Nd<;tzIEYbD3& 9w]PGǭ2e!+wTOxsPWKL,#%5,*˞nͱR`#7! 32'27676'&'&'&7676396: '2DaZD@bIH))rYĜ*,5%#,LKWPsxOJonlU\)GV#!#"&'32767!7!676327.#"@dOW0]+ )b9xA<[Fx,L 0_1s q_K!s7oQ>d1 7"'&7 6'&724V9-$DO6,9-$DO[~,`*l#FR`*l#FRva #!3a<l&pM]Vw!#76#'0#0?&'&zrdc&AqX.Ya*uh.nm3iFKI Mf7L[OF5`4 x #&'#&' #'@ֹ#ڹI( -ȹXVU65f7!S\)*`pp>WS170732#"7>3 !"&)=X<>++B$32676&#!3)!jjﰮDEXx@"(F"{f>d%E3!!F"!/ժq32!3#!3!!_M04ZTy_>>+B9,L B5(~B#333##'ݪϨttl`}ZZ5{ZSS0кvN #3!#"3+3m&L 2J.s #726!#S1?z eBS.+W++ 0+R2!#!#++33s&57&+7327676733Z[:UL\!mQ770ٖoJ+'/.M *5>0!47%73 #7&>4&'03  Y3es;tCoS zz w ȇ{{W9; #3!33#"")_>+B47332673##"'d_ /muh{ˀ~DMAr(Ex+:b )33333"++#333333#6"_>++B;%326&+ !7!32#~3!u++ )+ !3%327676&+ 332#"[f\G\w["u[+++J8)3;%326&+ 332#ꍹ3"u++ )L"'7 767!7!64'& 763 >)}y}|2!sD)xKHtR}_@}R̀sj #"32332 ##3(lk@@(kloJ{LKlu8o#CiyzjMf#.5476$3!#! 3!!"Edx )1{'g'e0:+wټ)H?{DGs7 -65# 3 6  &546?6767676%67ZTOF8-  123&+%5dS2/bLP`{WjYIUA   +td`#!2#!327654&#32>4&#>,ǀCycu4&Y}J6stSPPsTNʦ}J7u gQs}o`[+[` 33!33#+ڸ潸U8`66Lb47332673##"'& GGXvMaڸ[:T;0:o'+R8` )33333(ڨ𽨽`66`#333333#ڨ𽨽𽨽U8`666Lng` !!2#!# 7654+Xѳ(H #F`;p#Ѥϵ` )332' 7654+# ڶX[fM #SFڸ`;S@p#)`X`%32676&+3!2#.pٸXA֙YZ^X`;a6{? 67!7!654'& 763 !"a&gf,.H&tP69yհoGp{VkKW{ $6764'&"3  '&547##3&#%,B:SO9^ڸh4lkVd:A`;E`3.547>3!##;#";m0{ !ڸYT7G8n]*49;ImVabfm&Cibf&j#isV63#733!!63 76654#"sUU@M ~6 !3яLsFVs_A;m&vQg{%# '&!2.#"%!3267RRtt56aQF&<]sU,@H`M9++8*,A:pSdq;>s5{V=L=&jԯVM`"%2654+##+73267!32( F 0%VjX'#UuL!N5X)рiv;p#Ѥy`%27654+32+!#3!3# Fd(ddڨYlYp#Ѥ`9J3#733!!63 #654#"JUU@M>> !3яLsK^BA2m&v0nVm&C@lVH&wr` 33!3!#rڸ罸88`6f;%32676&+32#!!7!73!~J*++ ++q zQn`733!!!2#!32676&+nTTW; !H|wy͓LΧXZZZt\aH{E@3!3!F"?]/2$#!38=bX`:E !!##73!!#!ݡˡ!`!/>s^` !!##73!!!`aa!X$ ¸Ef#!!!2+7327676&#"!/V7ZY.<4~L!>GE&8]|9ժFwr|zKK"V^`#!!3 +7327676'&#_$;:B,5)edn*14! )`GQ``07 )~B#3333###'ݪϩsslj:`?`}ZZ5{ZSS/B0кv`#3333###'2ǫTTS_MU84XDDEPPlL_uN&KBuA{&kJ%3###33t]<)q"ˁE8h`%3###33Z6*ҢWھ[>4B?`/Z!#!#3!33#ʋՉ#v)v^9dV`!#!#3!33#bbڸWWZ`CX4K #3!!!#!"wlw"!d9` 33!!!#!ڨYYdfd`9ǖ6su&Uduq{&uh5 !#!7!!3#?+!t!+^++t` !#!7!!3#c#&#ɓZ 4D<V`33#ß~VV`lDJ33!!#!7!7 !RR!mP\PV`333##7#737Þ} // `l550%3### 33g]<8}3B`%3### 33Z6b*v4?Hk#6&#"#36?6?2a\Nqj`2X!P"˄+74H0I > ڗ*\cU'% T pTHK9,~Bm&J 2H&jfJ32+7327676&+#33]Y.<4~L!>GF%9]|r"ˁqwr|zKK"hV`3 +7327676'&+#33x::B,5)edn*14! )/Wھ[>wGQ``07 )&?`/f%+732767!#3!34L!>GF%v׊"w)whzzKK_9dVV`+732767!#3!3)edn*1fbڸWW``07 `CE !#!!3#"!/^ժ^` !#!!3#L$ȒZ`4_H #Hϸ1m&D 2HJH&dLN' *3uDH?&jdo{5m&I 2bfH&it\QpW{t\N' *+upW&j~BN' *WuJ&jjNN' *$uKBA&jkyL`50&L 7V&lN' *+uLV&jlRN' *+uRuZ&jrt\(H{t\N' *+uH&jLN' *uaa6&j&0&W 7V&w&N' *5uWV&jw&k&W 4Vf&wN' *+u[&j{ !#3"'&'&7332767673=^j+75G0H >,ZUNqj_2X!I$'% T pI*\z)b!#3#"&73326?36ZJ'A^s},44Nq!(~5" 2 N' *+u_&jHRJmR4bV{{TR`:\#`Z:&>73&'#".73327.'jA,?-9dQL}nb *_VM73I3_,@ J\9 ):D;x| ,\L06k2i#76.#"!!#>2@+ +`UVqH% ]!Wcܣak,[K00K[,#= |x::x|s`&##!".7>32 >.#"3!`!}WWjafon`Hu *`UVrK- &]V m=>ˍʀ;;ʏ7kT46ba6L>23##6.#"#kcܣaQ!W +`UVqH% + |x::x|a=J,[K00K[,@".73!!32>?3cܣbH!l *_VUsH% +|x;;x| ,\L00L\,)!!332>76."#7>2I^lld'g!D?32tW8mbG!~سs%^cA K1\in00nBB 7!!3!!!"I!ꪪi#76.#"!!>2@+ *`UVrH% !1cܣak,\L00L\,` |x;;x|*:"#>323##".>;7>&32>7#"\>ôqxwf "8RoIHpD.ToH0 .#":/&8$8-L1jysܒIKޓ4kq;3lr8{nڨm?AkP@&33##".7>3!"32>7H}!}Wgno\mj VuN/ "[VUtK( eBʁ;H֏ؑKBqpC4Tk75#76.#"#3>32 , +`UVqH% #T=Ona,\L00L\,3P&*;x|3!!+!dժ2>73".67##3ӌ8`?,;_{8e!H`1Kf>>fKIwo55owO2*B33>32.#"".67>7#2>7>.+Ƿ04S?eG% F6:4TSvT0-Tv[e4?yl[B%#?Wgu=@ykX@$a!##".7332>73T=Onbʌ +_VUrH% C%*;x|3,\L00L\,X/%.7>76$73(0R8&FhG0wuˮM-8@/7B(FO[3bV;3qqm`M-1<.'#".>32>76."#7>2"3267.8K\4P24gpHpGEdHgKEg8lcG"~سs%=W9?LK9}T=nf6a-S^&JnqO)MBa邂n10nBAkIHME23!6.#"#kcܣa!{ +`UVqH% + |x::x|J,[K00K[, $"!".7>7>736$33!m9 <`? {w?{;䀯=Y[ ,D,&KAȤ8Y>PQCzs3". y-^.!#".7332>7!cܣa *_VUsH% ժ|x;;x| ,\L00L\,89 .732>76.#!7!2>76&"#>32V^c 8h|Y8 ?iI!PBeH, h^edd& 1EW1@V- _MM`AqU1.Ql>?fF&*GZ0vpq{[i88gZ;hS<G`s".7#7!32>?3ecܣb! *_VUsH% +|x;;x|b,\L00L\,8h/2>?3".7>7'.+732'.#"009maG!~زr& zAR3k%-!{3zB) %,.Hon20nBEN(g N|!#6.#"#>2 *`UVrH% cܣaJ,\L00L\, |x;;x|m'6."#7>2#732>09mbF"~زs%!HYee`():*Z.Hon20nBEqhD!cEeNW##7>32#>.9YvN0 hᓝb  "ZGl IpX*9؜VV؂\OvO)E6.#".67>32!!33267>>.#"32>I &H7VpC  -Gcc[ pSg!D?(Lf9  +L>/2R91AGH 8{vlQ0e~y%L.+4^dzEEh|7)=(4[{G)0nw>23##6.#"#kcܣaQ!W +`UVqH%  |x::x|a=J,[K00K[,S332>73".7& *_VUsH% cܣb,\L00L\,J|x;;x|(\332>733!#".7iC +_VUrH% !RT=Ona`,\L00L\,ժ%*;x|-v=332>76.7>32#6.#"#"..EmDAt]@ >YfmeW9Qts\ /WA@dH. 1`}X&aw{p&KuO)&IjE:S>-&$.;SqLQn@@nQ4W>"!;S2BV<-1?af`yDE}|#76.#"#>2S+ *`UVrH% cܣak,\L00L\, |x;;x|8D"32>.'2 .732>76.#!7!.7>PeC')VIBdG+*~j`! 0EW1@U- ]c 7i|X6 ?hI!&ZL&AU`YC))CY`V@:iY;fQ:Ibq9_NM`AqU13Vq>:_D%9DHYj:!!#I""O, +3>.'.6>?3#^JjN9.&WHJlN9.&XItF6RG6S/gk23kg/WܜVzzVݜW.>2#".'!!#7#7"2>76.V[V{ HD:@",//"oK-2alI, ._vBDv^`wC *(Hf|gK*'HhA>fJR2 )3>#"&'.6?33"&7>;7">.#2>d0HeE"IUc;l6==!YUŚ @kgH|P)/D/ 'A| #ZRRnI/Ti!  iT_g83EfC B*F)md/5fVa(3#U⁐՘?mv%%#>7>73B >Zs@5~sX  1O@;jV>,M'fCb}*#>7632#".'332>76.#"Z~Z@BC}Y) 4Oj@*M=&1*,C)13sV$MvR3YB&0K6 %*?*  3!9!#z5`)%#".673327673327>73#7#"0uKDT% ~}GO*++}}/JI(+}ڧ-o?rHE.qw9=?78=s`32O//Nq"_VR,!Ds}V ab]Vsw %#".7>32733## 6& >feRfedST\ T^TXQ؈ۙRVT/VR+V3{3##6&#"#3>32 kRNq"{ڸ!Ds}ُV``abbr3!!326?3#7#"&bܸU/kPp!.!Dr{lLח0ad]Vsw%#".7>32733! 6& >feRfedT\ T^TXQ؈ۙRVTR7C )3!!!#/Uz%LVO{#6&#"!!3>32ONq",!Ds}Jl abV{+;6.#"#3>323##".7>32>7#" ?qRM{^C˷+!-X^g=fZeh$hz>HlC Kfx7".7>3!33O;lZD,ZrX? \\P Y{UU:laep;32Nq!ηuDs}-abV`!!3- V-327673#7#".6?>'&#"#3>32 JI()-|٧-o?HQ 1 JI()-Ϧg-o?HQ y8=?`7'32>7>.'#"!ZaYeS.`POzY: !&NzZ=`2onf*zΗUUzˍR!bSoAAoS3omf*BqV332673##"&7|Op!{ԹsDr|-{RadNBK]0E#7#".7>7.7>?3326732>?>.'#;Pb.d"O_n;Y~IWi8K+ CJ*  .'(N=MvV6 KCDzdG, /{[,J6Bve`D-?N( ?OU100$ EvV17]{DV3ywl'5{+V {3!6&#"#3>32 Nq"{ڸ!Ds}/``abo(;2.#"#7#".767#73>32>?6.'_2 56rtr5vm%t!!M\k>\{ErQD>5S0KsR2 OkHm *MnD Or,H4@qZ+^uAXuF76.#"'>32t*?' Qy|W >fG#MMGu+dqz@jg%W{rI/?$2msz~D;`C$%6"e-H47icK~q`LNB{QvV1!!".7>7".7>?3267#9& =Xn|@-gR, IBre%5P ;N'#?)Vx[:+A,:4 ElL<{7`q,`/'8% q7>76.7>32%">76.tX{qI  t&>) mW", '1' 4K]hm6fi)H+[N:  ( AK_ >cO}p_N+9 BI;=@#5ZRLQY45_Q@.5g4N3!ADFLR,RN=K?`B!!{$"#3>32!!7>76.>jR9 ڸ"EycB 8Qh@)IqS8*X4ZzFs`dcF}gSv94myPIa8wg`XJV~3326733!#"&JOp!йsDr|H/ӑRad-{.!#>'&#"#7#".673327673>32| JI((-o?GP | JI()-|-o?GP y8=e^`32TNq"͸,!Ds}J ab]H{J"`%!!3_&"ٸ`,V.!#>'&#"##".673327673>32| JI((֧e-o?GP | JI()-ѧg-o?GP y8=e 32#"&&#"32]?ww?deSffg'T\T_ccTVRۊٗQVTuZ{RV$+6##".'73".7>;2"6.#2>aӋRRKzdQ$8FW8b]% P}fV`;} k8vc^dACךU[7P2,J5.-PpCHpM'GH+OHBRZ`2Bu3326733!7#"&ܸPp!|_!Dr{l{/ad$R%3#3#1ӥ1R\$7'{vv?99>{)7&7676&767#"767#"763263 #6#"? ?B ~ ?B (('H(&ʑ\?!sBA?>BA?pпQQ92,"2"76767$32632&#"# 76%7676`,ΘA- IOaL@3l^0&Ͻ%JX4*oabu;\SfaƎ:F78UNQ8327&'"76#"%67&76$ #&67 #"'632`24F616C!/6!:pp2\ -;! 9' 2'#u`0'"/6OAK %[9.ȵ!>3 #"&?327#'&&776jefy3Lx/F. B BC quЍ h! ACBB_ )"32676&2 6''&7632#7676r$# %!Eec9};V #&!Of!*` %"%:yx~)RhKK>i 9"32676&&7656767$ 3276320! 76-676$" %"Y)=(qdz0y*/Fk?W0,S!sF3Ho %"%}@~Y9peDQcFlWNn-7V= ,2676&'&! 3%$6567&7'7$"%""/@Uppr=.<l"$~"L7 $! $?=Qm.uG4 0{$"276$#"76$76g ֛'&T*&''*(@@@PyxH *%"32676&"#" #"7323272 #6#"?$# %!Fz>6-U&*)C&"$C+7 %"%&PW0{#$"2"22#"7#&767663 #6#"]<"$,r#&##u|y܀ݹIedm%>{'&7766323276#6%$7"B CC "!()*$r11cRsBF;BCACKؼJjeh0 26#"73267$76767673!"'#@@>z]iF(+*s@0egu/ssM|"267&76% 3%$g+ܰ%'&!&%<9V &Ѩ'LB&"267&76! 3%$׀h*ܰ%'% '%<:۸W &Ѩ'hB P J"32676&"32676&+&'#"'&7632'3273767&76676'7$" %!$" %"cYt~@<w+''N f<<stZ!)Ѳ! >@#blb &"$ %"%IJTObo;4ˋP6A^g[oPI$0+&'#"'&7632'32737673"32676& kb{dXt~@=w+'(N f<<stm$# %!,PIIJTObo;4ˋ %"%2} ,"32676&2737673%&'# 67&76$# %!tn!<=#vi >hXt>. 2,! %"%4ˮ/IJ=%ۏ  ,"32676&2737673%&'# 67&76$" %"sn <=#vi >iXt=/ 2,  %"%4˯IJ=%ۏ`3323!"'#"763227Ӹ66?x>)`,88{ ,2676&'&! 3%$4767&7'7$"$""/@Ur>.<k"$}#L7  $! $&=Qm.uG4 - +"32676&6! ! &%$&7676$" $"T,>96mJG2))BX $ $8${{N& .%"32676& %&'&'&7!2767!"'676%" $"Il1D%,/MNfQ!S6=(w!$ $TrJco<7{"326! %&$7623 76! %@@$ƾ$E##e$!"SW@D?@w@@@@LBE>/} cbWZC %-"32676&%$ %&'&763 76.$" %!<<;z1 ɝ{#**E:8)"9] B<S %"$36H9G)#$67K 6"32676&$76%$76!232'&#"%$'&76$" %"T1$ 04KZEhrZ;U E?Oq\,)l %"%+DYl0yP^d6Qqt^}]Fk&$"2''&'$! '&7"32?6\gR1ʞ?F)+(#(&etmh% N)=md'aua ! &7623$76'76"4+-* A H B8 ?;kP$U.FM?>=({ ##"2#"73276#"IO7N&&&TR ;C >@{jVR777r'q/bT~i! ! !7 76! $%>=&=>?&'%~?>~Ti! ! 3!? 76! %%>=&=M'&%~?>wJ~T~i! ! !7 76! #7$%>=&=>?&'++%~?>~NTi! ! 3!? 76! #7%%>=&=M'&**%~?>wJ~T3<"36676'#"7632AB/D$e?Io[$N[u'"327"767&7623273,JH @@&,Ws> [yu?{EBXF@ '676%"7676! 6"3oc A"!7*9.`hH;A?~= h\$kb8:;-F_Zkf2)< 73733##7<****؎kP<r 632#"326##&иLT{-z!! 476733 654'&5473$a1ch A\??KibcW?3O)$YF,W9%3%! 47%67654#"&547! 654% !; -[ Q[|#4_X  A F+,Q7654>)jS:PrYz | f[|v 2K_[ ajBOeu>3['! 47!23654##7! 654# 3 `Eye,2 9 IdL] }!>>&-4T\GP?YN#! 47)!"363654# ; j2CM"9#Y|J$OFU[ڀGYH:Q?" .#67654+"#654+"#&54?!2363 .(7 wK@(66)M,3 w \{h4[`5.$N1P-+56fhhJ\QS1! 476733 654+7327654'&5473\Dp #wt @ ?R(#'&79BZ~/(I>3ه|0A'$"=T0KY2! 4733 7 7654%7%O0z '4B ; hk+2" 4? 3|g9 Ҁ 1b5\TҸ[?=4$Oۥ30@DgN{R=bbJ^ !! 4733 654&54767d=Ķ H? ]K[K<r'"xV}|8;?-Y%3! 47! 65'#z #A0)oYv"uR>_i=pd=v_-TnlY 5! 476733 7654##7654'#"'73263236;2LMe  /e40J>X>HAV++3OLg!k +34=Z{~,$*!tW;|_bc1:NW <K!# 473; ?654'#736?654+"!# 4?!23632654+"32VG48 /+ $PP7$ HS;(]Pw0s1x*FM22=2:(!e<'D:)i{ dyq+%^(i!@5`:DU7?1KV 76?&54?!3676?654+"#654+"'6&#"IA `_6Z/̌"+ dd:"<< +L2/fiS66PggJ^F>.5<(KHAJTUY"! 473$?## 47%33 73IFi /0l28+ .e/'18'!bMESB6 \'3! 47%3654#"#7654#"#323632654# 32\Yfe-8 n(U,Gܺ_ H#jJY!0%!#*!l.8/RAJ;(#$654'&# #! '54#"36326uAŲ4@:rBpc*7G06RNK\AM<V`M h3Y%3! 4?! 654+7327654'&5473bT?: XJ#leXf!saDdL۞h*"^)%w"]&/t[ ! 47!233654# 3 c/hsMWK[sNQ@vM<])6!23632#654#"#7654#"367  4?3$654+ <0d8( n)8_me+"G@DYM3b#!/%~߁Pc\Pc&fMrXD&YB5C! 4767 654'&547 547!33#;27654+"N4{ c{-2M] AAKUcB ]]&m%3=i^i$I&!}S^W/8M 0Ni7H?bJ+'%~_! 473363654# 3 g9«v)cvJ  `N_N^Q@O?HYC+%! 4767 654'&54?%773%[.U >s#..58#})0B@M}YiG>2Bwx}J$Lv?! 4?33 7654'#7327654+7327654'&5473F7x  'RdR[yuh-5>ee/'*ezLM h/.h~:PmYZ! 47654'3!27654'#7367654+7327654+7327654'&5473;T> ;kE D]D gkooz!&|W;F+ W`EG5,,#w j\Iy#B2)q""x 0/!I`#27&547% 323&'$654# ra jx>F)q) #< ` /M1(ZQmLI Jvjt| 7&U6({:F?O#p*!#Y3+!! 47%$7654#"7! ! 654'7)SX I]X{ =` *5 I;%7Y83<srg$z8A:08ԃM.')&G%\Ln-RYn('3! 654'7!+"'#; 3! 47)m; Y@D,2O;u 0-*Œ?.08Y(!p#09W$*!6?3! 5476%$47$7654! T 6u7 -C1 k7@u!yLL(/TK)."!).'syh"56&#"'6?&547%'54' |\_ZsWxUq:)^_ytJfFO=%SD@b*2 3#3#3#"""㰽s2"5476;%"654#"32 7֥ @$Fap% Xx$-80<LRQ(m2&q{ O3267>54&#"326?%!>54&#"7>32>32+3267#"&'#"&54671}i##N;ZeD;Sj+aUF5";Hj/]uUM1L#O8Rv2PyH'!b3C'\iV{0#"'&'732676'&+732676'&#"767632r53QXZd cTTL @?PPc_^T"f]^Vyx A@^  VJ=+,nQb54"[\m Pd %!!7!!!#73)q-я{,8P%#"&546?!>54&#"7>32>32#"&!3267"3267>7>54&:PzaUF6#;G_":Q;'8uP|4B=Sk<`(HCA_(Jb?@+TZ[nNX:2)+E@AD6NyGfkDL8GNdGB,jl MTEF5`V$QW/{  #6&#"71Ҽ7)e)/./  332678.8*f*/.+0[%!7!2676&#!7!#J$$^$WD"lp2rV$W?373!7!2676&#!7!#W'(A''##jxexE$$v:&"zlp2r+S")7!2676&#!7!2676&#!7!#rB0w!}o |p!`!`6, Y"iJo5FP;9JI9!c?Le !3#'!# l~n|U!#3#3!7##3!0;)it:T___D_P (3267654&#3267654&#%!2#!;okIjT1[_=]ydUZF+EO2BX?F(1]bFMX b8rt 267654&+2+$O=~؉*ؿN9Povu;ID !!!!!!.P0d;___ !7!!7!7!7ע;d0R_/__!#"&547>32.#"326?#7!`zPDP654&#"3267 &547> 'a`s$'`as*Y*>Z=hG6qhF6qרaJ^שcI 3267654&#%!2+#=XsHYyASJ1H]oP { #'.+#!23267654&#.9"TG C>zD|y:\hF[' ?]Z4DmNShPIK/CA !!#!Sڐ_33267>73#"&'.540dn  F67Q  od931qCBe#% =4!!!!4<8l$! !#D7> 333# #5|*Å|x yY>) ."326?#7#"&546;7>54&#"7>32KI+3A7]}Ot.{F`s§PI6~FD{8u E)/7wg+]58]Nm" 03e\Q:& 03267654&#"3>32+3267#"&'&54"&eu6D`~Ot1}Pl[;];@Ez8Wr??#6vh+f]85]:im+9g..-) 73#7#"&547>32267654&"-tt'k?W !@V -r .pNO/0P2;08,1]{w7+2^z O3267>54&#"326?%!>54&#"7>32>32+3267#"&'#"&5467 NC2%8@ +%4C 8 =5,[!&S-CXY:Q`J 51S02V$3K X2MZ )7",K) ',GH.P3>,1 `.+-,QEpz1 +/^%##%VIG4%267>54&#">32#"&'#3L5V$@?5V %C'h@gs4-0N>Tuu304=GH320?DK/0thQ=@G0/Og%"3267>54&3#7#"&5467>32h7T"@@5W#EkItt)h?gr3+/N>\103AGH311@CKDO/0ujP54&#"#"&5467>32!32676RIMB~AKC4Eub\8H  CKeU}wX=02k8, NR $(7!7654'&#"7>32#"'&5473267. !0m8LH9I2 !ݗ<'w$UT2!<&8 fW<\*0Q6O)3 /bW*0.547>32.#";#"3267#"&547>H93q?>j0Vh IUid^x \m>w5@v6o?# KX ]0*,Q>- )5a[C=S*0#"&'73267654&+73267654&#"7>32H93q?>j0Vh IUid^x \m>w5@v6o?# KX ]0*,Q>- )5a[C=S.267>54&#"#"&'7326?#"&5467>327302S#G?aHO"3d0.c2^g%nEar2-/F?`t3/2}32#>54&#"#>54&#"#3>32N16A PiN  05OiN /3Ojj F)/:&)=3M<g3B $H}g6@ # Cs6"#*++73267654&#"#3>32Xht9HW&HQnMtt+vHl>!pmwXEG$%?g`s^67d;*. "32654&'2#"&5467>gFGgF<{4/1\v4/2IHIHWxsM=BBzrL?AC4>32#"&'73267654&#"3i3{ "ߤ3\+&V:m Jmg!#4>32#654&#"4#a { 2XYuՠS2;6*3_zx4#"&54733267#a { 2XYuՠT1;6*3_zx%#3>32#"&7267>54&#"Fst'l?fp1,0M?Y3U#A@5W#FbP.1tiP>BD/(203@GG221@DKD!!;#"&5467#737'K1@jbK'P %R>B&TP+332673#7#"&5466TuU;5PoMut.qBR`|&,0h_b^76RG4}!7!267654&#!7!#K_fVlqi7"g32#"&'#N.08pdr6 RL&HP&:nPHJnf"2ZTZjwQwI,.uW[׸jp|aU `!!!7!!7!!7!!<mnm< pFFhˏ5iV);+73276?#"&7!7!BY&moZ980&|~ij>>~ #3>32#"&654&"26tt'k?W !@Vw -r .pNqO/0O2<08+2]{w7+2^z3#"&5467>32.#"3267!.i8D9;T8i2)Y5El%$'VV[).XX ` @?s'"@*bhj*!0#"&5467>32.''7'37.#"32654&NG/-0Yy0-0T  !YB2qIAi `TN:ACxrF;>E0 430pQ13 CJ$8%0#"&'73267654&+73267654&#"7>32PBŠ6o9,n?m U_chVeLW0pDEv3y a H*]fa;7#9Q0" ,] P75H3#"3###737>3<< xtx yvU/88P#P,l\>73#7!3#+7326?c3! Ut 2VPoQ/f18_*^j(kD|X ~5;xt4)2_zf[bF53N/8*332673##"&546UtU(FRmNtuJ+wGl?{#&?g`bO68d;)&3#!3#3!737#737#tt)>&&,r\PP\=#"&547#7!;+hH F)W 9t`:!(@Pp7 !#3!73#@ggP-PP#3!737#737#7!#&&-@-\PP\PP/3#"5476;33#7#"3276.ttŧytrx)? rPHB,^ssW?#5"  ;#"&547#7!:!8dM .!7X`32#654&#"eV!10X+a7[2SVR ;FixlX6V~a88f5/=w,!"Ck\[v+764'&#"#3>32;#"'&547B :G44NWW,`8[  C  1"U H/-B-56\sa88B$6/w6*3"%; )=x5*4")%F&'&#"#"/;#"'&54?32767654'&/&'&547>32,329V01 Y&v+9<  Cr'  :v=R21 g&h'@nb /$+0Wf , X6!9%/"19- *,X`>;#"+7326LG{no>; ~r-C;]MecU-:JxlX6%!!;+73276?#"'&547#737"BJDChu9"#,B"P#]m;t>g>t>uu7t7>=Jz0 N^B;\\`9C*D'0$+-6/&7!267654&'7!##"&547>72MlI27;!׎|]X\{'yK5\mY.`#{\8b(!S $Ry:6"3+"&5473;7267654&'&t [44*8a'"W8%/p+)mY/e##3{i{s !!!7!^=R^P!!#;#"'&54?!7!VG  2!V ^=R 5 X6,+; ^ ^#67#7!7!3632#7327654#Ma \&nIw n#8!!^R^=_jR ! 2#"&'73267654&+7!7!CJ9Cݱ;y>/xDxVhnL\& f2|mTP2I]R^% "654'&#"!3276  &5476& XY@8 M XY@9&S.S.l?32&VVL~g?f&VVM]XK_۩YK_ &$H ?{&DP&% 3;T&E2c&%;cT&E&%;T&Esuk' +u&&duf&vZ&FhjP&' 3w&Gcj&'wc&Gj&'h&Guj&'wu&Gj&':&G5&(Hf}&H5&(>f}&H5um&2&( 2bufH&2&H\P&) 3P&I 3N0&* 72;Hy&JP&+ 3THP&K 3c&+TcH&KN' *`u+THX&KjH?u&+buH&K&+TH&K0&,0&LJk&. +uZk&N +rucJ&.Zc&N2J&.Z&N2Nc &/2 c&ONci0&3 7 ci0&4 7N &/2h&ON &/2:&O k&0 +8uf&Pv P&0 3&Pc &0c{&PP&1 3TH&Qc&1TcH{&Q&1TH{&Q&1:H{&QR' +2' ,2u&R&tH3r&3 +w|Vhf&S3P&3 3Vh&S P&5 3&U c&5c{&U c0&Q 7c&R &5h{&U{P&6 3s5&Vc{&6sc5{&Vc{P&&6 3sc5&&V5P&7 3dP&W 3c5&7cd&Wh5&7hd&W:5&7:d&WPd&8_dm`&X0&80m`&X:&8:m`&XP' +2' ,8}&t&XH+E&9 ,\&Ytc+&9c`&YR`r&: -|\#m&CZR`r&: +|\#m&vOZR`4'j8$:\#&jZR`P&: 3\#&ZRc`&:\c#`&Z9P&; 3&[9N' *`u;&[jDP&< 3V&\t&= ..|Nmm&]dc&=Ncm`&]&=Nm`&]TH&Kd&Wj\#b&ZrVb&\r P&A 3_H"c&$Hc?{&Dct& .|Hc?m&d' -T&$ 2HJ&H&&&%Vzr&i%Vzr&7&j&kDm& 2D0& 7 Df'DDfr'F-'rjFPfC2Vf&)2V`& 2Vf&27&j 2V7&Tf'fTf'fV&h?ffvdxr73#3"e""ïd/Z9910!!P !/Zy@ 9910!!/yy@ 9910!!/yy@ 9910!!/yy@ 9910!!/y]&BB9@:og910KSX9Y"#73)~5@:og10KSX9Y"3#)/5@:on10KSX9Y"3#)/B#7B*t* e@3     :o g     991<20KSX99Y"#73#73& '~~y _@0     : og     91<20KSX99Y"3#%3#))/ _@0     : on     91<20KSX99Y"3#%3#''/) #7##7*s**s*;o \@0: Q   99991<20KSXY"3!!#!7!Roٮo\];;o@L       :   Q   999122220KSXY"%!#!7!!7!3!!!RPojoRRohn\\?!   & 104632#"&?}|}||{|?q?)/ w@<    :n    999991/<<220KSXY"3#3#3#<o;;///'3?Km@<%1= 1%+\C7IF:4(:"FG4"@ "G""".G"@(/99991/<22299990'32654&#"4632#"&32654&#"4632#"&32654&#"4632#"&H%'H_EDbcCE_yxxwyLaEEacCEayyxxy aEF`bDEayyxxy7a`JGacECcaEyxyEaaECcaExxy"GaaGCcaExxy @LP[f4632#"&62654&#"4626763267632#"'&'#"'&'#"&732654&#"/2654&#"2654&#"yxxyyaacCEڊE EfdE EfddeF FceF  FceeO:9QR8:O%'QtPR8:QzQtPR8:QyxxyaaECcyS  SS SxyT TT T{GacECcaa`JGaaGCcaEGaaGCca`F3`u`'jZ`r&',`#3W`u8`'j`*'',5m#@uC91907m+!{#R=s#@uC9190%77=+_# ^R?'4M_$#7>3273767676'&f #`o%ih\[..piR6 22T6J! 7 #D98``LAB\VBT=BR-;,,1Y7 B !#3#3!mVEF #73#7!!73'X;ޏ' 6 64x' 6^S?' 6'; 2###/%$ڤ0ЍH辸a $<4762#"&%#"'&5476767632276767654'&#"bJ&%6N89JKd??&&:9JJc@@Y6.,!/!!H8,. /#"@  ,kMO&&EDl{zOO&%ED8NtsdV'&5NtujO'&? !3!73#3#(wettPPD t@> JJ J  J: `   991<290KSXY"33##7!7 !iuw!#~ oy}y4&!!>32#"&'7327654'&#"1 <MM##A>IM`H~09v?}RR66`5782_ ?@gFAB0.rABaH() b64'&#"326.#"67632#"'&5476767632f''HbCB))Ha'\2TT%1;?LvBA.5BCRJJ-Bfg200?#"GFkC'&ThPP=87h?FC.7ABx]deRx<< a!#!u  $5A,:4'&#"3276&'&547632#"'&5476"327654'&M-,O`=>ZLc==H$%dcxBAkS,+̟POFG2UnNER98((A%%87V;F34();eHHfPOA@0.Ku99d]BBUZF7@11F2a4327654'&#"732767#"&54767>32#"'&NH`DC)(HaAB'.-3UT%.?>Lx-5RKK4.Bgf3//@DGFkE&&KKh PP9pgCBC.66BAx^Rw=< , !!#5!5!5j,__3!!f3_a!!!!ff`D_; #.547;%&e1/Q[jPO< 654&'3&&f00VQZiP#6&#"#367632KK;PZ;:Dy-@@Px^!{TP43`s^6~aTd\C{d3TtdTudCd4CdbTdaCdATdaTd?d8d di;fdi<fd)d%d.dszd$dXSd gd8nxdddd"ydD%dQ##"32.#"3267!!!!!!>DHEs.+mD;mC>F,o6667756GFDFG kN9,6<073&'3267#"'#7&'#7&'&476$;73&'&$/zE"/2. rd'ad"y#,'4yQ e_d zsZlL245+,#$- r9(YY34@W} XI FRlzd N2%6767#"476$32.#"3>32.#"JGkd'cef_ekR"PhM24WFy7W,6m ,Y348XGGa^lzkVeX]ɨ9!!!!3###7K4,-=Hϔ-#!!73#737#737632.#"!!!BB0@@!8y:&ecm.(*.BI+#6'&#"#367623632#6&#"#Se@ GT<=Inn1;:,teM=GhUNnM2FU<=In"xGM_a`f21>&>E3\__"&)''#!333#3#!###737#73377'(%hkkkhkk'(4&{{&&{{{?X32654&#+#32333632.#"#"'&'#"&54673#;732654/.545{TY=65M{9bcg9Y>^,T(#O)GQ_Y/J((cUc53pa 7!`". @:M46tY`;%y^ E326&##.+#!232676&/.7>32.#"#"'&;TdGT}CEEFty`L#6 3468J[ 1^#^Gv;d((]3OX 1Q#kN|45/b؍$~3YQKP%$((TT@I!**"&)-1'#73'3!73!733#3#####7!73'!!7g=&[8\89%=!Og!h "!Q;!uuguuguuuuE %2 #6&#!#)"33!3Ɠ5E@+SMrƔ6D?,SV*$oNw&ik-|@C$%|{c!|{ c '+a!e.'.-,+($$ &,$.9999999991<22299032.#"!!!!3267#"5#73>7#7]LJH'FGG28 /LU'PV1 1!*(A32#"'&'"32676767654'&'&67'>7632#"'.'&/#"'&547>3232676767654'&'&&#"32\!+"X3&; #$06- -$   #  U@'D[i&#  />$*)5' / 2/=! S6&X&8^) 5$  '*ޛkSo;48O07zfi8@ 3l8#?7%!73:'& )3^ ";6T$B~od4LNtFu05 .' #UB4F_A9pTne?<&#cE# 51A'632327&54767632#72767654'#"'&#"%67654'&#"~mfp,=,> Xa|jG) )l#tz`MJcg1 1.$Q./*`NkB04X``7a0<㫃%&†qJ< Na~hFl*!#??0W!}3!!&+!23#3#!###737#73!32767!%&+Vl[p[ `f(Su˳dcnmև %\MZ.+ZfJZYZ*/Y<[x&1#7&'&54776?3&'&'6767#7!'_L@_R_J&FYQaG=D;Gnpiuel_[6*5nnͦ$F_0,?'S*% ldYl7!3!#3##!##7373!/3|IH>2gbϿ'R{{{{{A667676&#"7>323#!!3267#"&767#736767!7e A^g'qV' o):7hہ*ti) WD 4cq<;''K={[/ {9b{DI--N@{ O/{!,&'&#2767#&'&776?)GLKff]]o)gc_\4d5]=?,e+\RV_P0- W6i----iH$"u9Bt"#BuflCg3 !!!!#!s!s!-+ժx p%!!##.+732767!7!&'&+7OF#O\cJ,+3|i.hc ݑVGO(32%3#%"&5467>3232654&#"5E$(H)-CD$PK1X.v@@+xF)Pڞjz'!4Rf}'!277Sq95/I:!*'?[jb!8Z@AKä^ChffDielmcfaBF5:3!3%!!!!!!Dfegq  dx+%THKTH632#64&#"#'?3%Ǘŋ]W!{ 1$ÑEmJHWEbOYbcJ %# !3!# GHMZMd q+y 02#"547"327654!!3>3"##726 4 42 3 b[y$U2 y$U2 .=.= 0Z/= 1Q~>;\>}N*3>"32>54.'2#".5467>32654&#%!2+#hjMMKLijKLkZZ\[~}ڶ[\ZZ&RXXRuJjhKLLLijJgZZ[~}ڶ[[}~[ZZICBISqmopB 33!27&#%!2+!67654'&` `s1:+YX*q jdZ)VV) (%#'#  %27&"676'&\ӿ,F E]]]][{ab[ 2222jT%%5$c$% &.2&'&+3!.+!!2!27&#676'&%3A::f&AVy-`5?vfAd)7%LK$201/O~hbb)j)V>U)- fh@6    :   Q  1 1010/91<<2<<90KSXY"###5!3###r}r7q^^-B0 %#!!!5!bJZCJ]d qddd J.m -)7 7673 $54$32!"53!25&'&#"6Ky {U>ZLtࠢ""38M{{M7M3TT<`xGZAEIpP3RQ4O/m{& 0'{uV/m& 0'tuV/{& 0'{V/& 0'tV/& 0'uV/{& 0'V/{& 0'{V/{& 0'V/{& 0'{V/& 0'uV/{& 0'V/{& 0'V/m{& 0{B} 5!!B#ZpZR#ZZM '#'"ZZ$MZpZ#B} '7!5!'7ZpZ#ZZM !737@ZZ#ZpZB}!5!'7'mZ#ZZ#ZߠZ#R#ZZRZM%7#7'3'ZRZZ$R"ZݠZ#ZZ#Za 7!##:nt':tna #5'#5!tn'dtna )53753dtnntda 733!ntd:ntB}3!'7!5!7ѓc}Z#Z㔎RZ#R#ZRB}#5!7!'7'7!'/cZ#ZߤRZRZRYxa532767676767632&'&'&#"#"'&/#7!$f ! +!3-68+2",j!!!3 .6+85.0$m: w '07)(6;C+ : ,:'+:Yxa5!5!#5#"'&'&'.'&#"'6767632327676:m$0.58+6. 3!!!j,"2+86-3!+ ! f:d+':, : +C;6()70' wB}!!'#537i&ڠZZ#ZZZZ#R#ZZM'75'3''#ZZ$R"ZZ&ZZ#ZZB}'73'7'7#'7!5hZZ#ZZZZRZZM77#75'73ZZRZZ'ZZ#ZZ&B}'!5!7ZZ#ZZ1ZZ#R#ZZB}'7!'7'7!'4ZZ#ZZ1ZZRZZB} 53#5!5뤤4Z#ZhZ#R#ZM %'3'3!5Z$R"Zh̠Z#Z4B} !'7'7!#3̠Z#Z4ZRZM 7#7#5!ZRZ4Z#Z̤M%'7'3'73!5ZZ$R"ZZhZZ#ZZB#(276767654'&'&'4#!5d >b-*,%:0Z#Z  *+(54<852.&Z#R#ZB#)!'7'7!"'&'&'&547676763"mEZ#Z0:%,*-11> ZRZ&.258<45(+  B#$>2+#5!5!54767676"3276767654'&'&'&l>b-*,%:0ΠZ#Z2)-019 o #*+(54<852.&ՠZ#R#Z};47(+ }  B#%?!'7'7!#5#"'&'&'&54767676";54'&'&'&e910-)2Z#ZΤ0:%,*-11> o #+(74;}ZRZ&.258<45(+  } B}X3267676767632267676?'7'7#&"'&'&'&'&'&""'&'&'&#5! ! Z#Z  > >  Z#Z" *!#$' * ZRZ %  '%  %' " Z#R#ZB!'7#5!3'7'<2Z#Z<2Z#Z Z#R#Z ZRZq` %7'7]JQgz=Zӄh PJV}e 5!#Z"ZǠZ#R#Ze !#!'7'< Z$Z9kZRZe !3!5zZ"ZZ#R#Ze '7'7!354'&/#7!J%%%'HD_SlhX[HJ%%%%Jw422-A8;>112-!:zJZ[ghX\HC+%%'GKY[eg[WMs2=>FD{2,/2{DF>H':Xy6#5!#52767>54'&'7#"'&'&'&54767<:!-211>;8A-224wJ%%%%JH[XhlS_DH'&&&Iz:d'H>FD{2/,2{DF>=2sMW[ge[YKG'%%+CH\Xhg[[IB}5!B#Zp{#ZB!!BMZZ#M3'#|"ZMZM#'Z$MpZ#B}!5!'7pZ#ߤZB'7!5Z{ZM!37ZMZGM!#73{Z#ZpB|  '7!5!'7 5!!ZpZ##ZpZZZR#ZZ*M !737 3'#'2ZZR"ZZ#ZpZMZpZB| '7!5!'7%!!ZpZ#ZpZuRZZ#ZZ#B|'5!!!!5 #ZppZ>R#ZZ#R*M73'#'#'3hR"ZZ$RZppZ#B|'7!5!'7!5!'7ZppZ#>RZZR*M%#73737#hRZZR#ZppZBA! '7!=!Z#Zp{Z{#ZBA! !! !5!'7BMZMpZ#ߤZ#ZB}!73!!!'7#5!!qVa6ZEV`6NZ#Z">RRjը;mRR:lNZ#R#ZRRB!!373'7'7#'7#537!7'!RRȚNZ#ZN|NZ#ZN.9#!RRRRNZRZN ~NZ#R#ZN RRB}!'7#5!7!5!73'7'%!7'!`]Va6.ZxV`6NZ#ZRR;mRR:lNZRZRRB}!!5!RRpNZ#ZNRRRNZ#R#ZNRM#'3'#'RNZ$R"ZNRSpNZ#ZNpRB}!5!'7'7!5!7NZ#ZNpRRNZRZNRRM%37#73RNZRZNRRpNZ#ZNRB}!!7/7'7!5mRRRNZ#ZNNZ#ZNRRRNZRZNNZ#R#ZNM'77#7'3SRRSQNZRZNNZ$R"ZpRRmRRANZ#ZNNZ#Z6a##7!#tn::n3:t:5p::6a '#5!#5'5C:3n::n:4:dp:nt6%753!5373:4:dp:ntn:nd:4:6%3!'3n:nd:4:n::p5:tB}5!!!!!Z#Zwgw"?Z#R#ZRwRwRB}!5!7!5!'!5!70"wgwZ#?RwRwRZRB}37773'''#5:;!\[`Z#ZCCjjZ#R#ZB}'7'7#'''53777Z#Z`[\!;:ZRZjjCCM%#5#535#535'3'3#3Z$R"ZtZ#ZtM533#3#7#75#535#5ZRZtZ#ZtB} !553353!Z#Z{Z#R#ZM '3'#7#7Z$R"ZnZ#Z}ʻB} !'7'7!+53#53Z#Z}ʻZRZM 7#77'3'3ZRZZ#Z}6B} !!#3#Z4ZݤZ#ZZ#B} 3#'7!5!'7뤤Z4̠ZZ#h#ZZ 5!5! !!? Ou]%uuv 333'#!#\^vtP uB !!75!!5 t]]Xv ###3!3,^\X& v 3'335%!!# #^\XtvpFguv %3'3#!5%# #3!^\^$tv~Fuv #3#!5#3/# #3!J\^^|HGetvJ~{GGMuv 3#!!5#3# #3!F\ F ^tvW~uv 3'333'37# ##!#^\fd^tv ^u9v #!5#3'%3'37#7# ##3!3^^ fd^tvJ^uB '#35!7'!!!5 5~t]]EF 7!##!#*:ntaI':tnIFEF %!53753!5!ldtn~ntd&Iv #7#3'# #3 3\^^tvP*OutuB}'0#"'&'#53676323'7'7%&'&#"!32764RvxN1kk2Ow9g' Z#Z 0GD2 & +JD5@3PO2BB4R,( : ZRZ11/0*M !#737'#'RZZ"ZZ$#ZpZ*ZpZ#Ba7!5!'7!5!'7'7!5!ppZ#Zp?ZRRRZB}#5!5!53!Z#Z[qZ#R#ZB}!5!53!'7'7!#p\Z#ZߤZRZB}#53533'7'7##Z#ZZ#ZߠZ#R#ZZRZB}#5##5#53533533ҤtZ#ZtZ#R#ZB}#53533533'7'7##5##tZ#ZtߤZRZB}53533533'7'7##5##5Z#Z8Z#Z8ߠZ#R#ZZRZ !! ?OuuuB 7% !5uzR##7 ! ?S:uuzRuu##% %!3!3hV[7l n7R{+u\ #&'&#"327673 u B!OO!B ocI7͙7IcL 0"'&547632654'&#"563 3276767&#" \m`cu\6% GGnth r5?,/H@3H5,Y:$UeI+HQ\N,tqzSd69->eSY׮l 7!!5!!5!!LLk+5!#7#53!5!!5!733!ZD2/+^^``kIb!0?"'&''7&'&54767>2"&'2767>54'&&cv-'''OO_@8vcu-'''OO_A:GE:;9($(#&GFF:;9cv8@_pm__ONP(-vcu:A_mp__OOP(-9;SPF($(9;PSF'O@*BBBB:91/90KSXY"#3 !q!#7!hqqPC?3 3ް2ް22013!!"&63!!"!0",Z(膆(\JN*"f_QQĪKM_fOPi%+i?33ް2&2'2$%ް2222//01%3!!"''7&'&6;73#!!#"!#L(0,:CyEB航6'|>v\JK-".4"$: 1cQı2#KK_ff_lFO]B/ 3 3ް2ް2201!3!!".>3!!"N=c(憆(c=֪I9[[9IPC?33ް2ް22 01&'.#!5!2#!5!276767!5 ,Z(؈膆(\JL, 1f_rĪKM_fOPi%+h?3333ް2$2%2&'ް222//01&#!5!27+'7#53!5!3276767!73&'&'(/-9CyDD舫6'{rx\JJ. 4 %:  1crı2ݪyKK_ff_lFO]B/33ް2ް22  015!&'&#!5!2#!5!2767>b(؈憆(؆b>,I9[[9IL9@ LL120!#!L^L= -@     <91990!!5 5!!LR%# Չ\P_X-yl10!!X!תXy!5!!5!3!!y!DCmILB#+U e+Gr?; /@     99190'%3##d)#Ӕ/}b%9;v 3'%3###"&'532654&+532654&#"5>32d)#Ӕ/>^c:r;Eq-evnmBJ]b`W,p;Eu2X}b%9lP|yQDJLl?<8?yvcG];e '%3##33##5!5 !d)#Ӕ/ڢtt}}b%9Soyc %.#"326"&'#"&54632>3"38\32#"&'#"&546329[=G[TFBi8\=G[SDCj~/[w~SNAU}^sdlkutcjmvu۠d|k֥s}T!3!T*,}T!3!T*p,33# NM^T,3 3#T^,$476767632#4'&'&'&#"#;9_UijB9 KGLV32326yKOZq Mg3OINS5dK t]F;73 ";@<7  6<Xy32767>32.#"#"&'XJF]t Kd5SNIO3gM qZOK?<6  7<@;" 37;XyG&'&#"5>323267#"''43OINS61-NSXIFJKOQdSP  ;@<7 W"323326X!!KOZq!Sc1NJOR`!t]D;83$777=X`y!!#"'&'.#"5>32326X!!KOZq Mg3OINS5dK t]F c;73 ";@<7  6<Xbz'767#"'!!'7#5!7&'&567676ǧfYUE5kIQ%\n*xrYQMoIF\<[ETFR q$"B2(d%(9L5XXy$!!!!#"'&'.#"5>32326X!!!KOZq Mg3OINS52'V t]Fجϯ;73 ";@<7 " 6<X1y0%#5!7!5!73!!!'#"'&'.#"5>32326Qu{hq,gqTKOZq Mg3OINS52'V t]FR=R ;73 ";@<7 " 6<Xy.1%!5!7!5!7&'.#"5>3273267#"'!!!!'hMEnK Mg3OINS523J:VQ FJKO!8!E$F";@<7 832326#"'&'.#"5>323326yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]Dï;73 ";@<7  6<а;83 $77 7=X0y8&#"5>327&'&#"5>323267#"'3267#"/'00NJOR:G67'43OINS520N]a91FJKO?J4r[DKKOdgb 7 ;@<7 !7)32326#"'&'.#"5>323326!!yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]D*!;73 ";@<7  6<а;83 $77 7=Xy7S#"'&'.#"5>323326#"'&'.#"5>32326#"'&'.#"5>323326yKOZq Sc1NJOR` t]DKKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]D;83 $77 7=;73 ";@<7  6<а;83 $77 7=Xy$!5!53276767632.#"#"&'y!JF]t V'25SNIO3gM qZOKج#?<6 " 7<@;" 37;Wy' %52% $'"51pZV(IٜXDz;%76767!##"'&'&'#5!!5367676323!&'&'&i1*+V WJRNMR  W,!::!,\HSLPM% +*%'H:^2:A<336G84^:H'@'H?Y L=@33/N0<^:H'%X`z!!5367676323!&'&'&!!i:!,\HSLPM% +*!#'H?Y L=@33/N0<^:H'%X`y'1 Xy& '11Xy'1o& 1Wz'1n& 1J. 3#3#!5!5J=>𹬬J. ##!!!!>7BX`y 365&'!!5!&547!5!!%43448>!0IG00GG2?8>;_8X`y !!!!"264&'2#"&546X!!IdddeH:l'**z{ BbFE``bq+((d:svvX`yK!!!!2&'56X!! BS X`yD!!!!73# X!!鏫 BZVX`yD!!!!33#X!!֕ BLVX`y!!!!!!'X!!߰ TU UT BX`y !!!!!3!X!!-e Bz(iE`07GO!!!!#"3###535463!3267#"&54632.#"'53#5#"&4632264&"X!!4@#mmC???DJB&G$$K&aqk[Q_B;18BCC?-I\\I-?p`ctiF6A?9i=$#tu#gSSSX`y*!!!!>32#4&#"#4&#"#3>32X!!."]?T\Y88EQY7:DQYYU;;R B=:xoHOM]QHPL^P%U20=X`y ,!!!!3#7#546?>54&#"5>32X!!ffc`--A6(Y1/a3\p$-, BiN2A+,/-7#!^aO&E++ X%y<@ l  l  <291<2<<990!3!!!'7#5!7!X}y}J;fժhӬXyB !!!!!!X!!!جX y%#5!7!5!7!5!73!!!!!'G=XkXU7Y Z:wSAw@Xy 7!!!!!!!!X!!!!߬Vw? (@l  <2291/905!5w!!LK Xy? (@l  <<291/90-5!!X#!!VVTw 3!!5!5V!!!!߬¶LK VTw 3!!-5!5V!!!!߬VVw#5!7!5!73!!!'5 p[5m{*[y~!߬`u,`vLKVw#5!7!5!73!!!'-5 p[5m{*[y!!߬`u,`vWy&%5767$'5674[šzآb|۠M)Ig#M(Jh#Xy %5%%%'w2rK/dtm0x0oVXy '75%%5%'rKnd.t'o0xEu0#oVX y!5!%5%%%!!'XC_^?sMN#N+PJ>`5Yd|5X y!!'7#5375%7%57'NEO>:fLNtt5\h}a5H<Vw?#%#"'&'.#"5>323265wKOZq Mg3OINS52'V t]FJ!;73 ";@<7 " 6<LKVw?!(%#"'&'&'&#"5676323276-5wKHGOZq M343OFGINIIS52'V t]FDE)!!;3 " @< " 6V w+.%"5>327%5%%%3267#"'&'&''}QINSE^AsMP#Bt]FJKOZq _4O;@<7փ_5Xc|6V w27'732767#"'&'&''5676?5%7%53;L t]FDEJGLGOZq P32326&%&%5$7$7wKOZq Mg3OINS5dK t]FJl#a;73 ";@<7  6<RO]ɗ9=}Vw*%#"'&'.#"5>3232655%$wKOZq Mg3OINS5dK t]F)a#l;73 ";@<7  6<R˖}=9"]OV[w67&%'&'5$774hmU֠Gc _eT2wnw2"O0Bj%V[w'567&'567&hmU*c _eT2Vwnw 2O0BDj%Xy_%!"'&54763!!"3!yɊD_`Dƍ^`Xy_75!27654&#!5!2#XD`_DȊɣ`^ȋXy> #"&'&5476;7!!!!"#'J_+30TD~K9# K^+#Eƍ5p5Xy> 32654'&'7+'7!5!!5!237RJ_+30TD~K9FC K9^+#Eƍ55Xy%!5%!"'&54763!!"3!y!ɊD_`Dƍ^`Xy%!=!27654&#!5!2#yD`_DȊɪ`^ȋX,y&%!!'7#5!7&'&5476;73!!!#"$UrG6:qYȲG5^_=R5 Yƍ5p&`=X,y!++!!'7#5!7!5!&#!5!27327654'&'92D4VqF53 D&#I`__ 2ȋ559`^`X0y!%!'7!5!7#"'&54763!!"3!!yR|ɊD_aDAQjfƍ^`5eX0y"%!'7!5!7#!5!27654&#!5!2yR|Da_DȊ]zTQjf`^nj^DeXwy1/3ް2/301!!!!X!w@Xwy1/3ް2/301!5!!5ywXy H/3 ް 2 ް2/33 3017!!!!!!X!!w߸Xy J/3ް2 ް 2 / 301%!5!5!!5y!w54&'&'3!!#!5!ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FތPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi372"&'&'&547676"2767>54&'&'!5ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FMPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9ՌOi3?2"&'&'&547676"2767>54&'&'77''7ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FBccccPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9ccccOi372"&'&'&547676"2767>54&'&''ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F,ccPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9KccOi73#2"&'&'&547676"2767>54&'&'ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi2L2#"&546"326542"&'&'&547676"2767>54&'&'h7b%&'qqnNL88OݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F)'%`8nqqMpLM77POO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi!'/7=E2"&'&'&547676%&'&'& 654'67676-ݾOO''''OOݾOO''''OOf:F-T1-F::E.S1.E:POO__pm__ONPPNO__mp__OOAϚ9FPQ9.9떖EQPD19Oi!;!!!!2"&'&'&547676"2767>54&'&'+{{ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F;gZfPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Pj.22"&'&'&54676"267>54&'&'&!5!ݾNNONݾOO''NON-R9;99;9FG:;99;:FFxmPpnPNPPNP^_mpON<<9;SQ;:<9;PS;:+Pi%!!!3!!#!5!3Ҍ8Ȍ7nj6Pi %!!!!53rM_Pi%!!!7   '3ͬc  ccc #c ccc Pi 3#!!! 3Xy!!#yӪXy!!5!3y--Xy!!5!!C!DZXy!!5!Z/`n103#`7 !!'  TS TS8X`y!532767>32.#"#"&'yJF]t Kd5SNIO3gM qZOK ?<6  7<@;" 37;XAy 755%5!5X!#!!ʶLK XAy % 5 -5!!y#!!!!KL VVw?  55!5!w!!!KLVXy? 55%5!X!#!Vw $75$&%&%5$7$7"nWlܜ86s˖}=9]OVw $'$'5%$5)n˱#lݷW680O]"ɗ9=}Vw)%*67&'&%&''&'57&%5$?7dMjTVʥ3˱!3a4m"cjX)3S][e﹏3N@%HZ-=}k$Vw)$(6%'56?56%7$'57&%D>WwZN(۷+/m")33 +Si063hiyje˖X[y3!!!'7#! !PYBzrYh?ݪ@?@X[y3!'7#5!!5!!PYzrY(s??ݪ@X>y!!!!!!'7!5!7!X!w R`RgfjfX>y%!'7!5!7!5!!5!!yRgw! RjfhDfVw?%%&'&#"5>327%5 %3267#"''43OINS:Z0!!x2XIFJKOQd>3  ;@<7 ҧK{"327V!!?E>XIFJKOQd>C43OINS:Z0"323267#"''&%&%5$7$743OINS61-NSXIFJKOQdSl#a  ;@<7 W"323267#"''55%$43OINS61-NSXIFJKOQdSa#l  ;@<7 W" # #h֣ͣG9> 3 3h*338> !!# #g֣ͣrcG9> !!!!# #gg֣ͣrrcG9)!##m ##7G9 33b"m!733QXG%C!!3#CrrCr[  C!!3# CrrCr[ %~!!3#CrrCr  ~!!3# CrrCr Xsy^!#y^ap$%%$~  %6 %!&'&"112*zz`XXroGGnY  67" ,J5PP5JX*77*#L8P"2642#"''7&546Ċnji56؝]QBɉLJo3NEQ\|G+-7AJT35#"&546;5#"&46235462+32"&=54&#"3#"2653264&"2654&#ςYxxYςZxxZE1/EE0uu0EE`Ev/EDaEEaDE/wZ\ZЂZwwZЂZ\Zwu0EE`E`E/1EE0E`EE0u0EE1/EXsy^!3!yߨys+~!#!r ~5!#r rS;+;!!3vrr;)3!rv;SLl4732#"'&'.#"0 Pd@7+ h$TA6?&H| #"&546323250 Pd@7+ h$DTA6?&Hk-khi !!!#%!!h\roa`޾"(I  !! #37!#3'Q''Ho99Ƀo!p=⻻}(TI #!!7!#3'l)okkɃ=r!r⻻+2" #/;GS_kw+7CO[gs!2#!"543!254#!"+"=4;2+"=4;27+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2%+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;22+"=4"=43+"=4;2+"=4;2"=43!2#\\K\=Kl]\\\\]ii\][]\\\\\\\\]\\]\\\\\\x::f>]Y"\I\\\\I`LLMK\y>>(I !! 3#)%3!'-''96=ûHJ7 hHH--JJ4!!!rrZm4 !%!!5!5!5!!mr4 3#3#!%!!5!!omrX4  ! !! !ZS;Z$m4& ;4 '3276'&#"!#"'&'!!67632!zzzzzzzzIwwHS;HwwI΍GG2GG$_EXXE^m_DXXE_3#%673#&'%676'&1rsrs2srsrTTTT@TTT|B B@B)B)΍11@4121tD'  ;4 !%!!Ybm5A4 !!!!;rbmY5A4 !%!5!mrXae4 !%! !2mrx1x4 !%!!5!!ZZmrϠZnZ4 !!'7!!!'7;rZZmZ1X1Z'327#"'$%'3632# 6'&#"zz=>lWWsPYX2灾vzz>GjX`OXG%3 33!#!!3/ .^+^kk4 7!!#xr;rc/Krmu4 % !%!50!53!ymrZ[z4 !!%!!''!;GZZHrZEZw%3 !#!3!###.77/^+kk4! !! 3!xS;ZK{mu4 ! !%!#5!5!"~~+mrz[Z4 !7!)!7ZbZmEZwZ4&  :4 % !3!5!y;Ūy_r4?'  94& ; :4D'  94& Z^' 24 !!!3#3#xrZm9&2 <?H& ; <8t'  <XyH&a <8XyH&! <8% *!%#567!676&'&'&'&|z*(2J E<iKH#&GIJE E|-2 M3 +O  ! r; ?9y?p  ! Xy*= 67 '&5677%"632327'&32767#"'&'.#"y{h p{"zi piE5 5dJ t]BFZCEF4 Zq Mg3ħĨ1P$s 7%ےs3 !Xy3#"'#&'&#"5>323326yKOGU43OINS52^NFz;7 (  ;?<6 '='3#3!!#7#537 `?+0'7ϺrSSr4!!!!3!!!'7#5!7!xrddeZmn;fժhӬ4&*!!!#546?>54&#"5>323#xr=TZ>/mNb^hC^XE&Zm]bRY;X1YnED98L\VBT=/6`;#"'&'#5"$lYoRQ`+.0`b;VT{6#"&'#76&#"32ttf,n䇅{WS<F`&#"&'#"'&37676376' 2KUXK2~)@V""V@)~`{gLHk{A>oRyRo>4y& 945{!%&'&#"!!32?# '&76!2!5! %%cjf_[_fMJOhk en(',g c\\c( r4`& :4`& 9Fy *'&'&3273;#"'&''&'&767,-b=MJMUHi;c( #) Xn^T).\-rv~ oik*%1)0T*XmY*). #5!!!#oo.ڭ%ۭ&H:07 %#"326=7#5#"&546;54&#"5>32hHH#[]E>Vcii!fHatLT5m4:j2O85%--JJ??8?vh+]85l[hmKDg/R: 953353!53!5!#"326=7#5#"&546;54&#"5>32as7s9s;#[]E>Vcii!fHatLT5m4:j2Op"r??8?vh+]85l[hmKDg..R:1=[%5!!5!#"326=7#5#"&546;54&#"5>32#"326757#5#"&546;5.#"5>320;0#[]E>Vcii!fHatLT5m4:j2Op""[]E>Vbii"eIasLS5m4:j2Op"rrrr??8?vh+]85l[hmKDg..R}??8?vh+]85l[hmKDg..Rk*.26:>B#5#"&546;54&#"5>32#"326=%=!!%%%5!55qi!fHatLT5m4:j2O85%#[]E>Vc,,,,]85l[hmKDg/R}??8?vhyyrDyyyyrryyR8~4<DLTZ`%#5&'&'&''7&5475'7676767537'5676767'7&'&'&'5'7'%654d3/D9229D/3d4.E9229E.42*  *22*  *2*88fWfEOPDfWg88fWeDPOEeWf8g * apa *  * aa * ba.43ab-45F^'04>2".33&'."#67>76#FVʓVVʓ"vv"Z83Pv"Υ"vP3ʓVVʓVVnh<8PvDDvP8"vP9~?9Pv"F^&0!4>2".7!&'."67>4'&hVʓVVʓDvP479;;74PvD"MʓVVʓVVv"i c;DD;g"vP F^ %7'32>4.#52".VFoDvvDDvYoʓVVʓVSwVF_YvDDvvDoVʓVV4!!!xrZm #53àZ03#s #5ˠАWeE%3 53Zz i#0s  3#àР˓[m#!!# q3#s!!`N um!5!#z3#zz3!5!`z m #4763!!"ƺoyeD9uߑfW#'&%'53 763:*enK==Mne( =C _AEc H<  3!!"'&59De{oVfd #3ƺ m 4'&#!5!2 9Deyo}Wf &'&3!3#76<(enM==Kne*!<McEA_I= 3#!5!2765 o{eD9ᏞfV3 wv% !!!!55!#uX ̼uu]]e! !!;bc;$<:.]3!3:~\T\~.%y5!!X3 2!@ 2 5!!5!!5!4)4𬬬 !!!!!4)4XXX 333 Nf  !!!@@@ Nf  53353353353𬬬 3333333XXXX 333322s's' !!!!@@@@22s's'!!!!\!!#!!#\!5!Z!!X!5!$Z!!$X3!-Ԭ3!-.*!!@Ԭ!!@.*5!3,,(!3,X5!!@,(!!@X3!!- 2Ԭ3!!- 2* #!!!P@ZԬ 33!!P-#,Ԭ!!!@# 2Ԭ #!!!P@.* 33!!P-#\*!!!@# 2*!5!3,Z,!!3,X !5!!#@PZ,( !5!33$,PZ,!5!!$@Z, !!!#@PX !!33$,PX*!!!$@X!5!!Z !!!!-XV !5!5!!,ZV!!!X!5!!$#Z !!!!$#XV !5!5!!$#ZV!!!$#X5!3!,-,Ԭ !3!!,-XԬV 5!3!!5,-3,*V!3!,-X*5!!!@,Ԭ !!!!@#XԬV 5!!!!5@,*V!!!@X* #!5!3!,-Z,Ԭ !!3!!,-XԬ !5!3!!,-Z,* !!3!!,-X* !5!!!!@Z,Ԭ !5!3!!$,-#Z,Ԭ !5!!!!$@#Z,Ԭ !!!!!#@#PXԬV #5!5!!!!P$@V,* !!33!!$,P#X*V !5!533!!$P-#ZV* !!!!!@X* !!3!!$,-#X* !!!!!$@#XԬ !5!!!!$@#Z,* !!!!!$@#X*5!35!,-𬬬!!!-,XX33*!!@@*DH5!5!xX333x 2 2H !!!!-Rx !!##xmsZxH !!3!!xm3-sZRH !5!5!5!,NX 5!###lZZXH !5!!!5!4l t,ND 3!!!--Dx 333!x,ԬxD 3!3!,(D 5!5!5!3,,D|X 5!333,,(DX 5!35!3̠| 3!!!!-- 2Rx 333!!xs 2 2Ԭx 3!33!!-s, 2ZR !5!5!5!3,,X !5!333xtZ, 2X 5!3!5!33t, 2H !5!!5!4R 5!!###sZZH 5!!5!3!!t,-sZRD 5!5!3!,-DX 5!333!,,ԬD 5!5!333!DX,!5!5!5!3!!!!,,--R5!333!!###s,,ԬZZ !!!!5!5!333!-s t,ZR, 4763!!"Q[yY[`~| 4'&#!5!2.-Yx[Q`~=?x 5!2653#xY[Q[~|2Ψx !"'&533![Q[Yyx2|~>3m 2>#3> 2> # # 3 3>ݲ}#$cc|5!F3F~|5!|iF3P|!XF!@F~|!|iXF!@P5!5!!5iVV333PP~P!!!iXVV#!#P@P~P( ?! ! !!!!#!#(!(F(!Z((!((!(h(!|((!(*(!>((3(i( G} F( #'+/3!33!33!33!33!33!3䟟䟟䟟mnmnm(%8K#!1!!!!!!!#!1!!!!!!!#!1!!!!!!!#!1!!!!!!F????">>>>#>>>>">>>>(((((!%)-13#3#3!3!##!#3#3#3#3#3#3#ޟޟ#|ŸŸ|Ÿm#( <#E( JZh!|i Q}h( Q(& Q& R S(& R S(& Q& S X(& R& S Xi( Q}(& Q X(& Q& R Xw!N<w7!!!xr$<w 3!254#!") ) xrVVVw& e \w !%!5!5!5!5!5!5!5!5!5!N?:IILII޸[["[[w !!!!!!IIN< w !%!!5!!!I) NDJPV\bhn27654'&#"&7367'67675673#''5&'&'7&'%67'7&'67'%7&'&'%6767%&'&$h%$%%34$&1++XSA N@`==k>P CRX++XYC P>k==l?L ?Q oL+ Nn;P?;@  nMNn3%%%%34%&&%s==`?J >PW,,WW? K?_==f?H?PW,,WU?H?^<=Ke+cL mCP`k<<!4(0847632#"'&7327654#"&#%#&7&'67&'67!󫪪vӤ=6 5N'V[S.U[R󫬬񫪪񿉊 ʯX[V[X[V[!4(0847632#"'&7327654#"73$3&'67&'67!󫪪vѦ=63QNV[S.U[R󫬬񫪪񿉊w  'X[V[X[V[!4!)47632#"'&%#$''&'6%&'6!󫪪4>;D@KDzcngk?dnhk󫬬񫪪I kpinipi !4 "*2:AIX3#''%#&'52#"'&5476!!'5%!!'53'5%3'5%3#'32765'&#"M==,/0#H 8&O6 |7iY06./==e6a&i1r4z012+KN2HQ>>>>f^2"/1]8`1"Y 4f2y`1B7#5#53'&'&54767&'&=33676=3#327654'&O&"}|fzg}}"&&"}UQn$mQU}"$nQUVV{xVVUQ<"{u^^\ _u{"#| zUOOUz |#YOT{zQPPQz{TO@>)4'&#"3276&5476327#'#53'&`____`oŠqk]^^]YYňÁhgf@> '"3276'&'7#5373'#"'&5476j___``_ߓqŊqYX]]XYfhhĈÁj0 '&'&376&+"'&5'476%7!Z{z[ZZ[~\YWmpN#ZX[[YZ[PQmp#TG*52764'&#"#463233#!5sPQPPtrQPyzg֏LQQQPPQr{{t|g*#"#53533#632#47654&#"#ddiqqCBigIIugzyUr}ppDtPQs_CS 7"27654'&7#"&54767##53#533333#3##h. @\ ! 2(>>?ZW~>'3|}}! -/@ /- !^'?XY??~YX?(F}R}hh}}hLS<#5#535&'&'5'73'3#'73'676=35'73'13|e{vw}wwUATwx|xxS@Wwx}vv|d|re{Eus~~suE|VAKtrrt@X{Ius~~suI{dr|*! #!!!'!27674'&#_82V)3{D#MHZW{s{?zK8QO##"'##565'##"/547?kM ,4N"DF &Fi?JO/FB!O {|Im<&=M2227632#&547636=4'&#"#4'&#"=` ]d2 cBU;/G;SXMB:@B ս;7hf% #>|\@9@O &&5 iC n:^O G  %2O7236;2"'##'65##"'&5476;235&'&=476jS c1=EO ;SCFRʝT6*F@E1;O+.`162V Yi8/D ;8[B VRP"<B+"'##565#+"'&575477;2732;276=4'&3&'"ih;F(wQ"DG".FWCNfBy" bODUq5u4  Pro@ |S`64 'RHIOq 1 ӫg 4D% 3363'$6'"I+4 puoS^*  3%#'#3%#';&2 IʗHj7*(,377#'#'547#5773%%,ppsr,'zzxz'984?/99e5>:_&*'$676#"'67667 h5X@!2 ^:H~](#7NE SRQO1̡LH~&77$7676&#""~EV*( :^#D89hH܏1OQ S u#\u ! !h i/u-1 3#˪5hppK #3J5hpu#\u h u-Xqy33#####533ܨDDDDXqy333333#######5ʨssrDDDDDD;Tw7!!!xr$<w!!!xr<w7!!xr$<w%!!Yr$<w% h5bcJ6bw bc66bw ! bc6bw! 5bc6b w #)-17%#535#5#5#5##5'3#5#5#5#5#5##5##5###5ZssssVrrrrsZVr~rZHNrrrrZZrHNbVrrrrrrVL +;#"'&47!2#.+3 654'&#ōߥE-(mPcJW3|i.ic g3,>jDEoQك^nhy]k7% P0EL 73#7 '&7#"326TD޸&;X2Pٔkk;!ؔ;!V+u'AҨz5.V  ##!!+732767-]r"n&moZ98'ij>>= %!#3!3jV" ѲR{p '#36 3265#" :D"&X2ٔ;!ؔlk;!}fu١5.yAҨb3!!#w! 9V.`#3!rdڸY`9Mh '"27676'&'2#"'&7673WA\j_,+:5 ]d97^`5;~ F?c%*9:e;ˑRh]c[斘,mKseg. !#7#"&'732676&+732+E؃Do/$2rI(=cݳ$w"$.*gQH{"2>   676326pJrҽp-pTT (nI^[]-=P@7'"]_$5zWXUdY 3#3)mKqD{9;#"./&'&'732654&/.54!2.#"y@Q!3?.E7@52ed)bpXeJ P_'Q]by٢UO #8$H -EBRZ)!:y(&;<GX('2p#7!7!;#".'&/&#!cO$FJ(Q!r I[35-5`o^+7&()6?$36767#"'&7676?>7#7 #`o%ih\[..piR6 422T6J! 7DS #D98``LAB\VBT=;-;,,1Y7!##m#7!#o3!3V!733;X)u$%#?>'./.76$32.#"22" &GI.gS%TOl /II{4<5/VVL89CFnY1^5YVe*X3# 3 X0X[*X3# 3u= $dX*X3# 39y X*X3# 3 X*X73#3#X*X3##3Ɉ X0XX3##3D $dXX3##3A XvX3##3̪} X:X%3#!#3 X*X!#!!/ yXX!#3!!/ <ẌX!#3!!/ xXvX!#3!!/ Xd:X%!!3I X{ 3'#':Yyj?CUC-U #'7374:Y{j?CUC 3#3#7G3f*D #73#73ހG3fRoUwD ~d37 !7 !7  |W|;pO:$-H'ŢcmZ3`!7 !7 !7 3T6T )~6|MY[ 7! 2LP)j *.1 V[{ 7! 2LP)j %X.1 V +732767!#3!&moZ98׊"w)wij>>9dTVH!+732767654&#"#3>32&ܥ0]W!{/tKb Ӝ G'QWab ^4X#%[Iw !7!7!7!>>I`3 #ʁX2qeQ10#ll+[V!6#"3;#"'&7# 7632!7!3#6M \kCYKG&}=&15&KߘK9|~jd !33###_>3B3)*{3>323##67654&#"# Kb jU8]W!{`ab /03L G'QWa %"32676&'6#">7.7>3%"*)e)*fo888-:G% ߞ"(gڜ-*/1|kI5b@`   :        2999991/<2<2990KSX9Y"3#'#"!#!##737>3/-J__۸%&Sec/яNb@K   :     99991/<22990KSX9Y"!#!"!!##737>Ѹ__)پ&{Sec/яNT#'-7>?67654'&#"06763237 e3232673#"&"*&"6}#|U">##$(8 } ~\79 32mn 6.mn=Y910@//]]KTX@878YK TX@878Y#]@ 91<90@/// ]K TX@878YKTX@878Y3#'#՞ј5c@ 91290@//// ]K TX@878YKTX@878Y#373-՞ї /mf@ 9910'F#ll{Wm> #.#"# C9wVSRo{xz=5]8796{m @  HH120332673#"&wYRSlw#m6978w{zP"@ 9910@ OO__]3#(Pck3#3#=Gkc'k#!#Vk,0#7676?>7654'&'"#"7676323#/! HK40 >2'@M*N=KL |2! !#OJ; 2bEBTY;X1-%#D>YHiTz _<ɐ0ɐ09mdhsR!uX/bb{\)?XXXXFs5\N9NR3R PRdHH;wb;T=Z7Tubs}\NX{/dJX/+X3?}\R///os55559999RRRRRPPPP37HHHHHHbbbb====wTuuuuuX/}}}}HHHssssww5b5b5b5b5bN;N;N;N;T,9=9=9=]A9=ZN7N7N7N7XTTT(}RuRuRu# 3 ssssP}P}P}P}P}P}R\NNN;T,Di0CHdAv3tHG'G97T%c}t *}x I oMX XArvsH9=RuP}P}P}P}P}pHHoN;ZRuRuN;To/HH5b5b9=9=RuRu P}P}s"RT4H5bRuRuRuRu* Bxxxvo0UQCp{BJyk%N2YCm!A Ecg00 r)c@a~D#'8[^az 4N"xQD H.z[]*fVhO/yw/"[?w*?'.lvf5P _4:u@0h<x4r?QE5t9R3t9E|EA_|8uH%{t2u299q\H1v t73ss55PEp99\"K&Eq5~sR3s&03HGdbBVVVuV1I+nXa;bbss==JVrfntEEsE~BVs+T9~VE_HHo5btptp~BVVRutta&&&HRbR\:sL@2/L88mWS(-8,R V'}5]+b]7JN]+o&5Nv\9wJ-#],u\2Ni=0H>M& 2 -7Ck(TTTT'@E_<{^,UTSX"W[Y"QK^lN)KU\3[]&_HLIS"-=#Rssi +V+A)&%$**? +.444D+#?[+#I3-*%3>*&/t [-%6P %H;;;swwhw:5H5>5b\N;TTT?bT00ZZZN N NhN:TTT:Ru33 hssshh::P_00::P}R\R\R\R\R\NNNT\_HHHH5b5b5b9=RuRu P}EEEEEEEE^|Ki||||||||8jjKif^uuuuuu ^22222222^EE||uu22EEEEEEEE^|[[[[[[[[22222222^EEEEEEE22[[[|[; Q8j99mj%% 22222TT?d//);??Z85=?Mx?'a?4baAaa\34baAa)%.$ "DQNN9-. w! 3<gpt:TTJyB%0 /////////////BBBBBYYBBBBBBBBBBBBq?QQ2BXXBBBBGB*BB*B*BBBBBBBBBBBBBBBBBBEEB*BBBBBBBB%uIXX+?;;;)}}?5XJWXXXXXXXXXXXXXXXWXXXXXWJJXXXXXXXEXXXXXXVXVVVVWXXXXVVVVVVVVVXVVVVVVXXXXXXXXXXXXXXOOOOOOOOPPPPPXXXXXXXVXVVVVXXXXVVVVPIr% % XaGX++|h}2H%%?XX%XX6FFHRFFF    :xxxxxxx||iEiiDDu777777?aa"!!!!GCL2 1rjD7~uuXXLVM zY)[{-U3TX[)abb??/^?DDDD@p, x0  H  @H d|`8TddXt8 8!l!"#0$T%%&&&'<''')|*+,$,-./01812345d6l7X89\:(;;<=l>8>?@ AhAABxCTD8ELFLFGHhIJKK8KhLMM|MNOhOPQQQRdS SSSSTTUUU4ULVTW4WLWdW|WWWWWX XYYY4YLYdY|Y[[0[H[`[x[\p]]]^^^0^H``````aa(a@aXb|bbbbbc chd|dddddeff,fDf\ftfffffggg4gLgdg|ggi i$i<iTiliiiiiijj,jDj\jtjjjjjkDkkklll0lHl`lxlmmpmnnn0nHnnnnnnoo,oDoppqqq4qLqdq|qr\rtrrrrrsu|uuuuuv v$v<vTvlvvvvvvww,wpwwx x$x<xTxlxxxxxxyy,yDy\ytyyyyyzz@z{d{t{|D|} }~~~~4LHP`l|8D@( pD`| $<Tl ,Dd| $<Tl,,D\t4Ld| $<Tlh00 @Xp\pPXp\D,tt tl| dT ,l|@@x<H \ „ÌTĸ0żDpǨHlppH̸T xϼ$ht,lҰ\ L\Ը,l4֬ <L|׌״(DTd|ڨڸLې4lHtݘݨݸ,<ޠ@P<T0Dl<dDTp,tT8Tl4|4DX$<Td| ,<hx(L\l$4d 8PhLxhXD`PD\tD4Ldl hhxlx`p 8Pl|(  ( @ X     0     < T d       ( 8    P   pXh|dtT$8ld 0HX0p L,TlT  ( @ t   !0!p!""h"x"""#$##$8$h$$$$$%%$%4%L%d%t%%%%%%&&$&4&L&d&|&&&&&&'','D'\'t'''''((x(()))()8)H)X)h)*L*+(+,,8,-H-..@./l/0(0181223344|4566d67p7889H99::::;<;L;;T>>?@@L@pAAAABC CD(DXDE<ELEFlG<GGHHHII(IJ@JPKKPKxKL0LMNNOXOP`PQlQRtRSDT4TUpV V`WWXDXYZ8Z[\(\D\]] ]l]^^|^_,__` `xaaLa`abbc(cd`eefDfg(gh0hi4ij8jkHklTm0mnhnoxpppqrhrsXt uuvvwLwxyyzzDz{|(|h}X}}~~|~$pD| X 0l00 TP8($DH88xHt< Dl@HLp,P $X$4Ld|0H`x 8Pl0H`x 8Ph(@Xp0H`x0H`x 8Ph0H`x0H`x 8Ph(@Xp(@Xp 8Ph(@Xp0H`x 8Ph(@Xp0H`x 8Ph(@Xp0H`x 8Ph(@Xp0H`x 8Ph(@Phx(@Xp0H`x 8Ph(@Xp0H`x 8H`p0H`pˆ˜°,DTlÄÜô(@XpĀĘİ(8PhxňŠŸ(@PhxƠƠƠƠƠƠƠƠƠƠƠƠPLj4Ȍɨ@ˤ|̼͈͈8XtАа@\,`|ҘҴӰԠ֜<l׈װ p؄ؘج$8L`tوٰٜ(<Pdxڌڠ܀@dd@Xp|$(@@X@hd\|<\|$XL|L<,tD8tH`xT4l,|@hX<H( LD  D    p   X   8 t   , h  L d(lL,|4h$H|$P|$P<8 t   !!!"P"##$$$$P$h$$$%L%&&t&'`'(p))*+`,T,--.H.`...//H/00X001$1l2P23`344t455p556T67 7t78089 9:4:;$;h;L>?D??@8@AAdAB0BC$CCD@DE<FFGH4HIJK|L LpLMM@MhMMMNN4NNO@OxOPP|QQQRLRRS`STU(U`UpUUUV$VXVVVWW(WPWxWWWX<XLXXY,ZZ8Z\Z|ZZ[[l[\\`a apaabbXbbcXccd,d`ddeDef$ftfg g\gghDhhhiii0iHi`iiiijj jkkl,lmmTmn8nPnnnoopTqrrttuPuuv4vPvvvww4wPwtwwwxx`xxxyDyxyyz z8zTzpzzz{{L{{||H|||}}$}H}l}}}}~~@~`~~~~@l@hDl<h 8`8l<pP Tx$Lx0dH|$T,lX,dD`|$Hp$@\x <Xt<,,@\t<\@\8h<X,Tp$DxH`@T4L|D<h l$d  Hp(DplX\l|d(| h,|`$`hx¤ PÀðĄńƈưhǼ4Hpɀ,Ppʔ@pˠ Lx̤@h͌ͼL`Ψ$XϬ(TxШи(hXd@@@@@լ$PHDٔ(0L۔ۼ  >+h@P_Bc V  %4 #     S 0k 0  ,( "m : & hCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain DejaVu Sans MonoDejaVu Sans MonoObliqueObliqueDejaVu Sans Mono ObliqueDejaVu Sans Mono ObliqueDejaVu Sans Mono ObliqueDejaVu Sans Mono ObliqueVersion 2.33Version 2.33DejaVuSansMono-ObliqueDejaVuSansMono-ObliqueDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/License~Z >  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DFuni01E0uni01E1uni01E2uni01E3Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F4uni01F5uni01F6uni01F8uni01F9AEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0221uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0243uni0244uni0245uni024Cuni024Duni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C8uni02C9uni02CCuni02CDuni02D0uni02D1uni02D2uni02D3uni02D6uni02D7uni02DEuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EEuni02F3 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0343uni0358uni0361uni0374uni0375uni037Auni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0462uni0463uni0472uni0473uni0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni04A2uni04A3uni04A4uni04A5uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04BAuni04BBuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04CBuni04CCuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni0510uni0511uni051Auni051Buni051Cuni051Duni0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564uni0565uni0566uni0567uni0568uni0569uni056Auni056Buni056Cuni056Duni056Euni056Funi0570uni0571uni0572uni0573uni0574uni0575uni0576uni0577uni0578uni0579uni057Auni057Buni057Cuni057Duni057Euni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058Auni0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1D02uni1D08uni1D09uni1D14uni1D16uni1D17uni1D1Duni1D1Euni1D1Funi1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D62uni1D63uni1D64uni1D65uni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Buni1E9Funi1EA0uni1EA1uni1EACuni1EADuni1EB0uni1EB1uni1EB6uni1EB7uni1EB8uni1EB9uni1EBCuni1EBDuni1EC6uni1EC7uni1ECAuni1ECBuni1ECCuni1ECDuni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE8uni1EE9uni1EEAuni1EEBuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni2010uni2011 figuredashuni2015 underscoredbl quotereverseduni201Funi2023uni202Funi2031minuteseconduni2034uni2035uni2036uni2037 exclamdbluni203Duni203Euni2045uni2046uni2047uni2048uni2049uni204Buni205Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B8uni20B9uni2102uni2105uni210Duni210Euni210Funi2115uni2116uni2117uni2119uni211Auni211Duni2124uni2126uni212Auni212B estimatedonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215F arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni2213uni2215 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangle logicaland logicalor intersectionunionuni222Cuni222D thereforeuni2235uni2236uni2237uni2238uni2239uni223Auni223Bsimilaruni223Duni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Funi2290uni2291uni2292 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1uni22A2uni22A3uni22A4 perpendiculardotmathuni22C6uni22CDuni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2312uni2313uni2314uni2315uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2325uni2326uni2327uni2328uni232Buni2335uni2337uni2338uni2339uni233Auni233Buni233Cuni233Duni233Euni2341uni2342uni2343uni2344uni2347uni2348uni2349uni234Buni234Cuni234Duni2350uni2352uni2353uni2354uni2357uni2358uni2359uni235Auni235Buni235Cuni235Euni235Funi2360uni2363uni2364uni2365uni2368uni2369uni236Buni236Cuni236Duni236Euni236Funi2370uni2373uni2374uni2375uni2376uni2377uni2378uni2379uni237Auni237Duni2380uni2381uni2382uni2383uni2388uni2389uni238Auni238Buni2395uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni2423SF100000uni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254FSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000uni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Fupblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2638uni2639 smileface invsmilefacesununi263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647spadeuni2661uni2662clubuni2664heartdiamonduni2667uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi27C5uni27C6uni27E0uni27E8uni27E9uni29EBuni29FAuni29FBuni2A2Funi2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2C64uni2C6Duni2C6Euni2C6Funi2C70uni2C75uni2C76uni2C77uni2C79uni2C7Auni2C7Cuni2C7Duni2C7Euni2C7Funi2E18uni2E22uni2E23uni2E24uni2E25uni2E2EuniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716uniA71BuniA71CuniA71DuniA71EuniA71FuniA722uniA723uniA724uniA725uniA726uniA727uniA789uniA78AuniA78BuniA78CuniA78DuniA78EuniA790uniA791uniF6C5uniFFF9uniFFFAuniFFFBuniFFFCuniFFFD dlLtcaronDieresisAcuteTildeGrave CircumflexCaron fractionslash uni0311.case uni0306.case uni0307.case uni030B.case uni030F.case thinquestion uni0304.caseunderbar underbar.wideunderbar.smalljotdiaeresis.symbolsEng.alt@֚ԴG}%2і}2»}Y&Y@&//2G@GddkY }:Y :  V f   XA W%d]%]@%XA~XA~}|d{dzy@}xwvutst2srqpq(ponmlkldkjkji hgf f f@ed.ed.cXAcbY ba`a`_]``_[%_]_@^]\[%\[%ZY ZY XW%XAWVW%VUTSRSRQPONBNSMLxKJ}KJ}IHGDCB2A@BAF@?-@B?>@?->=<;:9S:98(9S8(76543210/+ /.d-,+ ,+ *)('-'&%%2$#"!-!1 d 2 @:%%:B-B   :    @$--:-  d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++pyzo-4.4.3/pyzo/resources/fonts/DejaVuSansMono.ttf0000666000000000000000000121350412662716363020416 0ustar 00000000000000 FFTMYR,GDEF~qHGPOS9GSUB ;OS/2i@Vcmap\5@\.cvt  V0fpgm[kXgaspYh glyfYthead0'P6hhea'$hmtx5.h'locarTPAh3hmaxp't name`t!postљzprep:(.ɐ!ɐ!st   [ \ c d  DFLT&arab0cyrl>grekPlao \latnj SRB 4ISM 4KSM 4LSM 4MOL 4NSM 4ROM 4SKS 4SSM 4mark mark,mark4mkmk:rtbdB &.6>FPX`Rx | 558" p~yh&0  c"cz}wx{]j]jbj $6HZl~ P|H 0 P|H <P ,  T`  P|H    \ cvy~hhhn  "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\Xd|dh0`tXtX <|00hL( Tplx`xDJPV]j]j]j]j]jbj]j]jh|  &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| dt hd8|$|`HLD \t@4H(|XT(lt<0xx_` iFWW@TT[:B`LLL[Z[IWTL4K]l8@^  @bXXXXoM!TxxTT&~O[ XPXD,aDf\Ph< \\\0,0@, d `|tL`00@\  T`@L845D80H <D<x(LX$Tt0TLh0pLh<pl$d`0h8< PLdhx8TR m$v2z`M;)RDhp_)I R|o~4OOQRTs#$ ( d h l m [q  tuwxz{|} &,28>DJPV]j]j]j]j]jbj]j]jh N  $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<DhxzOhDh*hDhhhhDhhhvhhVhYDxhVVhxhhsDVhDDDDDDzzzzhhhh6hhhhhhhhhhh46vvvvhhhhhhhhhhhhhhhVVVDD6h6hzvzvzvzvhVhVhVhVhhhhhhhhYDxxxx9hVhVhhhhhhDDhhhhhhhxhxhhhhhhhhhhshVhhhhhhhhhhh 66hhhVVh"VhVhVhhhDDxVhhXVVjhhhhhDDVhhVVhVhV$=D]4:IWfoqr "#&36k W W Y Y ` ` c d |  "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |     $ * 0 6 < B H N T Z ` f l r x ~      & , 2 8 > D J P V \ b h n t z     " ( . 4 : @ F L R X ^ d j p v |  $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv| $*06<BHNTZ`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|hhhh66zzhhhhhhhhhhhhhhhh66hhhhhhhhhhhhhhh`hhh`hhv`vhhh`hVhhhhV22h`hh`hh`hh`hVh`hVh`hh`hhhh`hh`hh`hh`hh`hVh`hhhhhhhzzzzzzhhhh66hhhhhhhhhhhhh6666hhhhhhh`h`vvvvhhhhhhhhhhhh`hhhhhhVhhVhVhhhhh`6h66hhzvzvzvzv`zvhVhVhVhVhhhhhhhhhhhhhhh`hhVh`2222222hhhh`hhhhVh`hVhhhhhhhhh`h6h6h`6h`hhhhhhh`hhhhhhhhhhhhhhhhhhhhhh`hhhhVhhhhhhhhhhhhhhhhhh  `6666hhhhh`hVVVhhVhhVhhVhhhhhhhh22hhhhhhVh`hVhh`hhh`hVh`hV66Vhhh`hhhhhh`hVhhhh66h`hh`hh`hh`h)`)`YYhhv`vv`v`v`vZ`Zr`rv`v`V>>Vh`hV`n`nVh`hh`hVooooVD`Dh`h88PP>>Vh`hh`hVh`hV`Vf&Vh`hh`hh`hh`hhhVh`hhh6`6Vh`hVh`hVc`ch`h``h`hVssVssVx`xVssV`VhhVh`hh`hh`hh`hh`hhhh`h`Vh`hh`hVh`hV$$$$h`h ` >`>h`hh`hVh`hhhV$$h`hh`h`h`v`h`h`h`h`h`h`h`h`h`h``h`h`hh`h`h`h`h`h`h`h`5`h`h``h`''}{}hhRREE^^rrhhhh??aaiihhhhhhGGhhhhhh6h\aadd55g'ee``66LhViihhh`hhhVVhhVhhViiV11`h%$=D]4Kjsxk 5<:>BbDHggolqu"xx<< W W Y Z _ ` c d   "+12 $*06<BHNTZ`flrx~h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`h`hhhhhhhhhhhhhhhhhhhhhhhhhhh`h`h`/-3 ntz "(.4:@FLRX^djpv|>DJPV\bhntztwwwwt`~~`~`/ 8DFLT&arab0cyrlDgrek`lao llatnv SRB 4ISM >KSM >LSM >MOL 4NSM >ROM 4SKS >SSM > ccmp8dlig>finaDinitJligaPloclVlocl\medibrligh &.6>FNV^PT  d  d   $$XLMLM&   %hp5         ! % ) - 1 5 9 = A E I M Q U W Y Phjs# 4T'      # ' + / 3 7 ; ? C G K O S [ TTVVX\ahjprs"T'      " & * . 2 6 : > B F J N R Z TTVVX\ahjprs"  b  c  F G> $ ` ^ \  a _ ]  F G O LI LM33f  &(PfEd@ m`,,$, ,~!AEM?CXauz~_csV_  :UZmt{ .<[ex{-McyEMWY[]}  # & 7 : > I K _ q !!!!!!!"!$!&!+!.!_" """" "-"="i"""""""####!#(#+#5#>#D#I#M#P#T#\#`#e#i#p#z#}#####$#&/&&&&'' '''K'M'R'V'^'u''''''))*/+,d,p,w,z,..%..'t $CLPCXatz~br1Ya  !@Z`ty~,0>bw{0Th| HPY[]_   & / 9 < E K _ p t !!! !!!!"!$!&!*!.!S!"""""'"4"A"m""""""#####%#+#5#7#A#G#K#P#R#W#^#c#h#k#s#}#####$#%&8&&&''' ')'M'O'V'X'a''''''))*/+,d,m,u,y,|..".."RpvogUQNIHGFE75' A@?72/.)% )(&%# hdb\XVURHFB@86,*(&$ {yrmljfeda_; |snO g`_Q6.$ h g e l ~b!$ACELMP`gkmqz?CCXXaatuzz~~_'bcrs1VY_aEG  I  KLMN!:O@UiZZ`mtty{~   ,.0<>[be<wx@{{BCDah-|0MTchy| 4 E:HM`PWfYYn[[o]]p_}q     # & &% / 7& 9 :/ < >1 E I4 K K9 _ _: p q; t = X e {!!}!!~! !!!!!!!!"!"!$!$!&!&!*!+!.!.!S!_!" """""""" "'"-("4"=/"A"i9"m"b""""""""""""########!#%#(#+#+#5#5#7#>#A#D#G#I#K#M#P#P#R#T#W#\#^#`#c#e#h#i#k#p#s#z#}#}## ## ## ## ## $#$# %&/ &8& P&& && && '' '' ' '' ')'K 'M'M 'O'R 'V'V 'X'^ 'a'u '' '' '' 7'' E'' G'' H)) J)) K*/*/ M++ N,d,d W,m,p X,u,w \,y,z _,|, a.. e.".% f.... j k z"'   R pt v d ep֣ j    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a!rdei#xpkvjX%s\]gw ,l|cn!T@m}%b :q/0 "y'gwqstuzxvhf 7X!uu9!{Xm{o{RoZ!=fs +b#1N {T\q#w`j#fy```{{w`b{!{RNNfffHF?{L'oo5jo{-{3=foD7f}s, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-,%%I%%I` ch #:e:-hh/10!%!!hsr) @ <2991/03#3#qeR@1<20###Ѯ++J@0     91/<<<<<<<2220333!3!###!5!!5!#3hiiThiih)T/hTaabbNZ /d@9($)%/%$(!$, ( 0<2<1/299990>54&'#.'5.546753.'n|phumdfbdcӿdOTUPDNtd]gp^Vd-.)>B+/Qš ! *9V@/7(" "7(.+  % 4  + :99991/9999032654&#"4632#"&'%32654&#"4632#"&iNMklLNi@s..2H#)iOMllMMk@u--1?NjkMMljO0./t?``OikMMkjN0--uA9*7@b  -,.+2345617B7 1 +"1"!% (! 7+!(!(! .899999991/9990KSX999Y" >54/3#'#"5467.54632.#"3267>7#'&JKNSj抋20ǭAF;}Eap:6\[ț*\,#1h F'XmFD ̉dHG'%[M;I\ 10#+u @  29910#&547u;:\' @  299103#654\<<J+N@,       <2<2991<22990 %#'-73%+f9s9f9PsPbybcyXqy '@    <<1<<0!!#!5!CDDD/@ 103#Śc/dm10!!d 11/03#1fB7@ 103#ymL # @  $!"!$10@////////// / / ?????????? ? ? OOOO O ____ _    F////////// / / __________ _ _  $]]4632#"&"32'2#"M68PO98K7PP78NL0670xx~F &@ ## 1/20%!5%3!!:P6ȪuLJժ#Q@)%%B   "$91/20KSX92Y"%!!567>54&#"5>32uu5dF[pga Yd8ժ.>zO}BC12`tA7(G@)  #)&" )9190#"&'532654&+532654&#"5>32ggfbYhyI'Ǖ&$54zms{(( ۵{fo B@   B    $<291/<290KSXY" !33##!5)!3d-=@"   "190!!>32#"&'532654&#"+W,wN\aQFժ 21%%L$=@#  %"& "%190.#">32# !2"32654&?M0n#J݁%'dkuzl75@%%B"991/0KSXY"!#!5V+N #/C@% '-'0 $*$ "!0991990"32654&%.54632#"$54632654&#"hʁ򑁖Myz{yŗT!Ѳ!!ȟɠbx~~xzF $;@" ""%"  &%1902654&#"532#"543 !"&T?M/nI%'!dk  os'@ <21/03#3#'9' %@  103#3#Śc /Xyw!@('29190 5yR!÷X`y@ <210!!!!X!! BXyw!@('<919055X!R^^="{@B  %%B !    ) #99991/9990KSX99Y"#546?>54&#"5>323#=TZ>/mNb^hC^XE&bRY;X1YnED98L\VBT=/s 4p@1(+$ 4 '$+1+5' ( + . !+ -.5<991999990@ ]4&#"326#5#"&5463254&#"!267# !2kkkk%RӡP$J6l90?{:]x<!o?DF=?z% @A%%%% % % %  % B   / 91/<90KSXY"]@    ]]!3#!#hnl#+{q =@#   21 0!29991/9032654&#32654&#%!2)qﰖ뒃JF{f>p}qdƵϠ1.@  2 10210%# !2.#"32671M[?[MJVXI5))pn))=@@=R(@  2 1099991/0% 6&!# )`dVDѦHKw/N )@  13 21/0!!!!!!vTrwժFX $@14 21/0!!!!#o\eժH7fP<@!   6251990%# !2.#"3267#5!PQv@^PQ_ſCe){KMon56MI!H &@ 1 0 221/<203!3#!#)d+9 %@ 77 221/220!!!!5!!=99ժm,@    51990753265!5!#"&m[hqG`=QQD, @!% %B  0 291/<290KSXY"]@L&&6FUWX dzy{ ',+&:IGG[WXXWkzx]]33 ##wVhs@ 141/03!!dժVy @,  B    / 0 91/<290KSXY" ]@$  &)&) 6968  ]]! !###V+'F m@B10 991/<2990KSXY"]@&)&8Wdjuz &)FIWgh]]!3!#3+3u\ #@ 2 62510#"32#"32IIz~u+@  2 8 3291/032654&#%!2+#ꌝL/ϔu\=@   2 625999919990"#"32#"32ȗyHdIj@8  %%B     21  0299991/<9990KSX9Y"#.+#!232654&#NnRٲM{cݑohy]ҔYJ'@=  %  %B %( &919"0(9999190KSX99Y"]@ ]].#"#"&'532654&/.54$32\^mjikshulм V;53#"&'.  yVWx! 9FBjiCE:= m];<<;\lh?;::;>9L@)%%%%B/091/290KSXY"%3#3h_KKѪ++ @D    %%% % B    /91/<<90KSXY" ]@^ //+ ??8 ZZ  &*%*(+ % & 5:5:;: 4 6 TTZXWV[[RW X ] gh yvy v #]]333# #ŏӬ߿ʿD"+w @K % % % %%%% % B   ;/; 0 91/<290KSXY"7]@8  '()& X xyw !%+% 5UYX es]]3 3 # #VHNAu3B}%Y@.%%%%B<< 9991/290KSXY"3 3#%lk!mb E@%%B/0 991/0KSXY"]]!!!5!" ՚ow@=210!#3!XfB7@ 10 #%mZ@=210!53#5XޏH@ 91290 # #Ȳu-m/10!5/mPPf%@ <1K TKT[X@8Y0 #fx#{ )n@*  ! $   D >*22991/99990@00 0!0" ]#"326757#5#"&546;5.#"5>32=zl;^[fX=& 3qpepӺ)Ld_y64''RR2X 0@  G F221/9904&#"326>32#"&'#3,fd./xRXWS%{/@   F210%# !2.#"3267%JR%QNI]`A9++88*,A:;>{0@G H221/9903#5#"3232654&#"Z.deCSW;7W {X{E@&    IH991990!3267# 32.#"X㿮Xmi[ ^Z89++9 @Ţ'4@     <<2991/22990#"!!#!5!5463'cM+Qgc/яN{H{ )H@' '  $(*' G!H*221999904&#"326#"&'5326=#"3253ZLSbC,ml/9.,}^\::VZ,@  J  F21/<990#4&#"#3>32jq1sJ`cD .@ L LK <<1/20!!!5!!3#mnm`/BCV 8@   <2991990!5!+53263#XZZӜ} @:  B  DE 291/<90KSXY"]@R546Ffuv ('(;;797JIYYkiiiyxyy]]33 ##Gb{ZFB?  &@   L 991/990;#"&5!5![Y饵|~mo{"@'  MNMNME#K TKT[X8Y<91/<<<299990@G000000 0 0 ????????? #]>32#4&#"#4&#"#3>32"iJo5FP;9JI9!c?LeHEws{p{``32jq1sJ``cH{ #@  D>10"32654&'2#"hڜ-.VT{3@ GF221990%#3>32#"&4&#"326w.df SWWRw 3@   G>22199032654&#"#"3253#L-ed.+SY7:WSj{O@   21/990@%  0030@@C@PPPP].#"#3>32;zI.Dv6y.*`w"${'u@@    B %( OI"E(99991990KSX99Y".#"#"&'532654/.54632OS}{\JSjgTzEZ9..QSKJ#}##55cY1!1@  <<2991/<2990!!;#"&5!5!f^^uϪ+|b`>^,@    JF21/2990332653#5#"&økp1qJyaddm`e@)BIE91/290KSXY"']@%]]3 3#dEFr`T` @E      B    /91/<<90KSXY" ]@      &&)&))#, 96993< EI F J VX W Y fifij e vzx| r -   ++>>< H Y jih {yz|  ]]333# #àö`wBfL` @H      B  IE 91/<290KSXY" ]@ fivy  :4 ZV ]] # # 3 ^oo)'`?HkhV`@E       B   IE9129990KSX9Y"8]@v  &&8IIY ]]+532673 3Z.Gc".\mQ\GOLGhu:NN^Nlb X@BIE 2991/0KSXY"8]@68EJWXejuz ]!!!5!-}bܖ%$f@5 %   !  % $  = %<<29999999199999990#"&=4&+5326=46;#"3@k>>j@FU[noZUtrݓWV10#$j@7%   #%#= %<2<9999999199999990326=467.=4&+532;#"+DVZon[VD>k@@k>XXrtݔXy &@  '1<2990#"'&'.#"5>32326yKOZq Mg3OINS5dJ t]F ;73 !;?<6 7= @ <2991/0533)eq%!N@*   " E"<<<2212<990.'>7#&5473%C??BI9gg9ބ5(,-("9="+` 츸X>@     <<1/2<2990.#"!!!!53#5354632D>Cs3A,,ُ/яLB /@ (-  * -'! @') -0)$ !'$* xyx( $02299999999912299999999904&#"3267'#"&''7.5467'7>32d|[Z}}Z[|Z^.[20`0\^.[3.^Z{{Z\}~t]1]02[-^Z3].2]-_%@D% % %%B  < e e<<2<299991/2<2<290KSXY"3 33!!!#!5!5'!53%lkVoqZmo#o o#o!<210##  = 2>j@<#$93 $*0?#54&S9akԂ[]=:̭IWW9fqր][<;ȧH>=><''PGZsweZ54m@''TLf{xf[1,pE-Z/L-Z/L?F@aa1<203#%3#?}N1ID@'  &>>2J\ ^,8 8YD/210.#"3267#"&54632'"3267>54&'.'2#"&'.5467>`:o:u8g24r=г=rjKKMMKLijLLKLKKkZZ\[[[~}[[[\ZZ/lhȬJKKjhKLLLLLijKKJgZZ[~}[[[[[[}~[ZZ %)d@6  (&&  #*& (' j kji*22999199990"326=7#5#"&546;54&#"5>32!!|WHi1Rwu3}CD?kdPDN@Ms=@pABtZ\#! {w# /@    v v<2991<2990 5 5L-+-+#RRXsy^@ '10!#!X!^?dm10!!d }N4L@I  ] ] B   A)5)M  \\ [G#X;#Y//29999129990KSX9Y"2#'.+##32654&2#"&'.5467>"3267>54&'.XXP:&rk1=-7ffZJJDZZ\[[[~}[[[\ZZ~jKKMMKLijLLKLKKLbeG]C;P*T6?>5VZZ[~}[[[[[[}~[ZZgJKKjhKLLLLLijKKJ=b10!!=V+u @ STS 102#"&546"32654&hAu,-/OomOPqp1.-rBoPPlnNOpXy.@    <2<21/<<07!!!!#!5!X!dCDLIB}a@WWBA     @9991990KSX9Y"!!57>54&#"5>32eQdR1q?Ay;Jwrnaz3=L$$}k9wuF(\A          @#) & )99190#"&'532654&+532654&#"5>32^c:r;Eq-evnmBJ]b`W,p;Eu2X`lP|yQDJLl?<8?yvcG]f%@ <1K TKT[X@8Y03#fT` L@*  !   JF!99912<9903326533267#"&'#"&'øxo ! &D">K .Y\,T H;8 OOPNLPj; #@WW1 9120!###.54$FfNݸ/`103#`u)X 9A      @  aW}a 12035733!j c)t'+n 3@   jkji9910"32654&'2#"&546!!hfssfeusgʫ˫\{u༻߻`{\# /@   vv <<991<2990 5 %5 +-:+-#^R^  ^R^  Z{'=V'{ Z{'{& tVZ'=V'u !%@G  %%B! "$ $&# # )"#&999919990KSX99Y"33267#"&546?>54565#53%=TZ>/mNb^hC^XC&DbRY;X1YnED98L\V@T?%k&$ u@O ]1%k&$ u@O ]1%m&$ u  +@ /  ]1%^&$ u# +@O#@]1%N&$ u +@p0? /]1%m !@W % %%% %!%! %!! % !B     !  PPK/K!"2299999991/<9990KSXY"]@  ]]4&#"326!.54632#!#Y?@WX??Y:Arr@;nlZ?YWA?XXP!yIrrIv${g@7 % %%%B    c /<291/<20KSXY"!!!!!!#!3eex5ժFժu1&d&Nk&( uNk&( uNm&( uNN&( uk&, uk&, um&, u  Ic:1N&, u+1N ;@!    21 0 0<291/220 )#53 6&!#!!VD}}/`ŕ{HK+Fb&1 y"+@O"@]1u\k&2 u@O]1u\k&2 u@O]1u\m&2 u +@ /]1u\^&2 u 0!+@O0@!]1u\N&2 u +@p0? /]1;T .@     <91<290 7   ^t^_t\t%\^u^uw^ +k@:+)&  *&&, #* #)+262#5,999999991/9999990324&' .#"#"&''7&5327sT sV)+y=g %s9d/NZIn-QUPeQzQQFIRPJ=k&8 u@O]1=k&8 u@O]1=m&8 u $+@ $/ $ ]1=N&8 u$!+@p!$0!?$ !/$!$]1%k&< u@ ]14 @  28  32299991/032654&#33 !##ꞝL!󄃃}/V@1-'!  **.  !' $'$-DF099991/9904632#"&'532654&/.5467.#"#7C:oEBL;lAxC\[yqyrq|d1M*%]taQG_J'8O#kr#f&DC#f&Dv#f&Dg#7&Dw#&Dj#&Du){ C@I=70 6 %C "76. 3@:("D%=/.M/u MCM6+sD299912<2<2<999990@ 05060708]5#"32654&#"!3267#"&'#"&546;54&#"5>32>321xYS\JMWWLepO27Gn 'aȿuc^8>M<[|%!YHZqYa4+#"33)+RNPPXx+'#!?@=Bu%{&hF{Xf&HC{Xf&Hv{Xf&Hg{X&Hj@@]1Df&CDf&vDf&g @@ 0 ]1D&j +1H)@O B $ *'! !'D! >*999999199990KSX9Y"#"32.''7'3%.#"32654&Ŷ"#!H&!!#R-:/(  (-Y,\bPȑ^b n7&QwHf&RCHf&RvHf&Rg+@]1H7&Rw. +@ 0 ?. /. .]1H&Rj +@ p_PO@]1Xyo '@ w <<103#3#!!j!/ +s@>+,&  )&  *&& &,+,* # )#D>,99999999199999990 32654&'.#".5327#"&''m1$eA H#cC')d<]*,g9\ //4o0.0tGGq.78MBz;32#"&4&#"326w.dfSWWhV&\j%0& $ +@ @O /]1#&D%m& $+@ _PO@/ ]1#H&D%u'v$ur{'vYD1k&& Zu%f&FvZ1t' ~|&%f&gZF1P& K&%&KF1m&& Zu%f&FhZRg&' o{ ' :G @8@]1N{$H@ "  @"   GH%<<1/<2990!5!533##5#"3232654&#"Z1.de5yySW;7W N0& ({X&#HNm& ({XH&HNP& ({X&HuN'v1({uX{'vHNg&( $o{Xa&Hh#fPm' u*{Hf&gJfPm& 2*{HH&JfPP& 2*{H&JfP'*{HN'.JHm' u+ +@ /]1m' uKKQX@8Y@p`O]0?@!     1 0<22<221/<2<<2203!533##!##53!5*ʇʆ*9QF?@"   J  F<221/<<2990#4&#"##5353!!>32jq}}a1sJzz`c^' u, +@ O@ ?0 / ]1D7&w0& ,+@O@]1D&m& ,+@O@]1DH&u&,vFuD&LvPP& ,D` "@LLK 1/20!!!5!!mnm`/B =@!   "!221/2<220%532765!5!#"'&!#3!53#=Ga'&HHAA@-]@QQJKDuuêK I@&  ! <<<<1/22<220!5!+53263#!!!5!#3#ZZi,+뗗Ӝ}/BCmm' 0u-Vf&g&j.'N` @9  B  DE 291/<290KSXY"]@R546Ffuv ('(;;797JIYYkiiiyxyy]]33 ##Gb`/ZFB?sl' v/@ ]1 l' vOKQX@8Y@O]0s&f/ &Os' m/' Os'y`/'yOs 7@   1 4<2.9991/903%!!'7;NwdPo;jnL >@!    <<2999991/9990;#"&5'!5!%[Y饵P{;Pu|~$o/nFk' !u1m&vQF&*1{&0QFm&1 *uf&Qh&Q{aIV=2@  1021/90+5327654&#"#3>32=YZͧZ-,t|6~ij>>WotV{ 2@  JF!21/90+5327654&#"#367632YZ͹Z-,jqFE1TTsTU6ij>>~ʗ[\``21qpu\0& 2+@ O@/ ]1H&R+1u\m& 2 +@/ ]1HH&R#+@]1u\k& 2Hf&RH;@     -299991/220%! )!!!";(RH=MKF{ 8i@92/ & 8   #5/)#92& MuMCM,s9299912<229999904654&#"265&#"!3267#"&'#"32>32PVWMfRPhgPPcpP/;}Jb04TY/%W & +#T53+)CBDA88>A>Ak' u5jm'vU&r5 {&Ug&5 ojf&UhZJk' u6m&vVJm' u6f&gVuJ&6u{&VJm&6 uf&Vh/u&7u&yW/m&7 u +1~&W /-@  : : <<1/2<20!!!!#!5!!/s-  +ժA@B@!    <<<<2991/<2<2990!!3#;#"&=#535!5!f^^uϪ+|b>=^' u8/ +@O@ ]17&wX'+@/ ]1=0& 8+@ O@/ ]1&X+1=m& 8+@/ ]1H&X+@]1=U&8u O&Xu=k& 8f&Xe=&8vu^&Xvt' |:+1m&gZ+1%t' |< +1hVm&g \%N&< u +1k' u=m&vV]P& 2=&]m&= uf&]h',@   <991/990#!5!546;#"+cM=яNQFX%>32#"&'##53533!!#&#"32y,fd.{{aRXWSzzc)?@$   2 !$'+9291/9032654&#32654&#%!2)"#546xxtB>d{f>p}qdƵϠ/Wp1FqHX>32#"&'#!!&#"32y,fd.RXWS0 327654'&#'3)'KKOO{e5Fq>=DEdgh=< !&#"32>32#"'&'#'҈FDDFl,fttttdLL.zYmnnnRRX랝+,S|1/@21 <1@   0>3 !"&'532#"M[?[MIXVJ))gj))=@0230@=<g"%# !2676;".#"3267M[?ZO*ZT3,JVXI5))p*32j>5F=@@=^s!%# !2676;".#"3267JR%FC=ZZ-,I]`A9++8(8rGj>>~A:;>N3 !#"#546 6&!#FVD6<0c//&r1FHKwN#";5!! $5476%3羽EF5e{ɉ{+ˡd4 32654&#"5!#5#"32_.df,T}SW;7XR=G{ 7%2654&#"#"/532376?654'&'&'&'&32h(>vwf2BFKI I<' )iy|{L (=\RR $+.! -&N +@ 1 3 21@  /0!5!!5!!5NwrT+u\=@ 26 25991@  9905!54#"5>32#"327uVJM[׌~ S@=))yz~#7(>@  22&0)1@ )) #)90.54$32.#";#"3267#"$546IyhYbfgg"{ (({smz45$&Ε?V!!!!+532765| YZZ-,ժH#ij>>~V'$+"!!+532765!5!547676;'a'&QRF1i&&(W%6MI!RI ! 5 3 3325D`H&0tt?uo+EA&#767653#"'&54&#"#367632"&76/JI_BG}MKUv14IBe``  1:!5!!;#"'&5=,-Z٪\Y+z=>jf!!3!!#!!5!!5!!= 99ժi@n67632'&#"##3i~\/j!-<BmV c3r%3sh5476;#"33 ##YZ͹Z-,Gbij>>~ZFB? ;#"&=#53!5!;+[Y饵 |~ĎɎ1m% # #''3C\P"Pn@Jo |mo"%#"&33265332653#5#"&8"iJo5FP;9JI9!c?LerHE! s{ p{+`=R{#4&#"#3>32jq1sd``cu\  32'&#"32767\:DC; 9CD8 z~{{vu'y2 {'R-%63273# &#"327-N5>o毴o8=yy=Y+͠v~^VR{ 763273##"'&7327&#"VPeo~簵noVAA( /s%+n+͝u"mmB8!+#"#54763! 27654&+Vn]6<0``~'R@]M//&r1FRQE8DVT*3265'"#"67232#"&'#76;#"w" . [f,?N͹ /2FIWS.O#.+#33232654&#N76SٲM{cݑ77hy]ҔYJ'>323267#"$546?>54&#"iV luhskijm^\''Ƞ/ vp{DI--յ1#hcq<;{'>323267#"&546?>54&#"PZڒEzTgjS썉J\{}SO9!!1Yc55##}#JKSQ..xmvV[!&'&#"3;#"'&5# 54!238!n|wx'%dQW/R5-0A3=g)(V\`@oV !!;+53276=#"&5!5!f^^uYZ͹Z-,(Ϫ+|bij>>~`>/&#5463!!#ŃF1-/7&r1F+!!;#"&5!5!5476;#"f^^uϪ*YZ͹Z-,`|b`ij>>~/V!!;#"'&5!/s-,-ZߥZY+ժ~>>ji? '8v'q'|XdJ##"47#5! 54'5{no{x4xn8!L IL*!"'&533254'3\Z,,Zxznjf?~>> ILɸ#367632'&#"kSHcm.-PKG "(3t*b7m*9 Vm+5326?3 67632'&#"nQGJ|lLT3!;^2Q+31705+h:=HTN~) )!3!#!!5#5!!uP" "v՚i@5b!!!#!!5!5!!-8YbҖg 7654'&+5!5!2! $5dc{dd\^rhbVPKKKKIJG8+lh3! $54767635!!#" 76RUci r\\dc{dd݊hl+8GJIKKKK}LT` 5!!#"3267# $547676peje]\dcmTjdc^QVbܨ JKKK21%݊hm*8V$` 2767# 4%$54#0!!5! TMOQWPVa ejo0, 5%b|8d1a# 6323#!!5!5!67654'&#"п -"BP8u~i9Dc``JU?T<>< % 7654'&#!!!!2! &53h<= \^G/"icUPRz,ʞ[+3IJ I8+le[tG)}LT` 7654'&#!!!76!"'57?\]ȨgcUQԪ-5IJ,39+lhJc 4'&+#5333#!"'53276MJY>lunc9rO_}nw~FVrA}Vg{#36763254'&#"64QҸMNr98xܭz BR1pqWBA3#+9'6-3!!!!#!5!5!5!^^``l%m&$ u#f&Dhm&, uDf&hu\m&2 uHf&Rh=m&8 u#+@ /##]1f&Xh=' &8j2&&Xq<=' &8 &&X<=' &8 &&X<=' &8 '<zW{%' 'j$#2'q<%'j& $#2'q<0' )&fPm' u*{Hf&hJm' u.m' uNue\&v2eH{&vRue\0& eH&m' uy}LTf&h8Va&h#fPk' Zu*{Hf&vJ=%2763#"'&5!#3!3m6!h9Hն+ROf'MSb9du'Fk' u1f&CQk' u)f&vk' u/f&v%k& $#f&D%m& $#H&DNk& ({Xf&HNm& ({XH&Hk& ,Df&m& ,DH&u\k& 2Hf&Ru\m& 2HH&Rk& 5hf'Um& 5jH'U=k& 8f&X=m& 8H&XJ&6{&V/&7&YW}RT. 56$>54&#"57>54.#"5632 4o1\}p_s54&#"57>54.#"5$32Fp>!BlJc(v];?"AW?-1CA#E ptgDZX%KlaF='.`[b[3XpVU 32=t|6~kWotl(1%7276"'676#"'#7&/'&'&3232654&"m B{ rhG0wD &8Md\]P{#looԐ>>t#O9 Y%Z5H7WSCTV!!3+53276=!5!"YZ͹Z-,՚o*ij>>~Vb!!#+53276=!5!-}YZ͹Z-,|bܖij>>~%%P& $#&DuN&2({uX{&2Hu\' 'j2H2'q<u\&2' jH2'q<u\P& 2H&Ru\'j& 2H2'q<%0& <hV&\l %7276"#7&'&5!5!676#" B{wD3' rhF>O9aJwt#jlf{.%7276"'676"'#7&'&=4&#"#3>324 B{zgG1wD3'5ZI9!c?98>>r.O9aJs{``>t#O9aJ;>V` ,@   991990!5!+5326XZZӜ}xY12654&"&#"32>32#"&'#5#"323QSSQPQRRQP]=zz<\o\32#"&'#QSSZQPPSSPP]=yz<\o\GJR\D%QM((_]q"! ^`A7S]++ L8 rMq;> 3!!!!!5! d iA!#!5!7##'-.d'Jug[|BJ8jF{5.#"3#"'&/&/5632654/&'&54632OS}{\Jvh*L'TrGY3e2{zD>zEGIZ9..QSKJ#}^R ~$=&[5#`cY1!GJ!b!;#"'&/&#=!-j1 *L4[TrGY=Zb ~$=&[?%7"#5463!2##326&#v6<0~'ʌ//&r1F q(!2)#5332654&#32654&+3F뒃ﰖƵϠn,>p}qd?{ƕ)533!33##"&'.=)3267>5~9FBjiCE:  yVWx! A?;::;>`m];<<;\l9 #3#i++!!2#.+##5332654&#LN76SٲM{cmˉҔ77hy]w{.#"!!##533>32;zI[M ܹ.Dv6y.*l\<Ĥw"$8{ )32654&#"3>32+3267#"&'.>zl<^\fX<& +qpepӺ)Ld_y64''RR2{{0@G H221/99053#5#"3232654&#"Z.deэSW;7W Wy 0@  G F221/9904&#"326>32#"&'#3.de,-xSWWS^X $9@  !G! F%22991/04&#"326>32#"&'#46;#",fd.̸ZZ/xRXWS~Ӝ}}{0@ <1@   0>3 !"&'532654&#"JR&PNH\`@%++*,A:;>s:{!)47&'&!2.#"63 !"'3254#" 90%QNI]cU-RG+>jiáS,+;7W{7 &32654&#"%5476;#"#5#"32;mjkookjDGH8H$#${PϳQ{#T ij>>~SW;7WSzW{432!"&'5326=!7!.#"z [imX^^"++98ȷzW{?@ I H991@  9905!54&#"5>3 #"73267zXmi[&Z89++"Ƣ{ )%654'2273;#"&5 '&'&'& 56BL+>w9P!1jcGF:“֊>8E#ZuaQ`vg("chV({09@ 22&%!*2 &.F110&'&54632&'&#";#"32767#"'&546wA@Q[\ihWVLHHZ[c[[MaZ[V_A@^  VJ=+,nQb54"[\m({(<@! #)) )& )190#"&'532654&+532654&#"5>32U`LdKhiP_m"#ibQnW=JV^8{B#"&'5327654'&+5327654'&#"56763273;#"'&55c88hh@H9DDKCE@?qv|f6688h8AANODE<[KO 0A=1_JJm\["45bQ77,+=J++  OAf10`ZȢA"y( !27654'&+532764&%632#"'$Sv<;;!!;yUkbbkˠU^A;;LL67ss.gg=XVq^!+53265!5!!5!qZZh(Ӝ}}ؤg {H4&05476;#"#"&'5326=#"43#"326HH1:hh>CO6xn#{XЮmssngn^ ij>>~.,}^\:<bH4^ $!"326#"&'5326=#"4763!|LSbC,muq9b.,}^\:ᥟzX %#5!#"!2&'&#"32BHXN,>hhxpVHdbbd">:KMrqfqrfQk^".5 3 3265+]ܞ^+ r.$a32#4&#"#5476;#"}2rjrZZκZX `cJij|~V(>32+5327654&#"#5476;#"}1rX[̸[-,jrZZκZX `c6ij>>~ʗij|~23#!!!!!5!!5!! bnnl~ ˏ5i ^!#"'&5!5!; ̦ZXXZjf;8~|2^ !!!!5!!nnl^BXy&;#"&=&#"5>32!5!3267#"'.H>ꤶ./OINS)&r\FJKOUi(? ;?<65=>;7-4 ;#"&=# 5432!5!3#'&#"3[Y襵>5*GN\|~ܽ󠠄K9V  ;#"&5!5![Y饵|~(L-;#"&5#5!!2#"'&'532654'&+5!HHTgOE@KOPUCWJJX|~A$8+lh%12KJhj{!%#"&3326532653#5#"&2"hJn4FP<88 d>LfHE!s{p{`LfHE!s{p{)32+532654&#"#4&#"#3>32"iJoSN5FP;9JI9!c?S,5HEcԜ|~s{p{^^@;#+VM{+532765367632#4&#"{5D1DCWVV\^oA@01re22wx\__V{ 4'&#"#3>32;#"'&./]p@A2XVV?4<>OO__^edwxH10``A{ !3!##{yyH{  #"%"!&'&!3276ߌH?7?H4HH4{-m__mOmmOE`!!!!!"'&763#";d~~~~ KKKK`hghi&{.4'&#"3276=332#"'&'#"'&57!29PHJ|")u) }07_CC_vD8@jxZqosO++Oz2ee2z| VH&/#5!#3!535&'&76767654'&_|{_hd{{dE,HH,O1HH1ouu{{BnmBHImnI^732653#5#"&';zI.Dv6.*+w"$732653#5#"&';zI.Dv6.*w"$fV^;#"&=#"&'532653YZNb.Dv6;zI}}w"$.*+jV{.#"#3>32;zI.Dv6y.*)w"$jV{;#"&53>32.#"#,,[.Dv6;zI[[}?>rw"$.*ll2{476;#"!!5!RRҼj&$pnhb`022{!!5!4&+532jnnHlдRRV``bzW^!#&'&+#!2327654'&#7545â?;;alkpw?@@?w 66^q$%'^NMi++ST**zW^!#!3327673327654'&+jpkl|a;;?î545(w?@@?wSiQP^)%$q^667**TS++V{8.#"#"';#"&=327654'&/&'&54632N[DF20@RRz|hj&"nfdbbFF24@LLf?((**T@%$!*MLZ[705-,QK((%$JK}VT+5326546;#"ӳZZcMӜ}}¸Qg}VT!+53265!5!!5!546;#"4ZZ=cMh(Ӝ}}ؤiPQg}VT^;#"&54'&+532'&cݳ--Zg() |@>vV[!#"327673## 54!3476;#"8tn!ʷ5RWQîd%'3A0Ǜo@`\V()g+`!5!4&+532!!H^^uϪ+>`|bW!!;#"&5!5!f^^uϪ+|b >`!533!33##5#"&=)3276:CYYu>>|WMĤ45wEioabdo?ܤdqnܑkmhAw` !+"'&5#5!?27654'&'&UBr86FRQS&(g3XXBO\Ldqn``;612abdw7,H`!# #3T`` !# # #33”­jj`jH >;#"# #4N|lLT2"zHTlfk}3 3#f%.]}8 V`!!;#"&=!5!;4z `ۧ10%*`!#47!5!5!332!'3254#ejL<FX3<;4% 6[}LT` 2!"'&'5327654'&+5!5!ajbVQ^cdjTmcd\]ej8*mh%12KKKJiLh`$- 76654'&+5!5!2#4'07&#"327* \^ejeidTQ'd( }ŃcL;*1JJ$8+lgqUeR8y*K/K327654'&#"56763 #?W::fPONNLQQUmlprLbAr+#}swt#&'&5476!2&'&#"3ʪplnUQQLNONPc9:Vws}#+rAbLr3!"'&'5327674'&#˪plmUQQLNNOPc9:Vtws}#+rAbLrJ#476!2&'&#"32767# '&5nUQQLNONPc99cPNONLQQUn>}#+rAAr+#}_-sB (47632 7 654'&#"47632"'&_ԚO̵eddede"!/."!B^!"5Ԝ0ٍccƍffff.""""./B!!} -@   F!21/9032654&#32654&#%!2#!]_Z^UTTVeb`sci?\dU?.Vi}u"y+";#"3$''"'&5467.5476322s9@< <@7uTxVddjbbjdd6=76NJ@6sx>WVggWV6l0%#5!#"'&76325476;#"#&'&#"32t:{}|3H >ws}#+rAbLr #26&"3!!!+5#"32deen a^!;8NN8:j+^Lۓa31DD10ML C3276'&#"%#5#"'&76323!2#"'&'5327654'&+5!22XW3223WX2o#55JzLMMLzJ55#o ?M;319;<@3xAr;<78chqjtssttss_3d0110d^L$8*mh%12KKKJ6 ,326&"!5!332+#47'#5#"3233254#dees ai$\\#jKyyKj#n0 h*5j 3.#"#"'&'#"&5#5333#;532654'&/.54632/e6RR;Y%w026:8>qaQQo-Ek>v;NTj&g[{=l?((TT@I!,KL!&`>NM55YQK($)$V4%.!5476;#"+53276=#"'&5#53!3A &'p.6@3632&'&#"632#"'47&'#"&5#533254#"&57#3uZ310.///0l;;;2v1+\!raQQ$-WO=MT-E‚#+qrfr9DhT"2`>9KiNVH3+5327654&#"####53546;#";67632G11l?KJYhonjjhqij;/m(56Ft<;H``01/яNPhce22wx%7&'&#"#"'&'#367632327654'&/&'&;>ABgf$&n/<>][ALODKTLDCKEJIa54%"092?)TT?&$!,KL\[&:MV3-+RK(#*$JA 3!!!+ۊqvLۓB 333# #333# #ttttU=B!#!#!#!#kkUXrXJ 4&+53232653#9O%5zpcęaBþybV "32653;#"'&'5#"4'&+532Zgo0>*m/2O?*mbþyf\gę10A @ 32tNN^luu)qJy}wYYk\g88A3>32#4&#"#5476;#")qJy}tNN^lu43rB98wYXj\1Sw66WU 3+532653#wtgr,B0ttxlX6Vr8.#"#3>327.bjtt%uT  qksa97832653#5#"&'.bjtt%uT  qkJa97Q32653;#"'&=#5#"&'Q.biuB-r33$vS  qkJH VX66x a970!+33276?3327654'&+CGDDuj=%%(f{n!!!|K((((K|N;[--s?5/.B 333# #tt+5326?331]O\D05 {{bpEW(K/itf--452654DŽ@XX@rPPPP=>X@?X=>POPP"'&4763"3tPNNPt@XX@PPOP>=X?@X>^s327654'&#"567632#(y6$$>q31210336WEDGkM@*7K$@ ` XFh_@C^s#&'&547632&'&#"3kGDFV63301213q>$$6yMAmC@_hFX ` @$K7*@)f7@  91290K TKT[X@878Y3#'#f)f7@  91<90K TKT[X@878Y373x$@1@0#+=b$@1@0%#+=/#!!heJ'#!heJ#c#d>U 533##5#5uJu!5!J>ߖ/)HDV{ W @  P{P10K TK T[X@878YK TX@878Y#"&546324&#"326{tsst{X@@WW@@Xssss?XW@AWXu"  @  |1/90!33267#"&546w-+76 >&Dzs5=X.. W]0i7@!   PP99991<<99990K TK T[X@878YKTX@878Y@?       ]'.#"#>3232673#"&d9!&$|f['@%9! '$}f['@Z7JQ!7JQXfs%3;!"'&5k&&iWRd10`ZȢ% '&73733256/MMV| ;#"&5#5!88hr.EGWwl:Q[v/).#"#"&'532654'&/.54632P1j8WV>](}248{D@}=RX o)k`@q a//$)*+MWfk2-*SIXa #'#37 ͉H+^s#&'&547632&'&#"3kGDFV63301213q>$$6y[AmC@_hFX ` @$K7*@,X!!5!yЈ,X!!5!34,X3#!5hh,X3#!54,X%3!5 DfCfv)fg7w=b10!!=V 3/)H @ PP1<0332673#"&/w `WU`w HLJJLD@ a103#?Fj82#567654#"56J24C1xZ@Vƪ@$C!Xl05^ V{uXf%@991<203#3# fx)fh"#DVv'4f#!#͇fxx/)'ts/)H >32#.#"/ w`UW` )LJJL" #3ﻒY#55#53pp{53#3"pp{f3# qfyX0CbyX0v53#5#5L:#33T걈s^p!5!#.q532654&'3#"&=X.. W]0ihw-+76 >&Dzs5  "&463"]]3GGlbbG23GbL3!5353:^H#5!##걈c_I #53533##sc^5!tcV %+53276=YZ͹Z-,ij>>~V 73;#"'&5,-ZͥZY~>>jic/@a103#?d.@ aa1<203#%3#@ D  1  0#"&546324&#"326D]\\]bG33FF33G\\\\2GF34FG;@103#ﻒu)8  @ |1/90@ IYiy]!#"&'532654&'85xv-W,"K/:=,,>i0Y[ 0.W=u#v $s/#DU|/#5!#|J9X#"4533273273"h;tv gfv ifvtR)@  91<90373x)@  912903#'#m/8 @ PP1<0332673#"&/w `WU`w LJJL/: #.#"#>32w `WU`w LJJL5@!   PP99991<<99990@?       ]'.#"#>3232673#"&d9!&$|f['@%9! '$}f['@X7JQ!7JQ=/10!!>VєmB]Xy a&h!5&hhh5!Ĥ/'\ ]`LM'ogDdFFJ  26544#3GG3]]lG32GbU|3!53UJU |/!!|&b9X632#&#"#&'"#72h54'&' RJ 6"RH 0PQn +0PQn  &33Dt7"#4%62#&%n~vv<<tf3AntVH%#AnHV #"=3;X3Vh'fv?F&jrf&>/`yNf&DHf&f&D\f&pf&f&6&%$q%sJ%1/01/0@%%%%% !3yyN(=H+u\6@ 26 25IJ]1@ 0!5!#"32#"320qYǪIIz~,.%0/01/<0@%%%%3#3#+#Vy0F1H (1  <<1@   /0!5!5!!5HA)Aժ9u\2HVu3xm < 1 <21@ /220@% %%% !!5 5!!9" :A@/7%<uZ&/M@"2  &,2(0<<2<<21@&( '   /<<2<<203!535&'&547675#5!67654'&'Ͱa|{aa{{bL+CC+LL*DD*+v[ssZttZss[v*DD*7*DD*;uZ9@  <<1@   /22<<2067633!535&'&3I.K{bb{L-I["WDWx ֪ WW"J@@qqro prol 991@ /<20353&5323!5654#"J{n !o{1xx 7oȼ߅LI LN' u%N' uFf&(f&Vf&6f&3i& Fy *'&'&3273;#"'&''&'&767,-b=MJMUHi;c( #) Xn^T).\-rv~ oik*%1)0T*XmY*)Va!%#54'$QQ 0kEb6=q'0  Vm`&+532 3#-^1FAF[D~S]VH" 4"32654&%&'&54632&'&#"76hY(>f2BFUR I<' )iy|{L (=\ $+.! -&({R&%#457654'&# !5!OTJPE* :Lf.,KOxsPWKL,#%5,*3eaZiV{.@ J  F21@    /90#4&#"#3>32jq1s```cH9@ D >221@ @]0&'&#"!32762#"?HH?5@HH@<⇙8wyvs6`@ 1@ /0;#"'&'#5"$lYoRQ`+.0`b;`D # #'&#5~J/1Fe<2T`wtB`!367676'&'3!xdLjE.*{`T|p5dwY|rNįtR8&%#457654'&# 4%$47#5! OTJPE* 9MKOxsPWKL,#%5,*p$Rݿ &H{RP`!#3267#"&5!##P117,#J%q\T`PH? XVT{.@ G  F21@  906#"&'#764&#"326ttf,n䇅{WS<R%{$%#457654'&# !2.#"OTJPE* :%QNI]]^KOxsPWKL,#%5,*8(8*,A:nok` 2@  D>ij991@ 20"32654'&7'"763!aFH<Ηr{sPSے-- 2^!@  1@ /20%;#"'&5!5!!$lYoRR0`b3i`%27676'&'3%"'&5#5!tZ;jF-*RR"$vfwZ{s`b;+.0LVh )O@ '#* KQXYԴ0'']<<Դ0]1@' *2<220"27654'&'2##"'&7673A\VMMG*w|~~hA1LLNeˑRh]c[斘n,mKseg.YVx`#&+53 3;# t/1FC /1Fz ~,~VN`%67653#&'&533?>TyyT>?@WxؑoW@F`&#"&'#"'&37676376' 2KUXK2~)@V""V@)~`{gLHk{A>oRyRo>6&j3i& jHf&3if& Ff&$ # 76'&%$'&763 '7676] NI5|utf M2C6R6WpA{z Ʋ itR$ $6'&'&'&7!2#"'&32765JgNn-R Nr^ydPpw{A K~}Sj~"#4''&5676'&qO**d\txLJso@z8 vVOv~*+40r51_Tpf&"N& umVd'#&'&7673567654'&'ĸh]i^V5RR*aW4QQ(VyvaxxGnռFCImֹD9` !32376&7%&# 67#5!'TQ0'( 0A_+T3 4`/&'&7'&7676'&#"56776327'5!`ȍ=`[+9[R~!*`ȍ=`[+9[&͘7 cl|YDT|˩hl="pl |YDT|˩hlfMZuV\ #"32&'&32_{|^"y|•"jVH{ "32654&#&'& h1`{{_ mw.vRL#"32#457654'&#"'&76)MbzYTJPE* 9{2e+wTOxsPWKL,#%5,*˞nͱR)`#!"#457654'&#"76))I]]_bNTJPE* 9ۓ eUlnoJOxsPWKL,#%5,*8(X)V#!47632.#"!!#"&'53276`1c3$R,x:KAb9f.1d0W@Rd>Qoɏ?s!K_`7"'&76'&52n 'BQ_'BQ_[~,`*l#FR`*l#FRM #!3M&pM]!V!#56#'0#0?&'&bTB9[@[`7"7>9[@[|"O z:6hl0%[Ml |"Oz:6hl0%?[MVT{ 7636#"&')! $&#"32nttf,hՇ<WSs)%{FVMu\&'&#"!3276 32 5DC6 >BCD@qopުՈOz~ {!&'&#"!!32?# '&76!2 %%cjf_[_fMJOhk en(' c\\c(  {"056763 !"/532767!5!&'&#"'(ne khOJMf_[_fjc% ؜c\\c VT1&Vy ! !###V{+'`VO` !!###`{`UVT{##5#537636#"&'!&#"32wiinttf,;pp>WS17532#"5>3 !"&IXVJM[?[5=@0230@=))gj)1&0y1'yc4Nk' uLNN' 8uL*o/32654&#"##5!!676767632#"'4=N qjqjW.E~C%0"@X UkiS*\o-* % T pFwusk' HuJ1L@!21 0221IIPX@8Y0327# !2&#"!^?s}RooR}J6,N&= u+1m-%326&+32+##526!Z}y^ϬvH+W"%32654&+!#3!332#!Z}x_uwg)9dqco"676767632#4&#"##5!%0"@X UjqjW.E~-* % T pΗ*\o-k' uQFk' uOhm& ZH 33!3!#)v++B%$q2@   21 0291/032654&#!3)!qﰖE{e5F{f>dghq%s141/03!!/ժ!0@  1@ /2220%3#!#32!!3!7yŪM0'TB /ѪBL ҪN(x@   <<91@ B /<<2290KSX@ % %Y@ I:I:I:I:I:I: <<<<33 ##'# 3 0YY0S0кv{Z7F <@  B 10 991/<2990KSXY"33!#3+3Fm& O.F$@  1@  /<0#526!#v.+W++Vy0H+u\2H@ 101/<0!#!#++u31&/7h?@ B120KSX@%%%%Y+532767673 3;E,LE\mQ.-"X74oJ+'/.M *5>Bg@2  2 <<<<1@ /<2<20 KTKT[KT[KT[X!  @868Y3#5&%>54&'II Ǹ ˥II<{z WS ;P $@   1/22033!33#P)ˆ+BD @   021/20332673##"&nmuz[v~PEx+:r` &@   1@ /2<<0)33333`++</@   1@   /22<<03333333#<ຆ++B u*@ 2 2/1@  /0%32654&+!5!32#ϊ)+An ,@  22/1@  /<20!3%327654&+332#[fN+1@"#( +0! 3 632#"6/&4767676%67䐌x$[3#F#3bJ/P{3-wRIUA   +t` -@   F!21/9032654&#32654&#%!2#!_eUkUTTVe_cmcpPO^UCCVpou`@ 1/0#!6`ih`0@  1@  /2220%3#!#325!!3y-C7 "Ld64d!{X{H;`x@  <<91@ B /<<2290KSX@  Y@ I:I:I:I:I:I: <<<<33##'#3hh`Pl4_P({` =@ F 221@B /<2990KSX@ Y #33#b縸)`)H&o``"@ 1@ /<0!#!+53265 _7#U^`v=` N@B      221/<290KSX@  Y3 3###=ww`M` $@   F 221/<20!#3!3#b縸`9H{R`@F1/<0!#!#bW6`VT{S%{F`@ 1/<0#!5!и&6ʖhV`\cVec@    <<<54&xjjx޸ܸxjj ++ gsL`[|^` $@   1/220%#!3!3^渖L`66b!@   F21  /20332673##"&øknXrE5od]'+}U` $@    1/2<0)33333U(`66P`-@   1@  /2<0)333333#".𨐖`666L`)@  2/1@  /0%32654&+#5!!2#|y֜XZZZʖ;hi` +@   2/1@  /<20!3%32654&+332#S|yS[`Y[[[`;8`*@    F291/0%32654&+3!2#{֙YZ^X`;%{K@  <21@   IIPX @8Y07532767!5!&'&#"5>3 !"&A`^S E^]INQ%R9>;qdRp:A,*윜+N{ ?@#    221/904&#"726332#"'##3pLLqjUUe9ҝ暙?``B@  291@ B  /<90KSX  Y;#".5463!##r78r5ܖaUmV9{Xm&lC{X&lj##VT533!!>32564&#"##@1|yj{яLs`c. m&jvQ%{L@    F 221IIPX@8Y0%# '&!2.#"%!3267%JR%QNI]]E S^`A9++8*,A:pSdq;>{VDLD&j +1VM `%2+##+53265!327 RC'#U^5iрiv;A`%254+32+!#3!3 褽l`9#9533!!>32#4&#"##@1sjqяLs`cBm&qv0m&oC@hVH&z` 33!3!#ø`6 u%326&+32#!!5!53!q zQ533!!!2#!3264&+WH|y͓LΧXZu\aH{s@141/03!3!/2$@ 1/0#!38X`:Us 3!!!!##U/#˂>` !#53!!!!`¸ fs#!!!2+5327654&#/7qohfL>87||9ժFwr|zKK"VR`#!!3 +5327654'&#HRRQn!&&1`GQ``07 )3 3333###'0:YY{ZSS/B0кv;`33333###';M4hhPPlL_u7&Nu({&n%3###33VrwBh`%3###33,bG*B?`/Z%3##!#3!3)˪B9db|`%3##!#3!3ø縸*`Cq 33!!!#!ql"d9}` 33!!!#!}f`9ǖ6u1&dXu%{&hx/ %3##!5!!+s-B+` %3##!5!!ø&ɸ*%<\Vt`3 3#\IIT`lD%3 3!!#!5!5%lk! mP\P\Vt`3 33##5#535\IIT`l55%3## # 3 3XfuPHNAB}3BL`%3## # 3 3o)'o*?HkG#4&#"#36?6?2GjqjW.E#20@0X U ڗ*\cU'% T pK,m& M;H&mf32+5327654&+#33stohfL>87||wwqwr|zKK"hVm`3 +5327654'&+#33j:HRRQn!&&1'wGQ``07 )&?`/fH%+532765!#3!3HhgL>87)hzzKK_9dV`+532765!#3!3RQn!&``07 `CG %"'&'&5332767653##|#3/@0X TjqjW.Fժ$'% T pI*\z)b%6#"&53326=3##c<1Tsjqø~3$2 #%m& G+@ _PO@/ ]1#H&g%N&G u +@p0? /]1#&jg){Nm& L{XH&lu\QzW{u\N' uzW&jN' uM;&jm7N' uN(&jny}LT`8F0& O&oFN' uO&jou\N&U u +@p0? /]1H&uj+@ pO@]1u\+H{u\N' uH&j1N' ud%&jh0& ZhV&zhN' uZhV&jzhk& ZhVf&zDN' u^&j~s %3##!!/Bժ` %3##!!øȸ*`AnN' ubhi&j7R({u\4RwT:`ZwZ'>53.'#".53327.'S5 #"? F,X,pny@:hVL4=_3_,@ J\9 2D;x| ,\L06k2P#54.#"!!#4>2*:iUVh:@yܮy@k,[K00K[,#= |x::x|U|&##!".54>324.#"3!|}jzB?xonyA:jUVh88hV m=>ˍʀ;;ʏ7kT46ba664>23##4.#"#6@yܮy@:iUVh: |x::x|a=J,[K00K[,P".53!!32>=3*@yܮy@:hVUi:|x;;x| ,\L00L\,`q)!!332>54."#54>2q'@TXX$gC32vvnU3%MvvN%PT^cA K1\in00nBBxY %!!3!!CꪪP#54.#"!!4>2*:iUVh:0@yܮy@k,\L00L\,` |x;;x|+*:"#4>323##".4>;54.32>5#"yHExwE2Y{IHzY22YzHF"2#"4#$3!L1jysܒIKޓ4kq;3lr8{nڨm?AkP@U|&33##".54>3!"32>55}}Aynox?Bzj Vh88hVUj:Bʁ;H֏ؑKBqpC4Tk7=#54.#"#3>32=:iUVh:6Ony@,\L00L\,3P&*;x|63!!dժ]t2>53".5##3 $=`: I҅M`1Kf>>fKIwo55owO6)A33>32.#"#".5467#2>54.+64/sR<]QN-kOa8[4Szjw./-%HqsK* &J +%?eG% F6:4TSvT0de4?yl[B%#?Wgu=@ykX@$a=!##".5332>53=6Ony@:hVUi:%*;x|3,\L00L\,X[v%.54676$73v4[C'h{uk˵n>7<@/7B(=gbV;3qqm`M-_r.9.'#".4>32>."#54>2"3267.p^QeL#H$XpHxU//UxHgX2:%MvvN%PTB=IG?L>D|nf6a-S^&JnqO)MBan10nBAkIHME23!4.#"#6@yܮy@{:iUVh: |x::x|J,[K00K[,Vz!"!".54676736$33!zmP╣zs3D3y-^6!#".5332>5@yܮy@:hVUi:ժ|x;;x| ,\L00L\,@9 .532>54.#!5!2>54&"#4>32<}|;!HrsI#(MqIPB];~yAvedxC4L.Dc@_MM`AqU1.Ql>?fF&*GZ0vpq{[i88gZ;hS<G`s6".5#5!32>=3@yܮy@:hVUi:|x;;x|b,\L00L\,`q/2>=3".54>7'.+532'.#"3&NwuM$PTKuAZ8n%-{3~H (-/HuSn20nBEN(g N=!#4.#"#4>2=:iUVh:@yܮy@J,\L00L\, |x;;x|`q'4."#54>2#532>&NwuM$PT$>PXZ( ^.HuSn20nBEqhD!cEeNA##54>32#4.ǾVi9>ГӁ79iGl IpX*9؜VV؂\OvO`p)D4.#".54>32!!33267>4.#"32>1N7VvM*0Nrc{6DoLgC(^E/ %H>LpJ%CR2R91AGH 8{vlQ0e~y%L.+4^dzE 5GOP%)=(4[{G(*a64>23##4.#"#6@yܮy@:iUVh: |x::x|a=J,[K00K[,=332>53".5:hVUi:@yܮy@,\L00L\,J|x;;x|!332>533!#".5!:hVUi:R6Ony@`,\L00L\,ժ%*;x|ig=332>54.54>32#4.#"#".i0UtDAmN,BkkB5otsq8<]A@]=+IbnsnbI+A~w{ĈHKuO)&IjEH`B.0:Y_Qn@@nQ4W>"!;S25K8)%&1A[zR`yDE}=#54.#"#4>2=:iUVh:@yܮy@k,\L00L\, |x;;x|@D"32>4.'2 .532>54.#!5!.54>ڐ^66^IB]96ju=4L.Dc@;}}; GssG (LpI 1!=uL&AU`YC))CY`V@:iY;fQ:Ibq9_NM`AqU13Vq>:_D%9DHYj:J!!#ROF +3>4.'.>753#Ia;;aHIb::bIe&'ef((f/gk23kg/WܜVzzVݜW3.4>2#".'!!#5#5"2>54.7sr66r{ JJB,ʿg>@id;<_vBDv^`wC *(Hf|gK*'HhA>fJu\2G)3>#"&'.=33"&54>;5"4.#2>/P?ESb;l7>73y)Jg=2yjH'F_7DjA 1O@;jV>,MyXfCb*#4>7632#".'332>54.#"P7dV=BCh@"Cb@*QG5 6& 4G)20}sV$MvR3YB&0K6 %*?*  3!#zgi`'%#".5332653327653#5#"2"hKD]; HO<JI!c?rHE.qw9z78=?`32jqV1sV abIVw %#".54>32533## 6& &-fep<32ʔjq1sُV``ab-3!!326=3#5#"&/kp1rlLח0adIVw%#".54>32533! 6& &-fep<32jq1sJl abBV{+;.#"#3>323##".54>32>7#",U}RMsL'"IUd=fwEeh Gfz>HsR,5Wq<%A3$2("5/fq<*Ze :L-Sچf)'JiANsK%< IvV)=(5)I%!"2>5".54>3!33&;`F& BefC 6qo42n{:laep;32jq1sabGV`!!3 hVh+327653#5#".=4'&#"#3>32JI!c?H\3JI!c?H\378=?`7%32>54.'#"EZ!%9wv9=iPϔjACmPOmD5.NmD`2onf*zΗUUzˍR!bSoAAoS3omf*BqV332653##"&5kp1r{RadK%/D#5#".547.546?3327>32>=4.'D)ZwG?Uh;Y`3Ķ9T839 $2" 8C9W=MlC6_K9cI*, 5~[,J6Bve`-?N( ?OU010$ $EvV17]{DV3ywl'5{rV_{3!4&#"#3>32ʕjq1s/``ab2*=2.#"#5#".547#53>32>=4.' 86jd](xI?Qf>\[-862}'CX0Kg?-bm18 *MnD Or,H4@qZ+^uAXuF54.#"'>32 t*D07".546?3267#=/"?pM  +A,:4 ElL<{7`q,`/'8% qZվA'gVi`'%#".5332653327653##"2"hKD]; HO<JI!c?rHE.qw9z78=? 54.54>32%">54.):_y~y_: t&C1RH $7@7$IxPf}F*+VD*-6-2?kD)KiO}p_N+9 BI;=@#5ZRLQY4O`65g4N3!ADFLR,QO=K?`B!M{$"#3>32!!5>54.;>`@!1ycZ+7R5)?\<=c4ZzFs`dcF}gSv94myPIa8^XfVj3326533!#"&fkp1rH/ӑRadhi{,!#4'&#"#5#".53327653>32iJI !c?G[4JI!c?G[478=e^`32jq1sJ abH4{J"`%!!3&"`hVi,!#4'&#"##".53327653>32iJI !c?G[4JI!c?G[478=e 32#"&&#"32xx.dep<;pfgccTVRۊٗQVTH{R(V$+6##".'73".54>;2"4.#2>8zËKnb-{!GP]8bn::of{9H}t{yKc^M!CךU[7P2,J5.-PpCHpM'GH+OHBRZ`2BuH3326533!5#"&Hkp_1rl{/adR%3#3#Rdm$7'd?98>1"$/#4'&'3767653653#"''##53 ra{ .r &q,bS !/#12j{@E#$]|0q!<"5DAb1)*5"32767#"'&54767&'&'&76'##53|LAV:218UG&/=8O6-N@?_F?D(7-"Gq/#)^ %$ \*$@.!* n F?\KH* #TH#5DAbZw %3#%3#3#%3#ô ^ %3#%3#%3#3#%3#̠&!#53ӤR@dm 327654'+53367657M593pQf$h?FA@6b !eI(R[2* #53 3#ӤR%@-$%#5754&'./.54632.#"'/XZH߸g^aOl39ZZ8{4<5/VVL89CFnY1^5YVeU-"%56767&'&54767632&767/SD435gcbnZdF31`9:H:ZU!LOTAKv?=0ps2# &U~[&r}r 3# E' , ' qpE '  E'  X f'  [X f$$27'&5767&X$JԖ`e_'@5 ^vbĘe4)X ' [j%67654'&'3#"'532T! D' U _$?%#"'&7673327676'4/37653323#"'&' &!UNBAE3I0<^yM\dsቬ+;H2zm^\꜑#P}g£x&R" C~m8($' c a =%327654'&#"67632+"'&5#"'&5473767654'&'3HIj($@GgLK1ZX%5,0.3cM[|dh<2=B%A !  .DF-%!mNH7(M' Oc  %327654'&#"!#53367632,Ij($?GhKL1[W~.DF-%!mNظ\wLR! Xn*' X &/.Q&+8O\79LK5:,]-#4CK%63#"'&&733276u2lecw@A(IiTcI9(jzG1H*V\ss~B")T.327654'&'&#"&#4763&547632#XzL,5;(.;Dn2KxAZM\MObxX'*9:X DD(NOf7*(?$S-8AP6`'  "327654'&'2#"'&5476B!799[]KB{ƶ`Q%T*WE{R,,9.UMAx|KU#JNL 3 &"4'&!5 767&'&'&547632?,3/V%.-js1v-3t9>YH9!$7+(;ڮ.TVLh+bZ3[f5%#"'$47332767654'&'&767632#4'&y]H?BKSxlkA;"b^M`72'#}[7 0&huqc-##NG".*3:,=2IB="9), g:^M ' r rD5%5%DHHnnnnD&567&'&54763233"/#"'&5332767654&#"t$!lD?I'8 .4LT^s7Z $08 " ,d$* 9^W4'6O'&n=NV)qaK" %D5%%5%DHHnnnnnD5%DHnnD/&'&54763233"'&'#5276767654'&#" lD?I'8" +EɓV  , 8_W4'6O -n=*{nmp" %D5%DHnn0('&54737676537654'3'&x!9EO)"a 2=`KG g&ZGM'DA2omb}8T"RY$6s9It6X !Vz 4&"2>#"&4632XXXXztrrt?XW@AWX栠732767#"&'gC*6:*kWZZB6"D6{S )L}@"Fw \v4373ŠF3#< !#'3<&1yI !n8#'337673#" &1CRz6 *boajr!nUPymL%#'37676537653#"' &1/(0H/<(F!34.5WY9!nr|> @2%,*;l>3  *"2767#"'&54767&'&'&76`yg\NNYp0.VhG hRcpl?AOXj<9U9iDGTOA7.?#ou\N/ b \^xH a8' &6F('&5473327&'&5476&'5#"'767654'&#"%327654'&Cv-(;G--0M,Q;(J$"':AGb 41~!$@K5:,+  iEN@TSZ 'C49g=ql@H=.%4-+#%v%'r.C!0B7,g`o6oU%m`m!3/AbM3))I~R~R0>d{*>@ #% +BA?>pпQQ9l,"2"54767$32632&#"# '4%7654DҼJPi?3k]KM?oabu;\SfaƎ:F78UyP8327&'"'&#"%47&76$#&67 #"'632Y60I616*, !*:9u`0'"/6OAK %[9.ȵ!463 #"&'7325#'&&7'6et !yCBBquЍ h! ACBB|U )"32654&24''&5432#5476n$ % *|{e6Lj` %"%:yx~)RhKK>a 9"32654&&5456767$ 3276320! 54-6546$ % #vdz]#x.>r>>$o %"%}@~Y9peDQcFlWNn-7Vs\ ,2654&'&! 3%$4567&7'7$!% /@l~.ZA $! $?=Qm.uG4 {V{,@ //1@ <0%"32544$#"54$76f@@@)@@@@Pyxo *%"32654&"#" #"5323272#4#"$ % zw6^"$J7f %"%&PW0>{#$"2"22#"5#&567663 #4#"&퀀!7y{^܀ݹIedm%jh{'>@ $/<@ )1@")<@ & )<0&7'64323254#4%$7"6CBCbRBCACKؼJjeh0KT 24#"53265$54767653!"'#@>z]U]xTrs@0egu/ss}T|"247&76% 3%$Զ%< &Ѩ'LB}T"247&76! 3%$Զ%< &Ѩ'hBc J"32654&"32654&+&'#"'&5432'3253765&7465&'7$ % $ & cge~PLfrtгJwT\U &"$ %"%IJTObo;4ˋP6A^g[oPIcb$0+&'#"'&5432'32537653"32654&b\U{cge~PLfrt$ & ,PIIJTObo;4ˋ %"%el} ,"32654&2537653%&'# 47&76b$ $ 0nuhigeޘ %"%4ˮ/IJ=%ۏel ,"32654&2537653%&'# 47&76b$ $ 0nuhigeޘ %"%4˯IJ=%ۏ``3323!"'#"543225cиx)3Ɯ)`,88{s\ ,2654&'&! 3%$4567&7'7$!% /@l~.ZA $! $&=Qm.uG4 dm +"32654&&! ! &%$&7676=$ $ W6tm2JX $ $8${{N&ap .%"32654&%&'&'&7!2765!"'676F% $ WD NbfP'G!$ $TrJco<ob{"326! %&$'423 54! P@@<)"FTY=Ib" 2:%"32654&%!$76! 6'&'7%&'&'&7!276 54! H& $ JF r cXD NbfPt!$ $=1TvTEmMd 6"327$"327$7&76365+&7632676#4#"A?A?`ƾ5@VC?/@@@L@@@E>/} cbWZwj %-"32654&7$%&'&763 54\$ %  ʪ=A8)SUB<S %"$36H9G)#$67U 6"32654&$'&%$76!232'&#"%$'&762$ % T\oEh@MqKUPn^ %"%+DYl0yP^d6Qqt^}]F0&$"2''&'$! '&5"32?6*ʁ+=x #esZh N)=md[d'u ! &7623$54'74"m#!AVB8?kP$U.F~>=!{ ##"2#"5324#j=;C>{jVnn!r&/S~i! ! !5 74! $&>%~?>~Si! ! 3!= 74! %&>%~?>wJ~S~i! ! !5 74! #5$&>%~?>~NSi! ! 3!= 74! #5%&>%~?>wJ~T3"36654'#"5432AA\(DeN[̼o[$N[ux"325"547&5423253,r>Jm,Ws> [yu?{EBXFE '656%"'&76! 4"3VA!. {x9f>.`h>4A?~= h\$kb8:;-F_Zkf2)I 53533##5J؎؎؎P;r 432#"324ЄLT3z! 473! 4'$331=PU~iibcWOJf34! %56'&53!! 4<1~k  !TxY9vwsknWa!%! %674#"&5! % %a lշ._z-FH+,S.+RLo ۤTnB7W !,6752363 ! 54+"&$54+"32(TX[P<fI+yhZtԕGw, bb+]mLVW3! 473! 5 &5 3I%$&>8 wjs oeKrdW2'! 673! =4+5374# #&5! 24:ip II-<-]mxRbXxR~X{\+2 ! 75&7!  4'"6258TW\F ;IJ:QU-\p!7 %#65+"! 5!2363 6#&32Ԅ!S}GJtAFWHCBuccyi2#! #&! 2Ɣ#cZY4! 473! =+53254!5 4C(pbAAZZwfq211W2 #&$'6?! &65l_$^M>p v\Y1xOh_[ )euG4! !234!#5!  ! 4E%D˼  }>>&T2! )!"363! %22:M#@͹$[ڀ g7 ##654#"#4+"#&=!2363 K@BM{hIF`fhh&4!! 473! !#53274%$534D'e[?RܬW&Z~IJȕ!6G))=iY32! 3! 5 '%5%30>'Mhko 4>>HS2>+3|'! %!5!$! 3#3%! ! 5)54!  I8<rrr OfkQؔc7X!,!"'#!52'4#&3$5!23634+"32~ Mas1D>3LIvjt| ٔxukYf!! %$54#"'! ! 4'7fGD `U6I@bYsrg8A:ԃM){6\lY4(3! 4%7%#"'#% 3! #>U&;3̿0?7YpnWc$!6=3! 47$$5! nڞòd?;kHuLL8TWJ&)*y54&#"'675&%'% t_CCty`^q|ytJfI8=\ ۣb*#2 3#3#3##Ѻ/㰽;2"4;%"4#"32lѹF|pux$LRQ´){ B32654&#"26=%!>54&#"5>32>32+3267#"&'#"&1xYS\JMLepO27Gn 'aȿuc^8>M<[|%!YHZqYaq4+#"33)+RNPPXx+'#!?@=B2({0#"'&'532654'&+532654'&#"567632wA@Q[\ihWVLHHZ[c[[MaZ[VA@^  VJ=+,nQb54"[\mPDd %!!5!!!#53)Ḹя{ 6326="326&!54&#"5>32>32#"&'#"&PVWMZfRPhgPPTcpP/;}Jb04TY/%W & +ݮyT53+)CBDA>A>A2/H{  #4&#"Ð/.G/  33265G.Ð/.+[%!5!2654&#!5!#J^adlp2r?W75353!5!2654&#!5!#?idxEvDFzlp2r+")5!2654&#!5!2654&#!5!#HEws{p{``PDP. &#"32 &6 Ua`UU`aڜ›>pU?32654&#%!2+#XcbYSJKR]#'.+#!232654&#1E4p1M>ze\Y_[' ?]Z4D|vShPIKHM!!#!ڀ_A33267>53#"&'.A L67K $,*kCBk*,$=4!!!!4<8l$! !#n 333# #|ZkmZ|xyY>E )#"326757#5#"&546;5.#"5>32&ffMD_ntt&pQlT];y:Av7X|&??8?vh+]85l[hmKDg..RE )32654&#"3>32+3267#"&'.&&ffMD_ntt&pQlT];y:Av7X|&??8?vh+f]85lZimKDg..RG53#5#"&546323264&#"tt`??aVTSXXSTNO/00z{{ B32654&#"26=%!4654&#"5>32>32+3267#"&'#"&5kK84:/0n06@F2Q #S-E^T=bg~yI>;#T'1S&9NT8m\)3?26JUJLXP ZQ`.+-,`\`d1CH^#$"%G4&"2>32#"&'#3VWWaA?`tt]z{{.10/OgG3#5#"&546323264&#"tt`??aVTSXXSTDO/00z{{1!3267#"&54632.#" xn7yEB{9t\UTm 2gp fnZ_bW15!54&#"5>32#"&732671xn7yEB{9t\UTm 2gp fnZ_bWO(.54632.#";#"3267#"&546KQ3sCBm0W][Uhd^jrm>s0=r6]H5KX ]0*"0Q>-7;af]=SO(#"&'532654&+532654&#"5>32KQ3sCBm0W][Uhd^jrm>s0=r6]H5KX ]0*"0Q>-7;af]=SG '4&#"32#"&'5326=#"&5463253UQUZZVPɖ0i4>d+]V_E||D^tgxxz)f[bF5302QI !#5!#3#53W?浵ss#PP-8 33##8x0BVxyDI%">32#4&#"#4&#"#3>32B/UFj",2%j$/.#jj?'0@)&ug@Eg?Es6"#'[v+5327654&#"#367632v89hu9CGR,+tt54Ilj!pm;32#"&'53264&#"X.c43a1.\;muvl=_)ʮl$!~~!#: 46 #4&#":&{[YX[ՠxzzx:  &533265ڜ{[YX[ՠxzzxG#3>32#"&$4&"2uu`?@`8UWWbP/00z{{M!!;#"&5#535};JkPF7R]rTP[v332653#5#"&[tCGRWttkGlj{TPg`b^68~}!5!2654&#!5!#K`Yslqj=?g32#"&'#3LSbC,ml/#.,}^\VZ:5`!!!5!!5!!5!!5nnlphˏ5iV ;+53276=#"&5!5![YYZ͹Z-,0|~ij>>~G#3>32#"&$4&#"32tt`??a9VTSXXSTNqO/00z{{Xy#"&632.#"3267y.c43a1.\;muvl=_)6l$!}ut~!#QI+325&#"47&'&547632.#"632#"d&/\R@5a$^`^63302b3q>>>5|4 * &:/ZXX `@?@bj:)#"&54632.''7'37.#"32654&|s .sPm4\a^UV^%wp237,pQ57vonwwn=rO(#"&'532654&+532654&#"5>32T]5sQ0"*0] XK5HWz#"3###535463z>1tkqU.98P#P,gabo53#5!3#+53276=Ι<98h9\P\ m;d+]V_E|~4wuxzzf[bF53[v332653##"&[tCGRWttkGlj{TPg`bO68~C3#!3#3!535#535#4tt)r\PP\ap #"&5#5!;phq)99uun@PpFFI !#3!53#I?P-PPG#3!535#535#5!#?\PP\PPdm3#"54;33#'0#"3276ttdytrx !3rJMB ,|ssW?#5$ U| ;#"&5#5!98dv.FFXtp(QU| ;+53276=#"&5#5!9889ht9hr.EGbm;32+53276=14&#"#4&#"#3>32B.V"#23_uj3",2%j$/.$ii>(0@)&:;Sm;32#4&#"MU!1$XT8[]V;;FQxlX6V~a88wYYk\U|$54'&#"#367632;#"'&5:G()WW*+7[//$1!U&'H/Y,-56\sa8BDH V6X66x? 33##?-{{~: #"'&547"!&'&!3276&NNNMMNNX-(e(-W !-XY-!TUTTTTU=5cc5=J,==,:&/#5!#3!535&'&5476767654'&3fwx=%; )=xw>)[v<.#"#"/;#"'&=32654'&/.547632P1j8W*,]({44MN8> 0Br34@>?=RX!k)k`FG@98b/$+*MW33 V6X66x"192-*TIX00xY46;#"+5326 j{mo>1gr,B0]MecU-:JxlX6M!!!;+53276=#"'&5#535}J88hu956PF]m;T_^s!!#;#"'&=!5!jG$2!V&'G^=R V6X66x ^M^#47#5!5!3632#'03254#a\'Ln& m,8!!^R^=jR332#"&'532654&+5!5!dCP>i;}C5~Dx~uhn\' xM|mTPJS]R^: .#"!326 #"&54UYXUcVXYV&l~~g~]% &$ #{&DqP& %X&2Ecq&%cX&Eq&%X&Eu1k' Zu&d&u%f&vZ&hFRP& '{&GcR&'{c&GR&'{&G}uR''{u&GR&'{&GN&({X{&HN&({X{&HuNm& &(2{uXH&&H2XP& 6)'P& IfP0& 2*{H&JHP& +P& KcH&+c&KH5' \+X'jHKuH'+7u'KH&+&K&,D&Lk' u.k' %uNc&.c&2N&.&2Ncs&2/c &Ocs0& c 0& s&2/ &Os&2/ &OVyk' u0mof&vPVyP& 0mo&PVcy&0mco{&PFP& 1&QcF&1c{&QF&1{&QF&1{&Qu\&2'  2H'&Rwur' w|3VTf&SuP& 3VT&SP& 5j&Uc&5jc{&Uc0& =c&&5={&UJP& 6&VcJ&6c{&VcJP& &6c&&V/P& 7P& W/c&7c&W/&7&W/&7&Wd=&8d^&X=&8^&X=&8^&X=' ' 28'&wX9E' \9dm&wY9c&9dcm`&Yr' |:m&CZr' |:m&v@Z4'j$:&jZP& :&Zc&:c`&ZP& ;L&[5' \;L&j[%P& <hV&\t' .|=m&g]c&2=cb&]&2=b&]&K'jW&uZhV&u \'P& AH"%c&$c#{&D%ct' |c#m&g%& &$ #'<%cm& c#&scN&({cX{&HN^' *u({X7&wHcNt' |{cXm&g"c&,cD&Luc\&2cH{&Ruc\t' |cHm&gk' ub f&vck' ub f&Cc^' ub 7&wcc&b c{&cc=&8c^&X k' vuq'f'vdr k' vuq'f'Cdr ^' vuq'7'wdr c'vq'cq'dr%r' |<hVm&C\%c&<hV`'\%^' u<hV7&w\Fr&Fr&Fr&Fr&Fr&Fr&F&F&%r&%r&pkr&vkr&vr&r&&&p(r&(r&(r&(r&(r&(r&~Nr&~Nr&Nr&Nr&Nr&Nr&Vr&Vr&Vr&Vr&Vr&Vr&V&V&LHr&]LHr&]?Hr&J?Hr&JHr&|Hr&|cH&DcH&D6r&6r&r&r&'r&r&&&~r&~r&r&r&r&r&&&Hr&Hr&Hr&Hr&Hr&Hr&\r&~\r&\r&\r&\r&v\r&v3ir& 3ir& 3ir& 3ir& 3ir& 3ir& 3i& 3i& r&?r&JDr&1&Fr&Fr&Fr&Fr&Fr&Fr&F&F&r&er&vr&r&r&r&&&Ff&CFf(f&C(fVf&CVff&C6fHf&CHf3if& C3ifFf&CFfFVr&˜FVr&˜FVr& ˜FVr&!˜FVr&˜"FVr&˜#FV&$˜FV&%˜%Vr&&%Vr&'kVr&(kVr&)Vr&*Vr&+V&,V&-Vr&:Vr&;Vr&<Vr&=Vr'>Vr'?V&@V&ALVHr&BLVHr&C?VHr&D?VHr&EVHr&FVHr&GcVH&HcVH&IFVr&rFVr&sFVr&tFVr&uFVr&vFVr&wFV&xFV&yVr&zeVr&{Vr&|Vr&}Vr&~Vr&V&V&FH&F&FVf&˜FVy&˜FVf&˜F7&FV7&˜%m& %0& f&pf%V&rVr#525#53d7wF&jTVf&V{&Vf&V7&V7&Nf&vNf[Hf&DHfVH&r''r'8d&/H&6&&67&&m& 0& f&fr'r'$d&3iH& 3i& 3i& 3iVTr&VTr&3i7& 3i& %m& %0& [f&Dpf~ur&F&jr?FfCFVf&FV`&FVf&F7&FV7&\f&\ff&fJV&fvr53#3"ïddm10!!d dmy/10!!/yy/10!!/yy/10!!/yy/10!!/y]&BB-@ 10#53Ěb~-@ 103#1řb/103#Śc/-#5b %@   1<20#53#53Ěb5Ǚb~~ '@   1<203#%3#řb5Ěb/ * @  1<203#%3#řb5Ěb/ #5!#5bb;/ '@  RQ R <<1<203!!#!5!nn\];/<@  R Q R <<2<<212<220%!#!5!!5!3!!!/nnnn\\?!   V 104632#"&?}|}||{|?q?P1 #@   1/<<2203#3#3#P3f111'3?Kt@%1= 1%+C@&7IF:4(:PFz4P@ PzP"P.zP@(/99991/<22299990'32654&#"4632#"&32654&#"4632#"&32654&#"4632#"&H%'H_EDbcCE_yxxwyLaEEacCEayyxxy aEF`bDEayyxxy7a`JGacECcaEyxyEaaECcaExxy"GaaGCcaExxy DP\h4632#"&62654&#"'4626763267632#"'&'#"'&'#"&732654&#"32654&#"32654&#"yxxyyaacCE%'E FedE  FeddeF  EdeF FceeO:8RR8:OxQ::PR8:QzQ::PR8:QyxxyaaECca`JyS  SS SxyT TT  T{GacECcaEGaaGCcaEGaaGCca`$3`u`'))j`P'),'))`$#3$V`u`',,j`P&,',,,Z/#@ v29190 5/-+#Ry#@ v<9190 5 +-#^R^  '4%#56763253767654'& Yb^`_hon"!^XE&->B% #D9``LAB\VBT=BR-;,,1Y7 Bw !#3#3!XEFZ !53#53#5Xޏ!'  !' 4' ;g 2###׍辸= ,47632""327654'&'2#"'&5476"%F$W+,,+WX+,,+XLLLLJKKL @ !UUUUUUUUYnmnmmnmnH !3!53#3#z(洴ttPPD  5   @  W <291<29033##5!5 !wtt}oyc?}!!!>32#"&'532654&#"f6TTXYJz04?9= 25DIJLL...P\\PS****hQQ;JJKJ hh2112=!#!=HCD0;.="327654'&'&'&547632#"'&54767327654&#"hT-../RU-../P--KKKK--P]12PPPP210'(KL('NMK(')+*++*+NM*+/23Gc;::;cG3288Yq?@?@pZ88C#$$#CDH$$0.27654'&#"532765#"'&547632#"'&SP-..-PS+***(X/x==jDHIKLKKZ[-..44]\4421ab21hQP854&e_]]_eTSS}~A @ 32tNN^luu)qJy}wYYk\sa88=T;dXC{dB}TtdFTud C=d?}C>dIT?d=C@d;RAd0TBd?Cd8Dd EdifFdifGdE#d1)d:2ds}d1*dAhVd8g/dV|n{d%0dAHdG6d[v|dM%7d ##"32.#"3267!!!!!!;JܾL:9II9^o78?*?77IG8GI`{c9'.473&'3267#"'#7&'#7&'&76?3&',;8+$"5:lUXn;4";τPqJ8=0;i<)^_HH?WgjιKp(_Y,%6767# !2.#"3>32.#"YQbUYoHqWUnrV,e#7!v'/_HGghGG_^ٜu]\YC!!!!3###5ZpP~WHE9El#!!53#535#535632.#"!!!?-쿿=OL=tyB_))HmBo)632#4&#"#5#&'&#"#3>323 0?o5FP;]iJI9!c?L3!Bjbws{E78{``N#55YQKP%$((TT@I!*##` E326&##.+#!232654&/.54632.#"#"'&]``]z/YM$TP*N(:?>?>SZAm)naAt02k:WX?^)}j9>/b؍$~3YQKP%$((TT@I!* *-037#!3!73!733#3#####53'#53'33ٹpg1 2CYȿYD2FIn$uumuuwugu* %2#4&#!#)"33!3*ԕ|aԕ~V*$oN{&lz%%3p@< 1& (# #43('1)-&- 2'-4229999999999122<2032.#"!!!!3267#"#73&'&54767#70TJBN1Fi1OCHU,1u1!(*=Dl-.&nC>*( n -/ l*33!!###5<~rTws1s/!5!!77#'%5'+s-PPMMo؈onوn9-bw'67>32#"'&'"326767654'&'&67'>7632#"'.'&/#"'&54632326767654'&'&&#"32fbU!O3'A"+0.!. !  _ \5#?\k2,,#2!$(2( 4" )1>((E8&^ ,9Q F 9)ЗiRm:3Xwdg7? 2j7#=5(6$ 629T/ (2M !:5S}$@{mbq~Es/4 -& "TAB`]|@8nRkcd]aC".)5'632327&547632#527654'#"'&#"%654'&#"o|@X"07PYtaTk~j[IwmqJ2530D#24!`NkBX``S㫣†qJ`R{{{{{A667654&#"5>323#!!3267#"$547#536767!5? 7^\iV ^':,hski HE 4cq<;''K={[/ {9b{DI--N@{ O/{O!,&'&#2767#&'&576757O[TUeeUT[Y\Y[dsye]Y\[CvlCi----iH$"u9Bt"#BuflC/ !!!!#!/ss-+ժx hq%!!##.+532767!7!&'&+7q7V#7KPN76SٲM{cݑG; 74K7{P{V^77hy]M@f{L5N{1 %3267# !2."_|dT ȄE}=[~oi 7@,L]r4*:0N̾ (2.#"3267#"&54632%3#"326.2"&54:F#KVVK#F:-Q.~*Pʇ=II=323]W!{/tKb J G'QWab ^TH632#64&#"#'?3%Ǘŋ]W!{ 1$ÑEmJHWEbOYbcJ %# !3!# GHMZMd q+  #  "32!!3463"##526eb223b WU&WU&  1Q~>;\>}N*3>"32>54.'2#".5467>32654&#%!2+#hjMMKLijKLkZZ\[~}ڶ[\ZZ&RXXRuJjhKLLLijJgZZ[~}ڶ[[}~[ZZICBISqmopB 33!27&#%!2+!67654'&` `s1:+YX*q jdZ)VV) (%#'#  %27&"676'&\ӿ,F E]]]][{ab[ 2222jT%%5$c$% &.2&'&+3!.+!!2!27&#676'&%3A::f&AVy-`5?vfAd)7%LK$201/O~hbb)j)V>U)- fh@6    B     ` `_`_/91<<2<<90KSXY"###5!3###r}r7q^^-B0 %#!!!5!bJZCJ]d qddd J.%m -)7 7673 $54$32!"53!25&'&#"6Ky {U>ZLtࠢ""38M{{M7M3TT<`xGZAEIpP3RQ4Oe{'uV& {e'uV& tZ{'>V& {Z'>V& tZ'>V& u Z{'>V& =j{'?V& {j{'?V& >_{'AV& {_'AV& u_{'AV& >_{'AV& @Z{& {B} 5!!B#ZpZR#ZZM '#'"ZZ$MZpZ#B} '7!5!'7ZpZ#ZZM !737@ZZ#ZpZB}!5!'7'mZ#ZZ#ZߠZ#R#ZZRZM%7#7'3'ZRZZ$R"ZݠZ#ZZ#Za 7!##:nt':tna #5'#5!tn'dtna )53753dtnntda 733!ntd:ntB}3!'7!5!7ѓc}Z#Z㔎RZ#R#ZRB}#5!7!'7'7!'/cZ#ZߤRZRZRYxa532767676767632&'&'&#"#"'&/#7!$f ! +!3-68+2",j!!!3 .6+85.0$m: w '07)(6;C+ : ,:'+:Yxa5!5!#5#"'&'&'.'&#"'6767632327676:m$0.58+6. 3!!!j,"2+86-3!+ ! f:d+':, : +C;6()70' wB}!!'#537i&ڠZZ#ZZZZ#R#ZZM'75'3''#ZZ$R"ZZ&ZZ#ZZB}'73'7'7#'7!5hZZ#ZZZZRZZM77#75'73ZZRZZ'ZZ#ZZ&B}'!5!7ZZ#ZZ1ZZ#R#ZZB}'7!'7'7!'4ZZ#ZZ1ZZRZZB} 53#5!5뤤4Z#ZhZ#R#ZM %'3'3!5Z$R"Zh̠Z#Z4B} !'7'7!#3̠Z#Z4ZRZM 7#7#5!ZRZ4Z#Z̤M%'7'3'73!5ZZ$R"ZZhZZ#ZZB#(276767654'&'&'4#!5d >b-*,%:0Z#Z  *+(54<852.&Z#R#ZB#)!'7'7!"'&'&'&547676763"mEZ#Z0:%,*-11> ZRZ&.258<45(+  B#$>2+#5!5!54767676"3276767654'&'&'&l>b-*,%:0ΠZ#Z2)-019 o #*+(54<852.&ՠZ#R#Z};47(+ }  B#%?!'7'7!#5#"'&'&'&54767676";54'&'&'&e910-)2Z#ZΤ0:%,*-11> o #+(74;}ZRZ&.258<45(+  } B}X3267676767632267676?'7'7#&"'&'&'&'&'&""'&'&'&#5! ! Z#Z  > >  Z#Z" *!#$' * ZRZ %  '%  %' " Z#R#ZB!'7#5!3'7'<2Z#Z<2Z#Z Z#R#Z ZRZq` %7'7]JQgz=Zӄh PJV}e 5!#Z"ZǠZ#R#Ze !#!'7'< Z$Z9kZRZe !3!5zZ"ZZ#R#Ze '7'7!354'&/#7!J%%%'HD_SlhX[HJ%%%%Jw422-A8;>112-!:zJZ[ghX\HC+%%'GKY[eg[WMs2=>FD{2,/2{DF>H':Xy6#5!#52767>54'&'7#"'&'&'&54767<:!-211>;8A-224wJ%%%%JH[XhlS_DH'&&&Iz:d'H>FD{2/,2{DF>=2sMW[ge[YKG'%%+CH\Xhg[[IB}5!B#Zp{#ZB!!BMZZ#M3'#|"ZMZM#'Z$MpZ#B}!5!'7pZ#ߤZB'7!5Z{ZM!37ZMZGM!#73{Z#ZpB|  '7!5!'7 5!!ZpZ##ZpZZZR#ZZ*M !737 3'#'2ZZR"ZZ#ZpZMZpZB| '7!5!'7%!!ZpZ#ZpZuRZZ#ZZ#B|'5!!!!5 #ZppZ>R#ZZ#R*M73'#'#'3hR"ZZ$RZppZ#B|'7!5!'7!5!'7ZppZ#>RZZR*M%#73737#hRZZR#ZppZBA! '7!=!Z#Zp{Z{#ZBA! !! !5!'7BMZMpZ#ߤZ#ZB}!73!!!'7#5!!qVa6ZEV`6NZ#Z">RRjը;mRR:lNZ#R#ZRRB!!373'7'7#'7#537!7'!RRȚNZ#ZN|NZ#ZN.9#!RRRRNZRZN ~NZ#R#ZN RRB}!'7#5!7!5!73'7'%!7'!`]Va6.ZxV`6NZ#ZRR;mRR:lNZRZRRB}!!5!RRpNZ#ZNRRRNZ#R#ZNRM#'3'#'RNZ$R"ZNRSpNZ#ZNpRB}!5!'7'7!5!7NZ#ZNpRRNZRZNRRM%37#73RNZRZNRRpNZ#ZNRB}!!7/7'7!5mRRRNZ#ZNNZ#ZNRRRNZRZNNZ#R#ZNM'77#7'3SRRSQNZRZNNZ$R"ZpRRmRRANZ#ZNNZ#Z6a##7!#tn::n3:t:5p::6a '#5!#5'5C:3n::n:4:dp:nt6%753!5373:4:dp:ntn:nd:4:6%3!'3n:nd:4:n::p5:tB}5!!!!!Z#Zwgw"?Z#R#ZRwRwRB}!5!7!5!'!5!70"wgwZ#?RwRwRZRB}37773'''#5:;!\[`Z#ZCCjjZ#R#ZB}'7'7#'''53777Z#Z`[\!;:ZRZjjCCM%#5#535#535'3'3#3Z$R"ZtZ#ZtM533#3#7#75#535#5ZRZtZ#ZtB} !553353!Z#Z{Z#R#ZM '3'#7#7Z$R"ZnZ#Z}ʻB} !'7'7!+53#53Z#Z}ʻZRZM 7#77'3'3ZRZZ#Z}6B} !!#3#Z4ZݤZ#ZZ#B} 3#'7!5!'7뤤Z4̠ZZ#h#ZZ 5!5! !!? Ou]%uuv 333'#!#\^vtP uB !!75!!5 t]]Xv ###3!3,^\X& v 3'335%!!# #^\XtvpFguv %3'3#!5%# #3!^\^$tv~Fuv #3#!5#3/# #3!J\^^|HGetvJ~{GGMuv 3#!!5#3# #3!F\ F ^tvW~uv 3'333'37# ##!#^\fd^tv ^u9v #!5#3'%3'37#7# ##3!3^^ fd^tvJ^uB '#35!7'!!!5 5~t]]EF 7!##!#*:ntaI':tnIFEF %!53753!5!ldtn~ntd&Iv #7#3'# #3 3\^^tvP*OutuB}'0#"'&'#53676323'7'7%&'&#"!32764RvxN1kk2Ow9g' Z#Z 0GD2 & +JD5@3PO2BB4R,( : ZRZ11/0*M !#737'#'RZZ"ZZ$#ZpZ*ZpZ#Ba7!5!'7!5!'7'7!5!ppZ#Zp?ZRRRZB}#5!5!53!Z#Z[qZ#R#ZB}!5!53!'7'7!#p\Z#ZߤZRZB}#53533'7'7##Z#ZZ#ZߠZ#R#ZZRZB}#5##5#53533533ҤtZ#ZtZ#R#ZB}#53533533'7'7##5##tZ#ZtߤZRZB}53533533'7'7##5##5Z#Z8Z#Z8ߠZ#R#ZZRZ !! ?OuuuB 7% !5uzR##7 ! ?S:uuzRuu##% %!3!3hV[7l n7R{+u\ #&'&#"327673 u B!OO!B ocI7͙7IcL 0"'&547632654'&#"563 3276767&#" \m`cu\6% GGnth r5?,/H@3H5,Y:$UeI+HQ\N,tqzSd69->eSY׮l 7!!5!!5!!LLk+5!#7#53!5!!5!733!ZD2/+^^``kIb!0?"'&''7&'&54767>2"&'2767>54'&&cv-'''OO_@8vcu-'''OO_A:GE:;9($(#&GFF:;9cv8@_pm__ONP(-vcu:A_mp__OOP(-9;SPF($(9;PSF'O@*iiiiB91/90KSXY"#3 !q!#7!hqqP3!!"&63!!"!0",Z(膆(\JN*"f_QQĪKM_fOPi%+%3!!"''7&'&6;73#!!#"!#L(0,:CyEB航6'|>v\JK-".4"$: 1cQı2#KK_ff_lFO]B/ 3 3ް2ް2201!3!!".>3!!"N=c(憆(c=֪I9[[9IP&'.#!5!2#!5!276767!5 ,Z(؈膆(\JL, 1f_rĪKM_fOPi%+&#!5!27+'7#53!5!3276767!73&'&'(/-9CyDD舫6'{rx\JJ. 4 %:  1crı2ݪyKK_ff_lFO]5!&'&#!5!2#!5!2767>b(؈憆(؆b>,I9[[9IL9@ 120!#!L^L= 7   @  <91990!!5 5!!LR%# Չ\P_X-y10!!X!תXy!5!!5!3!!y!DCmILfB7+U e+Gr?#; /@     99190'%3##d)#Ӕ/}b%9;v'ue!;e'=e! %.#"326"&'#"&54632>3"38\32#"&'#"&546329[=G[TFBi8\=G[SDCj~/[w~SNAU}^sdlkutcjmvu۠d|k֥s}T!3!T*,}T!3!T*p,33# NM^T,3 3#T^,$476767632#4'&'&'&#"#;9_UijB9 KGLV32326yKOZq Mg3OINS5dK t]F;73 ";@<7  6<Xy32767>32.#"#"&'XJF]t Kd5SNIO3gM qZOK?<6  7<@;" 37;XyG&'&#"5>323267#"''43OINS61-NSXIFJKOQdSP  ;@<7 W"323326X!!KOZq!Sc1NJOR`!t]D;83$777=X`y!!#"'&'.#"5>32326X!!KOZq Mg3OINS5dK t]F c;73 ";@<7  6<Xbz'767#"'!!'7#5!7&'&567676ǧfYUE5kIQ%\n*xrYQMoIF\<[ETFR q$"B2(d%(9L5XXy$!!!!#"'&'.#"5>32326X!!!KOZq Mg3OINS52'V t]Fجϯ;73 ";@<7 " 6<X1y0%#5!7!5!73!!!'#"'&'.#"5>32326Qu{hq,gqTKOZq Mg3OINS52'V t]FR=R ;73 ";@<7 " 6<Xy.1%!5!7!5!7&'.#"5>3273267#"'!!!!'hMEnK Mg3OINS523J:VQ FJKO!8!E$F";@<7 832326#"'&'.#"5>323326yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]Dï;73 ";@<7  6<а;83 $77 7=X0y8&#"5>327&'&#"5>323267#"'3267#"/'00NJOR:G67'43OINS520N]a91FJKO?J4r[DKKOdgb 7 ;@<7 !7)32326#"'&'.#"5>323326!!yKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]D*!;73 ";@<7  6<а;83 $77 7=Xy7S#"'&'.#"5>323326#"'&'.#"5>32326#"'&'.#"5>323326yKOZq Sc1NJOR` t]DKKOZq Mg3OINS5dK t]FJKOZq Sc1NJOR` t]D;83 $77 7=;73 ";@<7  6<а;83 $77 7=Xy$!5!53276767632.#"#"&'y!JF]t V'25SNIO3gM qZOKج#?<6 " 7<@;" 37;Wy' %52% $'"51pZV(IٜXDz;%76767!##"'&'&'#5!!5367676323!&'&'&i1*+V WJRNMR  W,!::!,\HSLPM% +*%'H:^2:A<336G84^:H'@'H?Y L=@33/N0<^:H'%X`z!!5367676323!&'&'&!!i:!,\HSLPM% +*!#'H?Y L=@33/N0<^:H'%X`y& Xy& ''Xy'& oWz'& nJ. 3#3#!5!5J=>𹬬J. ##!!!!>7BX`y 365&'!!5!&547!5!!%43448>!0IG00GG2?8>;_8X`y !!!!"264&'2#"&546X!!IdddeH:l'**z{ BbFE``bq+((d:svvX`yK!!!!2&'56X!! BS X`yD!!!!73# X!!鏫 BZVX`yD!!!!33#X!!֕ BLVX`y!!!!!!'X!!߰ TU UT BX`y !!!!!3!X!!-e Bz(iE`07GO!!!!#"3###535463!3267#"&54632.#"'53#5#"&4632264&"X!!4@#mmC???DJB&G$$K&aqk[Q_B;18BCC?-I\\I-?p`ctiF6A?9i=$#tu#gSSSX`y*!!!!>32#4&#"#4&#"#3>32X!!."]?T\Y88EQY7:DQYYU;;R B=:xoHOM]QHPL^P%U20=X`y ,!!!!3#7#546?>54&#"5>32X!!ffc`--A6(Y1/a3\p$-, BiN2A+,/-7#!^aO&E++ X%y<@     <291<2<<990!3!!!'7#5!7!X}y}J;fժhӬXyB !!!!!!X!!!جX y%#5!7!5!7!5!73!!!!!'G=XkXU7Y Z:wSAw@Xy 7!!!!!!!!X!!!!߬Xy? (@( ' <2291/905!5y!!LK Xy? (@ (' <<291/90-5!!X#!!VVTw 3!!5!5V!!!!߬¶LK VTw 3!!-5!5V!!!!߬VVw#5!7!5!73!!!'5 p[5m{*[y~!߬`u,`vLKVw#5!7!5!73!!!'-5 p[5m{*[y!!߬`u,`vWy&%5767$'5674[šzآb|۠M)Ig#M(Jh#Xy %5%%%'w2rK/dtm0x0oVXy '75%%5%'rKnd.t'o0xEu0#oVX y!5!%5%%%!!'XC_^?sMN#N+PJ>`5Yd|5X y!!'7#5375%7%57'NEO>:fLNtt5\h}a5H<Vw?#%#"'&'.#"5>323265wKOZq Mg3OINS52'V t]FJ!;73 ";@<7 " 6<LKVw?!(%#"'&'&'&#"5676323276-5wKHGOZq M343OFGINIIS52'V t]FDE)!!;3 " @< " 6V w+.%"5>327%5%%%3267#"'&'&''}QINSE^AsMP#Bt]FJKOZq _4O;@<7փ_5Xc|6V w27'732767#"'&'&''5676?5%7%53;L t]FDEJGLGOZq P32326&%&%5$7$7wKOZq Mg3OINS5dK t]FJl#a;73 ";@<7  6<RO]ɗ9=}Vw*%#"'&'.#"5>3232655%$wKOZq Mg3OINS5dK t]F)a#l;73 ";@<7  6<R˖}=9"]OV[w67&%'&'5$774hmU֠Gc _eT2wnw2"O0Bj%V[w'567&'567&hmU*c _eT2Vwnw 2O0BDj%Xy_%!"'&54763!!"3!yɊD_`Dƍ^`Xy_75!27654&#!5!2#XD`_DȊɣ`^ȋXy> #"&'&5476;7!!!!"#'J_+30TD~K9# K^+#Eƍ5p5Xy> 32654'&'7+'7!5!!5!237RJ_+30TD~K9FC K9^+#Eƍ55Xy%!5%!"'&54763!!"3!y!ɊD_`Dƍ^`Xy%!=!27654&#!5!2#yD`_DȊɪ`^ȋX,y&%!!'7#5!7&'&5476;73!!!#"$UrG6:qYȲG5^_=R5 Yƍ5p&`=X,y!++!!'7#5!7!5!&#!5!27327654'&'92D4VqF53 D&#I`__ 2ȋ559`^`X0y!%!'7!5!7#"'&54763!!"3!!yR|ɊD_aDAQjfƍ^`5eX0y"%!'7!5!7#!5!27654&#!5!2yR|Da_DȊ]zTQjf`^nj^DeXwy1/3ް2/301!!!!X!w@Xwy1/3ް2/301!5!!5ywXy H/3 ް 2 ް2/33 3017!!!!!!X!!w߸Xy J/3ް2 ް 2 / 301%!5!5!!5y!w54&'&'3!!#!5!ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FތPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi372"&'&'&547676"2767>54&'&'!5ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FMPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9ՌOi3?2"&'&'&547676"2767>54&'&'77''7ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FBccccPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9ccccOi372"&'&'&547676"2767>54&'&''ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F,ccPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9KccOi73#2"&'&'&547676"2767>54&'&'ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:FPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi2L2#"&546"326542"&'&'&547676"2767>54&'&'h7b%&'qqnNL88OݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F)'%`8nqqMpLM77POO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi!'/7=E2"&'&'&547676%&'&'& 654'67676-ݾOO''''OOݾOO''''OOf:F-T1-F::E.S1.E:POO__pm__ONPPNO__mp__OOAϚ9FPQ9.9떖EQPD19Oi!;!!!!2"&'&'&547676"2767>54&'&'+{{ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F;gZfPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9Oi372"&'&'&547676"2767>54&'&'!5!ݾOO''''OOݾOO''''OO~GE:;99;:EGFF:;99;:F2mPOO__pm__ONPPNO__mp__OO=9;SP;99;PS;9IPi%!!!3!!#!5!3Ҍ8Ȍ7nj6Pi %!!!!53rM_Pi%!!!7   '3ͬc  ccc #c ccc Pi 3#!!! 3Xy!!#yӪXy!!5!3y--Xy!!5!!C!DZXy!!5!Z/`103#`7 !!'  TS TS8X`y!532767>32.#"#"&'yJF]t Kd5SNIO3gM qZOK ?<6  7<@;" 37;XAy 755%5!5X!#!!ʶLK XAy % 5 -5!!y#!!!!KL VVw?  55!5!w!!!KLVXy? 55%5!X!#!Vw $75$&%&%5$7$7"nWlܜ86s˖}=9]OVw $'$'5%$5)n˱#lݷW680O]"ɗ9=}Vw)%*67&'&%&''&'57&%5$?7dMjTVʥ3˱!3a4m"cjX)3S][e﹏3N@%HZ-=}k$Vw)$(6%'56?56%7$'57&%D>WwZN(۷+/m")33 +Si063hiyje˖X[y3!!!'7#! !PYBzrYh?ݪ@?@X[y3!'7#5!!5!!PYzrY(s??ݪ@X>y!!!!!!'7!5!7!X!w R`RgfjfX>y%!'7!5!7!5!!5!!yRgw! RjfhDfVw?%%&'&#"5>327%5 %3267#"''43OINS:Z0!!x2XIFJKOQd>3  ;@<7 ҧK{"327V!!?E>XIFJKOQd>C43OINS:Z0"323267#"''&%&%5$7$743OINS61-NSXIFJKOQdSl#a  ;@<7 W"323267#"''55%$43OINS61-NSXIFJKOQdSa#l  ;@<7 W" # #h֣ͣG9> 3 3h*338> !!# #g֣ͣrcG9> !!!!# #gg֣ͣrrcG9w!##mZ##5w33ϸ"mZ!533X%C!!3#CrrCr[  C!!3# CrrCr[ %~!!3#CrrCr  ~!!3# CrrCr Xsy^!#y^ap$%%$~  %6 %!&'&"112*zz`XXroGGnY  67" ,J5PP5JX*77*#L8P"2642#"''7&546Ċnji56؝]QBɉLJo3NEQ\|G+-7AJT35#"&546;5#"&46235462+32"&=54&#"3#"2653264&"2654&#ςYxxYςZxxZE1/EE0uu0EE`Ev/EDaEEaDE/wZ\ZЂZwwZЂZ\Zwu0EE`E`E/1EE0E`EE0u0EE1/EXsy^!3!yߨys+~!#!r ~5!#r rS;+;!!3vrr;)3!rv;SLl4732#"'&'.#"0 Pd@7+ h$TA6?&H| #"&546323250 Pd@7+ h$DTA6?&Hk-khi !!!#%!!h\roa`޾"(I  !! #37!#3'Q''Ho99Ƀo!p=⻻}(TI #!!7!#3'l)okkɃ=r!r⻻+2" #/;GS_kw+7CO[gs!2#!"543!254#!"+"=4;2+"=4;27+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2%+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2'+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;2+"=4;22+"=4"=43+"=4;2+"=4;2"=43!2#\\K\=Kl]\\\\]ii\][]\\\\\\\\]\\]\\\\\\x::f>]Y"\I\\\\I`LLMK\y>>(I !! 3#)%3!'-''96=ûHJ7 hHH--JJ4!!!rrZm4 !%!!5!5!5!!mr4 3#3#!%!!5!!omrX4  ! !! !ZS;Z$m4&  4 '3276'&#"!#"'&'!!67632!zzzzzzzzIwwHS;HwwI΍GG2GG$_EXXE^m_DXXE_3#%673#&'%676'&1rsrs2srsrTTTT@TTT|B B@B)B)΍11@4121tD&  4 !%!!Ybm5A4 !!!!;rbmY5A4 !%!5!mrXae4 !%! !2mrx1x4 !%!!5!!ZZmrϠZnZ4 !!'7!!!'7;rZZmZ1X1Z'327#"'$%'3632# 6'&#"zz=>lWWsPYX2灾vzz>GjX`OXG%3 33!#!!3/ .^+^kk4 7!!#xr;rc/Krmu4 % !%!50!53!ymrZ[z4 !!%!!''!;GZZHrZEZw%3 !#!3!###.77/^+kk4! !! 3!xS;ZK{mu4 ! !%!#5!5!"~~+mrz[Z4 !7!)!7ZbZmEZwZ4&  4& 4?&  4&  4D&  4&  Z^' 4& ' ?H' 8 t'  XyH' 8aXyH' 8!% *!%#567!676&'&'&'&|z*(2J E<iKH#&GIJE E|-2 M3 +O  ! r; ?9y?p  ! Xy*= 67 '&5677%"632327'&32767#"'&'.#"y{h p{"zi piE5 5dJ t]BFZCEF4 Zq Mg3ħĨ1P$s 7%ےs3 !Xy3#"'#&'&#"5>323326yKOGU43OINS52^NFz;7 (  ;?<6 '='3#3!!#7#537 `?+0'7ϺrSSr4!!!!3!!!'7#5!7!xrddeZmn;fժhӬ4&" 6`VT{F`4y& 45{& ,4`& 4`& Fy. ! //1 /<20#5!!!#oo.ڭ%ۭ&H:07 %#"326=7#5#"&546;54&#"5>32hHH#[]E>Vcii!fHatLT5m4:j2O85%--JJ??8?vh+]85l[hmKDg/R: 953353!53!5!#"326=7#5#"&546;54&#"5>32as7s9s;#[]E>Vcii!fHatLT5m4:j2Op"r??8?vh+]85l[hmKDg..R:1=[%5!!5!#"326=7#5#"&546;54&#"5>32#"326757#5#"&546;5.#"5>320;0#[]E>Vcii!fHatLT5m4:j2Op""[]E>Vbii"eIasLS5m4:j2Op"rrrr??8?vh+]85l[hmKDg..R}??8?vh+]85l[hmKDg..Rk*.26:>B#5#"&546;54&#"5>32#"326=%=!!%%%5!55qi!fHatLT5m4:j2O85%#[]E>Vc,,,,]85l[hmKDg/R}??8?vhyyrDyyyyrryyR8~4<DLTZ`%#5&'&'&''7&5475'7676767537'5676767'7&'&'&'5'7'%654d3/D9229D/3d4.E9229E.42*  *22*  *2*88fWfEOPDfWg88fWeDPOEeWf8g * apa *  * aa * ba.43ab-45F^'04>2".33&'."#67>76#FVʓVVʓ"vv"Z83Pv"Υ"vP3ʓVVʓVVnh<8PvDDvP8"vP9~?9Pv"F^&0!4>2".7!&'."67>4'&hVʓVVʓDvP479;;74PvD"MʓVVʓVVv"i c;DD;g"vP F^ %7'32>4.#52".VFoDvvDDvYoʓVVʓVSwVF_YvDDvvDoVʓVV4!!!xrZm #53àZ03#s #5ˠАWeE%3 53Zz i#0s  3#àР˓[m#!!# q3#s!!`N um!5!#z3#zz3!5!`z m #4763!!"ƺoyeD9uߑfW#'&%'53 763:*enK==Mne( =C _AEc H<  3!!"'&59De{oVfd #3ƺ m 4'&#!5!2 9Deyo}Wf &'&3!3#76<(enM==Kne*!<McEA_I= 3#!5!2765 o{eD9ᏞfV3 wv% !!!!55!#uX ̼uu]]e! !!;bc;$<.: 1/<03!3T.%y5!!X3 2!@ 2 5!!5!!5!4)4𬬬 !!!!!4)4XXX 333 Nf  !!!@@@ Nf  53353353353𬬬 3333333XXXX 333322s's' !!!!@@@@22s's'!!!!\!!#!!#\!5!Z!!X!5!$Z!!$X3!-Ԭ3!-.*!!@Ԭ!!@.*5!3,,(!3,X5!!@,(!!@X3!!- 2Ԭ3!!- 2* #!!!P@ZԬ 33!!P-#,Ԭ!!!@# 2Ԭ #!!!P@.* 33!!P-#\*!!!@# 2*!5!3,Z,!!3,X !5!!#@PZ,( !5!33$,PZ,!5!!$@Z, !!!#@PX !!33$,PX*!!!$@X!5!!Z !!!!-XV !5!5!!,ZV!!!X!5!!$#Z !!!!$#XV !5!5!!$#ZV!!!$#X5!3!,-,Ԭ !3!!,-XԬV 5!3!!5,-3,*V!3!,-X*5!!!@,Ԭ !!!!@#XԬV 5!!!!5@,*V!!!@X* #!5!3!,-Z,Ԭ !!3!!,-XԬ !5!3!!,-Z,* !!3!!,-X* !5!!!!@Z,Ԭ !5!3!!$,-#Z,Ԭ !5!!!!$@#Z,Ԭ !!!!!#@#PXԬV #5!5!!!!P$@V,* !!33!!$,P#X*V !5!533!!$P-#ZV* !!!!!@X* !!3!!$,-#X* !!!!!$@#XԬ !5!!!!$@#Z,* !!!!!$@#X*5!35!,-𬬬!!!-,XX33*!!@@*DH5!5!xX333x 2 2H !!!!-Rx !!##xmsZxH !!3!!xm3-sZRH !5!5!5!,NX 5!###lZZXH !5!!!5!4l t,ND 3!!!--Dx 333!x,ԬxD 3!3!,(D 5!5!5!3,,D|X 5!333,,(DX 5!35!3̠| 3!!!!-- 2Rx 333!!xs 2 2Ԭx 3!33!!-s, 2ZR !5!5!5!3,,X !5!333xtZ, 2X 5!3!5!33t, 2H !5!!5!4R 5!!###sZZH 5!!5!3!!t,-sZRD 5!5!3!,-DX 5!333!,,ԬD 5!5!333!DX,!5!5!5!3!!!!,,--R5!333!!###s,,ԬZZ !!!!5!5!333!-s t,ZR, 4763!!"Q[yY[`~| 4'&#!5!2.-Yx[Q`~=?x 5!2653#xY[Q[~|2Ψx !"'&533![Q[Yyx2|~>3m 2>#3> 2> # # 3 3>ݲ}#$cc|5!F3F~|5!|iF3P|!XF!@F~|!|iXF!@P5!5!!5iVV333PP~P!!!iXVV#!#P@P~P( ! ! !!!!#!#(!(F(!Z((!((!(h(!|((!(*(!>((3(i( } F( #'+/3!33!33!33!33!33!3䟟䟟䟟mnmnm( '/7?GOW_gow3##3#3#3#;##;##;##;##;##;##;##;##;##;##;##;##>>>(!%)-13#3#3!3!##!#3#3#3#3#3#3#ޟޟ#|ŸŸ|Ÿm#( #E( Zh!|i }h( (& &  (&  (& &  (& &  i( }(&  (& &  w!N<w7!!!xr$<w 3!254#!") ) xrVVVw&  w !%!5!5!5!5!5!5!5!5!5!N?:IILII޸[["[[w !!!!!!IIN< w !%!!5!!!I) N"-?33 #&'&+"'&#"/573;2?"#'57#&'#"#567635a)8)kOkaKA-'= //G),Y=  !H$ /+HDH)+) $., fYYx !=Z Lx73&'37&'67&'67&'67'32654'&'7654&#"3672 $54767&'&47'&27632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5430'&327632#"/#"?#"54?'&5432&5432&56327&5432'&327632#"/#"?#"54?'&5432'&327632#"/#"?#"54?'&5432PO~ )*+')+(@&'$||e?/A}]\B-71SLoWj\vLLr%%,* #$ )*n$ % +) $ #*+    ? '+&()&(+&p   % % +) $ $*+*EC*Z*,)-)-*,%&%&fБfU 5HhfeefhH2pu^QFs棥sKQG4 4  22044 22 9       L%('*%)(*%)(*t     144 22 0r!2CTev+&'&54?6?6/&2#"/547672#"/547672#"'=47672#"/54762#"/54762#"'=4762#"/547672#"'=47672#"/54762#"/547672#"/547672#"/547672#"/5476l=.%G\&#- Lj.N 0d&K4i    }    g    s            &                            H5-V"*2-.T<:U'EGE'DN-ֈU]\`CDcbF]WWZA@ZZ@AZZA@[[@AZKPrqqrPGeޝdMP䠠P }2ٛk A4&#"26%4&#"326#"547&'&4632 $54'&'&4632XP79NNqO.N97OO79N']EacDC_\n\U>DbcEXFDbbDEaaEEaaEDaa+G詄UUSj멏i LVV6 "32654&7#"32?ɏǾ/`TcȐɎ;P12Y.1"264&"3264#"54327&5432#"'&'@KjjjiOiiLKirqrtPssrqQܩZTdIU )5AMYdp{3/&76'!'47653!476=332654&#"#"&54632'#"&54632#"&54632&'&676&'&676'.7>'.76$6&'&%6&'&6>'.>'.f<;.=+,>/;Kyz~LZ|WX{{XX{IE11EE11ET    m       ;   R       s@dd@s}>}=/NnN/=}>@MllMNkk& % I% % "!$# "! "!! & % % & %-5AMYdp|5#!4'&'5#2#"&546"264&"264"2647>'.7>'.%676&'&>&'&7>'.%7>'.676&'&676&'&753!476=3''676%27/&76'77&'&/#?6'&7liilYz{XW|{bEEbEd      8    @     .HxttxH%?%5E$6  6$D5%?%-5!!1(~(1 5,4t4(4N4(4t4;hhh%%#%% $ %_ $ $!"!$!/!!!" $ $ $ %:-,GtG,-: XLRqqRLX ![$n[ii[n$[!ob !!'!tKZGkcn "!!'!##&+572367676hNn_5, S Grj3#-EmDJ~o.(*!4\tR~UL !!'!  ##' CI3Z  > << 5DCX << ; YD36273 ##'5&< +Z@\\DC ZY\5#,5>~3+&=4%3+&=4%3+&=43+&=4%3+&=43+&=43+&=4%33 #&'&+"'&#"/573;2?"#'57#&'#"#567635@)A({@(@){A)A(@A(^)4 'iOj_J@,&< //F(0'&&'ܐ'J&(lN5  >! )&V?<?$&$ '& ZN N />Eqw!674#!!6?676'4#'323276767654#3#&'&'&6%67!672!&=75$/563&43!32+'!67#>54&53 *,  3)="(&)09$) L&TE` MPA[MH Y $ ;&&e=O%/ N ,8(.7L1Rf~H8SQ,zH%9D6 )jGP@4Rjd_*KfsDIR 9! O  -]&C+/3#"'43727&'#"$472776725676&5&U8)$ tJ .; d3f,"3' VD ( GL/7;;,g t^F$< LD&?>X4R !/# I ? P?D!)Mv>/z2!"&54676737#&'&54>;7#"&546767!7!"&54>3!6763!2h!.)g$'30!/&j ! /:(/  )/ 9)/  9)0:*/z2463!2!2#!!+32#3#i9/ ! j&/!03'$g).!*:0)9  /)9 /)  /(:!!C4&#!"!&3!!"3!#";#"3&'6737#&'6737!"'67!7!"'63!67!2e;'+pCCo CCCC2CCKK<LLKK%JJ60"2=2).=<==<@=:>=;TT USUT UT83$QE!D72654'6#"'4#"'54#"'54#"'675674767#%$4:JILLHOKHLKIhghgighgD>-sJ1 b6'SS cRR SS?SS\\K\\;\\]]!A*>K!!D!254+'3254+'!254#!'!254!&#!"0!463!!2#!!#!3#3lCC2CCCC oCCp+'q=2"06JJ%KKLL:=@<==<=.)g$38TU TUSU TTE!C32=732=7325732'654&#'%2&'&5&'5&'IKLHKOHLLIJ:4$N->DghgighghSS=SS SSb SS'6a!0J)K>*B \\]]:]]J]] O!%)-1523656;2#'7+"/#"'+"5&54775'"'5476;25'7&567635&56;374765'75'76=4'&+ '"'4!#"'&36365&5&#%#754'&5&&547'5367&7+&'&'735&2?"5775537'7'3533553535'32767&5%2?&#%55'575775775uo,Mz"060D/5I:2'5:6&" *:D:S46$.e QN5  u4MDa 6bUP+ ,H;`I23N5( (#I0M '^5%#!:X+ "*  6W}W:uW4 5vT & /H3V XD9\SL+&31.d+%X!Q $2``KPPPG[6%# Qy- 6[[3GK[O`_A[-)$t7 L-$ L6=" (CJ#R"0 :~GB{~Eoj<4S[Za LC5 ) .U%+Z&)͢ 7e<ILAaMoK33K@G6 $$(& (''&1/----2)( (-((d.'-T?OK8T$ !T3(-<((')))())( &2%2#"'&=477654'#"'5473t\*e O@UCXq P S. P ӍMOb>YaYƮ58l7P P@ $0<FX + &=%6&#"3 6=%&#"';27!54767%!&'&'2+"'&=476^7\Pg㑵Hr'.)%sM M#fC-7!%A.; ӎw:kKqz +H*G;M tu/&((AA&:+C;."/ 8Pi>'67&&&'6.7#"'&'#"'676'773.#'6'5676&&5476'&'67&&07 ^< 1x,B5@2 JVMv!#uA+UBDX[f*;-10)..C,sB#HKU P]12<0VQ }%'H6-T}^$k7 R2'7f!A\;y?1!50BEt"!zkQ;0qu0\oi:5oPZjsXFaPJGl;4ejN^1F[q7&&'7'6&'$#&7'&#"'5&767#&''5$'67'6'6'5$'67'656&'67&'6'&'''5$7676'&&'6'63&7"7&'7&'7&'7&'6'6%676767&77&77&''5&"'6%35&'.54>23#67#&8 p +WDTc'H @XO`= ;*)8 kDv/Pk-J KDhGa D`gBD6DDD =3dTDW, :g j)Yi#'WtI-9w18$^8;./7-I)jS)'#i\-IM91D;8%a7/.D=uRNBR&'%QBNRq d2 D s98C ["|44&3, '2^3R T(B?#'9C- !y ~#Z10>N?$%Y4 )%FN? ({ usis< 3(&^T05<>7;,#4[:O(vAfGEtYB z^~4j #,;b:['~Av@~EQ Bak4~_H#T2 $$$$ 2T"`q$&'6&'67327&#!65#&3jjdnh wWVݱqZre[c7 7 cyX ,35'533#3!'#'5!5!5#53!5!5#!!ʶ~~ blvF F A<<3ffX苜qXGccGap 3264&#!2+73 #'#5# 3m`hh`2`Ĉѳh|;vvʷ}f33#!!#'!'57!5#'5735 64pzp7d+!#!573#'5!3!'573!#'73!#'5IxOOTxSVVdY\yvVPPvIyY',32#' 37+ &5%6323'#57'53mJl{~m@+ݼh4144'0>,_ vNknmmnObs32732753"'#"'432364'5;+"'#"'53275'&'&54?5572'#&'&547634%476='4&#68$$B )Z>&A_;i88u-o1bFGfQ_M5mwLbkjI,K=''8 0##Rm4 ڹ+ܴ5!PP"4\=ѻ"8Qý32#"&546324&"26%#"5432itvxsq1"00" 0/B//B/#a`ir|H!//!"00""00"!/0 _b #>DJPV\bhn27654'&#"&7367'67675673#''5&'&'7&'%67'7&'67'%7&'&'%6767%&'&$h%$%%34$&1++XSA N@`==k>P CRX++XYC P>k==l?L ?Q oL+ Nn;P?;@  nMNn3%%%%34%&&%s==`?J >PW,,WW? K?_==f?H?PW,,WU?H?^<=Ke+cL mCP`k<<!4(0847632#"'&7327654#"&#%#&7&'67&'67!󫪪vӤ=6 5N'V[S.U[R󫬬񫪪񿉊 ʯX[V[X[V[!4(0847632#"'&7327654#"73$3&'67&'67!󫪪vѦ=63QNV[S.U[R󫬬񫪪񿉊w  'X[V[X[V[!4!)47632#"'&%#$''&'6%&'6!󫪪4>;D@KDzcngk?dnhk󫬬񫪪I kpinipi !4 "*2:AIX3#''%#&'52#"'&5476!!'5%!!'53'5%3'5%3#'32765'&#"M==,/0#H 8&O6 |7iY06./==e6a&i1r4z012+KN2HQ>>>>f^2"/1]8`1"Y 4f2y5+ +"'5$76%&'547327676=&#; hz0/O{[(*TQ~`NO =tR[\ 8d<+% &56;2'5$%75#"3vh0.P~N^(8P,VRZycOpO >S\^ f`1B7#5#53'&'&54767&'&=33676=3#327654'&O&"}|fzg}}"&&"}UQn$mQU}"$nQUVV{xVVUQ<"{u^^\ _u{"#| zUOOUz |#YOT{zQPPQz{TO@>)4'&#"3276&5476327#'#53'&`____`oŠqk]^^]YYňÁhgf@> '"3276'&'7#5373'#"'&5476j___``_ߓqŊqYX]]XYfhhĈÁj0 '&'&376&+"'&5'476%7!Z{z[ZZ[~\YWmpN#ZX[[YZ[PQmp#TG*52764'&#"#463233#!5sPQPPtrQPyzg֏LQQQPPQr{{t|g*#"#53533#632#47654&#"#ddiqqCBigIIugzyUr}ppDtPQs_CS 7"27654'&7#"&54767##53#533333#3##h. @\ ! 2(>>?ZW~>'3|}}! -/@ /- !^'?XY??~YX?(F}R}hh}}hLS<#5#535&'&'5'73'3#'73'676=35'73'13|e{vw}wwUATwx|xxS@Wwx}vv|d|re{Eus~~suE|VAKtrrt@X{Ius~~suI{dr|*! #!!!'!27674'&#_82V)3{D#MHZW{s{?zK8! %#"#&5463 67!2#6#";z\)MaBuh __ itBaM(]y tt[+##+tt\5."264&'67>3"#"&54767&'&#52hq៝rd:BJ|^d#!p⡠q $c]7A;{26XY "zz" YX62 &'5 %$ 56?6'.j拈|*xIIz'&|JJx, F42$8"3264,'5'&54632264&" &$#"&547>ȜmmNMm} lyzU<Mnnnm+}7 lyzU<|||,&(uO#eaHG||||Q'(sO#e‹`IH=! <>'.463227#"&5454&#"&'&5476766&D9BB8Ğv?W:pbW~tp) "-ff)-gtpQ@3AA:ACj›GmN?ijbvr56WGe((Wi0154d)-?/6?2>32>32#&'567'6'#4&&#4'3>64&"-S5,9"\0+Fgv!4u|W")^,k ikdS!eb[_[H|NYC:RHB=G`SnU|#!!!53&54632!!5#67654&"U't00Z =yy= :]ZssZ JjkkjJ 2f4%353'5#"'&''#&&#4'3>32>32YE;<<-!&Y*dx cf_Oz.*O2)7Ze``b<`WuALh`8!5!1##'!5!_drrPk^K{U_W{'/27632#"'#576&#"4'5267>327&'"2XCZd}uud$gq~dV)40tlx!&%"dLk}:Uwma4 sOHK{wY@x A63276327632&"'&#"'&#'6327627632&#"'&#"'&#'YR #{=('%{XNCEz>O&z>'(#&R #{=O&{YNCEz>'(%{=('#&ee22ee$l66kd23dEPdd33dd$l76kd34eE^s#!5!37!!'  L34((C $Td67&'&"!3!67>54.#"!5&'.54>325467675#53533#63232>54.#"3'8xpA?9l9>@q<;9'D} 5RTP=: SSPSS ;r>>p  p>>r> !A% )RSQ1 )6BB6) 1QSR) p  ""V{zHNRh|&'4>32"'4>32&'4>32&54>32&54>32#!5!'!567>54.#"32367>4.#"323732>4.#"327>54.#"732>54.#"I )),(?)(#!3()3$))BG!(( K{mg,;h IXI L$  P   H''1|G''#s%'')7$ ''A  ''HTݬ9.%~~ rF)~ wpa!'-23353#3!53573#'5#5335!75!!5'57!ePPeeQQeDpH>H@A~}}~00mrTTreppe-!7CQ^&54767&'&'5676767&'&54>32!535#5##3654."!2>4.#" 1""#@%@#!@% ?$##0 ܍a1%?E?%4,/--+D,/1+ 4;AB<>"  "#>"">#"  ">#10$ITNnVB, n ?%#Naji-/4^t&AYcgb3%' + ((NV8OQĿ>:<uyg**5 k5h P[32>4.#"732>54.#"!5&546767&'&546767&'&4>32'&'.#"+L)+L*+M)(LH     > |n @: !:;! 8An} E04`a30TL**LTM((     ++x: 8>>q ?9 9? q>>8 :x++c^UZbbZU^jg% $Tdhy47&'&";67>54.#"!5&'.54>325467675#53533#63232>54.#"!57#&'.54>3234'67632#7$5oh<:5d4:;i865%1MNJ96 MMJMM 68JNM0v    +0 +/0U-,,+,.T1/, 9j9:h  h:9j9a &LMK- '2==2' -KML& 1  V//X//X//V6HLP&'4>32"'4>32&'4>32&54>32&54>32#!5!5!M ,,.*C,+%#7+,7%,+ FK#++ PDNAM**4d;K))$'**,dY&"**E #**L:ƥ??@@=%)5!5!3353#3!53573#'5#5335!mD^JJ^W^KK^׋LLZZ,}}uz%yuu{{u}--4@4767&'&'5676767&'&54>32!&7535#5##3 1!!#?%?#!?% >$""/ _1+ 4:AA<="  !#=""=#!  "=![1=%T e >6.HC'L"'G 12h[FH`[$%ok+*8d .Ncv[.7&546767&'&546767&'&4>32 w "E> #@!!?% =E!w ./@ =CDz" E>"">E "zDC= @/.QO##"'##565'##"/547?kM ,4N"DF &Fi?JO/FB!O {|Im<&=M2227632#&547636=4'&#"#4'&#"=` ]d2 cBU;/G;SXMB:@B ս;7hf% #>|\@9@O &&5 iC n:^O G  %2O7236;2"'##'65##"'&5476;235&'&=476jS c1=EO ;SCFRʝT6*F@E1;O+.`162V Yi8/D ;8[B VRP"<B+"'##565#+"'&575477;2732;276=4'&3&'"ih;F(wQ"DG".FWCNfBy" bODUq5u4  Pro@ |S`64 '4'&'~ v '  w (  w ' $k=F F>jG3~Pjb^*IerN{̑?qJAe}Ωv6\~x(ONPPNO(!8?|EE|?8!r!_3#"/4?23D-!]UF+}{<!/3#'654'&'#"547326Rs9W5[S%3;B[/OBC'*|<j_g#"=4?2%#"=4?23ɧ%QM?ˠ)TK7(w7џ5s ?|O"'4723!#"5472!5YA>RHIOq 1 ӫg 4D% 3363'$6'"I+4 puoS^*  3%#'#3%#';&2 IʗHj7*(,377#'#'547#5773%%,ppsr,'zzxz'984?/99e5>:_`qE#&#"'5654'5673;54'56732733273+&##&"#&'565*G1 VV2Is3'{'3sI1VV 0Gs3'{'3sP3+1='3sH1WW1Hs3'=1+3PH2WW2H. ;G7567&'&'3#6737'#&'7#&'6735'67#3335#5*)SR))&*&';((:'&)'ȶkkn\\[[nȶ kk n[[\\n`ff/ee.((&(;((:(&((@))SS**n\][[o jj |o[\\\n jj e(P( /N#.6CMhw!2732!'5675&'&=32#&'567637&/7&+"+&'532?4/%32#'#&'&=4?#'57335'3!273+#&='#"/547354;2?!&=35-,;K> #WU* y "њHV ηz/;@"q=o )we)$IY'L ALaXwH >X%CII$PC/DN6g+  b% #  jnN :3 O+5{bQ< ,d-  X] f '^ JJA!< 8 2E35733!&54?'7'7!!"'&%#'73676'77'7'&'676}]} =--HW(7*! >y*1c{F=.,H-.'d(#Y+GC8957jN})%%tGl5nm3(,H:0/(_kiN}!N920K 1DW3!5>7>54&#"5>32&54?'7'7!!"'&%#'73676/77'7'&'676@.#5*"I?6O"[m" c<,+GU 5) <|w)/ayD<,+G,,&a(!>B<#q'%NG91 M7835hL{'$$qEh3kj2'+G8/.&HghL{ L8*/D *(=Pc#"&'532654&+532654&#"5>32&54?'7'7!!"'&%#'73676/77'7'&'676D|q%N24H'CB=9PS3464E>6O#]o 32EX!#2632#"&'532654&#"&54?'7'7!!"'&%#'73676'77'7'&'676Hevyn$L27C'y*1c{F=.,H-.&c)"ERUHHS S /(*. 8956jN~(%%tGk5nm3(,H:00'\jiN}!N91/K "7J]"3264&7.#"632#"&54632&54?'7'7!!"'&%#'73676/77'7'&'676]'00'*//l+2>AB(S`dT^dyg7<,+GU 5) <|w)/ayD<,+G,,&a(!.T--T.H D&RECSukf{7835hL{(#$qEi4lj2'+G8/.&HhgLz L8*.- X.A!#!&54?'7'7!!"'&%#'73676'77'7'&'676n!?/.JY08+"(@},2fH?/-J.0'f*#&K:;68nP*%'wIn7rp5).J<21(vmmQ"P;:1-K':7&54?'7'7!!"'&%#'73676'77'7'&'676N!?/.JY08+"(@},2fH?/-J.0'f*#:<68mP*&&wHn7qp5 ).J;11)wnlP!P;;199'9HR!273!567&#2&'676+&'67'#'6765'533!273+#/#"/47$,7Jv I MO $p%|I ^ [T<K"(~GW$?8?])( EAs#L, T 0 ` +WVۄ`$$a.|%2<J\e3 + &=762367#&'&#367&#&#"3274/"34?3'35732?5#+'535^-J|@h'\-e@<r2&H); uZJM =9jl:jgb.Qi2Q|酝:*}( dpR!h j `]_i$x:-(^%,3"ؿEa HMP E7g /:BR`j # &5%6; 65%&# 327#57&/#2#&'676+'%3#'#&/47'3327##'%3#"/6j1M{ǮG&z v$ExݨE(+=R:n:D!s Y!gQKum;} uA;>e=g¯Cy??ԢB|*>w4I ' 5@` bC$ j$H?iM!%.|7H27&' # &5%6367&#'.7&67263'#%; 65%&# mJB|e6O}°I+o|BJn^jaygwaaygxaj^w$FyتFG퇢D{C?` B]ww]B JХC}.?yP%.232#!7&!"4#".54767267p   {u*_ Jcllm8*#I%<($|ʀX#{Nwt7mnld4)5:IIIB,<_4767632#"'&'&!%!!  >W$`4 Z|b<_/374767632#"'&'&4767632#"'&'&!%!!    UW$`H    Z|b<_/GKO4767632#"'&'&4767632#"'&'&4767632#"'&'&!%!!      UW$`H      Z|b<[/G_cg4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&!%!!    /    UW$`  K     Z|b<_/G_w{4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&4767632#"'&'&!%!!    /      >W$`  L      Z|b<V/G_w4767632#"'&'&%4767632#"'&'&4767632#"'&'&4767632#"'&'&%4767632#"'&'&4767632#"'&'&!%!!  0      /    UW$`+    .      @  Z|b.t )2 $$ >54.#"4>32#"&h..--t*Ƅ2..2/.y )62 $$ >54.#"4>32#"&$2#".46h..--1.-.y*Ƅ2..2//2..2/.t 2 $$2>4.#"h-..-t*f/2..2/.j '2 $$2>4.#"$32>4."h-..-q.-.1-j*f/2..2/y2..2/R7!!R-ӖR7!!%!!RMzM; 67'&/#'3#67$#%ׯP==Ͱ̼bN+#!f"K++!|o554.#"##"'5##"&'&'0!5!5&'.4>32!!676767'7' :!9!"9 :! F GF;kY_1278e56d:81)RLk<GG E~^ : : ; ;NG 5 e4G( Li) enf77fne )i (G4e5 G( Pm 9Y%&'%67&673&/'67'&'"&'4?&'37' '7 &/7&'#>7$%88EFu/- 6uNDL22LENu/80uFD8jU45B%y\A@Yy$F 0=/0 ,-X70 ;~*2 %% 2*~697X-,oo  +F9d1 ) ( 1d9C1*CT'&#"'5&767#&$'&%'6'&'''$'676'&5$'6%'.54>32D$ "@F,NNNvF8p^Lb2  N**+ B@0"AR/0?wA%od/D&3.YaQ/5#3$"uI' @3/u= =#n- .... w3% % 32+#".7!"&'&'#&=4;73737D*$#GFHH%#Ι+(&aa'm99m9 3.055_4i4_550.3k  #tttk"632&'.'#####֊v)%8 _^>:k{ZG_?g@`H,>|:=+,j,,<6O/233<bbJ 132>4.#"367#&7&$735&'.4>2,P*+P,.N+)PƗd"/%(MM~95DLMNMD2)WN,,NWP**g!ʇw֜s~  &JJ&?GO277''"/&'&'7&'&'7&47'6767'676?  6"&462EG#96\>42(p __ p(24>\69#G#:5\>42(p __ p(24>\5:'NmNNm U%4m+3 EJ5:6JE 3+m4%T  T%4m+3 EJ6:5JE 3,l4%T '\nMMnM* ? !&+05:?DP3&7"7&'7&'7&'7&'6'6%676767&77&77&'"32654&'5&'.4>323#67#&#"'5&'&547&"'6%6761a$O` "NiB*4l,4"U47),3($aM#"aT*BF 4,=44#Y3,)0BB0/CBO"!-$F$FJF1.#- -#-2MJF$G# 8<g7*!2U6J%n=_CBnT> rYw0d "*7]6U$u=n;wBLz >\e0wZ3C.1BB1.C(N "%""%" M#p.PA.$;QW$.AP-{ "R &.FR2#".54>&'767&%76'&''67&'&'&'67676547676'&7>3263'##"'&'&'&54767&'&547676&'&#"6&%6767&'&'&676&5467&'&6732767&h@9h),)RP|  |PR-*g:>/**Y&()((')&&)')(()% @9f+.TR"`33`\_ .np, 00441/ ,pn, ]]&&()&&EEEJ032WyQT.,d9@.**..1230IDE%&**%&F+.SEFE.IMMI."#FES. !  ";-0.--.0IM+.REF$$1.%2S_`Q2%-1OQQO2-$3Q`_R3&.>GIIG"" 7447#.$$FER/+L"  !75/57%"IJJI* )p~67&'67&'4&6%67.'4'6&&'6767&54?67&'&#&'#&'5&'"'67&'&47632>4.#"72#".4>"0'-, )*#'05%"*%%, ),,"GNYI'+""$(JYNO21, 9,4=SM:7,: -12-[[Z[]WXIOMKLMN2 Y{\ bCWDJgABcp7L^BML0b \u]! @R%KlhhO+ww+O hhlK$PZX'@D 0:)ww*;0 EA&XZw[[[[GJMMJ"( %3!'#!52#"62#".54>o:5(67%'$(n H0L*I"33'554#$/* PR 6h"&>I > >A>!!ua!&5476'#5!+{h_a66mHHm.rZy'#"'&#"'&'&'&547676763232767676'&'&'&/&'&'&547676762!2!%3276767654'&'&'&#"&#"3276767654'&'&˗Pz  ,D@   7;+  23  M98G ):               r         0   L:5U        .\ r26767654'&'."#"'%"'&'&'&54767676;27>764'.'&+"'&'&'&547676762%632$"26767654'&'&#  #  @!R763276;%326767654'&'&'&#"6767654'&'&'&#"32ɓ E79E"  21 +96  >B+  # zOo              49D   /    "    :           =JZx-4H67&'&'&+"'&'&'&4767676327632 #"/#"'&'&'&54767676;276276767654'&'&'&"276767654'&'&'&""'&'&'&547676762"'&'&'&547676762'&'&'&547654'&'&'&";276-&#"+"276767654'&5476%327%&"'&'&4767628?.  !  !a=?^'_)\?=a! !# "!.8?""  "  "  "   f  2 .?E S@6f G=. 2  ŕ6@  B   )_>9 9>_)  % I        ? *        ;d.      ?P   !-  @( ,#%>  NpNM&_*# (! &) ,,f&  ! (K_  Z0-  Yi D   cp-)L &gK1 [N3$ n/ "!0{I"H#fmt2>,7HBI.;/8[, Q[z)  .)S9L *E   '+(4%(4  *X >  7A) 0'-570+I;-% *#%(0  ]'5.  U-9Lp{7654'"'&#"+"'7&54?67676763276323273#5%6767'&#"6%"/67#"27632327654'73654'676547& t!M#l5G;@\ 2BX-0%-m * '?,N ?'!&R;-> <\-R5-6E!"$b$6$!q",; t@P"#C  *FS "DX@! %z$(`]jMP   &O/+@ p_u<  3  DMKZRdYL6D_YBI5.!!''kGWz")3SZ67654/##3276?7%754'654'36767632#"'&54767632'0,,; (| w| ki5.U,\\    %g .  ;,-0j{w {w3V. T, \[^     -5& '-EL4'&'&/767675'7! !'7!654'!4'!!$4767>2"&'&'!654'$$CCC||]V|V#u    9Z(f(Y7%$66%"'&'&'&47676762%'b&I    )^tN/  /dIW?    @ViDV /  V%&%$64'%%&'&'&"27676@))< "  " ]NO]    9|23277632#"'&'&5476"# 6v>? (-=%P8j?  #j<  y"$"JrB23277632#"'&'&5476"" YTo k%,02?=V8jiA{C {u+'qP?  ' 7 sssssstsXrsrtsssr@Q  ' 7 5NB2632#"'&'#"'&547677&'&54763267632676   Bt  ah>) c!  ,Hs *ܡ   },"2A "  {3+Q26#"'#"'&'#'&'#"'&547&'&54767&'&54763267632676  ΂    NjM  rkW* & \ *3 #ﳎ*3 Tv! ( 5+" , @V #!!!!!%!!!!!!!!#!5!3 ;E;JEJJJ<;E;EJK!IKV{ !!!!!!||uv9f !!#!5!335#*+մ*ִw0r!!%!!!!!!/0``1/`1) !!#!5!3^^^~S3!!'#'!!#!!3!5LDʃDMA #5!#3!3'3#!#35!3###5353;9nj#5AI##0vQ#"#3;54'&'&'!"3276767653#4'&'&'&+3!52767>5/]LED73!&&54GBO]63H>SkS>H388]OBG45&&!35FEL]63H>SS>H38882I32367675&'&'.5467676236767>32#"&'&'&'#"'&'.546767675&% >#"? ?"#>    G   >#"? ?"#>  G     F  >##> >##>   F    ?#"> >"#?4'&'&'&'.54767676322767676767632#"'&'&'&'&'&#"'&'&'&5476767676765"#"'&'&'&5476767632B ,#,+%) 3!, &&*-#''#-*&& &$0 )$W$) 0$' L+,$&&$,/"&&$b3") M*,%&&%,."'%%0 )$W#) 4!, &&+,$''$,+&& &$1 ,#,+$)0267632#"'&'&'3&'&'&54676763267632#"'&'#"'&'&'&5476767#6767632#"'&'"'&'&'&54767#"'&'&'&54767676325##"'&'&'&54767#"'&'&'&476767632&'&547676763235#"'.'&5476767632&'&54767676h             -  (                  '    *             .         +j276767653"4'&'&'&+sidUS+*+'WPihtthiPW'+*+SUdi),)URhexuhbXR,,,,RYaitwfgSU),%t?247676763"'&'&'&5!276767653"4'&'&'&LEA86:4DDMMDD4:68AEtjdVT,*+(XQjhvvhjQX(+*,TVdj-76DCOME@:66:?FLOCC67-*UShgyvjbYS,-,-RZbjvyghTU*,(8 %%! !)ttJHcdecH]F]~]^C5 )!%%!2#"'&'&'&54767676hzt@z@Az@t{ne_RP)((&SNcdome_RP)((&SMdd0x}*(QObbrle]TP)**(QObbooe]TN+*(.'"276767654'&'&'! !_)(""""()_)(""""(Y$(*/.*(#  #(*./*($]^#< '1%%2"'&'&'&5476767! !#xxa)(#""#()a)(#""#(YDgghgD^I^W $(*0.+($  $(+.0*($ YZ(8 3'7'3!%%!! !hE۱CCDe g  g f ҁссi:]^= 3'7'3!%%!7!7'7!hTDEDDTNPPIQ2P11P2#mm(? -5%7'%!! !] P  gfeer­696ƌ]^^. /'%!!%!77!yrryyqm"_^^l%%tu%ߴ߳!63% %#'-7:|:||9|kֵֵkֶWz`37'%7% %#'ZZZZZ]^Z^ZZ˛ʜm˜˜mʜ0o #'!5!73!P6M6P$6PMP66R#6QLR6$Q6L$z     - h<_K <; L_zK <; J`;<_  '!'/7'?!7% % -[9^[[ZG^ZZz'}*}zy}*}'q^\\ZG^ZZ:\O}zy}*}'yz(}2 % %  h_y(_^(zFGs% % -hVHzVUzHrVU{HUVH% % -hhhႁhhhႂhhh$h7% %' 7-'hX5 5XV6 6g5VW6 6WV5 0t/37%!!%'#''7'%!5!%7'77;[TA:#T8#AT[TA#9T#8AT T8#AT[U@#7S#9@U[TA8#154'&5476276767632#"#"#"327232#"'&'&/"'&5476=&'&'#"'&'&54767632332?&547'&#"#"#"'&'&54767632676?>$,.c.,$> ]5 71+: H3> kR  Sk >3H :+17 7Z  >$,.c.,$? Z7 71+: H3> lR  Rk >3H :+17 9X  ib9@R'))'R@9dg  8d< +$;)01):$* \570+9 F3= kQ  Sj  =3F 9+077Y  >$,.a.,$? Y770+9 G3= kR  Qk =3G 9+079W     > h`9@Q'(('Q@9bf  7c<+$:)/0(:$+HH#:.'W4,CEH@,4W'*>&DL:Z##KGW,f ',;[;;+*Q--}KOW*AA*WSGu5-U&+;;[;,)  '+;[<>**Q--}KNW+@@-USFu5-S(+;>Y;+* !67654'&"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"#"'&#"327676%32767654'&'&#"#"i/)F)/,UK:M $\/8E(5>H6-EFJA-5H;8)D7.\# L;KU,*UK;K #\.7F'5>H5-DE-6H<7*C8/\$ M:K U+:6-21 4 $:<;$ 4 22-6 O;(A7##7A(; !*:#.#;&Rm!CcJMU)??,RMJcCoS%9#.#;)!  );#-$:'Qn!DcIMU*??*UMIcD oS%;#.$:* f /D;;D/ $i"276767654'&'&'767632#"'#"'&'&'&'#"'&'&'&5476767#"'&'&'&5476767632&'&5476767632o00'))'00o00'))'0]0+)*+%# #+%0%##&&.0%+%   #%'.0$,#0%-# #%'.0$.  #%'-1$,#$%*/0961/*%%*/1690/*%) "*&0-(%$$$)-0&*!&"*!$$)-0&-#%(-0&*"" (-0&*"$$(./& n%#"'&'&'&5476767#"'&'&'&5476767632&'&54767676267632#"'#"'&'&'&27654'&'&'&"67&'&'&'276767&54767'&'&#"276767654'&/?676767654'&'&'&#"h &,&1/(&#!$&1%-$!&$/'.)2$-%c%-$2-*++&$!$-%1&$!#&(/1&,& =s0 9 55%R 9 !_  , 9 R%5s  _!#'"+'0/)&$%%).2'+$ * '1.*%%%%*.1' * "+'2.)%%$&)/0'+"'#L% %L %#M L:2(&6  _ M#%   6&(2: -[3b &'#"'&'&'&547676763267'&#"327%327676764'&'.#"7632#"'%&'&54767676324676762676322##"'&'"'&'.5#"'&'&'&54767"'&'&'&54767676&'&'&'&'&'67676?&'32767677676765&'&'.#"7676767&'&'&/326767674'&'&'67'&'&'&#"67'&'&'&'67676767"276767654'&'&'"'&'&'&54?&'276767654'7654'&'&'&"67'&547676762    (  b  (       #!"G"!# * " ' ## G!""  '  Y m    ( y   ( O k  w  m Q (  O (     ? ?   + / L* / *   +. M+ .*   !!!! '? ?' "#& #'"!!  '? ?'  !"!  $&  m P    O        m     y    O k         b       %j<\l"276767654'&'&/2#"'&'&'&47676762#"'&'&'&54767676% %-[''!  !''[&( !! (TB39)+,+76?A3:(+,+76>tjeVT,++(XRiiuskdVT,**(XQijtuz"z!uv!z"z#&(,-''""''-,(&#e)*:6?;97,+)*97z88,+,*UThgyricYT+,,*USigtvjbZR-,zvvz"z vv!z29"327632#"'&'&/#"'&5476=#"'&'&5476763232?'&#"#"'&'&5476763254'&5476276767632#"'&#"27654'&%&'&#"327676%327632 654'&'&#"#"i"(-+S I9K #Y.6C&4<F4,CDH?,4F96(B5-Y" K8IS*)RI8J "Y-5D&3<F4,CC,4F:6(A6.Y# K9I R*"(-62 #9~3 #9; 01+56 00,5`%;G,A $.?'!3&@!*Yx$ ImPT]-EE0ZTPmI "zZ)!?&3!'@-$ #,A'!2'?!*Yx$ImQT\.EE.\TQmI#yZ)!A&2"'?.#~&41%%14>3t-3>41%%14>3f^CC^B%@#@@%@#-4>41%%14>4-3>41%%14>3+  V  ++  V  !r +?Sg"&46277''"'&'&476762"'&'&4767622"'&'&4767$2"'&'&4767eeeBABA#U##U##U##U#V%**%V&**KV&**&V%**~ffeAA$AAV%**%V&**V&**&V%**#U##U##U##U#  &3@MYam+%5%32476;#"'&'?632&54?#"632/&54#"/72#547"&462"'&=3?_?6 6  6] 6'?&M&C_CC_?&M&< 'L&&L'!6 6^6!6 >_CC_D<>"l267632%632#"'%3#"'&'"'&547#"'&54727%#"'&47632%&'&54763&5476h!#;'&1'h 9##8)'1!, ;#A#; '&1')8##9 h'12;# 4%.&! 6 = 6%".% 3 3 G%.56 = 6 %".G 4 $8 ! 54."#"54$32632#"_ ɀ~~a>E  %!#!3!p EE?p9E=V %!%!35!cE:d FF8 %!!![:F:\;[0q %!!7!N]<N;)G+t  ,o9; #q !rQk!k`!733}b>v!#7#)iC~'  #'  #g]jOS2#"327676765#"'&546;57!##"'&'&'&54767676%#     42;%-n`Ԯrr#26A@:V7:$)&7.Yq   % $.277g[(dVDQ49%*,04?()-#52&'&547676762"'&'&'&5476767hc"$njln(Lfe*+$$$$+*e*+$%%$+! #'(*dRjjSc*('!"%*,20,+%""%+,02,*%"%C&'&547676762476767622"'&'&'&5476767hcn(%X%&&&W%(nؖe*+$$$$+*e*+$%%$+,Dj*('(&,,&('(*kC"&*,11,*%##%*,11,*&".i%%&%&54767676247676762hhÔ*(42u24)(()42u24(*i\=97,*+*96@@69*+*,79=Zr_'#"'&'&'&547676763"'&'&'&5476767632_dA=;0-/.=:DD:=./-0;=Abx1.=8DC9() 1F="%".4"tNa5&$4! /.r<@6B2L_0>Q#kI|"rz7&)?),%=^K=.C26F@13.!9+cM313N676 547&'&327#"'#536767&'&'&5432&5476323254'&543253%5@26`', =NR6#!vWR>4 2:O t51"".1&X.RO A5ȏ )T/186,FAS :#(=:tA0 9SD 'A#5}11BO9 "'&'&'&547676763"3ᗊpm8884qlYTN! !C@RP]e:6pltm9:'62~~jf77"05276767654'.'4]PR@C! !NTYlq4888mpe'67fj~~27&:9mtlo7:fkR !&547jljjlyyxzQqpnc$0!!676n wu;;vi43f$lcC}U# 3tD}U 3 DutV.! !JV. ! JA! !m^\GHB ! ^^HHv!'7DWWWW|'7'7WWbW>W^$#"&=4&+5326=46;#"3xMe,,fLx1d=AOOA=dƂ׈ihDŽOߍOi(326=467&'&=4&+532;#"+5nCFVU$#Cn5BB*)p//oTBB¥P⎁AAPDBۇ45iDCS/~ #!5!3}t]} 7%d^=]d>S~5 /%0~##t] '-f\=]d]>-!'7!. (``I)=2"&'&'&5476?!".'&47>3!'&'&54767>2 '!  `!!  !' ,&   &   S~&!5! F78-x!5!5 V(Mr6u #3#3#3!!5 鴴ZZ---I(,,,,S~  55!#3#3#3F9UU**b]^bUUUS~!!5 F7.`tq!%  qR{V$%! S%@{V t%226=3!5 5!"'&'&'&6  $hI$  h$   6<47676763!5 5!"6  $hI$  $   $O!! e 6n55!lMlTwccwekl!5!!53 ' !_[y"kd""e/l5!!53 ' !_["/d""5 !73#57!%!6UcGjbzbdǩ""ap 5!'53#'!!!7%acߎA[؁(ZqZ{{{ĒҒ}TM %'!'!53 !;qKRnKa26wwIw22wT}> 3#5!7!!! ZQtZQ0L>ssjLK2Nu '!53#'5!'7! !pSn%R&%Ua2wKJ,Lw22w)1 '7!573#5!7! !r&j&St&SpWl6qM,LLyy77y@!6767632#"'&'&'!  6IYZgb^UMI%&&"LF\Zfc^UM3!t:6I&&&#LHZZhc\UMH'&&#L2<tt XNy "&*.37#37#37#37#5'!!55!!3'#3'#3'#3'#r+qr*rr+rr+rV{{*q+*r*+r++r+9Ɔ\]t] 7&#"7'7 #%5#t69.wZY96t".*X/S~k 55!5!!7'!nnUVGG8:ȏu\j '327'' #395t".Y/Y"u69.xXXN2%&#"6767&'&"67632&'&547676767}:"  s %*&*(&"!#!"O>>;*E/4767!"!47676763"'&'&'&5!3!&'&5v  5 $ %% $  H vgMME %!#"!% EMu\2&'&'&'&54767#"'&'276?&'&'32\":  #'$'$#Y@I:86s6::I  #&'#'" X  :5*+B67"'&'&'&547676$47676762"'&'&'%&'&'&547676762$[ /  H =a=   / ZI=X  q> d(*c     XJn.676767632#"'&'&'&%&'&54767&'&54765 #&+*1)F-Y)) .EOO/3S>>S&/ #$))%#]]#%))$#&e"'&'.54?654'&'&'&+"#!".4?64/&4676763!2;276767654/&54676762 I ]]I    Q      Q  %eg"'&'.54?654'&'&'&+"#!".4?64/&4676763!2;276767654/&54676762 GKa u~iKG E     2 +#76767&'&/3#6767!5!!5!&'&'g?j7R=y66y=R6k?VO S+ +Sd _8=eyu'&utj>jijR"53#5#"'&323276'&#"3UVn၂pVTMLNMMNLA+l99lg-,..VVy ##! !+532765YZͷZ-,'ij>>% %!#3!3iVO7n lѲR{jR"%#367632#"'&'&#"327"3UVn၂pVT+MLNMMNLl99f-,[љH3!!# 9`#3!{`9Lh '"27654'&'2#"'&7673A\VMMG*w|~~hA1LLNeˑRh]c[斘,mKseg.!#5#"&'532654&+532.Dv6;zIMcݳw"$.*gQH{"264   676326^\`s-!\[^[]-=P@7'"]_$5hXd3#3h݄D8.#";#"./.'&'532654&/&'&54$32\^mjj3!2_Pf-Efoshul]^ V;5#5% Yb^`_hon"!^XE&->B%DS #D9``LAB\VBT=;-;,,1Y7:w!##Z:#5!#J&w3!3XZ!533XKN,X3#3,X0X,X3#3,$dX,X3#3,X,X3#3,X,X%3#3#,񈈈X,X3##3X0X,X3##3$dX,X3##3X,X3##3X,X%3#!#3X,X!#!!yX,X!#3!!Ẍ,X!#3!!X,X!#3!!Xd,X%!!3XT| 3'#'L:LjLCUCT| #'737:LjLCUC( 3#3#'( f*D( #53#73 fRo(D }dA!5 !5 !5 ||pO  'ŢcmZn`!5 !5 !5 TT  |MY 5! u .1 V{ 5! u X.1 VH+532765!#3!HYZͧZ-,)ij>>9dV4&#"#3>32+532765bjq1sYZ͹Z-,`c6ij>>~'IJ !5!5!5!J>>I3#qe10#+D^V4 ;#"&5# 5432!5!3#'&#"3[Y襵>5*GN\|~󠠄K9T !33###TÆ3B3z^{3##4&#"#3>32Ҍjq1sL``cH %"32654&'6#"467.5463%!"h{1PAž(rڜ-*/1|I5#7N@*        JEE<2<2991/<299903#'#"!#!##535463wcM%ɩQge/яN#7B@#    JE E<<991/<29990#!"!!##5354637cM%۸ɩ{Qge/яNE &  E &   X&    X&   E &  E &  x X&   X&  E &  E &  r  X&  z X&  E&  7'E&  -x&  m&  E&  E'  y&  |&  EW& LLfEH'L4W & LK& L]r&  tj' v ?' ,~ '  ~&  j ' t ?' ,x  '  X f&[ {X f&  /}>\/' a8 >/'  8 X f&[ X f&  &8\/' 8 8/' 8 X f&[ *X f&  B\/'  >/' 8 X f&[ 2X f&  ^\/'  >/' 8 a7&_ ` D' \ 'LU_` 'L  ]2%#"'$4733267654'&54767;#"'&'0Qcpl?AOL64)>.VhFd((&*=#>2(I=/ b \^xHj<9"1B,f%TOA7.N?+ B C cG& J' T +9'  9'  6   &#"'&47332767654'3;#"'G{579T?:!e#"#V4^Wt<;?xC3^w3UT8@0&6D%3!"''&5473327&'&5476&'5%67654'&#"%3276'&'&bJDv-(0g:-0M,QG$"':AG 5' 343%@K5:,+ CfN@TSZ 'AO@H=.%4-+#%v_U[1C "&_ R,:%&'&5476&'53!"'+532767654'&#"%3276'&'&\:-0M,QG JF$"':AG 5' 3CfN@TSZ 'AO844U@H=.%4-+#%v_U[1C "&_X %!#53 76=3`H՞,1VV,1jٻX%#!5!276=33!!"^L,02,*VV,1jj1,r| WX'  X'  Dt&tiDu"k ;#"'&=31,cK\WL71,\W+DvDw&iwDx&ixDy&iy0z&izVz{&{iU-O&U|'|  9&U}9'}' L 3&q}`>Z '}2> U  &U~ &~V V&r}Y|_'} W'}p '}p  UL 3;!"'&L2,PXskj1,\eE&  E'  X&  X&  &p j'  QE &  E '  #' R ' R E&  E'  ,&  R&  ,X f&[ X f' p \/'   /'   X f[X f.%3#"'&'&'27'&5767&5$(1{R=IrbJԖ`e_$m3HZdP]vbĘe4)@5 [_w\/&'&'&5672+5327676SSgURHKLXJKݣdht^#4b4bBPH:jV/);#"'&'+53276767&'&'&5672~AI2h6:H~D&_ ] ` '   $aF"'&''#"'&7673327676'&/37653323;#"'4A@E2J/1%=Gq_ev!UNBAF3I2Q, E;eF*L$Ftg£x&R" C~m8*@#?9,%#GvOdT7323;#"'#"'&'#"'&'+53276=32765!5d*,""@e;6:HO7* F35C%&F:DF*7@`*%Rx&),`X I6[m8*6759,%#GvOdTtg$&a 6 '   '   ' R  c G%327654'&#"%;#"/+"'&5#"'&5473767654'&'367632Ij($?GhK=.';4f&4-//3fJZ}eh<2H7(28`@%(jm=vbw $A7. *727&'&5763"327%+5=K4X}ں>SF8I \Y];d}M4F!Ť$/%+532767&'&5476762;#""654'hkB;(aD hYYf MXD=pʨ4/gg/($'UZ'-)74--38)-'bM,(U __ z F&g w L'  1F' w 2L'  3&  *jL'  ?' ~ ~' " }t&  ]~' Nk ?&  ,~~&  l!D#"'5327654'&'&7676;#"''&5473327676J&P DfXRNB8D-<9_h$$EB|=Q&v+6(  %z|qe))5!27654'&54767;#"'&/P/62 hF F&,@XB:f"``h$$EB|=Q&v+6(  %?+)x.CKm $;#"'#"'&&733276761,+K8N3h^}cw@A(IiTcEkj1,3]?~/"*V\ss~B")9(j )5!27653WP,1se\,1j%)5327653;! hL,02,VV,1jkj1,TnGt4%327654'&'&#"#"'&#4763&547632;#" zL,5;(.;D Kp#IxAIM\HT(RfV*9:X DD(PNKOmf7*(?$G@^m*%'+53276767632%327654'&'&#"d`p@ht4,+^]EE>/4:''5)24ed0$#1P8O$*ME5EX !a%m/%#"'+53276767632;#"%327654'&'&#"|an@h4,+^]HB3&id>/4:''5).4fb0$#1P8S1>/E5EX d%6&   ' w &  R&  Rp $&'&'&'3;#"'&'#"'&5476 xRot$8pqZI-&8:m*12e CY>)2'+eO,3;I0D-=67654'&#"27&'&5476&'5#"'+5327654'&$"':A4N--0M,Q;(Jxb 41~! @H=.%4-+#%v iEN@TSZ 'C49g=ql)D%'r.C!v-3j  ;AWE L9P)8K6(S/VL_+Y9K1\Sr|7%&'&'&54767632;#"'&##"'&'&73327676 ,)MW,.@"##C@"*5NhLy $Eq:<3, $.=N/[ EW3235huFX^"!ݺh^bNm/>ZUb=*(DV\BAL89DEnY1X;YTBEb%$q%1&R'N(X)fP*H+,m-.s/Vy0F1u\2u3u\45J6/7=899:;%<=#{DXE%{F{G{X{H'I{H{JKDLVMN Omo{P{QH{RVT{SRwTj{U{VW^Xdm`Y`ZL`[hV`\b]LF#7fo-L7NFf@103# qf?Q@ aa1<20KTX@878YKTK T[X@878Y3#%3#?Zk10K TX@878YKTX@878Y@&  //// //]]3#@   99991<<990KTKT[X@878Y@t        !      ]]'.#"#4632326=3#"&d9 #(}gU$=19#(}fT"<9! 2-ev 3)dwyi10K TX@878YKTX@878YKTX@878Y@ //]#1Ś7]@ 91<90K TX@878YKTX@878Y@ //, ]3#'# ӌ7i@ 91290KTX@878YK TX@878Y@ //*//]373 ӌ Zj@ 9910'3$ll/{m > #.#"/  waSRd {xz{w8796/{m @ PP120332673#"&/w dRSaw m6978w{zPa103#PXck@ 1<203#3#\zkcyk#!#uŚŚkf$(#5476?>54'&'&56763#7:)*%>8'9@B9Q[~CG=9. bEBTY;X1I*%#D>"Y`LAB\VE'*==010!!=V045!5!5gr4!5!;r4!5!/r^s 2"&46"264hrL&'◚vURyVP%`8n|TyRTv?F3#%3#?53#73#'3# 3#3#'3#}}dE&"'&547332767654'3;#"'&')+ubEW-7!K4/+X`t8A>|0l7%5A8>7KZd3(,*OQ/9?0654'&323276767'&54767632#!V)B,4((7(*HTO<?aNbNLZB`.NJ|m+M;3*)3P& ]027EW4,E$2Hf3Џ,' 3;!"'#!5327&'&5476762"67654'&'ȷ$&ň($28 D@$ 8P2*I1C299(M.L,0W 5+5DE2.4!.@%&'&'&54767632;+'$'&547332766'&'&#"B.y9()Wp8c2X]0Lh^E>>:lu{/"'"5 9Ld/  #+m=E2X:SmJN}`kI="'&4762<R8R8z?@?@@?@(8)*8@@@@@??V<D@!B  10291/<2990KSXY33+532765#5YZ͹Z-,˹iij>>iE'&5473327654'ߚ)+ubj{G{yo9;>|0lALj@G}t8654'&32676#"'&54767632'&'&5473)B,4((7(*H[b?zKbNLc9g'!.9ΊMRVV+M;3*)3P&;f4KCW-3E$2Zwfj}ػH(E  '  &  E'   X '}8[X ' [X ' T[&_>x9654'&"32#"'&&733276767#"'&54767632)B,4P7&,Hir$$xZT0A&?zKbNLZB`.,L95T2R$T&()X\^-"2(hLDV,4D$2Hf3C'm6#"'&47332767654'3dG{579T?:"FHt<;?xC3b`L 3H'pq'(rpTza_<ɐ1ɐ1UmhR!9\XdffXXX%fmVuu/9%fZH{{{mjdLhX%?wXd=+XBFjX%%%%%%uuuuu%){{{{X/hh%%%{{{{{{{f{f{f{f{FmLuuuHj j///%h%F0<<^u?4An1mu -V8xv// 'J}}}9%uz%%)f{uu}f{=)/%%{{uuhj/}%{uuuu%hjxx/!79{{{zz8{zffX(hhgEfjjzz}}}v_AHHf}i_6w#6AAQ0^^))$=$=>/VX%V[^,,,,,)=/?VX)"V//yysb^_s?$U))//=X/UU?p6%%u%Vux/%uuJ%F63F 6DtP3LYF633Ft"p"m94u`!Y4uVUm"h%!Vu/hBPr< A<L}i{;=hcL|}PhN{{# A#h uU;bq}/%\%\L;%%){uzuz;}uuuhhhhhhAhuwU6`x+U]6[_6V6@6``A`6!i@F3uGzygIrIBIGhrh=hgfhhW(Hd11ZdL EEEXXX$$ zzC6LDDDDDD0V\~~6EEEEEEXXXX ~~>ly|as{jK}}cceesdaobwU0[SSSSxEV_In7777-7777Vv7kn#)+?+77LL,:d^E<<.?AEEGGG11OOGI8%[:X::GM[%#H[#{:GXQ:OWbG[CaIGdUU~%%UU?::[xM Fa^M3:%{{{}{{{{{f{7VmVmVmujj==////9d9dLL%hh%%%%{{{uu    ' ' ' '%h%h%hFFFFFFFF%%llMM@@cc66'33333333@EFFFFFFFFfFF633FFFFFFFFFF%%llMM@@ccFFFFFFFFfFFFFFFF%%%['/66333333%%[p?FFFFFJdd??PZZ!!=H ?I=;0A=XBF ?I=;0E1:1A8V%AG[M {_m *{%*/.j5'/h:TTJ B%0J%  BBBBBYYBBBBBBBBBBBBq?QQ2BXXBBBBGB*BB*B*BBBBBBBBBBBBBBBBBBEEB*BBBBBBBB%uIXXf+?;;;)}}?5XJWXXXXXXXXXXXXXXXWXXXXXWJJXXXXXXXEXXXXXXXXVVVVWXXXXVVVVVVVVVXVVVVVVXXXXXXXXXXXXXXOOOOOOOOOPPPPXXXXXXXVXVVVVXXXXVVVVPIrZZ% % XaGX++|h}2H%%?XX%XX6FFHRFFF    xxxxxxx||iEiiDDu777777?aa"2"Y::;+6.ocN8/&/// "N`ya7g!!!!GCL54=/U28Y^CVpj6gv2 1.rjD7`./<KD$>K--9.7.P<<<<<<....RRs'J*R*"..:=4%=?D-W&W;%J@3@V90~0G+%(C(#(=(.6W0$2$0174!$%2 02)!"$E=80+Qg.rA1fnCDStSt-IS-6SS`{{66O6ee5a}T2)XtSuN*u5&%287uuXXjV%jLZZ,,,,,,,,,,,,,,,TT(((AnTz##EEEEEEEEEEEEjjXXXXXXXX`` 6|DD"DDDD0VLZ| LEEEEEEXXXXXX``$$  zzjC G6LZ||@S__R%fmVuu/9%{{{mjdLhf?y77//Xf=^?EjEEEXXXC6LLLLL,x 8d x ( d  4  ,(PD  !!T!!""@"#p$$%%&8''p'(P)L)*+ +,,,-P.<./ /0123X4$4T5$55567L8d989::<<=`==?,?X?@<@ABBCC@CPCDHDDEE FF8F\FFFH$HHI I$I<ITIlIIIJlJJJKK<KtKLMM4MdMMN@O$O<OTOlOOOQQQ4QLQdQQQQQS S$S<STS|SST8U<UTUlUUUVpVVVVWW,WDW\WtWWWWWXXXDXTYY(Y@YXYpYYYYYZZZ0ZHZ`ZxZZZZ[$[\D\x\\\\]](]@]X]^P___0_H_``\`````aa,aDabHb`bxbbbbbcddDdddddde|fffffg g$g<gTglgggggghh8hPhiLixiiijj<jTjljjjjjkk4kLkpkkkkklldlmmn ndno`op@pPpqqlrrtsst txtuXuvv\vw wTwxxLxxyy,yyzlz{\{|L|\|},}h}~~ ~<~~TH d0 dt4Ll ,DTt $<Tl, 8Ph(@Xp0H`xLTD,Ld|``\XxD@h(4pxdhl(``0XtD$$(x Lh LXH$L<L <T @4 @`XT4 (8HXÔ,Ĉ@pŀŰ,XtƄƔ8ǜȴTɈ<ʘʸ,L\l|ˌ˜ˬ<l|$4Pl͘Ͱ,Pl΀Δθ@pϔϸ0`Ќ8d 0xҸH|\ԈԘԨԸ$Txդ րּ֘֨8`p׀טװ 8Phx؈ؘ(@P`dtۄdt 8PhlߨL\44D`pLTH(@Xp,0HDL l htpLPh@X DT l|HXP`p`p(|XLHX$|Xh`<L@ \  L  p     @ X 0P`l<<p`0H@t8Hx@ 8DHd 4L\l 4Ld|0H`x LxP  x  !""`""##$<$%<%&&&''(D()\)**\*+L, ,4,-D-T...<.L../ /@/00h011d112|2303P34`445x56p667,778P899::L:;; ;@;<8 >? ?X?x?@@t@@AA,ADA\AxAAAAAB\BtBBC$C<CDDDEDE\EEF F$F<FGTGGH`HIIIJhJJK@K\KL$LhLxLLLMMXMNXNO O\OP,P`PPPQPQhQQQQQQRR(R@RXRpRSS(STTT(T8THTULUUUUVW4WX\XYZ$Z[0[\`\],]^d^_``\`ab$bcTddeTeefPfffgDggh4hhi<iXijTjjjkk|kllm\mnPnoohop0pq qhqr8rsst tuuuvlvw<wx xy yz,z{{|||}d}}~L x$d <l(h(XDlTdt\@TX |(|PD8|$`@`P@D8` Xh(Hd| $<Tl4Ld| $<Tl,D\t4Ld| ,Ld| $<Tl4Ld| $<Tt $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,D\t4Ld| $<Tl,<Td|,D\t4Ld| $<Tl,D\t4Ld| $<Tl0H`x,D\t,<Xp(@Xpˆ˜°0H`xÈàð(8dĐĸ4pŬ,ƈt8TȬȬ(Hd˄ˠ˼ h̄Dt͐ͬΈ0Ϙ@фѴ,`(<PdxӌӠӴ,@Th|ԐԤԸh֨PT4ڤ,tXttXh|<d`Xx8Xx THxH8(p@4pD\tP0h(x<dT8D$H@@l T4p(dH` $ h     H   ( x  0 d   |Dx | $$8HT<d((` h4 0 !h" ###$%% %H%h%%&&l&' '`''(4))*(**+<+|+,8,|,---.<../\/0\0112023<3x344556 6t677l78,8|899:X:;;<<=|>H>?@ABDBCHCCDD@DdDDDEExEFFTFFGXGHHI(ItIJ<JKdLLL(LTLLLMMPMpMMMMN NHNpNNNO8O|OPPQQ$QDQdQRRLRSWXX@XlXYY\YtZZZZ[[T[[\<\]]d]^^P^^_<_T_l______``,`H```xaabDbbccc$c4cLcdc|cccde8f8gh8hijj4jljjjkkLklkkkkl l@lllm$m|mmnnDnnnnno,odooppPppqq,qPqtqqqrr$rHrlrrrrss<sdssstt@thtttuu8uhuuuvv<vdvvvw w0w\wwwxx0xXxxxy$yXyyyz0zhzz{{<{p{{{| |L|t|||}(}T}}}~ ~@~l~~~0tL| `H|8T <Xt  <Xt8Tp(D\x$xDx(Dp Pl$@l<X ,`0H(<4d,p$P|T L|h0Xl , |,|DDxT@,PH0X\0̴D΄Ϩ@tӠ` ֠4t|@ی\L߄t8d\hhP8X<|<<tlP`t@DtLt l  0 D8T|t\ l "<#')X*+,,.0l12345(55646x7@84889$9h99:h;P;<>?C<CDhDE`EFG,GHHHI,IIJJK,K|KLNDPDQSUPX^_tabc<defhPijk\kkkl$l|llllmm4mPnHnopDpqrs4ssttDtltttu u4u\uv0v`vvvwwwxxXxxyy0yTyyz(zTzz{,{x{||d|},}~ ~h~hH|t8`(T0`4$x$Hp@l$Lt<d4`<,$  8Ph(@Xp0H`x 8Ph(@Xp4DTl<L,Xhx$4L\t,D\t,`x 8Ph  |,DT(Xp(|(@Xp|p,t 4Ld|hx  8Phllllll ,<L\l| ,<L\l| ,<L\l| ,<L\l|4<8Ìø(Ĭ,tŜŴ|Ǵx0`ɄɼL,,D\t$<͜ʹ  +kW_B]  @  4     S  b     " :R & hhCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. DejaVu changes are in public domain DejaVu Sans MonoDejaVu Sans MonoBookBookDejaVu Sans MonoDejaVu Sans MonoDejaVu Sans MonoDejaVu Sans MonoVersion 2.33Version 2.33DejaVuSansMonoDejaVuSansMonoDejaVu fonts teamDejaVu fonts teamhttp://dejavu.sourceforge.nethttp://dejavu.sourceforge.netFonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Bitstream Vera Fonts Copyright ------------------------------ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. http://dejavu.sourceforge.net/wiki/index.php/Licensehttp://dejavu.sourceforge.net/wiki/index.php/License~Z   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghjikmlnoqprsutvwxzy{}|~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  sfthyphenAmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflex Cdotaccent cdotaccentDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflex Gdotaccent gdotaccent Gcommaaccent gcommaaccent Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflex Kcommaaccent kcommaaccent kgreenlandicLacutelacute Lcommaaccent lcommaaccentLcaronlcaronLdotldotNacutenacute Ncommaaccent ncommaaccentNcaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracute Rcommaaccent rcommaaccentRcaronrcaronSacutesacute Scircumflex scircumflex Tcommaaccent tcommaaccentTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongsuni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019FOhornohornuni01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEUhornuhornuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DDuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3Gcarongcaronuni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F0uni01F4uni01F5uni01F6uni01F8uni01F9AEacuteaeacute Oslashacute oslashacuteuni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217 Scommaaccent scommaaccentuni021Auni021Buni021Cuni021Duni021Euni021Funi0220uni0221uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236dotlessjuni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0243uni0244uni0245uni024Cuni024Duni0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Auni025Buni025Cuni025Duni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268uni0269uni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276uni0277uni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295uni0296uni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2uni02A3uni02A4uni02A5uni02A6uni02A7uni02A8uni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BBuni02BCuni02BDuni02BEuni02BFuni02C0uni02C1uni02C8uni02C9uni02CCuni02CDuni02D0uni02D1uni02D2uni02D3uni02D6uni02D7uni02DEuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EEuni02F3 gravecomb acutecombuni0302 tildecombuni0304uni0305uni0306uni0307uni0308 hookabovecombuni030Auni030Buni030Cuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320uni0321uni0322 dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334uni0335uni0336uni0337uni0338uni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0343uni0358uni0361uni0374uni0375uni037Auni037Etonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonosuni03D0theta1Upsilon1uni03D3uni03D4phi1omega1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni0400uni0401uni0402uni0403uni0404uni0405uni0406uni0407uni0408uni0409uni040Auni040Buni040Cuni040Duni040Euni040Funi0410uni0411uni0412uni0413uni0414uni0415uni0416uni0417uni0418uni0419uni041Auni041Buni041Cuni041Duni041Euni041Funi0420uni0421uni0422uni0423uni0424uni0425uni0426uni0427uni0428uni0429uni042Auni042Buni042Cuni042Duni042Euni042Funi0430uni0431uni0432uni0433uni0434uni0435uni0436uni0437uni0438uni0439uni043Auni043Buni043Cuni043Duni043Euni043Funi0440uni0441uni0442uni0443uni0444uni0445uni0446uni0447uni0448uni0449uni044Auni044Buni044Cuni044Duni044Euni044Funi0450uni0451uni0452uni0453uni0454uni0455uni0456uni0457uni0458uni0459uni045Auni045Buni045Cuni045Duni045Euni045Funi0462uni0463uni0472uni0473uni0490uni0491uni0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni04A2uni04A3uni04A4uni04A5uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04BAuni04BBuni04C0uni04C1uni04C2uni04C3uni04C4uni04C7uni04C8uni04CBuni04CCuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni0510uni0511uni051Auni051Buni051Cuni051Duni0531uni0532uni0533uni0534uni0535uni0536uni0537uni0538uni0539uni053Auni053Buni053Cuni053Duni053Euni053Funi0540uni0541uni0542uni0543uni0544uni0545uni0546uni0547uni0548uni0549uni054Auni054Buni054Cuni054Duni054Euni054Funi0550uni0551uni0552uni0553uni0554uni0555uni0556uni0559uni055Auni055Buni055Cuni055Duni055Euni055Funi0561uni0562uni0563uni0564uni0565uni0566uni0567uni0568uni0569uni056Auni056Buni056Cuni056Duni056Euni056Funi0570uni0571uni0572uni0573uni0574uni0575uni0576uni0577uni0578uni0579uni057Auni057Buni057Cuni057Duni057Euni057Funi0580uni0581uni0582uni0583uni0584uni0585uni0586uni0587uni0589uni058Auni0606uni0607uni0609uni060Auni060Cuni0615uni061Buni061Funi0621uni0622uni0623uni0624uni0625uni0626uni0627uni0628uni0629uni062Auni062Buni062Cuni062Duni062Euni062Funi0630uni0631uni0632uni0633uni0634uni0635uni0636uni0637uni0638uni0639uni063Auni0640uni0641uni0642uni0643uni0644uni0645uni0646uni0647uni0648uni0649uni064Auni064Buni064Cuni064Duni064Euni064Funi0650uni0651uni0652uni0653uni0654uni0655uni065Auni0660uni0661uni0662uni0663uni0664uni0665uni0666uni0667uni0668uni0669uni066Auni066Buni066Cuni066Duni0674uni0679uni067Auni067Buni067Euni067Funi0680uni0683uni0684uni0686uni0687uni0691uni0698uni06A4uni06A9uni06AFuni06BEuni06CCuni06F0uni06F1uni06F2uni06F3uni06F4uni06F5uni06F6uni06F7uni06F8uni06F9uni0E81uni0E82uni0E84uni0E87uni0E88uni0E8Auni0E8Duni0E94uni0E95uni0E96uni0E97uni0E99uni0E9Auni0E9Buni0E9Cuni0E9Duni0E9Euni0E9Funi0EA1uni0EA2uni0EA3uni0EA5uni0EA7uni0EAAuni0EABuni0EADuni0EAEuni0EAFuni0EB0uni0EB1uni0EB2uni0EB3uni0EB4uni0EB5uni0EB6uni0EB7uni0EB8uni0EB9uni0EBBuni0EBCuni0EC8uni0EC9uni0ECAuni0ECBuni0ECCuni0ECDuni10D0uni10D1uni10D2uni10D3uni10D4uni10D5uni10D6uni10D7uni10D8uni10D9uni10DAuni10DBuni10DCuni10DDuni10DEuni10DFuni10E0uni10E1uni10E2uni10E3uni10E4uni10E5uni10E6uni10E7uni10E8uni10E9uni10EAuni10EBuni10ECuni10EDuni10EEuni10EFuni10F0uni10F1uni10F2uni10F3uni10F4uni10F5uni10F6uni10F7uni10F8uni10F9uni10FAuni10FBuni10FCuni1D02uni1D08uni1D09uni1D14uni1D16uni1D17uni1D1Duni1D1Euni1D1Funi1D2Cuni1D2Duni1D2Euni1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D62uni1D63uni1D64uni1D65uni1D77uni1D78uni1D7Buni1D85uni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E00uni1E01uni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E3Euni1E3Funi1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Cuni1E7Duni1E7Euni1E7FWgravewgraveWacutewacute Wdieresis wdieresisuni1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Buni1E9Funi1EA0uni1EA1uni1EACuni1EADuni1EB0uni1EB1uni1EB6uni1EB7uni1EB8uni1EB9uni1EBCuni1EBDuni1EC6uni1EC7uni1ECAuni1ECBuni1ECCuni1ECDuni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE8uni1EE9uni1EEAuni1EEBuni1EEEuni1EEFuni1EF0uni1EF1Ygraveygraveuni1EF4uni1EF5uni1EF8uni1EF9uni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni2010uni2011 figuredashuni2015 underscoredbl quotereverseduni201Funi2023uni202Funi2031minuteseconduni2034uni2035uni2036uni2037 exclamdbluni203Duni203Euni2045uni2046uni2047uni2048uni2049uni204Buni205Funi2070uni2071uni2074uni2075uni2076uni2077uni2078uni2079uni207Auni207Buni207Cuni207Duni207Euni207Funi2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni208Auni208Buni208Cuni208Duni208Euni2090uni2091uni2092uni2093uni2094uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209Cuni20A0 colonmonetaryuni20A2lirauni20A5uni20A6pesetauni20A8uni20A9uni20AAdongEurouni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B8uni20B9uni2102uni2105uni210Duni210Euni210Funi2115uni2116uni2117uni2119uni211Auni211Duni2124uni2126uni212Auni212B estimatedonethird twothirdsuni2155uni2156uni2157uni2158uni2159uni215A oneeighth threeeighths fiveeighths seveneighthsuni215F arrowleftarrowup arrowright arrowdown arrowboth arrowupdnuni2196uni2197uni2198uni2199uni219Auni219Buni219Cuni219Duni219Euni219Funi21A0uni21A1uni21A2uni21A3uni21A4uni21A5uni21A6uni21A7 arrowupdnbseuni21A9uni21AAuni21ABuni21ACuni21ADuni21AEuni21AFuni21B0uni21B1uni21B2uni21B3uni21B4carriagereturnuni21B6uni21B7uni21B8uni21B9uni21BAuni21BBuni21BCuni21BDuni21BEuni21BFuni21C0uni21C1uni21C2uni21C3uni21C4uni21C5uni21C6uni21C7uni21C8uni21C9uni21CAuni21CBuni21CCuni21CDuni21CEuni21CF arrowdblleft arrowdblup arrowdblright arrowdbldown arrowdblbothuni21D5uni21D6uni21D7uni21D8uni21D9uni21DAuni21DBuni21DCuni21DDuni21DEuni21DFuni21E0uni21E1uni21E2uni21E3uni21E4uni21E5uni21E6uni21E7uni21E8uni21E9uni21EAuni21EBuni21ECuni21EDuni21EEuni21EFuni21F0uni21F1uni21F2uni21F3uni21F4uni21F5uni21F6uni21F7uni21F8uni21F9uni21FAuni21FBuni21FCuni21FDuni21FEuni21FF universaluni2201 existentialuni2204emptysetgradientelement notelementuni220Asuchthatuni220Cuni220Duni2213uni2215 asteriskmathuni2218uni2219uni221Buni221C proportional orthogonalangle logicaland logicalor intersectionunionuni222Cuni222D thereforeuni2235uni2236uni2237uni2238uni2239uni223Auni223Bsimilaruni223Duni2241uni2242uni2243uni2244 congruentuni2246uni2247uni2249uni224Auni224Buni224Cuni224Duni224Euni224Funi2250uni2251uni2252uni2253uni2254uni2255uni2256uni2257uni2258uni2259uni225Auni225Buni225Cuni225Duni225Euni225F equivalenceuni2262uni2263uni2266uni2267uni2268uni2269uni226Duni226Euni226Funi2270uni2271uni2272uni2273uni2274uni2275uni2276uni2277uni2278uni2279uni227Auni227Buni227Cuni227Duni227Euni227Funi2280uni2281 propersubsetpropersuperset notsubsetuni2285 reflexsubsetreflexsupersetuni2288uni2289uni228Auni228Buni228Funi2290uni2291uni2292 circleplusuni2296circlemultiplyuni2298uni2299uni229Auni229Buni229Cuni229Duni229Euni229Funi22A0uni22A1uni22A2uni22A3uni22A4 perpendiculardotmathuni22C6uni22CDuni22DAuni22DBuni22DCuni22DDuni22DEuni22DFuni22E0uni22E1uni22E2uni22E3uni22E4uni22E5uni22E6uni22E7uni22E8uni22E9uni22EFuni2300uni2301houseuni2303uni2304uni2305uni2306uni2308uni2309uni230Auni230Buni230Cuni230Duni230Euni230F revlogicalnotuni2311uni2312uni2313uni2314uni2315uni2318uni2319uni231Cuni231Duni231Euni231F integraltp integralbtuni2325uni2326uni2327uni2328uni232Buni2335uni2337uni2338uni2339uni233Auni233Buni233Cuni233Duni233Euni2341uni2342uni2343uni2344uni2347uni2348uni2349uni234Buni234Cuni234Duni2350uni2352uni2353uni2354uni2357uni2358uni2359uni235Auni235Buni235Cuni235Euni235Funi2360uni2363uni2364uni2365uni2368uni2369uni236Buni236Cuni236Duni236Euni236Funi2370uni2373uni2374uni2375uni2376uni2377uni2378uni2379uni237Auni237Duni2380uni2381uni2382uni2383uni2388uni2389uni238Auni238Buni2395uni239Buni239Cuni239Duni239Euni239Funi23A0uni23A1uni23A2uni23A3uni23A4uni23A5uni23A6uni23A7uni23A8uni23A9uni23AAuni23ABuni23ACuni23ADuni23AEuni23CEuni23CFuni2423SF100000uni2501SF110000uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250BSF010000uni250Duni250Euni250FSF030000uni2511uni2512uni2513SF020000uni2515uni2516uni2517SF040000uni2519uni251Auni251BSF080000uni251Duni251Euni251Funi2520uni2521uni2522uni2523SF090000uni2525uni2526uni2527uni2528uni2529uni252Auni252BSF060000uni252Duni252Euni252Funi2530uni2531uni2532uni2533SF070000uni2535uni2536uni2537uni2538uni2539uni253Auni253BSF050000uni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254FSF430000SF240000SF510000SF520000SF390000SF220000SF210000SF250000SF500000SF490000SF380000SF280000SF270000SF260000SF360000SF370000SF420000SF190000SF200000SF230000SF470000SF480000SF410000SF450000SF460000SF400000SF540000SF530000SF440000uni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Fupblockuni2581uni2582uni2583dnblockuni2585uni2586uni2587blockuni2589uni258Auni258Blfblockuni258Duni258Euni258Frtblockltshadeshadedkshadeuni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259F filledboxH22073uni25A2uni25A3uni25A4uni25A5uni25A6uni25A7uni25A8uni25A9H18543H18551 filledrectuni25ADuni25AEuni25AFuni25B0uni25B1triagupuni25B3uni25B4uni25B5uni25B6uni25B7uni25B8uni25B9triagrtuni25BBtriagdnuni25BDuni25BEuni25BFuni25C0uni25C1uni25C2uni25C3triaglfuni25C5uni25C6uni25C7uni25C8uni25C9circleuni25CCuni25CDuni25CEH18533uni25D0uni25D1uni25D2uni25D3uni25D4uni25D5uni25D6uni25D7 invbullet invcircleuni25DAuni25DBuni25DCuni25DDuni25DEuni25DFuni25E0uni25E1uni25E2uni25E3uni25E4uni25E5 openbulletuni25E7uni25E8uni25E9uni25EAuni25EBuni25ECuni25EDuni25EEuni25EFuni25F0uni25F1uni25F2uni25F3uni25F4uni25F5uni25F6uni25F7uni25F8uni25F9uni25FAuni25FBuni25FCuni25FDuni25FEuni25FFuni2600uni2601uni2602uni2603uni2604uni2605uni2606uni2607uni2608uni2609uni260Auni260Buni260Cuni260Duni260Euni260Funi2610uni2611uni2612uni2613uni2614uni2615uni2616uni2617uni2618uni2619uni261Auni261Buni261Cuni261Duni261Euni261Funi2620uni2621uni2622uni2623uni2624uni2625uni2626uni2627uni2628uni2629uni262Auni262Buni262Cuni262Duni262Euni262Funi2638uni2639 smileface invsmilefacesununi263Duni263Euni263Ffemaleuni2641maleuni2643uni2644uni2645uni2646uni2647uni2648uni2649uni264Auni264Buni264Cuni264Duni264Euni264Funi2650uni2651uni2652uni2653uni2654uni2655uni2656uni2657uni2658uni2659uni265Auni265Buni265Cuni265Duni265Euni265Fspadeuni2661uni2662clubuni2664heartdiamonduni2667uni2668uni2669 musicalnotemusicalnotedbluni266Cuni266Duni266Euni266Funi2670uni2671uni2672uni2673uni2674uni2675uni2676uni2677uni2678uni2679uni267Auni267Buni267Cuni267Duni267Euni267Funi2680uni2681uni2682uni2683uni2684uni2685uni2686uni2687uni2688uni2689uni268Auni268Buni2690uni2691uni2692uni2693uni2694uni2695uni2696uni2697uni2698uni2699uni269Auni269Buni269Cuni26A0uni26A1uni26B0uni26B1uni2701uni2702uni2703uni2704uni2706uni2707uni2708uni2709uni270Cuni270Duni270Euni270Funi2710uni2711uni2712uni2713uni2714uni2715uni2716uni2717uni2718uni2719uni271Auni271Buni271Cuni271Duni271Euni271Funi2720uni2721uni2722uni2723uni2724uni2725uni2726uni2727uni2729uni272Auni272Buni272Cuni272Duni272Euni272Funi2730uni2731uni2732uni2733uni2734uni2735uni2736uni2737uni2738uni2739uni273Auni273Buni273Cuni273Duni273Euni273Funi2740uni2741uni2742uni2743uni2744uni2745uni2746uni2747uni2748uni2749uni274Auni274Buni274Duni274Funi2750uni2751uni2752uni2756uni2758uni2759uni275Auni275Buni275Cuni275Duni275Euni2761uni2762uni2763uni2764uni2765uni2766uni2767uni2768uni2769uni276Auni276Buni276Cuni276Duni276Euni276Funi2770uni2771uni2772uni2773uni2774uni2775uni2794uni2798uni2799uni279Auni279Buni279Cuni279Duni279Euni279Funi27A0uni27A1uni27A2uni27A3uni27A4uni27A5uni27A6uni27A7uni27A8uni27A9uni27AAuni27ABuni27ACuni27ADuni27AEuni27AFuni27B1uni27B2uni27B3uni27B4uni27B5uni27B6uni27B7uni27B8uni27B9uni27BAuni27BBuni27BCuni27BDuni27BEuni27C5uni27C6uni27E0uni27E8uni27E9uni29EBuni29FAuni29FBuni2A2Funi2B12uni2B13uni2B14uni2B15uni2B16uni2B17uni2B18uni2B19uni2B1Auni2C64uni2C6Duni2C6Euni2C6Funi2C70uni2C75uni2C76uni2C77uni2C79uni2C7Auni2C7Cuni2C7Duni2C7Euni2C7Funi2E18uni2E22uni2E23uni2E24uni2E25uni2E2EuniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716uniA71BuniA71CuniA71DuniA71EuniA71FuniA722uniA723uniA724uniA725uniA726uniA727uniA789uniA78AuniA78BuniA78CuniA78DuniA78EuniA790uniA791uniF6C5uniFB52uniFB53uniFB54uniFB55uniFB56uniFB57uniFB58uniFB59uniFB5AuniFB5BuniFB5CuniFB5DuniFB5EuniFB5FuniFB60uniFB61uniFB62uniFB63uniFB64uniFB65uniFB66uniFB67uniFB68uniFB69uniFB6AuniFB6BuniFB6CuniFB6DuniFB6EuniFB6FuniFB70uniFB71uniFB72uniFB73uniFB74uniFB75uniFB76uniFB77uniFB78uniFB79uniFB7AuniFB7BuniFB7CuniFB7DuniFB7EuniFB7FuniFB80uniFB81uniFB8AuniFB8BuniFB8CuniFB8DuniFB8EuniFB8FuniFB90uniFB91uniFB92uniFB93uniFB94uniFB95uniFB9EuniFB9FuniFBAAuniFBABuniFBACuniFBADuniFBE8uniFBE9uniFBFCuniFBFDuniFBFEuniFBFFuniFE70uniFE71uniFE72uniFE73uniFE74uniFE76uniFE77uniFE78uniFE79uniFE7AuniFE7BuniFE7CuniFE7DuniFE7EuniFE7FuniFE80uniFE81uniFE82uniFE83uniFE84uniFE85uniFE86uniFE87uniFE88uniFE89uniFE8AuniFE8BuniFE8CuniFE8DuniFE8EuniFE8FuniFE90uniFE91uniFE92uniFE93uniFE94uniFE95uniFE96uniFE97uniFE98uniFE99uniFE9AuniFE9BuniFE9CuniFE9DuniFE9EuniFE9FuniFEA0uniFEA1uniFEA2uniFEA3uniFEA4uniFEA5uniFEA6uniFEA7uniFEA8uniFEA9uniFEAAuniFEABuniFEACuniFEADuniFEAEuniFEAFuniFEB0uniFEB1uniFEB2uniFEB3uniFEB4uniFEB5uniFEB6uniFEB7uniFEB8uniFEB9uniFEBAuniFEBBuniFEBCuniFEBDuniFEBEuniFEBFuniFEC0uniFEC1uniFEC2uniFEC3uniFEC4uniFEC5uniFEC6uniFEC7uniFEC8uniFEC9uniFECAuniFECBuniFECCuniFECDuniFECEuniFECFuniFED0uniFED1uniFED2uniFED3uniFED4uniFED5uniFED6uniFED7uniFED8uniFED9uniFEDAuniFEDBuniFEDCuniFEDDuniFEDEuniFEDFuniFEE0uniFEE1uniFEE2uniFEE3uniFEE4uniFEE5uniFEE6uniFEE7uniFEE8uniFEE9uniFEEAuniFEEBuniFEECuniFEEDuniFEEEuniFEEFuniFEF0uniFEF1uniFEF2uniFEF3uniFEF4uniFEF5uniFEF6uniFEF7uniFEF8uniFEF9uniFEFAuniFEFBuniFEFCuniFEFFuniFFF9uniFFFAuniFFFBuniFFFCuniFFFDu1D670u1D671u1D672u1D673u1D674u1D675u1D676u1D677u1D678u1D679u1D67Au1D67Bu1D67Cu1D67Du1D67Eu1D67Fu1D680u1D681u1D682u1D683u1D684u1D685u1D686u1D687u1D688u1D689u1D68Au1D68Bu1D68Cu1D68Du1D68Eu1D68Fu1D690u1D691u1D692u1D693u1D694u1D695u1D696u1D697u1D698u1D699u1D69Au1D69Bu1D69Cu1D69Du1D69Eu1D69Fu1D6A0u1D6A1u1D6A2u1D6A3u1D7F6u1D7F7u1D7F8u1D7F9u1D7FAu1D7FBu1D7FCu1D7FDu1D7FEu1D7FF dlLtcaron DiaeresisAcuteTildeGrave CircumflexCaron fractionslash uni0311.case uni0306.case uni0307.case uni030B.case uni030F.case thinquestion uni0304.caseunderbar underbar.wideunderbar.smalljotdiaeresis.symbols arabic_dot arabic_2dots arabic_3dots uni066E.fina uni06A1.init uni06A1.medi uni066F.fina uni06A1.finaarabic_3dots_aarabic_2dots_a arabic_4dotsarabic_gaf_bararabic_gaf_bar_a arabic_ringEng.altuni066Euni066Funi067Cuni067Duni0681uni0682uni0685uni0692uni06A1uni06B5uni06BAuni06C6uni06CEuni06D5]A GA% } % 2  %%@Y}2}Y&Y@&//2G@Gddkߖږ؍ }:Ս :  ϊ̖ˋ%}Ś   ]%]@%AA dd@2(-}-d   ..A]%]@%%%A  %d%BSx~}~}}|{zwvut uu@t tss@rqponSonm(nSm(lk2ji2hgfedcbcbba`_^Z ^]d\[Z [Z YXWVUU2TSRQ}PONM-MLK(JIJ7ICIHEHGCGdFEFEDCD7CBCC@@ BABB@ A@AA@ @? @@@ ? ? ?@@d>=-=<;(:9B9d818K76-65K404K3032B21-10/-/. .-,--@ ,,,@@+*%+* *%):)('&%B%E$#""! -!} -KBBF-B-B-B@  @   @    @  @7    -:-:-d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++pyzo-4.4.3/pyzo/resources/fonts/linux_fonts.conf0000666000000000000000000001367312772442440020260 0ustar 00000000000000 /usr/share/fonts /usr/X11R6/lib/X11/fonts ~/.fonts ~/.fontconfig mono monospace sans serif sans-serif sans sans-serif DejaVu Serif serif DejaVu Sans sans-serif DejaVu Sans Mono monospace sans-serif serif monospace sans-serif ~/.fonts.conf conf.d local.conf serif FreeSerif sans-serif FreeSans monospace FreeMono 0x0020 0x00A0 0x00AD 0x034F 0x0600 0x0601 0x0602 0x0603 0x06DD 0x070F 0x115F 0x1160 0x1680 0x17B4 0x17B5 0x180E 0x2000 0x2001 0x2002 0x2003 0x2004 0x2005 0x2006 0x2007 0x2008 0x2009 0x200A 0x200B 0x200C 0x200D 0x200E 0x200F 0x2028 0x2029 0x202A 0x202B 0x202C 0x202D 0x202E 0x202F 0x205F 0x2060 0x2061 0x2062 0x2063 0x206A 0x206B 0x206C 0x206D 0x206E 0x206F 0x3000 0x3164 0xFEFF 0xFFA0 0xFFF9 0xFFFA 0xFFFB 30 pyzo-4.4.3/pyzo/resources/fonts/SourceCodePro-Bold.otf0000666000000000000000000026413012662716363021147 0ustar 00000000000000OTTO`BASEI|:CFF AZH+DSIGIGDEF/,/4\GPOS%@d GSUBݞ50 2OS/2tP`cmap >x headN6hheaW$$hmtx,maxpPHnameI?x_<KKpXPXXKX^2    ADBE ` "EET#X{9`: T ` y$Rv    A _ Fg ( r $G k 4+ _ 2w # H:_ ,: : : *:Copyright 2010, 2012 Adobe Systems Incorporated. All Rights Reserved.Source Code ProBold1.013;ADBE;SourceCodePro-Bold;ADOBESource Code Pro BoldVersion 1.013;PS 1.000;hotconv 1.0.70;makeotf.lib2.5.5900SourceCodePro-BoldSource is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Adobe Systems IncorporatedPaul D. Hunthttp://www.adobe.com/typeCopyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.http://www.adobe.com/type/legal.htmlTypographic alternatesAlternate aAlternate gAlternate dollar signCopyright 2010, 2012 Adobe Systems Incorporated. All Rights Reserved.Source Code ProBold1.013;ADBE;SourceCodePro-Bold;ADOBESource Code Pro BoldVersion 1.013;PS 1.000;hotconv 1.0.70;makeotf.lib2.5.5900SourceCodePro-BoldSource is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Adobe Systems IncorporatedPaul D. Hunthttp://www.adobe.com/typeCopyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. http://www.adobe.com/type/legal.htmlTypographic alternatesAlternate aAlternate gAlternate dollar sign v   !"#$%&'()*+,-./012345:=NX   %$&(?FEGIHsrtv tzw  kLxRTU{69Vy 8Y7[Wtuws0/9@Z`z~1Ie~7CRTYaeoy $(.1CIMPRX[!%+;Ico     " & 0 3 : D q y  !!! !"!&!.!T!^!""""""""+"H"`"e%%%%%%%%&&j''R 0:A[a{4Lh7CPTXaeoy#&.1CGMORV[  $*6BZl     & 0 2 9 D p t } !!! !"!&!.!S![!""""""""+"H"`"d%%%%%%%%&&j''RvS~Wd LKHA>5, ZB;5 r}Z@8#ܰYۯe2<DJ4^ " v zwux Rt T=LNWXY[stuwk   $%&(?Urstv;<KOPRQSV\ ]^gZ hijko r#v'x)y*~.z01234756;=@>DJKLV^_`degfmluwxyzW>{+M{|}~l]hn^fkm`glVXY_adhiTUmp!q"89:<ABCabcijop?@ABCDEFGHIJ_`abcdef|,}-NOPQRSTXYZ[\  ]$%by2SourceCodePro-Bold.  F$U|2b4#*18?FMT[bipw})06=HSZaekry '.5<CJQX_dkry $18?FMT[binu| %,3:AHOV]dkrx $+18CNU\`fmt{ $+8?FMT[bipu|#*05BIPW^elsz ")07>ELSZahks{    ( 1 4 A I U ^ f o |    % - 5 ? H Q Y c m v    * 4 = E M W ` i q {    ( 7 B L Y _ e k q w }   # ' . 2 9 ? C J Q X _ f m w ~  $+29@GNU\cjqx )0;BMT_fqx '2AL[fu+6EP_jy &-4;BIPW^elsz ")07>ELSZahov} %,3:AHOV]dkry !(/6=DKRY`gnu|"AmacronAbreveuni01CDuni1EA0uni1EA2uni1EA4uni1EA6uni1EA8uni1EAAuni1EACuni1EAEuni1EB0uni1EB2uni1EB4uni1EB6Aogonekuni0243CacuteCcircumflexCcaronCdotaccentDcaronuni1E0Cuni1E0EDcroatEcaronEmacronEbreveEdotaccentuni1EB8uni1EBAuni1EBCuni1EBEuni1EC0uni1EC2uni1EC4uni1EC6EogonekGcircumflexGbreveGdotaccentuni0122Gcaronuni1E20uni00470303Hcircumflexuni1E24uni1E2AHbarItildeImacronuni012CIdotaccentuni01CFuni1EC8uni1ECAIogonekJcircumflexuni0136LacuteLcaronuni013BLdotuni1E36uni1E38uni1E3Auni1E42NacuteNcaronuni0145uni1E44uni1E46uni1E48Omacronuni014EOhungarumlautuni01D1uni1ECCuni1ECEuni1ED0uni1ED2uni1ED4uni1ED6uni1ED8Ohornuni1EDAuni1EDCuni1EDEuni1EE0uni1EE2uni01EARacuteRcaronuni0156uni1E5Auni1E5Cuni1E5ESacuteScircumflexuni015Euni0218uni1E60uni1E62uni1E9ETcaronuni0162uni021Auni1E6Cuni1E6EUtildeUmacronUbreveUringUhungarumlautuni01D3uni01D5uni01D7uni01D9uni01DBuni1EE4uni1EE6UogonekUhornuni1EE8uni1EEAuni1EECuni1EEEuni1EF0WgraveWacuteWcircumflexWdieresisYgraveYcircumflexuni1E8Euni1EF4uni1EF6uni1EF8ZacuteZdotaccentuni1E92uni018Famacronabreveuni01CEuni1EA1uni1EA3uni1EA5uni1EA7uni1EA9uni1EABuni1EADuni1EAFuni1EB1uni1EB3uni1EB5uni1EB7aogonekuni0180cacuteccircumflexccaroncdotaccentdcaronuni1E0Duni1E0Fdcroatecaronemacronebreveedotaccentuni1EB9uni1EBBuni1EBDuni1EBFuni1EC1uni1EC3uni1EC5uni1EC7eogonekgcircumflexgbrevegdotaccentuni0123gcaronuni1E21uni00670303hcircumflexuni1E25uni1E2Bhbaritildeimacronuni012Duni01D0uni1EC9uni1ECBiogonekiogonek.djcircumflexuni0137kgreenlandiclacutelcaronldotuni013Cuni1E37uni1E39uni1E3Buni1E43nacutencaronuni0146uni1E45uni1E47uni1E49napostropheomacronuni014Fohungarumlautuni01D2uni1ECDuni1ECFuni1ED1uni1ED3uni1ED5uni1ED7uni1ED9ohornuni1EDBuni1EDDuni1EDFuni1EE1uni1EE3uni01EBracuteuni0157rcaronuni1E5Buni1E5Duni1E5Fsacutescircumflexuni015Funi0219uni1E61uni1E63tcaronuni0163uni021Buni1E6Duni1E6Funi1E97utildeumacronubreveuringuhungarumlautuni01D4uni01D6uni01D8uni01DAuni01DCuni1EE5uni1EE7uogonekuhornuni1EE9uni1EEBuni1EEDuni1EEFuni1EF1wgravewacutewcircumflexwdieresisygraveycircumflexuni1E8Funi1EF5uni1EF7uni1EF9zacutezdotaccentuni1E93uni0237uni0250uni0251uni0252uni0259uni0261uni0265uni026Funi0279uni0287uni028Cuni028Duni028Euni029Ea.aagrave.aaacute.aacircumflex.aatilde.aadieresis.aamacron.aabreve.aaring.auni01CE.auni1EA1.auni1EA3.auni1EA5.auni1EA7.auni1EA9.auni1EAB.auni1EAD.auni1EAF.auni1EB1.auni1EB3.auni1EB5.auni1EB7.aaogonek.ag.agcircumflex.agbreve.agdotaccent.auni0123.agcaron.auni1E21.auni00670303.azero.onumone.onumtwo.onumthree.onumfour.onumfive.onumsix.onumseven.onumeight.onumnine.onumuni00ADuni2015uni2117uni2120at.caseasterisk.ahyphen.auni00AD.adollar.azero.supsone.supstwo.supsthree.supsfour.supsfive.supssix.supsseven.supseight.supsnine.supsparenleft.supsparenright.supsperiod.supscomma.supszero.subsone.substwo.substhree.subsfour.subsfive.subssix.subsseven.subseight.subsnine.subsparenleft.subsparenright.subsperiod.subscomma.subszero.dnomone.dnomtwo.dnomthree.dnomfour.dnomfive.dnomsix.dnomseven.dnomeight.dnomnine.dnomparenleft.dnomparenright.dnomperiod.dnomcomma.dnomzero.numrone.numrtwo.numrthree.numrfour.numrfive.numrsix.numrseven.numreight.numrnine.numrparenleft.numrparenright.numrperiod.numrcomma.numrordfeminine.aa.supsb.supsc.supsd.supse.supsf.supsg.supsh.supsi.supsj.supsk.supsl.supsm.supsn.supso.supsp.supsq.supsr.supss.supst.supsu.supsv.supsw.supsx.supsy.supsz.supsegrave.supseacute.supsuni0259.supsa.supag.supaEurouni0192lirauni20A6pesetadonguni20B1uni20B2uni20B5uni20B9uni20BAuni2215slash.fracuni2219lessequalgreaterequalnotequalapproxequalpiinfinityuni00B5partialdiffintegralradicaluni2206uni2126summationproductuni2113estimateduni2190arrowupuni2192arrowdownuni25A0uni25C6uni25C9uni2752triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1uni2610uni2611uni2713uni266Alozengeuni2032uni2033uni02BBuni02BCuni02BEuni02BFuni02C8uni02C9uni02CAuni02CBuni02CCuni0300uni0300.capuni0301uni0301.capuni0302uni0302.capuni0303uni0303.capuni0304uni0304.capuni0306uni0306.capuni0307uni0307.capuni0308uni0308.capuni0309uni0309.capuni030Auni030A.capuni030Buni030B.capuni030Cuni030C.capuni030Funi030F.capuni0312uni0313uni031Buni0323uni0324uni0326uni0327uni0327.capuni0328uni0328.capuni032Euni0331uni03080304uni03080304.capuni03080301uni03080301.capuni0308030Cuni0308030C.capuni03080300uni03080300.capuni03020301uni03020301.capuni03020300uni03020300.capuni03020309uni03020309.capuni03020303uni03020303.capuni03060301uni03060301.capuni03060300uni03060300.capuni03060309uni03060309.capuni03060303uni03060303.capuni03020306uni03020306.capuni030C.auni0326.auni00A0uni2007space.fracnbspace.fracuni2500uni2501uni2502uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250Buni250Cuni250Duni250Euni250Funi2510uni2511uni2512uni2513uni2514uni2515uni2516uni2517uni2518uni2519uni251Auni251Buni251Cuni251Duni251Euni251Funi2520uni2521uni2522uni2523uni2524uni2525uni2526uni2527uni2528uni2529uni252Auni252Buni252Cuni252Duni252Euni252Funi2530uni2531uni2532uni2533uni2534uni2535uni2536uni2537uni2538uni2539uni253Auni253Buni253Cuni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254Funi2550uni2551uni2552uni2553uni2554uni2555uni2556uni2557uni2558uni2559uni255Auni255Buni255Cuni255Duni255Euni255Funi2560uni2561uni2562uni2563uni2564uni2565uni2566uni2567uni2568uni2569uni256Auni256Buni256Cuni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Funi2580uni2581uni2582uni2583uni2584uni2585uni2586uni2587uni2588uni2589uni258Auni258Buni258Cuni258Duni258Euni258Funi2590uni2591uni2592uni2593uni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259Funi0258uni02541.000Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Copyright 2010, 2012 Adobe Systems Incorporated. All Rights Reserved.Source Code Pro BoldSource Code Pro/H [Kenr?Gd )-9s J^ezMajo} adov}    " ' 3 8 < |   ! ' 5 M Z n x  , 0 B J U 3 8 ? C I P [   + 0 5 > O a l t  ").Ic{,1>ERV`jov}27JQW`gnuy ")07=APV[`nty ".5AMYeq}8 +.ա[u_d^BNՁ;-)6)˘ΩeS /ih // ii/G`϶=`8G S ,66ҿ XBi|ZzX~Z{{y8i&kwqd{lqR~~~ Mk6U5<L_ôxjP<<!)5,Spgs~bdmsGRNZ ai36)ERɿwpWA3"4L/ hļ`I}xi}5nstws9?/FS#* 8b V $ kzu__zHC[ qhip *r ' K^L nt_% S0:w 'B' L K7 F, ž­@Ƴ3Txqqo \| _VO< $  Ĩ]9 u ! /9 7 X    ˩  + ˲˱}o\R9,< '1 oRIpqizyzggqp 4'  +r8~ ty w w Q \ Dndcoqges:  ٔK VN Ifum^] ,[[F7 ` m @@ FUw Vm M   T v r  %  ĴbRSbfUUg> OaĶ`RR`bP rFv IXк\FF\WImw  7v@   v ԍ9jzqgh  eٔ +<% f 8 \^]= pfs!;gbd<a=,>cGzD@ IJ?L_MN&|uzLM[ (/9BNdx*IW`i$]g Xe|Q(58U8i . T o $ Z l   / G ` v  4 d (r&AfE/IVp 'Zq:[{CSez,Ht@7HY 7DS =s#PoIp!1CXlx  - F [ u w !!7!]!y!!" "O"""#+#<#K##$$A$k$%%>%&L&'''%'F'^''( (y((((());)Q)l)))* *H**+:+P+}+++,,,,,-<-C-I-P-w-----../.M.R.j..../3/M/f///010Q0r0011=1`11112282O2l2|222233K33444445'5n555666=6b66667 77778S8v889 9%9J9g999: :*:c:::;<;c;;;<<\<<<==q====>>*>H>f>>>??4?K?b??@@f@w@AAYAABCBEBBC.CoCD DgDEEeE~EEEEFF4F\FFFFGHGGH1HdHHISIJJ|J~JJJJK K KL"LwLLMMTMMN.N0NNNOOoOPP_PPPPQQQUQQQRLRRRRRRS&STSSSSSSSSSSSSTTTSTTTU.UUUUUVV;VWWBWWX?XYYqYZ&Z`ZbZdZZZ[[[[#[.[7[B[J[R[Z[b[j[s[}[[[[[[[[[[[[[[\.\8\?\E\\\\\\\\\] ]A]]]^E^j^_/_U_{_______`@`f`p`a`asaabb@bbbbccBccd d:dde9ebe{eeefSfggogh%hhisj!jjkRkl?lm-mmmnn no#onopEp~pqqkqrr5r7rmrrrs s5slssttCt}ttuuvvxvwwIwwwxfxyyAymyyyz zLzXzrz~zzzz{!{A{{|$|e|||||}}}})}9}R}T}V}f}~}}}}}}}}~~~$~&~1~B~U~W~z~|~~~~~~~~ .>Lr(Wt€Jp߁:`׃ (c̃-Ukƍލ6VxĎ)4DQ\q̏׏'5CO]kvۑő֑#7N^q(N$NKش)>7`l,kPa,:y! ?)',$$s!Z.؟#`')ЫwZZmlCY?ܰrRPem;>+- 8(T+K C''v+''+B ,o% .'F'l  -2) 'b vB1E$ ~ q N!\ l9L>r !vq Y ']%'q -2!'YٴrGGeg:F +p*ϵ=a8GFb}xw\Zt ?h"/0"iH j%B 'S%m'D >)C+%Os_ #  % phEX *>xD}IwCx~w=0)1cji8RG2Q}m}i{bw||L9G' S7 |1|"$ |! ?,?[Xvgd@''Uqko_+0  K'|6)|X Q'=>hhR#)6+鹷s]jqo~jNhX1  i'BaXR/H1~'??ǩʱa u:Q @(0ꀓsom'N Q^shssgjaqz@XrqicA UwH  IX9'? XbIX9'c  uw' JX%'b'.  ww  '/  _ " wjhx>-@VTqgc܀ <'%HTrkowp 6)?-'<XbbS#)6+鹷s]jqo~jNh '| T &8&&B', ?='xR#xqwn=J!=~{Y~X|W|}{=~( w)  .ݚomo92@6; _;~p|m~ozw|X25 Cpq$kR :P nD! :P D! :P: {D! :RI  ! : #Pa\ Z \ B! :3M.hV! :PW⵼ғd! :4zqpy{uty!ι]HH]dPOeD?! :PR V D! I:^XC@ :5LG5rs! ] G Z~?,zDǜ̝כG>JO._ */Y/0_ ] F  0~?,zD! ^I c?,zD! e+@Yq;poV^eh=eٓ+d?,zD! I: X|: @ B3K O c! K  O c! v/ lO c! 8=Z $C[ Hkzu__z! I: XW⵼ғ@ s):yǝ̜כG>JOi}|vy_  jx[[MOiˮwğʜ+  %e +) ))'0 !{!ܴmFF`f<IMϭw[VjoEVOlמ &b*@JCu"Z.qDv+ |Ahkft\)G纭xo_I8BlT rhF [P iґɫ> +- aO > +-  > +- Ud >.+?- gZYgk^^k8P(T+K SUd T 8(vX+tK * Q8(Nt+K VjeCP''M ; 1CP''>1;7CP''@ CP''>Ud C P 3'oabomccm[ob\ CP N'CP''x⵼Ғ;V ih{;DZC.P ?'xgZYgk^^kT CP yWx'w.C5''Tבʨ_zBŇxywrCP 1S'@j|zmgVV_^0^C7 ''  PC7 ''! !GC''ːg}Oxz}x,CeP :'нu eٔKq;ooV_eh=T C P yW|'@N` ;O.sC'iv^\NDj~~vv*B ,o% { ,o% 㴼ғ;V hi{;DZ .,>% y2 >, z% V ,o% UN<d ,R% %" ,gH% Aj}zmgUW^^0^. 'F'l H: I. X'zl f+. %Z'l y96v : F'%FFdN',F'+NJz'F'z -L֍ -NO  -?:  ֠(-@I   ) -< 3k(- -v⵼ғ:W .k>3(-vՋ -NN 5v(?-S5T (^X@(h-v s(TB(hTnv^_K }}wv,h,(,X,(,2  : w)pJV  'b >0:  ""r;w'b ",-̐ry> 'b QJ  Icw'Wb T  'wWb u.T  'wWb V .Q 'Nb - '&bb'AbմZIB1E$ X q * u !N!^ n9L&>O u !N!^ n9L>Nu !DJ!] m9LþZ@I  y>! 9LZJQ u .!>!] m9L^ T X! 9L~* Q^^! 9L-> r !aߍ> r !6WO > r !T: >+PRP+!@I  > +Q Q+!(<>+mm+!!> r !⵼ғ:W> r !eߐ > r !WNT F +X+! >)++!h5T ] !# PT ] !=  GT #! ,T e+Ye+!2teٓLq;poV^eh=T F +X+z!T: bYR/ i.>RK.Yn|/iyj * +vt".>*  42 (t+12{Ia2> :z 6|˛Q > : aU> : U>)++:G5z 6|˛Q >+PRP+:I  >hz 6|˛Q T F +X+:OG`϶=`8G6|˛Q sv++ ~uvFh // iA)5zmqjbWOi˭G`϶=`8G P'S%e ;'D  P'S% R U };'D yS '%+ Hzp0D 9I 'X%m z$p0D 9I 'SrX%zP.+ĴbR|$p0D 9Q 'hY%m 9'W hQ> )C+%+ > )C+% > )C+%xUN<d qAv)+%L_ôxjP<<!)5,Spgs~bdmsGRNZ6&WlՂjI siU5y2F )+%uQ >.)>+%@T F )~X+%CH*  lv +X.8+:ݥƱol@5wgc`{nv|xtX?2imQPv;?F(OsNqKOv(+ WWe? j:v(-soUt 2^X@(sQOsh_ # V_ # ">O _ # I:  # @j}zlhUW^^0 _ ; @"# a\ Z bnmccm_ # _ # ⵼ғ:W_ sx"# _ # Z _ # >N_  C##H"# p gqpihp_  CH"# 5^  ; "_  CH"# _  CH"# z. .gqpihpZcT w X"# * _ I "# ]5s #v*w "1,!zz\cMDi~~vvذݲ%"4=l`MNn_ {yWac7јQ _  Qo _  o _ I "G5S јQ j}zlhUW^^0 O  ):   )Y<' v' O ' v: ) ' rt<.k>3(' u  w^X@(' m* Iv(?' k5֠(' w@I  PS8q+ PSqUN<d .y>ST WpSB ev X w']%' .2!0''0XٴsGGeg:k<*_)/(dWWTexwӲQz{|g))ii&'7Pla^^ ; ~1~"$ =^ ; ~1~"$ +2;^ ; ~1~"$ S=J7 0# w1w"$ J>{ w{w  7 1 :11'|1|"$ {:j]]ki_^in \]ki_^i7 kQ'}1}"$ zkOz^ bH'|1|"$ {=N @7 j'@1}@S ,66 s'}1}"$ ,~_ ;r9Sq ;qN r^ LʰɈ G{1{" w̎{@J;1XAʔd}Mxz~y7 e׈ $U b<|{ }@M {@ٓL}@p;{} T 7 ; Wx'ހ1"ހ$ S=Jv.^ ӹvR U { "} { ^ ӹvR o$ )!} \ ^ C| 'u1u"$ = Jyftm^u^  s^ ? j6'${@@1{@"@$ z=} M { ٓL} p;z T 7 blWiH'  1 " $ @=N @.f8vv{ &P'1Qrz`bPt̝,66hgS#)6*麷s]jqo~jNgCo--wrT Xq] * Q pI]  4v D%p .jqo~jNh⹴ætiIn'vA/D/_>hhR#-00-vX ͐;1  ;1 Kf;1 g;1 6 X1114 f ]ji_^inj]]ki_^iXCei z9\ ' x9:$XIkWi '.T kWti '.X> ֐˩^ X0#1  ȷ@d~upmZ 1 ̎ :S 1 ̎ N Lʰ1@ ̎ yXe] ƎP:{ep;5 T ;kW gv.fru_d^BNՁ;-)68 +.qtlcZO2~sx˘ΩeS3#,& a ue w & Xrqic APܵa IuWbQ & Xrqic A@B a 9uQ & Xrqic A`3` a un:Q & Xrqic A@ za 0#u:Q (`0ꠓsom'N Q^s`hssgjaqzXrqicA,V ꠍ Uw H ǎO` Uw'W'H NB f+ Uw'%V'H #z96v %oJJ'@IJ'mI'Y@UgedZ ٖZ <Z J 0#'? ڣ ؙ  111g'Z1? 1; ca'? z Z݉'|? N $Z `E J'9? {`IX9'?  f$IXP?'  P* f$?'   dXb;'c J:wuw' JX%'i{V ' KXY' b'. DO  MrMb':. 3-- /I"0'X$;ɯjlttad_KĴbRRbfUUgy2b'. { T b'2W. N.T _'2W. y.Qb'. J-b'lstbd5( (;~$SK 9;ɮI wX-̀w ր ;'/  % ;'/  IE 0#'/  ڣ   yS '''/ ܀ [Q \Pީk: I'Xw''/  * I 'W''/ ڀ ~B Q '/ ܀  U'' `@TccNTfMe^Fl 'nJ'_(I^hbds  " ږs  " fs  " OJ_0#" Fۣ ܙ  _15115+" ; _gg+" gz_^^+" N $_+ " tْs  " ET M X+" _>+" `_+`+" fӺ̎9S _+`+" fӺ̎N _Lʰɿ," f޺̎XAʔe}MJ _eL_+" ^ن| M p; T ǂ+X+" OJv*  yvu5iOGsw{hϣ~GT^Ub[Z,9BP`]Saq}9se  w$õS55wSgaw9,ưZoYyquq\kπrT(Z`lTpscRw%7,9oБ]T_9^5  s 9 rg^5  s 9g^5  _>+9`^5  _e9 b N_]*ܫ  ȷ?^5 ܃ ځT M X+9m G^5  fv# +j t9  ,9+. }swldY{ϭT55iTGGij yS ':Դ| TҾQxti[PH j 5EI XM'Ҵ| T* wwXM'M| TԀ>* Q 'Դ| Tu ;&8&&f ;&8&&6 ;&8&&6 q+4v.&&LWA3"4L/On;/?qzzqpplZZ's~<%/:ȩO-A-B'BZ, ;-q+2vB'2qnkeCkBff xC:wiF ܴsiy2B', 3;V T B'YW, VB QB'0, pk11N'r1, y n h ;p|R#|qҾ h ;p|R#|qC h ;p|R#|qgh 0#pnR#nq$ӣv nȶ@vd~unpl[ h 1'&11"'yR#uqzn h p|R#|qzh 'NJ'yR#uqyv x9u:$h 'qm=zqy{θ^HH]cMzMc{xooyxuh +p|R#|q+1++1h ;p|R#|qҝ h 'A&&='|@R@#z@q|Z yM< zqu}Z < zqu(]Q\(}Z < |q{: ZWe'R#qb.h ^'u'zR#zq|^ f-v- P'R_iq~fTz'>ҿBqyacP͟?='ztZ#xqVtQ h ;pz|W zBZ#|qVzQ h ;pz| ztZ#|qVzQ h ^'w'zz}`Z#zqVyQ h epznbvn vN_]*n v mȷZ#nqVmQ Xo'zm M8Z#qVQ  0 ) پ  0 ) Zf 0 )   w111) Pn ]ji_^i ;Cxq$Q2;+ ;Cxq$f ;Cxq$J 1 11C|q$; IXCxq$j* I6v-?YZvgcB' ']˧^2%^_VrjpXb'c  w'N!L'KMmeY܀ ,CڧWoS\x[Hi!`cl&|k#K' \6 l2L 8 &+^_Upmp]ɫ^,++?RTpgg\ `t@_+9+8Bod`0T55OT5ZbiO.Yt09X6 14$qp@|SmB^d_[3gu. 8ð²mKd)x$/ 56"2IBxgR/ҸlCpqkdH`nRIjH7v@w'B''ihqycUx'>,+'h |k}{xxk~}{xw(NîVoũ|Mw+ 'x'X2aKkNfwlm~Գۼ8 '>S2KU}~_!ӫn@Bff v=J!~WXY~(v ;p9Gago9>>zY[Z88Y[Zz$vwБusnRG[`iWY\i&kw7va  ''ACW3_P`5JY'L0w'2_B'ʀIugbZ5"''Q`eUUeƶ`P )vww'='4I,)ugbZ5"1|J''I𭣐#K' \6 l2L *ږ*a_q , WV*''*$VW'}ʽ`!"YZLLY"!jaajl^]m ' _)J# j]g 0)LiII;S L@ 'N 1 vb9''tG M;xש+ n_S]g#)iOUYmFQTaEW)bQb m;F&551 vi'|A&!& ktnz@jEDXfCO[aElYZ{ıpJC\lYrm_F1  s/5\Oq_]*Ͼvs[U(U=(5Jq@a#nD1 k+ReٺhZd`yfpbh U9!FǤ.IZAa_q mDaa om`  AaL|# `к\FF\WJIW: TLLLUaïgSTfaUeUa°fTTfaUeUa°fTSgaU^Iv P&kS^w +wP&S`RR`aOPbĶ^Iv N\m!hn71 Le^FkZC} ZT}T۝Zi I. Z۝@   D..DF//F8tUtG&IG X 22 9:.PP.G&dG.PP:.9J  22X ,T]],KTK,TA==JTT,r}ib8xnDҗƆÞ>QiEdMhnf5fhLfDTr,A=ŭҀnɖQ>=ݞ}SVXP?nCon?N[USx}94XZD |+4X'4VD J+ZZoL  ˷+0,t5a~E=~E5tD,F+0Fm; 5 Xkk5A A5 55 57vDLrWdvLpys|=+aL>jIL1ipuYmk[YP=rxybcejr7@Rp媂}jIvp w&;&p&WaT<-<mxÊ ?﷯\}x{r_pƦ|pjW09@ F EҸG5Z {d_xwbx˼ž 327 77OO..OO7:ǨOai\2*}|~v=4+( vi$ew4ӎM+غucbs}}odaA`]UXr|v{}xoutYOj{<o( vi$'&@ #H,7 (? tdi|O$dpIA{,x*^SX}/uut}njwr@ R I&8#${(? sdi}O$=U޵ZT,w*_SY}0uut|njwvOdOuOO7<>#>0-}6-}E[˷:t!l~,=~,!tCR:  >+> J @`kJ KJ $n ¨ > JZ 82 J G x yF =y ڻe h+??/C eS{riKU9U@kK˾6yuww}ulmg`S<1)FdNkqyv}u  Z 2  hG2 :x 2:y èy ٻL66LL$$IrtТyFGttrjwvXK,.WTzriKV8UiǴŰx\𳙙VIU eS辡sxxtojvVǭɎ' [W4OǾĨ]9iĻ`I}xi}5ortws9 yuwv~ulng_ R<2(GdOkqzv} 4&S><Lﲸbxvq­~sg^)3K< J૭kb$ZutB\oid"^R@A>ٲND.`@nqxvg{XZk6{PsټZ3Oe{ǭؚ{ww@Gc;emzr{{svoz@izyuq1qrvyjhu{q k@[hsts#ܸ =yܸ 1y]|w^{xrmB~yb4`4U#J8T尠zy~~lw@3۶xoysoyszo]esrnvnewtq~= jAZisqo6E.uY8hhyup6U < J੫Z)U:ipqjj<Ťy{n\^qUn5T:%pwúNUbhzi]R.R`NTp{w{{dcigCoo!4Qyzxu]v4~5A>$\#lpv|sjS#`:Wֺ^   ([+ijh[+## 񑹎hlm% -y(jkjvDx'lijx(%ywxX3>qXywx~l2Bny=ܯ(ckll\#ӆzt{zj;++A;U:';UڵB8HP!ڵB P((/1FG&a^~reLy\f{yv`pziay]6LhHr˹8lrmkEAH'-Hުenuv[qglgr}v{yw"'0"L::KO77Ocpm`_pmcX@{09uB21khhhg{l2BA9vnf`_fm:9FUƱdPOee[[e! HbYF()&{LN5L7oqz[LTLK'_ō&)J°zj': 2&=qpp75DZTFp0wmEwv'x7E=T=E=''x'>>>7'Nx`x_x_wyxNֽ '0hlhx]J^vfrמyrZIG# 6)jKGTF)=άbL#J\rǣ-vw{s}o{n`W:*v02!%0z,+ xwȀ!v\oVN&u`s@&z&{zqUyxzw# Qvvww'1gY>}~_sѾFrnozi,oihY'YW$&s- N1$`} (Ӑǫջ &)6IHF{{{o<FQ^^Ot0wŻV)\{}al[Pt|jQF!@2EW[EWFvkx#]}fpQŰuY"CM w( K9G6k յA. 77Cqrum\mʾx  Z.A.|HlpZ52HG󹴝g&ӑvkŸů(x(&'*rxl}argDj2!KHW]HWkPm-v ?h#ww'vv5VⶬynggdWTs)k#p/ KRu%ggbv% w'\Mhˡ3tqrzm:}zbUS:.&v) QX"z'\ɕv] AGG պ}b:Gj\{[s?A 8]7OtӦ?쁴qf)vw '2;F F 'Z6b:T6b:|6J o|D@Y8///FFa@K67KL$$IruСyFGttr<6[XOA6[[NB0K76KL$#IrtϢxGGutr!ߨ2 T\;;Wuv~WWvztWZ_j(hvsmtdah$K*;@<pmwkwhv1)ZñȝƟ8t2N*++*NN*+ٿeG]I_EUYipwitV**EqF**VF<**<qq<**TVFI'EX+IEIvZIRQss,Đ ->sr S0Srr:Surs3QAJ35JRepl].N0<%8ece]gW^AJwX{[1 qCkcV.N(4onOq tOPgrlwl-Y<sq,@OCFP 66 6pmU ^  +^+U y=yN%')N%&kN%' ~XU͟occwoIeXWAAWHw;#Җ;J;E t, ,;"Ҿ t,1l,n111S; -N $+CCђR* Ms?}zqi~~  j5fjj t8qy__P;#Җ0R0u ;J0R: nS@@S@I  d33d-N $R ~R⵼ғ:WR* Q.k>Q 111S; P ]P<:L`LG5M4M+CCђ07R ;E0N+h ++ +0IRL  $  |Gql\SUnroϥt`nsvgkxpvxf}?g?ر}|&_zyap~cR* l ]lnbbnldcmZnbbnldcmyPO`aoUg `6 Mq? si5Mp? j5fjj t8qy__Psgox\ZL ~~vvf5fԎ9jyqhhy96Q6J,&&]PZPMP##P3f  |uPZPP u  ; "f  u< ']Q]'PZPP ]Pf  :PZPP b. .X cZX cS 5;5̎9S` R7 "Y1` PS 55̎`N R7 " `i  G^5̎XAʔe}MJ R^I ,e-J-ц| M p; Re"C"RtȦeٓLq;poV^eh=BH !F xu HOB! ] F3 ^! HOӿ҅R + HO?58қM p; R8=R HO-*I --цήിʎFqzweezFLWR""RʭᶷҒHmzx__zHD_rZ-5jn},8lGYr FvvW8FvF7vW$xY x$x xW {> {\\8> 7\\\rrr& rrr\rrr( rrrW {"{***8"7***\PPPP& P   \PPPP( P{Y !7Y !{ 7 P =!{ 1 ![ 9 { 2 {J {7Jh7{ 77 P J{ J[ { {Y  7Y  7{=   7$  G{ 8M771  8M7$ G7 P =x{ 1 x =  9 GI[ 9 x8MF71  8MF7 GI { x P = WP F71 !P =JGw{ 1 W[ 9 WhF7 |[ 9 G { WP vIP F7J P GJ{ vI [ v| hF7|| [ |G|{v| P = I3 H`  3 `=` { 1 I = |  9 I[ 9 | HP|` |P|G1  ` |P|GQ P`9  PGG|8MF71 | 8MF7 I ( |(`9  ` |{ | ***{****8***7****\& \( W 4FvF 0 $xx !{ 8 7J[$J7wk !b/ 78 [FJ0C{J b{7 [J k Jwb [FJ b/ 77  [$ 7wk xwb7 K 7J [$ JCF/ b78 8 7W[F' 7J Fv0 bv7 [FJJ k  wb78 8 77 [F0'7w{Y $F`99`P `99`F$$P |$$$F`99`2 `99`F$$rFvF||CC{{CC{ FJF4 {} { {J{4 $Y 8FJF5{ {W 8{J{5$ W FvGJJGw*$G  G FvF7J!*{= W  \  \ \ \ \  \ \ \| | | VV|V  |  |w uu|u **|* |@\|* |w mY  8*m*YHYVtY " 3  8*mY"CY*HYu=Y  )7YVtY1Y m" m  Wmu3 *^m77 \|@\ \*    &  |  &\ |wїXzחm b_  *  * /1eLz@97M3:fnu "w|"'IXr * 2 6 < H N  ! @ G O ]  6 B O W f p  $ 4 9 = B I o    $ + = H P Y b o v z 5>MSXoz1>DQ[eipw~#,3:AFRV[fqv,16>LTbpx| !-6BFR^dhrzZ鹷s]jqo~jNh+6) ǜ̝כG>JO0_ D_ */YN,99,9+ϭT55iTGGi =-32=%"4=m`MMn !&9phew_ap o;رw ~}rr$W࿩xoaN3=n (0 som'N Q^shssgjaqz v(pfD; I1wYw\vWwwxI12fD ĴbR, ;9ϏV\]9>mzZ[Yj8$l8\YYmz$  Xe^W&(,#&K\ehB Bb32qnkeCkBff x o <Ѭ:hkft\)G纭xo_I8Bl $;ɮjlstbd mI'YAUga` N ,0آOrh_uW9RమpaI=-,9 '4 + bT\;CQc޽sDhpo~k vzzttzpz{~'YbµqhةTDGRgTc~wm{lxa{ _Z'f|=t /{zybq~cjgg,99, X  :v{ C 3  + $+   ]ji_^inj]]ki_^ih 'A== / Wdvzytuy@pz{~ 'Ybõph@UCGRgTc~w m{lxa ,' [' +ĴbRDǜ̝כG>JO 0/WdvzytuyCpz{~'YbõphCUCGRgTc~wm{lxa\SbeUUhFw W  }/S : Z;)M'4 + bT\;CQc޽sDhpo~k 'mI'Y@Uged@' j}zlhUW^^0 Y MIttQE(2[00FU% $)6 ܍9awjcc 0: {9 }$ kCx {yWac7 I o$ iީk: Ъ/{ ' qg } $ ;d h bnmccm '6)X Q'=>hhR#)6+鹷s]jqo~jNh 7 w + #+  g#4 I' [CvjDnkc`"w}H f  # L66LL$#IrtϢxGGttrX c J ݩ j  vw0 '| T F 'F' 'F' C+ĴbR yuww~ulng_ R<1)FdOkqyv}{yWac7јQ K'  i.G//GE.Di 'L{u1uI{' +r+  g l L7vgMw P |l|lbFShg^yl_Rmjbx !@AڻUnԨ<GAc@"ڢq^gnrhdvepuY{G`϶=`8G F7 ˫ 9 { [v 4&S v K ^ +`+ ӎ | | @6[[B6[X  5A A5 5 Ȏ ھ8 V vw [[ vww    ᶻӒ }zqh~ (+$i-wD r uI v ! _  :Q"! [L[LL \\U!H [zsmL{st CD : Jm jj  VQ6  | |  N õa,   _eh<9ClYM1ů ( M   5  v/ ^ *' rwcbQ N`  ީr T  | ' \ki_^i  Yݩ L|L ¿TBC 'K ƭ(u% p|zpj^_cg= twxtoiwwOiʮi c @ K K* kwuX P  `` ; w `t 8PmV  q |v w } \ )'  \  ^(^ \ 0;7SI ' ^X $ w;  P {  \m {J!^eh<l1]   | 2.  $x9  4a  ,&;V Ti8MPc#a X  |  44> Ug 6 xzy;  8jwvX e  Z '  ͱi > Le^Flq{w X# T7A^l,@J;DkAB&J$F2@8=HC/7R4HO*L>$H/H/t51>%F 77777AAA^^^^^^^^^^^^^^^^^,,,,,,,@@@JJJJJJJJJJJJ;Dkkkkk6k!ABBBBBBB&&&&&&&&&&&&&&&&&&&&&&&FFFFFF2222222@@@@@@@@@@@@@@@@@@@@@@@@8888I:======================CCCCC///777777777777777774444444HHOOOOOOOOOOOOO*LL>> >>>>>$HHHHHHH///////////////// ///////tdtiiA5555555=111111>>>>>>>>>>>>>>>>>>>>>>>FFFF/H*H/H467/< 42 ///////////////////////////////8R3*#.<>=58R3*#.=>=5bgR3EE*;FFPP<~ijiJJ@YYG0eFT||@svS|$@C-`.OC[COX$FFTFFtbFFFVF44F><! ;K b995NoppEE"^^^^^^^, ,, 5KLggnn~~../055UW]]ll lDFLTlatn8   aaltaaltcasecaseccmpccmpdnomdnomfracfracnumrnumr onum&onum,ordn2ordn8salt>saltJsinfVsinf\ss01bss01hss02nss02tss03zss03ss04ss04subssubssupssups      82HB  08@HPZbjrzd*4Nhfx:N>  *$$  6X $     &0:DNXblvKgn~ .5]l<DEFGHIJKLMPQNO6789:;<=>?BC@A  UVWXYZ[\]^_`abcdefghijklmnopqz !"#&'$%X()*+,-./014523 Sr s\VWXYZ\]^_`abcdefghijklmnopqSrs  &,2>JVbnzU[D6(E7)F8*G9+H:,I;-J< .K=!/L>"0M?#1PB&4QC'5N@$2OA%3/-!/6789:;<=>?@ABC  "Y G$.  "$'*/06LNx1z\y     DM6CDQ6?!"5     RRUU""[[v\ !#$%&'()*+,-./012345  RU[v   "" -.DEFGHIJKLMNOPQ    8DFLTlatnmark&mark6mkmkFmkmkLsizeRsizeVRN"*2:BDNL>~\d 7_ &,28>DJ,PPV\bhntzt&h,J ,,b)BGJ+8291/3:4U90YB;A3S8 (.-C1?Y:=#5&.1,,v~  &,,\0U7uFT "K "(.4:@FLRX^djpv"^|d,8\"AN+/CL@6).1U59%3YDj02&&p?- >IB;F7$&(q & , $$ &,28>8DJP,a$P 7,,,,65KL4gg6~~78;<>?@/0AUWC]]FGIZ[]^./ 5345006WW789:IJ*   $*0ideoromnDFLTlatn V0~ *H o0k1 0 +0a +7S0Q0, +7<<<Obsolete>>>0!0 +9  UY%>Tѧ0<0p)48{̺0  *H 0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 960129000000Z 280801235959Z0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority00  *H 0\Y@WjE@ 3X%*Dx#}֬cEr'Luq9OBu Ǝ o#_p)6Ɇ籚 S=}$E3vqdLe.hE0  *H L+,&Oݦ (g/|蕼l,QsؤSN&vW^!ѱ!XiDD9\ܜVEL=2Qb}r6:kNd d00yBٸ>0  *H 0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0 120501000000Z 121231235959Z0b1 0 UUS10U Symantec Corporation1402U+Symantec Time Stamping Services Signer - G300  *H 0Yft=}zD{GjUNPG 8Oix vg f-̵sY{3nfZ^R7bђY5EYM$hBr:hk"-NƯ䠫<(#rSgwQ00 U003U,0*0(&$"http://crl.verisign.com/tss-ca.crl0U% 0 +04+(0&0$+0http://ocsp.verisign.com0U0U0010 UTSA1-30UI&`es8ͿWo0  *H 'xrm5ĈmK-盋?9 f\TNoeg |RkzȬB|,=fsr֏Q Ѝ1RO^}JuN+)+]~n'{#b)f,ν'x1"ijGC_^|}bM "Vͮv M٠h;004+(0&0$+0http://ocsp.verisign.com0U00AU:0806420http://crl.verisign.com/ThawteTimestampingCA.crl0U% 0 +0U0$U0010U TSA2048-1-530  *H JkXD1y+LͰXn)^ʓR G'/8ɓN"b?7!Op18UN$ҩ'NzaA*^ݻ+>W~ +;R8'?J00 ;x`7QFȖx0  *H 0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 061108000000Z 211107235959Z01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CU0<U 5Digital ID Class 3 - Microsoft Software Validation v21#0!UAdobe Systems Incorporated0"0  *H 0 S](aq<9>Z8-kJ'KwdOcƲ p5R!2, bדq Ѯq`UtRЮU\7K\ԒbjsPVJ^rӒk 'OQ^ig( |Zxkv2Cr?riT CԒtJ0K@XW`֏COe?r^q) xK͡{0w0 U00U0@U9070531/http://csc3-2010-crl.verisign.com/CSC3-2010.crl0DU =0;09 `HE0*0(+https://www.verisign.com/cps0U% 0 +0q+e0c0$+0http://ocsp.verisign.com0;+0/http://csc3-2010-aia.verisign.com/CSC3-2010.cer0U#0ϙ{&KɎ&ҧ0 `HB0 +700  *H haRĎA}o˷k8eb1Dۛ93l[O-Ycּ+t{-t6^{8L,N*􍕁3dHD"4_cqhQ8% 0 ~;# % 2Rٚ*>A./oI l+/}iߔv4+v.>jA/l @@O!gs`K(50 0R%VK30  *H 01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CUߖqU&J@<& m%{Ͽ?/wƵVz;T0Sb4Z(LN~[uGr.4L~O =W0֦6րv.~4-00U00pU i0g0e `HE0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0U0m+ a0_][0Y0W0U image/gif0!00+kπjH,{.0%#http://logo.verisign.com/vslogo.gif04U-0+0)'%#http://crl.verisign.com/pca3-g5.crl04+(0&0$+0http://ocsp.verisign.com0U%0++0(U!0010UVeriSignMPKI-2-80Uϙ{&KɎ&ҧ0U#0e0 C93130  *H V"4aHVdٌĻ z"G8J-lq|pO S^tI$&GLc4E &sЩdmqE`YQ9XkԤyk Ar7" #?Da̱\=ҍB=e6Դ=@(#&K ː]L4<7o 4&ٮ Ś!3oX%|tXuc?1|Sv[퓺]!S‚Sc P=TR,=.ǓH10 001 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)101.0,U%VeriSign Class 3 Code Signing 2010 CAt%SMIh0 +0 +7(10 *H  1  +70 +7 10  +70" +7 10www.adobe.com 0# *H  1EгR=9UP70  *H ߚxAiRr n0r.HXͪ^6 ٷNI+9Ome`%;,q~ r'snPt;@Z?8З~7'ݧzkno' BYn.rQ?f&g vNc{A$7kCJ\j^͙"F)8Gg8z ^'Ტ&xBScϊ?g`)u#-9Ƣ̏7 \oR=5k/w*0{ *H  1l0h0g0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CAyBٸ>0 +]0 *H  1  *H 0 *H  1 121205232836Z0# *H  1Iuюώ (na|0  *H ]ߗ/KaN^VϪӍ[bطjlU7x`8y)V?+`^O 7Lw}"| v[u%@ fK')3g5P)@Tb8'r;h4Lfhspyzo-4.4.3/pyzo/resources/fonts/SourceCodePro-Regular.otf0000666000000000000000000025700012662716363021666 0ustar 00000000000000OTTO`BASE?$:CFF rHX headl6hheav$$hmtx<"maxpPHname  2V # H:> ,: : : *:Copyright 2010, 2012 Adobe Systems Incorporated. All Rights Reserved.Source Code ProRegular1.013;ADBE;SourceCodePro-Regular;ADOBEVersion 1.013;PS 1.000;hotconv 1.0.70;makeotf.lib2.5.5900SourceCodePro-RegularSource is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Adobe Systems IncorporatedPaul D. Hunthttp://www.adobe.com/typeCopyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.http://www.adobe.com/type/legal.htmlTypographic alternatesAlternate aAlternate gAlternate dollar signCopyright 2010, 2012 Adobe Systems Incorporated. All Rights Reserved.Source Code ProRegular1.013;ADBE;SourceCodePro-Regular;ADOBEVersion 1.013;PS 1.000;hotconv 1.0.70;makeotf.lib2.5.5900SourceCodePro-RegularSource is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Adobe Systems IncorporatedPaul D. Hunthttp://www.adobe.com/typeCopyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. http://www.adobe.com/type/legal.htmlTypographic alternatesAlternate aAlternate gAlternate dollar sign v   !"#$%&'()*+,-./012345:=NX   %$&(?FEGIHsrtv tzw  kLxRTU{69Vy 8Y7[Wtuws0/9@Z`z~1Ie~7CRTYaeoy $(.1CIMPRX[!%+;Ico     " & 0 3 : D q y  !!! !"!&!.!T!^!""""""""+"H"`"e%%%%%%%%&&j''R 0:A[a{4Lh7CPTXaeoy#&.1CGMORV[  $*6BZl     & 0 2 9 D p t } !!! !"!&!.!S![!""""""""+"H"`"d%%%%%%%%&&j''RvS~Wd LKHA>5, ZB;5 r}Z@8#ܰYۯe2<DJ4^ " v zwux Rt T=LNWXY[stuwk   $%&(?Urstv;<KOPRQSV\ ]^gZ hijko r#v'x)y*~.z01234756;=@>DJKLV^_`degfmluwxyzW>{+M{|}~l]hn^fkm`glVXY_adhiTUmp!q"89:<ABCabcijop?@ABCDEFGHIJ_`abcdef|,}-NOPQRSTXYZ[\  ]$%by2SourceCodePro-Regular.  d$V|14Pź#*18?FMT[bipw})06=HSZaekry '.5<CJQX_dkry $18?FMT[binu| %,3:AHOV]dkrx $+18CNU\`fmt{ $+8?FMT[bipu|#*05BIPW^elsz ")07>ELSZahks{    ( 1 4 A I U ^ f o |    % - 5 ? H Q Y c m v    * 4 = E M W ` i q {    ( 7 B L Y _ e k q w }   # ' . 2 9 ? C J Q X _ f m w ~  $+29@GNU\cjqx )0;BMT_fqx '2AL[fu+6EP_jy &-4;BIPW^elsz ")07>ELSZahov} %,3:AHOV]dkry !(/6=DKRY`gnu|"AmacronAbreveuni01CDuni1EA0uni1EA2uni1EA4uni1EA6uni1EA8uni1EAAuni1EACuni1EAEuni1EB0uni1EB2uni1EB4uni1EB6Aogonekuni0243CacuteCcircumflexCcaronCdotaccentDcaronuni1E0Cuni1E0EDcroatEcaronEmacronEbreveEdotaccentuni1EB8uni1EBAuni1EBCuni1EBEuni1EC0uni1EC2uni1EC4uni1EC6EogonekGcircumflexGbreveGdotaccentuni0122Gcaronuni1E20uni00470303Hcircumflexuni1E24uni1E2AHbarItildeImacronuni012CIdotaccentuni01CFuni1EC8uni1ECAIogonekJcircumflexuni0136LacuteLcaronuni013BLdotuni1E36uni1E38uni1E3Auni1E42NacuteNcaronuni0145uni1E44uni1E46uni1E48Omacronuni014EOhungarumlautuni01D1uni1ECCuni1ECEuni1ED0uni1ED2uni1ED4uni1ED6uni1ED8Ohornuni1EDAuni1EDCuni1EDEuni1EE0uni1EE2uni01EARacuteRcaronuni0156uni1E5Auni1E5Cuni1E5ESacuteScircumflexuni015Euni0218uni1E60uni1E62uni1E9ETcaronuni0162uni021Auni1E6Cuni1E6EUtildeUmacronUbreveUringUhungarumlautuni01D3uni01D5uni01D7uni01D9uni01DBuni1EE4uni1EE6UogonekUhornuni1EE8uni1EEAuni1EECuni1EEEuni1EF0WgraveWacuteWcircumflexWdieresisYgraveYcircumflexuni1E8Euni1EF4uni1EF6uni1EF8ZacuteZdotaccentuni1E92uni018Famacronabreveuni01CEuni1EA1uni1EA3uni1EA5uni1EA7uni1EA9uni1EABuni1EADuni1EAFuni1EB1uni1EB3uni1EB5uni1EB7aogonekuni0180cacuteccircumflexccaroncdotaccentdcaronuni1E0Duni1E0Fdcroatecaronemacronebreveedotaccentuni1EB9uni1EBBuni1EBDuni1EBFuni1EC1uni1EC3uni1EC5uni1EC7eogonekgcircumflexgbrevegdotaccentuni0123gcaronuni1E21uni00670303hcircumflexuni1E25uni1E2Bhbaritildeimacronuni012Duni01D0uni1EC9uni1ECBiogonekiogonek.djcircumflexuni0137kgreenlandiclacutelcaronldotuni013Cuni1E37uni1E39uni1E3Buni1E43nacutencaronuni0146uni1E45uni1E47uni1E49napostropheomacronuni014Fohungarumlautuni01D2uni1ECDuni1ECFuni1ED1uni1ED3uni1ED5uni1ED7uni1ED9ohornuni1EDBuni1EDDuni1EDFuni1EE1uni1EE3uni01EBracuteuni0157rcaronuni1E5Buni1E5Duni1E5Fsacutescircumflexuni015Funi0219uni1E61uni1E63tcaronuni0163uni021Buni1E6Duni1E6Funi1E97utildeumacronubreveuringuhungarumlautuni01D4uni01D6uni01D8uni01DAuni01DCuni1EE5uni1EE7uogonekuhornuni1EE9uni1EEBuni1EEDuni1EEFuni1EF1wgravewacutewcircumflexwdieresisygraveycircumflexuni1E8Funi1EF5uni1EF7uni1EF9zacutezdotaccentuni1E93uni0237uni0250uni0251uni0252uni0259uni0261uni0265uni026Funi0279uni0287uni028Cuni028Duni028Euni029Ea.aagrave.aaacute.aacircumflex.aatilde.aadieresis.aamacron.aabreve.aaring.auni01CE.auni1EA1.auni1EA3.auni1EA5.auni1EA7.auni1EA9.auni1EAB.auni1EAD.auni1EAF.auni1EB1.auni1EB3.auni1EB5.auni1EB7.aaogonek.ag.agcircumflex.agbreve.agdotaccent.auni0123.agcaron.auni1E21.auni00670303.azero.onumone.onumtwo.onumthree.onumfour.onumfive.onumsix.onumseven.onumeight.onumnine.onumuni00ADuni2015uni2117uni2120at.caseasterisk.ahyphen.auni00AD.adollar.azero.supsone.supstwo.supsthree.supsfour.supsfive.supssix.supsseven.supseight.supsnine.supsparenleft.supsparenright.supsperiod.supscomma.supszero.subsone.substwo.substhree.subsfour.subsfive.subssix.subsseven.subseight.subsnine.subsparenleft.subsparenright.subsperiod.subscomma.subszero.dnomone.dnomtwo.dnomthree.dnomfour.dnomfive.dnomsix.dnomseven.dnomeight.dnomnine.dnomparenleft.dnomparenright.dnomperiod.dnomcomma.dnomzero.numrone.numrtwo.numrthree.numrfour.numrfive.numrsix.numrseven.numreight.numrnine.numrparenleft.numrparenright.numrperiod.numrcomma.numrordfeminine.aa.supsb.supsc.supsd.supse.supsf.supsg.supsh.supsi.supsj.supsk.supsl.supsm.supsn.supso.supsp.supsq.supsr.supss.supst.supsu.supsv.supsw.supsx.supsy.supsz.supsegrave.supseacute.supsuni0259.supsa.supag.supaEurouni0192lirauni20A6pesetadonguni20B1uni20B2uni20B5uni20B9uni20BAuni2215slash.fracuni2219lessequalgreaterequalnotequalapproxequalpiinfinityuni00B5partialdiffintegralradicaluni2206uni2126summationproductuni2113estimateduni2190arrowupuni2192arrowdownuni25A0uni25C6uni25C9uni2752triagupuni25B3uni25B6uni25B7triagdnuni25BDuni25C0uni25C1uni2610uni2611uni2713uni266Alozengeuni2032uni2033uni02BBuni02BCuni02BEuni02BFuni02C8uni02C9uni02CAuni02CBuni02CCuni0300uni0300.capuni0301uni0301.capuni0302uni0302.capuni0303uni0303.capuni0304uni0304.capuni0306uni0306.capuni0307uni0307.capuni0308uni0308.capuni0309uni0309.capuni030Auni030A.capuni030Buni030B.capuni030Cuni030C.capuni030Funi030F.capuni0312uni0313uni031Buni0323uni0324uni0326uni0327uni0327.capuni0328uni0328.capuni032Euni0331uni03080304uni03080304.capuni03080301uni03080301.capuni0308030Cuni0308030C.capuni03080300uni03080300.capuni03020301uni03020301.capuni03020300uni03020300.capuni03020309uni03020309.capuni03020303uni03020303.capuni03060301uni03060301.capuni03060300uni03060300.capuni03060309uni03060309.capuni03060303uni03060303.capuni03020306uni03020306.capuni030C.auni0326.auni00A0uni2007space.fracnbspace.fracuni2500uni2501uni2502uni2503uni2504uni2505uni2506uni2507uni2508uni2509uni250Auni250Buni250Cuni250Duni250Euni250Funi2510uni2511uni2512uni2513uni2514uni2515uni2516uni2517uni2518uni2519uni251Auni251Buni251Cuni251Duni251Euni251Funi2520uni2521uni2522uni2523uni2524uni2525uni2526uni2527uni2528uni2529uni252Auni252Buni252Cuni252Duni252Euni252Funi2530uni2531uni2532uni2533uni2534uni2535uni2536uni2537uni2538uni2539uni253Auni253Buni253Cuni253Duni253Euni253Funi2540uni2541uni2542uni2543uni2544uni2545uni2546uni2547uni2548uni2549uni254Auni254Buni254Cuni254Duni254Euni254Funi2550uni2551uni2552uni2553uni2554uni2555uni2556uni2557uni2558uni2559uni255Auni255Buni255Cuni255Duni255Euni255Funi2560uni2561uni2562uni2563uni2564uni2565uni2566uni2567uni2568uni2569uni256Auni256Buni256Cuni256Duni256Euni256Funi2570uni2571uni2572uni2573uni2574uni2575uni2576uni2577uni2578uni2579uni257Auni257Buni257Cuni257Duni257Euni257Funi2580uni2581uni2582uni2583uni2584uni2585uni2586uni2587uni2588uni2589uni258Auni258Buni258Cuni258Duni258Euni258Funi2590uni2591uni2592uni2593uni2594uni2595uni2596uni2597uni2598uni2599uni259Auni259Buni259Cuni259Duni259Euni259Funi0258uni02541.000Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.Copyright 2010, 2012 Adobe Systems Incorporated. All Rights Reserved.Source Code Pro1F}@8C_vz-f%8K$4Nchv 8B .38z'9>Bav     2 B V t ~ 9 b   * Q ` o t  $ 7 X c k q w z    0 7 B I N T Z a h m ~   &-39@GMR^q *=Paenuy %,15DSbq )6CMPW^bglx *5?CNT[fq|Ї 3 ,$ȥno^]zR!?9 *3߳U* J .57.7?M@..M ԠӠ?CB'q$-q$\I K69ekZSɩSI PCP,:.OZ<(ԠӠ?CB'?q$-q$\\c _M]_yprezqlqwTI ijq}gghwM/C 4G49 M繳t{qokM^̆{Y1;>K"ɥǓiP 1 7v7z KzI . @  P rkkrulkuvto$3G!Q`r հ~]YEY iw]Iww{Qz/wF wegovecntv\ ivm[^TW nG &б .M, ?M@. ut] n IE v 'I87&78I!'2n vH\ 5uOEP^^^ kul^^uYSU Xw [l]M B8 H /sn HC #TD5Z" uk _9Ҵt5pqp~mTjI M߲fWlmnf>FK"gvA T qxwqrw ÒZ]] tlY T tslls=s p8Ued X5@a##p wwqrw@wqZ  f~WcבЦ m0w 8 +9 hiuljuvg6 rnd0LL-QλiGhl~vrא}Ը9SYQD(]l6BԽt  [  ` -- `` -- `CCC1 1 C biyswzq{ nRnRdHUkjXnlZTml^s 'EJOr٤]IIdE'ơwiruyoo}erqd|ʐ/KrX cZZsXLD   UUdfTSfpvujivupV=J8oI$ cבЦ +<+x ř}Ǚ} 11 ~"L"9 MB67AM ЮĮ[FEh\RRh~}|^f{] r == g GpVU- lEl K\9O1^`P b~~ h Qjre^U3-  Kg  (  )eO1]U8'@^nbаɯr E л[FF[[:8Zgp ` >Ix 2> iw]Iww{Qz/v Yxxqpx@xqqwxqpx.?j7SEa[-XYe #B##C3`=aqvws EEcVUdhVUipvwljuxrnv~|ru  gyuko C1CwqTW_ B |? (ˡ ` n|}w@kxX  lP`fpZYrFK"ҳt5ppq~mSk[ d K v a<- QHLl *_ z8 J5?  ;[ `)G OO"D_q;p g~yvkn[ `> *  *a 0 [@ T K [‰ ( 0 nG K g}Vm /Ph  HKi p  Z( ^ksmYYs^V 1 1  r=   X " w x Wmodd \ dtf\\tX  d @   f  `H (a%: K llx _ Jm zqmqwT k  [ 1  qq eY G  ( K (  njjnrbarlla zIM77$ 9ᠹZ p9߃Z YIg^^giXȣ_G?d_VuooL>L E \ Rm   dr J4@ u~{st ɒ\g~yvkoZ e˫zijp}hghwW Ȼ  =r C LB  D Xjw =o6o Y G T " =)q 2qq J  K  LvU   0  J 0 v7 :8PmV w  zHv br 11 ( @bG8fYY1 0)~ _1N \ T F0: T Dv\^]= pfs!;gbd<a=,>cGzD@ IJ?L_MN&|uzST`#+3@Pcm/9CO~09MW)6Pdp!7hDz9Yx'@  ^ & 4 B O b I _  H p  O o :Pj,DQi;Q9Qp.N\l{ !GjCd -Oz&5HY6Nd|Agzz/H\tm!Ag 3Y  4 j !B!"C"""""##-#A####$$?$X$$$$% %1%R%w%%&P&&''H''((+(L(|((())%)M)g))))**A*K*h****+#+L+f+++,,+,P,,,,--S-d-v----. ..:.R.i....//0070M0c0001g11122>2_2w223 3K3b3z334L4m4444555l5556:6i6677e77788=8J8W88889949j99999: :':G:]:v::;;l;z;;%>d>??^??@%@8@V@p@@@A ANA}AAABBNBBCC1C_CCDDSDjDDDEECEfEF6FxFFFG7GvGGGH2HrHHHI,ItIIIJ4Jw@wdwfwuwwwwwwwwxxx'x5x7xExjxxxyyyKygyxyyyyyyz!z4zQztzzzz{{8{W{j{{{{{||/|U|d|||}2}A}u}v}w}x}y}}}}}}~~:~g~~~~~ "0>N^l|8ET[m|ǀр݀(Fery΁܁4TeƂւ*E[h{Ӄ+6@UjĄ߄ ( R (vrr%% D s .ѐ ߓ wcvFj  \ l6A . ! v $$*%]l,.Ta77$77K'&K7(1zwGWu Oe&()&fRu2I KS ;4 &vѰp$wd$6"u=zMs>s{t!3 (  EcjkEXK31<{m~pyev|z/</K )nJW ) x## |:*DRMn^]V96hX]} ,  lj |: U J ?L]][M= bM77``گ⵪yH |ב@w  B@Uؠ0@I@AT @@8 E ' WV9)< m)Z  vzww~u9- jK' X} !wAct 9+EOLl`^܄GG8/4jX]wLJ 6/ì3GGIU]^P:  &G` H& b* z9" x 7w]z<wZ{[z]zyw8  [N NP7vzwkokoNA4B"zpwmypwwz>0?~%aM  :K" K"  +;+x mK"D9/Z#f Q ȑ"D* _wnovwnov@vonwwnov~T"DD<Rsb"D- OۯÒr"D2wljuxrpvcVUdhVUi(D" VHTPK"v D+(7D<ˣMi"CD%9/!iK"xD69/!iK"U3~ !v9/!iK"o|}w </!M/"v D~ +;+x 7 - +CA"ΘE q" - AC"E q"v\ϿwE q" 껈;H?o|zw;^TW ^ivm[u"v D~ OۯÒ)7hBD<]ԠӠ?CB'G*~zttq$-q$\\htc[U οaM ҨѦO''aQ$S2Sj`O##d5.H_ mCA\e1jwٟ0OD>aE^'S+i6V\a^qO:97¶sgeM<?gY&xiJ ؏Ʈb+0~I b+h5 b+~? +@Ͻ> IC K> 3?މ> AI^R (ZR (6I R ('tR (6YR ߇(wnovwnovAvoovwnovR ߓ(Ԯ R (lۯÒXkvl^]vYSUR (lskkrulkuKR (mF R <+(Py R 9~Z(Ȑ^j|xwfnV\l]M R a(CC< R a(u6K< R (`=aqvxr}8< R ߋI(< Ta o}}wktY]peWKR ('t8F h-R ?fse]TRmb)% e+<+x % ۰ÑYkul^^vXSU% @l8[% % ~ % .% 9⺸Zw%  j|xwfnV[l]MWD s ,tv D s q4bDD ߕ+s qƐXdsf]\tXPGv\8I-FK K Fa.O.I .5 9/Ĺ. 9߆. 1İ.& .aۯÒYO(.a@.? <t.E Kh.b4h-hyhKknidRt~{ss7h77.E77Ѽ tlY9 4˓ fTye`mGwcO6I OhEw1c"/PhlDwcKT iw)cF Kwch,KwcG,?wݑcwlXv v 9:_Hܶw+Fj o3v ^ n6A 6I v ^ n6A 6? v 9ڊZ~V@ f@6@A ZV Yf U@QZ lD " 6A PTyesnGv ] m6A s@K  6A m3?  6A b. ! yb. ! I b. ! F5 ! ۦ f Q ! 5"1! 0 . ! ۯÒYOb. ! Zb. ! ? ! 30M! o8a. ! YCCB 8a. ! 6KB 8b! I~ |8B 8᭵H! B Uao|}w ! F5 83. MxbWO8I!'`foly7ﷱwh&\U<bVN(#f*;PL.kcv(gveGcS|e8`((cV:' e;vS;. R'I87&78I!'2n vb. R yLb. R L0MRl 7'I87&78I!'2n vR#f Q ȑ%'I87&78I!'؀2n ؀vR 4'I87&78I!'2n vh";~{tsEe#((#fB7@hnpb_Rm'I87&78I!' H-2x  S ;4  VHIC 9 S ;4 lY !5snGS _P4 v  DS _P4 v މCAR>DS _P4 ? ޘX?~RS _P4 b&7I b&5 b&7? i5SNƾ̽se[C91A'+\qbovHKV`.CGZYQUgjK K:l8&8SyesnG&V@&U3fvx4űf^ -UqXMIi\Qcjj[V_nU ,lJ$2A%vѽ| iUv`plEle@plYvfye`mG4?vѰ9O! p$_p$6I p$,5 $ڦ f Q pߍ$1pߘ$Ԯ pӺ ۯÒYOpʯ߷깺 p$@p$6? p¹ߐ88$aT2 pߐ$>K02 pߐ$2 zHG Hpߐ$K0K Տ 3pP0$Uh"w$9/&rrciTRmѫխ,:.NZ<Rlab( ) |## /M ( ) |## (Z ) v## M W :ۖQu) l## S[U W ۍl) y## \ tsllsU]W ۩) z## i W ؾۢ2) y## -Om5 W Ƹ츥) ~## -.dUTdfTSfpuujiuuqZ( ) n## MKo ) ## ",W I@˯) ~## 9y  W ) v## MRW, W ) v## MR5E  W) ~## MR)` ysW 7 ۡE@) v@#@# KX]p Ko G ڀ) #ڀ# Mր ۀDG,W غ¤ ) ~## Gmj{ W غ¤ ) ~## %Gx f%{ W غD4) ## -O{ p` yrW ظ滆ۡE@) v@#@# -O]ktmYYs^VU]p Ko ؾۢ\@) <@#\@# ^-Om @,bo cȏ) Mdrb]Uh ¡ K69ekZSɩSI# =/@V4UھRinzol}f=c [(K^gMkycLOTqqcSʢXC8r7R*)쑴̟C8G%ɼrefn]h^ri^ngv-[!CG&LF6hX]hh9v.B`4$DRMn^]Tv^ IːƦgibYpO@, vhcQ<'.7*0jK k , sx Z, ߈ +D , x } , ,X88hݵ w: R/PhK X 5i,? X gv-[!=[`]r[1Vവ{b.C9.B[BI1^`P!-,03ŮvKӐU (J %C U (J kK U (Z  ͚U ּZ( J KI(HKU   usllsU]U  Ӟ U ؾા1 &ʐW`KrX5 U  &4KP  &4U IH BŕY3rkU :Q [U U W RWFU W R5E U B R)` yrU 7 ੵE@ X` n|}w@kxX]p KP G   ( DG4bP N|o^]zR!?9 *33 ,$nsf^WSU*H (Z|בw HBU(0PIPA0T P8 P PΚH ؾ2iבw ڄBUD0ڈIֈAHT ֈ8 0 XLDH 3 ڰ F H ?ב0w بB0Uh0ذI԰ApT 0ְ8  _t΁H ּZ(|בw HBU(0PIPA0T P8 x KP PH Īp3  ! H ‹Q]בw BBU"0DIDA$T D8 ZV E ' WV9z+<+R jw' WV9q>FbDE ݗ*' WV9p*ƐWv+[!Chh9.B`IJ۫_/V?SbUW(< (< G < *(D:bݸ< R Z݃<5 usllsU]u.< nݨ<m5 <r Ul<zŕX3sj7)< nG b@X)pogdUS;Fb@zpogdUS<mG Z  *(D-vzww+~~u9\ ~5vzw~9κ- 2-3hҼ- 8wd:vOv{gopPfA,-`ݹ|- ye`|mGKs- >4Ks-  f4?9- 0! {gooQfm770H:DE=OR w+j݄K\,(' X ' XQQ֯ ΢:݌Rỳ' X R -"u' XSye`mH ' XuFR w' Xp\F?' X! )κޑe GzШa3Y"DYdZ\ !1߼ !sx/ !܀ } :R!ߘf\ } !\} !} ؾा2!/ʐS} #!ݑ Z(!   ܢK !0h4} IB! W!Q W, W!Q 5F U!Q } 7 ࣵF!ݥXn}}wjxY]o K/ ! G3^vP͋w@ld_M>.diqnz貭}slfXPbXU.7CP`VMnlz5tg<,J^DDz?d>OGh>6.ɹԧBdnyps~iCd [/L`_Bpq[M(>.6ꓫƞB9]WH\ 5u](W3@ M](WM]IBWH\ 5u]:RWD f" H@\ 5@uK ]W /H\ 5ub 4~ Snu{su7.7.-uxjbY2?M@..M*(&G`  - &srGjr` f nG*&G` 4Qʯ Ƣv  GҞ` \,v * ݂G`  x,? &GԞ` EH(& HG & [܀ ΚH־& 0[ K ܢTv^H[ABEO=F_r npea`i44OUcUekjK k L3Oǿvm-`~& =:ye`~mHH& ShF K& ZFlv%ږڬ~ªfZ61l0^blfSdjfjQoz2]yQ!?9hb* zTv]`v@b'y~if]-n{G Mz ?|iI ܴpk-`b|* $g |5Kbݤp* A,?bj* |ݝ* dUK ( z9" | d K ( z9" | +e K (Z z9" v Ѻ K :ބQ~z9" j   gyuln[jUp]LK |uz9" u  usllsU]K ޗz9" z  K ސ2z9" u fmu5 K װ޴븮z9"  fdUTdfTSfpvujiuuqK # z9" | #C##CK Z( z9" n ѻK Һގ8:@z9" z@ pZAxqZf6K ގz9" z MpZAxqZK ގz9" z dNMKLNpZAxqZK ގz9" | ;$xqZApZKz9"  Lv4K i.˸z9" ~ y bZȏz9" :`lfcUh ƨݤzs" x6Zu -@ t] p] K i-˹ݤzs" ~6Zu K :ރR~ݤD@ f-zs" l6Zu Kݤ dIzs" 6Zu D C D + TZ [N NP74 Κ * [N NP7X tlB (%rW1 (%xG %W +D.%f ulB  %pd,rY p% 3oefoqedsi%ZCŕY3 :7R%]&V (aM 9xaM j M ,KM 4w2AtM}||X9!etF^YVeWuurGb*VI0fOM+2%2&45Q3jwCber9+CROn^\]9)C JB6iY]mZ  wA/GEgaVG <ݱko^MmK.jΉ;KKbdzþtT  \='?[ KB6g[\C*DJKhbd\Gzbr:} G 7!3LkdbV??@OWdfV[o(5U  3!+0zy#BJ!R]^nUlq$ 3S,17r%2!34(+@Kum[SJ)yy׀'SR+4Oa6 I$ LvU?wz9QRdmQ;k9GõBK z=YrtrigyHYrtrhgz<*VSoè|KVvvzwZz9&QFeBeyiXxD|׼پ8 ]!VS~|_N]4{H v8z/]z\[Zvwz)SS_aQ(!z^_]^_]v$ρwБytt"RA+gv\X` w{ȢLvvvzw0z9G/pVU`R O % z9xemqeeqneh?wXv% %9Ddɠ  \='?( ^='1 ?( ^='sx?(Z [=' ?:࠹Rs@ V@=@'&V ?k \='+ T uslls? ]=' ?૾2 \='ʏS?װи빣@ _@=@'cq?Z( W='h  ' U -=U'h,?iIˮ _='oŕX4 ? W [=' B8 GW,? W [=' B8 G6E ? Y _=' B8 G*_>bX?7 ૴F~ [ = 'J4@W ]p ' G @ @=@'@ GF?¤ _='fGGljb + G?¤ _='mFx e%b + G?D=Ǿ _='b + Gqz8 ?滆૴F~ [ = 'U ]p ' ૾V . =V 'W@ʏS֠Fb jȐ O`lecUSnv~|ruƨzI'a6 I$ aG 6 I$ Κa߬16 ܀I$  WLDa 6 I$ F a_6 I$ h _t΁a6 I$ { K IKaߴ6 I$ $ a:ߡQs@6 @I@$ ZV ۊCDFSkicwbESieQ\cScx^hecЭۣ?x@pKfVOQa]:4RG4\UUK\PT5!?Ĩfr~cR  hj4""4jh "6o&5t$@B6ڥĽ0 bMFQW+KMi\^^԰aT%Hc#n:L jcxcjn\Xk.w۲'BIj,Nʭ[gg_vYBeHs#;iccjm\[n$ l l1uu$ )e )7O1]V8'@^nbWɯ_1]U8'@^naWʯ1fV`mk]a ]a q)vfV`lk^`nEfVLhq1vfV`mk^` ^` $Y $g $9 4:9c,4;$s9i,4,4ch9:   YAghY+E<v-[%:Yҿ< 3 y(cK:ys 2 HW J@>v-Z&:Yҿ< 3 v`(KwD"`r``5:; ; 0Qx(5Rx( Y4"{%c1[2%{]M4"Tsq[X`g=T`Α€{wmi_Sr9P-4ekgW`ejdRnq}o+Å26@z @^ 2 L LhL @v8 e@vC@ܼ rdH Lr@ / @ !; kk Ds KlJz J^ > >h> J8* UeJCJܼ dݥ >rJ 1/ J ; kk F ?lz ^ z  hK=K>cqhmjtg_R\#<8 eCܼḎhjuljvvf6 roc0LL-QϻhGgm~ur׏} r /  i; ? 3k?3k  lռz Q::QP##M`hۮj;=hd` Yozt4 g`C[bqhm ռ[@ c _M]_ypreI hkq|gghw*Tո9SYRD(]l6BԽռ@vfgouecntռ۽ hivljuug6 rnd0LL-QλiGhl~urא~ݥ rշ 1~gpjxifmbiysxyq|%&\bohإZIMWhZgyyizltbռܵA{uWsqtqbyVUx~P=H[\RReϮ@gdTpwyR =(DJOr٤]IJdD'R=dI]jOJ=>rJOOj̲nxwqqwwo+Ơwiruyoo}drqd|L WyL |@>@r|N4ffyrmK6~II9 M鵴pzup}jL`иʥzsj]1;M 9 M߲fhK Uoq:dnmj/aW>r<[¥ĵg)gP$ĉ@_fom]mDIhURlҾb6Dav³Ҥ}ݻ}zyDP_Ceozs{|tvpz@ixxqr(^dmn``lpei@[grlkK<C!a@woowxpoz <8!VzmMwsuw]~`@vpovxpozhQQ, VD34K X:AaҪv}|hw%zziuzziuzq^gtujzvkfwvn{YhAZhrllVy|@>~s~N5cfwsmW[69 M߲T[WlmpKͫwxrW]lWmUH$6rw轷JQ`bzl`[6YcVTuz~}mrrwVZhix:U뭭~wwqPw$%%V1;[30OKtfkr}i\yWK_9^ԼX' NMDkkkNDIS,+jmm+OgFlmmf.Xg.mlmfFK33їxxwE55GeIyyy`G. ojnF>KԄtf}sqdp&&ʾohXQi轻:3SOF轻:GN&.E.,,NL-]c}vkbɮeMyāEMjiWUkW q@K\t_tĺVmnnf@DK''M߲S[mmOlgjTiʹt(opq}mA8C+AƽPHHQY<>Y_liZ\li^r~27~t_41kdcbd~sl4_^5ul~c]]dl76]U;;VUJJVTc]BO*JR7BѡXag<@NYdRdjՅ%&Kvήuhj$v?AQ|| aba(W yyxb[̺wxM5\IJI[I2x2KKK6N9/?s_u_s\rvt/?IμM]cbrR/OxshcNG!'-1rK_P`00ǮŽ "@^՜0oshwa{slaQX!|37!6 ~#* UwwOuhi[DzSbEPso%~rUFvwy{M )"ϓ^vv͆w"kMvx{h]rݧQVhchs]~|6tmhe.$6}}|e.$s6 K* \-|M(Ӑ­8ƼQ$v?;ARb rrs;^wxwm4_:ǵF x4GxqM:}ӊPFPaCJ %7AfOAeӶCv j#Yj?Ud0m=jK ~1 (9@X5 Ҽk Afhi{fFcξɳnqDH/Z/i8hlW26FH𿴢`Aِvִƴ lY6jj`YuYrBһy^]LAo9#( @fP@g ߵӴK:IbJ8"wSF=wvaz`J85sggTNoOw&w2 PT |'wƑvv͊ww)N,U)$;\idhs^fulfTMV*"x/ RW ~N(Βv Z4GtNL?_;Hebqhnjtg_R\#<`dZK[<_m^ k2e˪zhkq|ghhvldiwϿƴp`^N\`ypq}Է9SQD])]l6B]ԽY [ p O  鼛g=`[܀ڀº_C[cqhm -O  ڀ \[܀ (p a<9 d[ʢ\[" O _M]_ypre2I hkp|hghw- _ @i | @ZIMWhZgyy i{ktaxʝ\[<vfgouebot-@_ Ai@| AZIMWhZgyy@i{ktaA\[ 0V(>J8oI$b9 WJJWIWJMJ+ &.4.4.3.2_.3.3_^.2.3\2 kuUku2 5) =l5lYAhhYT,B*     __ ,t <__<  B-ffB-B BB-DfB__fBDB-EJJRIRJMJJ DD-CT,Bz,$N?Mz0,MɡT`Тĵ7 <`XYl-h)ɢɢTXТĵ6x <XXYk-hТĵ6ؾVDtp{lRa<XXYl,hUWBG<`פ~l}1XEL${#ކ"%M!,Y*PH\r\>и9fJ2CX\Eblm_I9KL#\5GK>Գʰ\KRi\PXe_l? ^gpJRqhl`IpOۊ`îյ>i|'8p.UZyX?j8ZOWwSvL?5@E;dSU9- B2,ub8MUraa2iW2M(b[%™xws((\&#SS},X)P^ҫ*@n'OFvuvm,mfffY}f(fK\" "\3KPNfL'22'L0S .HVs rhLVw AA^쪶qI-\5==IatspvcZbU?8LM/pwovmxWQŸȝƟ8t2N*++*NN*+ٿeG]I_EUYipwitn]OJJ]O`raO]KKO]na]]OKGK]O]rr]O]JGJO]]a6_]Z]6Z_Z3 " ڼ ׻ ^qllqsggr9((9DDHNȽY56NYH9:2`2929XgokePZ !43:Q Ӟ 3 g`g34$i43:r`NX=rn4B*`&paZSe61%?.zffgbh]NX |{%w2'U0.#Dl]TSe0-sUؿYq+rMLgsk~t1YZrq.JSN(88?8\LKKLF'F<<'.0:)e -扽Y0gϦl^]plG-ok0Y^FE^GqMHѼ H3@  !!%}H1e LH Stp *`\Ӿ>2ʐScdq#^^ϑS3E@X g}Wb֒ЦqjUMHѼ .=JVH3@ .J  .8{V5 p [9/Z[p f pQ mDDm Ӿ>2ʐSXEXۯÒYOS3^^@*`\_* j_1;̻ˣcdqO2O#^^ϑ.SV .8? #^B#n#C#.-VI>x 8>x pRqs^\^svwʴĥykux|nq~mysfyGF Gݓɬ~Pn~}|coySvSv3]* j]vonwvopu@wnovvopulc<`[yes`5E@VE@Uh~Ǝjrd\St{ssbB*bƏX?D;Һ<89j_f6_¹<88PDT2  M__ 02  dNLKLN__ j_2 zHG H <$wqZ@wwqrw_ J0O LQ W`,Va@`CCB O LQ `5F Va@`16KB  ULQ VU3~ |8B 7 =FDϥX V \=\=1 > rF\0F\0F\rrrr=r=r=\rrr& r00r00r0[  *=*=*=1  r*0*0*0\PPPPP=P=P=P=\PPPP& P0   a )#r0a 0#z H 5r0z 5}r )w##{)0w5o05U )= r0 r5 rr50}r w   {5w!o5a )G)=r0a 0 r)) r$0H )=)dz H  rrV r$0 dr0z }r )x x ))w! wdW {)0x!   d o0x}r )) `d0{)0);1 5 5`do0}r :) ;[ `G)=;[ `r d0 `) :0 {: ;1 5` ;1 `r d50 o:0}r ))) )=  `)0 )))! H W {)0) ;d ` ;d Pr `dV ;d d ;d Pr `  d`d V   ;1 )0  o0[ *****=**1 ***r*0*0*0*\==\& 000d:d::~ $x  Ka G#gK$)))r= $w\0rq |#g$w\=r))w\~ KG=gK\r)=)=)= K\w=0)|=q gr \w K\=wS Ka GG=g$)rr)=0 K )rq |x=q gr r)x=)rw $x= S dK: gr r))))rK )r :=~ gr :r) = |GG=q gr r))))rr) ~ rq a $$ $dO,,O}O,,Od=$ $$}|=$$ $d=O,,OU O,,Od$ $$= d5=|= ==| ==aa=GGaa=G[ ddCwE w=[ C$wa w=1 dd4w1 r0w01 4$wz w0[ d:r d0dq 0$0wd)=)d[ d:G#0))w0 \w \w \ w  \w \w \w \w \|w| | VV|V  |  |q uu|u **|* |@\|%|q mY  m*YHYVtY 1  mY"CY*HYu=Y  )7YVtY1Ymm Wmu1 *^m77 \|@\ \%   #   |  # \ |q zחmb_ 12k@IXdi'X}Ivd~ ?CTi (4t    ( , B N g | * = A H R Y d ~  " 4 ? G f s x  ! ) / 5 E K U Z ^ e l u    & < C I R Z b g l s |  !.BM_q}!,7BHY]cgw} "0>LX^bhqz+0<?FNU]chns~%ߵ{b[`]r[1V66/Ʈ (ge#((#fg('I87&78I!' LUbpS:l8Gijļ w&Vۑ˴d]TTqTSX U`ZR #+,-/ǫ)=WX.ZSY7G2Tݶ{bv[_]r\ j 9ЭdF?sr`|]B97ʲqifR4> h bhL3Oǿvm[ABEO=F_r npea`i44OU IJ۫_/V?SbU dcd%<Y0UUU}|FPF~U|U}UV08 4XЯ bQ :!Øy~if]-n{G M "~|]h}Vh.OZ<    V ef G 6 ֦ f Q  ؀ ( 6/Ů `C[bqhmjtg_R\#<ye c֑Ц d D- $q HG In}}wjxY]o f}Wi dR{*c-7 C\[} 1 d L آoxYwxn=bqv @n}}wkwY << < n0  ^N]_yprezqlqwT [  D;1 `Q  nEfVMh[eW`lk ye Wc( KA ȑ] ;[ `r )0 PG  Yo{t4 j V3-x H( \| ۶A{ P  1 E } BB 0 S лðuldT98D  ) p 11( ot?FpQ =)=)=)r50 :)0 ر’ )0    #4E4#D# i-4-4dh9CI v  >w 20( < * w U t ] 2 :9d-4; Vm  L V * Vl k g~ Vo]L 9 W }qr K$x=0 lLlLL=q 9Fi]VW¦ٿ PrcZZs 1LdQR\ [ÉDkuX]peW  v o]L:  iug^R]#; ] nFeVNh0  (  eVH pܵ )/9:A*)D 0X r) C  = VEtp{lRa  q S?] V d: L 0P0 Ѩw ɑ\ a0  LD zt_ ;_  K =b~qv onda{O&  )!`)x=Qv? \| u unm*^M( 1 r\0 KP < W 9 r_P   G oW  \m tlltul afdugZl _a  󯻆 rv$w a KVsK Ц ,U  XF ex | Y 0 vw " jw  (Z )[ e̪z 5  7 [m  TPsZ^ WL , r ? )=r $xE } h`h ) ~qp w X> gBUs5O_QbQS0f1dC*O+ 6&AQ]P<EgH]Z7jQ<]<]<HEM3@1G BBBBBUUUsssssssssssssssss5555555OOO ____________QbB5QSSSSSSS0000000000000000-!0000000ddddddCCCCCCCO*****OOOOOOOOOOOOOOOOOOOOOOO &&&&&&&&AAAAe>QQQQQQQQQQQQQQQQQQQQQQPPPPP2<<<EEEEEEEEEEEEEEEEEHHHHHHH ]]ZZZZZZZZZZZZZ7jjQQ*QQQQQ<]]]]]]]<<<<<<<<<<<<<<<<< <<<<<<<sLHHHHHHHXEEEEEEMMMMMMMMMMMMMMMMMMMMMMM11111111GGGG<]7^<]=?E<M.?63149<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*GbE9'8MFDCGbE9'8NFDC m{_ggR`UUPP<zcxcccT[Hp11WhUR:UM5:q1`N/+m+T`l80&UUfUUxkUUUoULLU'ML>$F4u!!w&&HH&&!!Mbll#, ,, 5KLggnn~~../055UW]]ll lDFLTlatn8   aaltaaltcasecaseccmpccmpdnomdnomfracfracnumrnumr onum&onum,ordn2ordn8salt>saltJsinfVsinf\ss01bss01hss02nss02tss03zss03ss04ss04subssubssupssups      82HB  08@HPZbjrzd*4Nhfx:N>  *$$  6X $     &0:DNXblvKgn~ .5]l<DEFGHIJKLMPQNO6789:;<=>?BC@A  UVWXYZ[\]^_`abcdefghijklmnopqz !"#&'$%X()*+,-./014523 Sr s\VWXYZ\]^_`abcdefghijklmnopqSrs  &,2>JVbnzU[D6(E7)F8*G9+H:,I;-J< .K=!/L>"0M?#1PB&4QC'5N@$2OA%3/-!/6789:;<=>?@ABC  "Y G$.  "$'*/06LNx1z\y     DM6CDQ6?!"5     RRUU""[[v\ !#$%&'()*+,-./012345  RU[v   "" -.DEFGHIJKLMNOPQ    8DFLTlatnmark&mark6mkmkFmkmkLsizeRsizeVRN"*2:BDNLZ8d 7_ &,28>DJJPV\bhntzh2 >2ht>>>,,#[+KSA2;-17-:W;3\<>-V/'0@=\?8 2%,,RZ  &,,[9V:x"0 "K "(.4:@FLRX^dX"Xjjpv||^,4["LO+-RZ@6/'7V2;#):\s59&&{A =C &*vm& , $$ &,28>D>JPV,+] P 7,,,,65KL4gg6~~78;<>?@/0AUWC]]FGIZ[]^./ 5345006WW789:IJ*   $*0ideoromnDFLTlatn V0~ *H o0k1 0 +0a +7S0Q0, +7<<<Obsolete>>>0!0 +$ J 0<0p)48{̺0  *H 0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 960129000000Z 280801235959Z0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority00  *H 0\Y@WjE@ 3X%*Dx#}֬cEr'Luq9OBu Ǝ o#_p)6Ɇ籚 S=}$E3vqdLe.hE0  *H L+,&Oݦ (g/|蕼l,QsؤSN&vW^!ѱ!XiDD9\ܜVEL=2Qb}r6:kNd d00yBٸ>0  *H 0S1 0 UUS10U VeriSign, Inc.1+0)U"VeriSign Time Stamping Services CA0 120501000000Z 121231235959Z0b1 0 UUS10U Symantec Corporation1402U+Symantec Time Stamping Services Signer - G300  *H 0Yft=}zD{GjUNPG 8Oix vg f-̵sY{3nfZ^R7bђY5EYM$hBr:hk"-NƯ䠫<(#rSgwQ00 U003U,0*0(&$"http://crl.verisign.com/tss-ca.crl0U% 0 +04+(0&0$+0http://ocsp.verisign.com0U0U0010 UTSA1-30UI&`es8ͿWo0  *H 'xrm5ĈmK-盋?9 f\TNoeg |RkzȬB|,=fsr֏Q Ѝ1RO^}JuN+)+]~n'{#b)f,ν'x1"ijGC_^|}bM "Vͮv M٠h;004+(0&0$+0http://ocsp.verisign.com0U00AU:0806420http://crl.verisign.com/ThawteTimestampingCA.crl0U% 0 +0U0$U0010U TSA2048-1-530  *H JkXD1y+LͰXn)^ʓR G'/8ɓN"b?7!Op18UN$ҩ'NzaA*^ݻ+>W~ +;R8'?J00 ;x`7QFȖx0  *H 0_1 0 UUS10U VeriSign, Inc.1705U .Class 3 Public Primary Certification Authority0 061108000000Z 211107235959Z01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CU0<U 5Digital ID Class 3 - Microsoft Software Validation v21#0!UAdobe Systems Incorporated0"0  *H 0 S](aq<9>Z8-kJ'KwdOcƲ p5R!2, bדq Ѯq`UtRЮU\7K\ԒbjsPVJ^rӒk 'OQ^ig( |Zxkv2Cr?riT CԒtJ0K@XW`֏COe?r^q) xK͡{0w0 U00U0@U9070531/http://csc3-2010-crl.verisign.com/CSC3-2010.crl0DU =0;09 `HE0*0(+https://www.verisign.com/cps0U% 0 +0q+e0c0$+0http://ocsp.verisign.com0;+0/http://csc3-2010-aia.verisign.com/CSC3-2010.cer0U#0ϙ{&KɎ&ҧ0 `HB0 +700  *H haRĎA}o˷k8eb1Dۛ93l[O-Ycּ+t{-t6^{8L,N*􍕁3dHD"4_cqhQ8% 0 ~;# % 2Rٚ*>A./oI l+/}iߔv4+v.>jA/l @@O!gs`K(50 0R%VK30  *H 01 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1:08U 1(c) 2006 VeriSign, Inc. - For authorized use only1E0CUߖqU&J@<& m%{Ͽ?/wƵVz;T0Sb4Z(LN~[uGr.4L~O =W0֦6րv.~4-00U00pU i0g0e `HE0V0(+https://www.verisign.com/cps0*+0https://www.verisign.com/rpa0U0m+ a0_][0Y0W0U image/gif0!00+kπjH,{.0%#http://logo.verisign.com/vslogo.gif04U-0+0)'%#http://crl.verisign.com/pca3-g5.crl04+(0&0$+0http://ocsp.verisign.com0U%0++0(U!0010UVeriSignMPKI-2-80Uϙ{&KɎ&ҧ0U#0e0 C93130  *H V"4aHVdٌĻ z"G8J-lq|pO S^tI$&GLc4E &sЩdmqE`YQ9XkԤyk Ar7" #?Da̱\=ҍB=e6Դ=@(#&K ː]L4<7o 4&ٮ Ś!3oX%|tXuc?1|Sv[퓺]!S‚Sc P=TR,=.ǓH10 001 0 UUS10U VeriSign, Inc.10U VeriSign Trust Network1;09U 2Terms of use at https://www.verisign.com/rpa (c)101.0,U%VeriSign Class 3 Code Signing 2010 CAt%SMIh0 +0 +7(10 *H  1  +70 +7 10  +70" +7 10www.adobe.com 0# *H  1n/ז7WOZ0 +]0 *H  1  *H 0 *H  1 121205232853Z0# *H  1,ju{/)js0  *H AlFLabtԈ9XS4H[gn^ R;xݑ1K@rp؍j/߃ܲ^lbzۑgxڳpi)4sw/Z˔^8Udr;EHLk>pyzo-4.4.3/pyzo/resources/icons/0000777000000000000000000000000013166630336015012 5ustar 00000000000000pyzo-4.4.3/pyzo/resources/icons/accept.png0000666000000000000000000000141512662716363016765 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥KSa;vvl dD!P{$; ż,Kݽ6cL2r^H)-jsNm֔2qQB̽BatoL#z {q' r=)La8,u%2Rg>ݾW ϛJ߸Pd makD|=G Vn6[Įd桚(Pm.0Q`'Fb#&ܧ6aP׏Q12[+zi; ]C17оpI9̾jD}›?7ayze,hXAK^3*bk @+wQ=!}uXzq:g쯺n= :d+_GTA;Ր Jƣ.!P)5!H:epր"݂"Kyw|{H2!i~3z_X;okBZK* ^R:O(jF*^ȰS诿_ gЬycIENDB`pyzo-4.4.3/pyzo/resources/icons/add.png0000666000000000000000000000133512662716363016257 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<oIDAT8˥Ka[/Y()%X(olNۖskn.-h;8fEP"jïMGˈ}yພ羹$I.tulu AX:𼂒ZHh1DnZJOJB{Z?`2`S=N$ő=;a &jw qJG#<"N2h8޵`6xցn_+ ~Zto}`x%XЛ͈ hXѿƻ/}BJ_G&|Qr-6Aރ EL⬡\U3:WUh[C6+ 6.f *K͸ܝFq ou4܄?d|XҥMvD` *_[ #A20liR|xq`4w=\uQ m+G|%$5Թ5RO*YGMUO Gqj4ְ(X& s1c˭(LVf RdjQ '-1ATA>U j4,pV"4L$e@.ArBY a~myY])Q8tNLܞt2"I o=CSd)__AF(IENDB`pyzo-4.4.3/pyzo/resources/icons/application.png0000666000000000000000000000072012662716363020027 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<bIDAT!nQ{!M`IXBb HФIA۩$UtH9̝07( ~||& `rǤ 'yx8yur=;w:.fIP4F*|yCz43ݯE8;MP1^<B PB,ZRC*CwcaZL\L =Rf&Fv$Hh@$H D@ݝ.a{Ih@$<İq;ƺ,HH$VBc5:P MaZiԁ%A$=]"6Sla=bKzgr4n~]_~8[N]Te6NTpLllKh<_;IENDB`pyzo-4.4.3/pyzo/resources/icons/application_add.png0000666000000000000000000000115312662716363020640 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥=hTQEb,4.i0QXaI R&`XUXJ1 !E RXEVc |;3ϑӗ /ܨֽ0PSsU1w\5cduq|ቸ0Po˵ݹn_80ϖ%bW}-ΕIn7503GІƺ:ݝi A 8bBH@Dȉ,7MR\"0s" !uljY@c.@"R@3BS IBlI^?BHXIFq #"Ћ7Ox_Qvrs)oϲDd.$A >Їo* C#F>3mo߿&8u"Og^\\^Z~|N2lIaTk+,Tf06YrV_m,uu;HO>}}>/nf׹8/pone7|KIENDB`pyzo-4.4.3/pyzo/resources/icons/application_cascade.png0000666000000000000000000000101412662716363021467 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍ+Q_\""Yv,R"6%hb5%Z…(]kFw¼~gb593nK,σhygOY k>fm#2y|ĿD+KoGwSQ)uCIf G/Ĥ#jQIe2SK>byZ~9 g7z›&8\hp$StcXpE h tJ.4l~duI*|y?< &y}{" .Z|GS:*cz x F@(#pn *;.vBC_?ş t.Ѝ/Vl| m=.d0nSu0ATѣPyw|Tꍉ+#{"WbIENDB`pyzo-4.4.3/pyzo/resources/icons/application_delete.png0000666000000000000000000000114212662716363021350 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATOhqp-fA)(ɑr B\VkrsB  (O7/Vn,I;gv>VS"PBr#X<h?h+ќk0ۥR{+5_*(ŕc}ԉk-jY j #@!< #Jx%\Mx8D͒aJLX)f 3 QD"WSN-2B$+nSP jY!Ddf a QDA-©gD"2##zxc?UY $afE ~tWaS&׏oö́9l|:~7~_%2*;|' &yC3׉>j;Y=䭦SGL"*jD ro[A]1S2ɕ !fx#?\o3B$Y\Su!" Br&1g-C/"ѯDK[+,"!/y쇛$n`(u/Ia;Sj88gN ED ~([&"1#70Tl:OQL!@W[3)Tz_fRYt=KR4BjFOuԟ 9)Km>H6UV/ō5$/֞K}^T;>_+$Yk{}?~4{<,LpIENDB`pyzo-4.4.3/pyzo/resources/icons/application_lightning.png0000666000000000000000000000122012662716363022066 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<"IDATka~ͱ60[JI-7\lWRS,\p@qG[n.pJ-IjL~`<'\z93 v_R"*jfJ]_8xkW/$Da$}mW򖔾,ReĸJș)Q%g?c0Q4(&xQt["Q.ɑSA枰f=i:}}5PUh DNT@Gn"w!2C(i1\CbJDAXzHum gpK~x-g7B RMTȩ Q(Rt}X m1u{Gn>Ò:mGPU"'ZIu6WhPkZ@ d3/_as/ZCș(QI(OCK+|PF4MR&4T6f_ߥ4$o=ԌM}7pQTwc?mk<}0,p jJ427x(pm߯^d3rੱ;˄z9z|%;986IENDB`pyzo-4.4.3/pyzo/resources/icons/application_link.png0000666000000000000000000000127512662716363021052 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<OIDAT8˥kRaƅ>}/Pj "A *dֲCK $3TF2i5I $5?Q+*%ox깟غ\u~9sqGL%xp5\"~sB'AUpnl0 . 51Q?SĞ)htzlջX[O_kMNlèrA]YAGRA][m Mjntyz ݦ3B ԡ0PVQPohJ<s@G;; 1I}vA!g*@y@jwxN# !H  b\Fms ƅ0t:aT `d ,DPVQT|E=y'N_^gaŖmR=Ln.//#rv)*n90_ ѿV\ٿ0aX`ſ4ܟ@_P303'×^ 4G)1vUMB9 i;N@ͺ@Ռ㡻zZIENDB`pyzo-4.4.3/pyzo/resources/icons/arrow_redo.png0000666000000000000000000000116112662716363017667 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥OHQ7Zcf9UԢت fdPlQ RȕnE"à6e-r#apd6Yc|lDe{yDQؿ<>3;uDٖ47KgAg)g$ujUO 2kp s܍ { t?<(7YՔLX]`$7id? Pָ#i)8:S.άj[/Uv loL3 k&~j~MLGJf2Bʔ̘Zf=EfQd֟wLۅȌ#fd&ʝulՓ;xLH֌ ÉȠȠ~+:#hh#5=&N.ehiL(B:* ƑCsΑd`˚囒}06HWz*``ܶ+z>"Y HE# ]|zE^`%bŢvq<ߎ!l;;qOud_W!D0_"-IJx - tn$%!?[ؕҲגm8UޜBHÝu{N*Aƍck쿞&rPPz>2I)3A.uFk4KܖA.qsjw߈`LwЃK$H wh5G0zg'hcA` 1?1mR"@Qɩ`h70ɒpGZupvUO$M c'qCGH L+@Tk?FEkҼ갺|IENDB`pyzo-4.4.3/pyzo/resources/icons/arrow_rotate_clockwise.png0000666000000000000000000000113212662716363022275 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˵NZQy8DF NJ5XBр/DiEmAD0@Դ8Ҵ% Y@41_go,9'(ٹ-;5Cc-^؊֐X >څտN~1ՅW v\N)i7mp- $t+@UVlf0˼߂|@%#ZDD>U㮤##Lz(27ύ > YAW _yydzr7<B8WEeSO`J\''A9-K0BFg}5Հ# w !zBq7˅+đ6s۔CeTH`9ܷyhvע)DH&TiHH9KI$ mֵL\i9IENDB`pyzo-4.4.3/pyzo/resources/icons/arrow_undo.png0000666000000000000000000000116712662716363017711 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe< IDAT8˥S=hQ=RBIqՂ(E*(HqppRZB'A$CP",N`iJ sCI4V ps^9͔MVqzę@e4 +,RXPܣ'.Ԑ0;?s9:FԤ`)XWgZ$2AMA#kCv%k^џl@% S A%T ߞ@;CDܑLK/L6Bα s(ɐ[7f\ȟ+hTFut@DLD %#Tt}TtZDLH]3].TJP -K'o:W(D<@*BxKyX\E Mݡ؅X/ =4*6 \O_ٿ눊>IENDB`pyzo-4.4.3/pyzo/resources/icons/bug.png0000666000000000000000000000140612662716363016303 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˝OSQ\^[تtz% l$***2: iRp c0ąKw&&`44(eKɽwwrOX\HiscUQz@;քIdTaˀ)jC'يKT8=ʯ9ނ^zΘ1OFZ[W-Gz?&%*MGnN!aO>Nc[ɨX·0Nqg*1Sub|{g|fz)̾&\ 5\ 0 3i D;`|0>A?Tx4^`oqs`>ʦ`fCv@mX[r\At.)G[ Ì`N1)BWs+:NdsVa*DX.pB&B]H@T3@Pڏڠ wVP63yp-4 Ǽ $H'9{m@U$ZjCX:TgL::?[#{1P=.2F\iA-D 77qXIפb4kaAj% ͼj&Q˫H&s. `jKLE3*ΫX w6_l=@hߊv ,qqIENDB`pyzo-4.4.3/pyzo/resources/icons/bug_delete.png0000666000000000000000000000150412662716363017624 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˅SkLQA_yGŠPT4B%S)ɦ-o,%P HiGtU49>2-;9>sα`S  {8~zߕlI.scʪtwy>wu4s~%$T][5y|Վ>bS6:p5>(abֈBu2Dޗ uuBI,DC`xՌ7m~VM$DkǪ r:ٵX$unh'uZt\t! L(0@POMh UR'xZ?myBnBR ]1n@X5'HLf/kQܟ>I #qw1VQL]I[,pnhdY:c"K!'qg6*4x].81S罜iIq%Bdi#!i Cke8aXH`en(RxuWîV3pݬ@\>%4"4B[9s!t6cqt5*;i-sp*{ޘAF(N|#Cf}&rm6yu֙lJbDHn4yH>RS*@Y 𕌏>m|aRvSinkNWŻ=x"f^/-W/-d6Ed0erHycF#]gjLL4(TQ٫w5Z}yw=,g/<N~s ka'6Θ_+AK6M%UnAjsJ|_5i "=:$otO ^32 η!((M4Ƚ&b݇< Ք}Q2}[DyNŧX`<&=ؠ9iNR>"3ɝV`AZ6 h:XM12{‘;19rÅpȱ ԺQ!炜@FwH_HjEoہHZ+ fͦ]uܡ G\ChS,F Au7e:iL7 Ɠ Z gJH{#PცL^a%XTF}~SBvO bwA$ bgĄqZh2d TR|Tc{UheNϥ`}Keı LrHgj818otpOIK: R9M9s٥f Lʒq%Z7}KwFl /5YLz 0,, ?0Vt/ 9+/dh'QLga-E7Q4kJ F k<:MIENDB`pyzo-4.4.3/pyzo/resources/icons/bullet_yellow.png0000666000000000000000000000043712662716363020413 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8= Aq9d1d(Yn]qn<Τp VYuN$K>,z ]oCvUX&VytL2kуF'mw&B;6Y$ ZŽ=DUemӁfZ/Rm Cz/ tB ̛NJ a;rd N,XX'>Kry88 x__Y7ͫʹZȋ ==tajD_RA#(Uxf84ݹ9n2$b'C{M9Hj9,=ݫ R,2Z˾u0(fԘycRݵt#]畸ݿIENDB`pyzo-4.4.3/pyzo/resources/icons/cog.png0000666000000000000000000000100012662716363016264 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(UQMKa^ tT2 .EQС:EeQPR麖QRq%!O^wm!c333 To^%ۚ\Y|gz),{ b,پ7g| fʨ'TǂLGUM W0FDNC \`BASJk4$0SrI ZHd (cO:/B&69I#M:X61đ|hK5AJTȐ9Zf) 9+<OQ  **4KrHk?WDj L>c!i9BL%^0n9ֈs>i ֓i붻k>Gj ]IENDB`pyzo-4.4.3/pyzo/resources/icons/comments.png0000666000000000000000000000105512662716363017353 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍOPYML܌&.]$$κHA<–CXG((GIK^9Y:p V8y~h"+j).UP"K m:ruS( Qr?рOeb նUZ"[n_絖)TyXywe-~, BhX)7oȅ0i=] !B.m V}a~v$;Ř0!B.\]}ŕN.Z|*[\r> 0#B.8CQu]mKO= /QVISL`[w!4}\⦤ȠD$pb gAF&*⯂eM~yE'$]I8.H* <o\QIn |깕pL᥄}.Ȃ-.P'm"|_ MLOu#\> k@Pt]=?^$x./EB8DvԘ PґeTM6'^_z8"WcloシTp.KuewlΉoYܳpZ6(4C/4ʰ~QЇjc>LFޙ|؃$%ˎ t\;IENDB`pyzo-4.4.3/pyzo/resources/icons/comment_delete.png0000666000000000000000000000104412662716363020510 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˭I(aGƔ(E]&˅pqHl%98(L,%d cƒdyg̘}~yO@MQp^9p 'ґ`ovo ґsvO8wO%ׄ}&\Kw3oΥ-Mп"  >`#ao2s.oAUF o_H`xM'{Qb|3*qήye-H*DZN@ͯ% ]ՙX-Xh[ so1 Q? nj<%P%e@m8qS*9䊹hVmU0&yݭ>;r^Je0qN=1"Y!s yQЗP\L%Q;_ל:ӯTHX 9TIENDB`pyzo-4.4.3/pyzo/resources/icons/cross.png0000666000000000000000000000121712662716363016657 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<!IDAT8˕NQɉϠVȑB[( &^M6M| Dްҙv:官J-%Nd LB>w_3:*WrlNC/-좕B'{ u_a46ҽbߡE%D47;ٻƩ;8ˣ}>6[ӕS@*Z Qk>~͵hB\9uxZvYb J Cيٽ?BYvn&kft$,d9Zap\^ Y7 QJF 9=Q4 ؜Io SBpsI) Fv(@yՎވc\@ %% Z2h'@d(<|áaJuM@O⤁LGjd!X8Af 5J i K->w62ƾWH}:mP]XB0QX=ib_g=!Ftt…clrIENDB`pyzo-4.4.3/pyzo/resources/icons/cut.png0000666000000000000000000000121012662716363016312 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8c?.5Sˮz78ɀQe6LIt殷cq!z |v j/Xi@ %1|hl !|! Y#uUNw]˼ H3u t]E>k%IfoRD:0`~ | (r on3oG0!$V *[W0_-+ dW&2ZfMFVJpiF&B > Rg- ~ CmڴER ឫ p5ްy+21Kawh` #aZ񽞆TZoLѓ`"(?'ˎJvKކ|:G9[aw82 Jw f'ymzsӘTsw__ιIrIENDB`pyzo-4.4.3/pyzo/resources/icons/disk.png0000666000000000000000000000115412662716363016460 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT=]Uе;C`DD Ve h*B"6VV"c vba"NB ޙ{~jo|x&,K*o=]߻_v<}tu.R[Ҝ9Xsy|o~ |u_Bk6Yf7bQڴ^Й<T::֊Ǧ7Uݕuwٞ ,6}4'kٞF%*T⏿NHZ'}&NGۯ_鋾ﴌ*UD:tHُZkƦ/F%8A#хJpFQ$Q5:H0' QEDDK$e]t4IJDUS*!BxKOƈ5Hup9Q pҫj'ɬ4nx93W3j7˜oO$*yHI$H=m?!|nmIENDB`pyzo-4.4.3/pyzo/resources/icons/disk_multiple.png0000666000000000000000000000126312662716363020374 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<EIDAT8˥KTaLGA Jh%")% B jUH/h'1h!Vmč!m"PpՌ43}[XZg}<1pCcuq(!jcl*ڤ6bLx? eb3]Wm%t1Off{/kXLq gݘdV9PJ kq7s_kCJHDLZ^grj.s3i=ѐȬdV?6xQR f2 .8bsR!e/vT^Bу|GDWW!ԚIiv Q۶hiR@M,I)zïVQaё0tv(lSjr/eb)B0Fңw/o3A#67;ǀMŽ1rNG\jr#`ᠩ{Eg(=o@zo'NnIC &NՄ$c`TO Nkk>o a1Z,-+?+rw!/-FIENDB`pyzo-4.4.3/pyzo/resources/icons/drive.png0000666000000000000000000000053212662716363016636 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(ϥ=JA^{@3o0x O  dFSeнVPI׌}`XkD #5a5ϑɓym;w+CSf5|8#Z{Y_Ԇ`%ܦ^So>1JH$sABY"Dő fEIܐH`Nٿ]?0!HgZ/6LIENDB`pyzo-4.4.3/pyzo/resources/icons/error_add.png0000666000000000000000000000130612662716363017466 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<XIDAT8˕]HSazݐ ʾ#5rwEAP\eb.0&(#QhIͦqn馳>Z/x99ƲuG ˊ=)v{=ޅ{ HK #t\ckoV,N uK7(i7BF5% H3)!Bۍm&ca4$bi x& Bv KlQ|&XJpKWC.Bl,Y"f1я9fjһ]Hh^I*tu cAVrgoVP& *.DXAކbDLgl;f\R<զSlIgV)4o)yu:_\ VWAU2㞙VcG|If .ABkzf(_@Mq|G3A ډ[|L:{>RY}Y s6KO`o7Lw¨z%V(6ja;ʹ٢i+Ri~<\{P $D s' ywgRqIENDB`pyzo-4.4.3/pyzo/resources/icons/find.png0000666000000000000000000000122312662716363016443 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<%IDAT8c?%4ydÞ'/XfƦ"ʀGviQTo@5 )%k;o47Y%u ZbVTyE,Xݣo6ةm=x)k?"eKZ帹yLe? '?e}KjmѹÓ .dfay 6\Vo]Z9մ6\K/bĬ` *YBc~Z: ola_[S1qIbR/*.?##;L@ : ]_@$e;@1/x%|W4 |F=c 9WA12 5zaN?(8SSQEQBZ(`‚?֎nx% P`_IU P,aj(X@K*п9}ce&R12hZTֹU|&8cP罈`r'L_Ck_Lz 8T!•x$ m-`ruCI /n>e躾. r e@r.zJ G|e c ~GWVh),t' ު]h߻cF$mR#xt`j7^GaiO*j.7>\[JX:'L(6 pĆݯ -UP#2 ߒLjk¬a,#2NyV޸7F+Qp4f;,pD%dC3qmO sQ( ִs<3"tv3QȻ[./\'K.Rwn82AoHNZ@ o s@@2Ի/BR*IENDB`pyzo-4.4.3/pyzo/resources/icons/folder.png0000666000000000000000000000103112662716363016773 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8œA[EtQg7wALCWA0P017p2=:A3cb2'pܪ$Vme@ ݬ2OTO1W/`z8% ;O;9P#9B}^nO;Ǫo~ d~E dpɳ__jd\YDdQBPi#N{={{Lp\ahΣhݻo?]}{ p??pÃ&2Ʀv|*8GTJZ |u1-o]Yeڅif'sgNp1ӵ D0A¬{yԢ޾~VD@J +Z։啗9n@MLkZ !jȅ֯-\1eSs~J-0eUlʖ?]:fhB U swv‰[d@ʤp ZjU睼p L9z)dk`Z6jB{ hE?\F~0_XŻ{lc^-e@&mesmt)Pԛō˯ #H-i s9@r|jGaB9ɶAdDOX}H ?i!<))IENDB`pyzo-4.4.3/pyzo/resources/icons/help.png0000666000000000000000000000142212662716363016454 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥Oa[Ohjj8'r t,)53 j꜊o"&ʗ b"_DxU5Z{ۮg c#Z@'U4Ş6$S=:!O;4A*g; G hhh!9U@8rk2ه.s~ƜYG# W'?ElqNdkr5XpP˄: 1XcHP`%:I]`t)*\C8h3G ?~sA jMG]Njf}s!t&6QcBU 4P۶A3-nSXxm$vJ,hHVb v(y; 6*P(GtFOw>B1#C6K#G#O4P$ꃄsfV36 h7eo %cGY')O4˹@tmA,YiVtZ΢043$BH|=/_>8U}T!su4-WNV8 (wOo^uŕr#ɞF֮`!r pzyeHnVZԜგ[C*³2??\S +K;EСzrc%5*cb]3_槻i4|vQ @hԎdÅ"@IzSlՒ,Ѿ1AֆFޟXq AǏd'bβE.r`o+)ȶ6P)G!wGCqnfG SJy8ux8q8+g~jnBs14({^&xqXxXƘ0 `~Mqrd;;?ln]-G "8:Z &V#_M_G_8T-y/LZOr_wnYf .m[/-q_1rdߪr^LJ&KӼ~-<]0(Œ1n+iU ' 4`)7 r珁s?w ?{Y!IENDB`pyzo-4.4.3/pyzo/resources/icons/keyboard.png0000666000000000000000000000107212662716363017325 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8SMKQ=##FX[*H"E6Z(q%!t[[iRŕ AII CD1x>zt 73cp#f=B١hz*S9a0GJ5+me*LZA觮߉h6Gp -Ґ6< =Ay_< Fvќص"䚟)mMTz.LFQcBH8{)hXv-3G1&`'eIK17tVc8Bce!Nkf&y8~&?ν v%씩6vXѶi|ZM`6r+&6 SZ hXvJ ׌\ׯU?`JMٽνxa~tPIENDB`pyzo-4.4.3/pyzo/resources/icons/layout.png0000666000000000000000000000074012662716363017043 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<rIDAT;kTA'G8SAV+ĔN|V/`g!VڥBF`!( =93wЅE7IFĭ<|s 8\.@̰L.c I,?xXwrD~Y=YdLoua_[\0ⷳ'RTQW"NR{A(瀋8"nual'S-} ]Fl`~'S{ضaC? ]M'sC59QXnfi%A;BvN+I V{O_Hm{뚚`j^Sd){j IJ=UIENDB`pyzo-4.4.3/pyzo/resources/icons/magifier_zoom_out.png0000666000000000000000000000122112662716363021237 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<#IDAT8˥oHae^'", ZD(z^XًQFT(ˎgednn?Ά9w^6fv݊`E֋{xT?Nj^fZ&la σBIFx"Ȥ@%d0) LRԗ<i&g.;pDddfD<lY ֱ/hbRT7%`8*|1ZD؊uߘ2*\n: ym|nێ Z6U:!#ʭC;ێdwx}3֋HԬDIN?ɂTd 2OzsQ^.C1H"勻-)>El4:Dokn H(_L@qw4W4M.LXzn zG,iˏBlSopN㋷荔x 0[;Z[M[Ol6wYá_TXmXcBzXP@V}np t[c⚲|mEIENDB`pyzo-4.4.3/pyzo/resources/icons/magnifier.png0000666000000000000000000000114712662716363017471 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥kaǿ$F?P*BEpԭ 8TwݺM;TE!"AVD 1 &oysV$yg>jص֖9b C cxfz ̶3a Bhw#2IdST".N:0 ZcݓTOtE|CC>)qWDk6'w.zkJpQ0 toIO^> o?u(gOĵM?2gV\g6W'ghz"Qh}+FDjދ%7.mIs33\r(~J72ˡIENDB`pyzo-4.4.3/pyzo/resources/icons/magnifier_zoom_in.png0000666000000000000000000000125012662716363021216 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<:IDAT8˥[hqƇ]D]YhXtUXZjHc!$D LkXk[+ۚ}n$sjtI}/oɯ3kݦ%4(0~ _ <=qfz!xWAԮl4seUjbHk]Y͝˗:te%k~IIENDB`pyzo-4.4.3/pyzo/resources/icons/monitor.png0000666000000000000000000000114412662716363017214 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˝kTA]Լ`>4],EHQh i" &*,`iG3A%彙ݱxC]fw~33̌zn!Bxޣm]"֓.3886A ̬}ƌlnmq+EWϓeo\p4]TC4 X04 ʅt% }6q2=V}.vDžw}z᷶/ bPfvf`džY:LI@A JD(JΜ#w o^7=  BFh}RBdq\ֆX=)jCL5QB ZBP!AD6&:@ч,!dLP Z]ABP$)H ~bԜ/G&FdHВ:}soUkZq7+qj^a(d4'?/g:x#m;+ǽgvf}7->hgߞ1+8--&;T߶řlw^~p=,+2tD] "(669]f߅I@A JD(JwɆ^߹V7=  BFh}RBd4jcS1Xe }O$hɍ#N}ྦྷ3K?}bB8L A##=}-`xU6o_ qu]ђ>郖[6༮ akrԋoZ%Wλ1G_4\::]*,/_w4E+;}V IL}oG)g$[?/`/} -%nQP`IENDB`pyzo-4.4.3/pyzo/resources/icons/page_save.png0000666000000000000000000000140612662716363017460 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDATMVe78bDfai*QT@h妍P;]B@hQTP6Rd%XAT(T0sxM(a6/AV]-_cgNʞ| #[/?_8s|qmibGm)%deUץOl]z^té̦m "PmYܻٶ'׵s3@I JD(J.][s} ÷h&L8)me4ZkR6DE'.X?갳a+k>+4;uʚ/~{=ov:_ >rIqԎL*2V#nt+"֜LI)IFءjMFn VnaG5^n(#e?y?Wu\%2dzjnl-[6әF:;=2?9oՑUXU/F/ˮIENDB`pyzo-4.4.3/pyzo/resources/icons/page_white_copy.png0000666000000000000000000000046512662716363020700 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(uMn0E9GBꂫtP kBH$4]8&瑟J&ѨF%@ds3W$q&гݨyFz\MMIENDB`pyzo-4.4.3/pyzo/resources/icons/page_white_gear.png0000666000000000000000000000062212662716363020637 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<$IDAT(m=KB[#. 54PQC5h8BEI1aEZ=sn#ų^p iDA} h!$Uv.Xt YĤ'x@ӠD9"N49pd< )M+$EY5Y;_!oƐ\HIENDB`pyzo-4.4.3/pyzo/resources/icons/page_white_text.png0000666000000000000000000000052612662716363020710 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT1nSAǞ4A(ISp.KPp"QA7%1Ӣ;+Z######DWk=C?|Xj9昇SekMs9NNVG@kD)4hn.Q@nJ)1]:;1@ T:ti:I$fM-+g]^LvIENDB`pyzo-4.4.3/pyzo/resources/icons/paste_plain.png0000666000000000000000000000113512662716363020024 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍ?hSQ//&%h?IDH ., .\B7ApN.:H:b'KEjim޻|ɋi{n$ s私O FT*V:z;z-/A@Xz晱R$a]$1sq HXidgAĖP8! \n#<𴃧 '">`bPw |?2MZrw 3R$$ 8m6K}!E(8nt:HY_ !ׁ9[PV^ Pp xR1.; ,;ہ!(>$D;x&)BSDVgwxvu \yv*)DQ4q6;;33;0Ezdk7IENDB`pyzo-4.4.3/pyzo/resources/icons/plugin.png0000666000000000000000000000111712662716363017023 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˥=Hqf~\?,yoX{KC7PcM54EkS$8V*wJB%{s^1㪋g99IDp8ラ%w%I =@]mvbS?|b~?GժilT70p,@ۙܘ^Y3$ՙOƪew2ŷŨԼ}v!>!9s0־ 1bj ?`PybxX!f0#c~P ܃v|-+?e7x12ο+A h? @?(߅bUٳb2,mad,{w6V fjо}P4MUay?#c{S(fо;L6LY"60ldd̛vLcE?j \RamO(#cS'"4A8\t33Cp*u}ጌOgN(-a #AyaT?Pz& ~Q L#$qΪ "I Plt Z:sIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_application_refresh.png0000666000000000000000000000127212662716363022631 0ustar 00000000000000PNG  IHDRaIDATxڥS[HTQ];$>H, B^&I3#!FL~$K1џE(GNܹsO^Ï6fk,r}mK1f񌢀 Ƞ0~#Ӑz7z~r\}1HM}y_8 >92s X]ˌ\ՀkaQNB.<`#Q($EFdw`bF! !QUL.y=ٲ@ikE#..QFn+1#k'b}nfGk-kA|@t, DskrvǦy bCWxz%٭u|LJ E߈xB1z3 G5&E-xVJHAU'}̬T4Ň709DX; FƇnDB;li?hc1xLhÌgW{r")&:XXzYʼnXeU9^4H;n^QBo"`!`z4I'$eތ|t-Ͱl=> `/ ua))dYC/__9E^H'vb &4 IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_application_shell.png0000666000000000000000000000122212662716363022275 0ustar 00000000000000PNG  IHDRaYIDATxڥS[HA=/] zȂ$(UrzL,Kԋ76Ѳ@ BKc2 /7>ЋaΜ| + 㒬US1nVȔ9NsY,ӞUz.t{]m-Ӫ<6<9aQ?AY'cZњe ַ?/Rʹ!fɏEgqÑlAq`jfVyX$IſfchZ"'b"Jq34\v1V^姢.&X:zY2"ه[3a_B$:Ub@.AYX56$&$@$@,vZ-4)CkTe|.|#*IÁj52T*l*{6[Z7=&zA.S,!p@& 8H""qx/R\EJV9@*WpK.jHZ/Rفe L~K A?ii OǙ^'õ'$egTxGoqv2PrRO(I4@ IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_debug_continue.png0000666000000000000000000000066412662716363021606 0ustar 00000000000000PNG  IHDRasRGBgAMA a pHYsodIIDAT8Oc`RP6ݩ6%3pg1a }t91sC_D1yݭWXy_ig꙾Nu>hC&zT> 7d /:3Ͻ0sO>dcM[o8Xf_* Gs6Lv[߳RyRPB3SJM54$) "?ɶ]k&ts̳W >~!vbmæJIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_debug_next.png0000666000000000000000000000113112662716363020726 0ustar 00000000000000PNG  IHDRasRGBgAMA a pHYsodIDAT8Oc`4`lmS7D_7FO={W|A&P VgHXg{/4 Sx'rP H-! SAOw_~my_teY'Ͼ0sԀ,ni A _湉Kw %۲O?Yr%ȫp@9d Ha䢀G |a @l`@^P r*6dȆ\(|@ 7Ҡd\"l/XW?mqRh7ǚ;uZccW5RκO{bW5A7EV0xNt~v ev뵛* m"pH0M\?o}^^pn߶e15|O9D q>.)-gï>}s? ' Y.`3z[yIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_debug_quit.png0000666000000000000000000000114112662716363020733 0ustar 00000000000000PNG  IHDRasRGBgAMA a pHYsodIDAT8Ok@LJ +Z7ĨuTW&Y;ݬY[TP_ )[6k.вXq<=72ĉɰf.PGpZ (\%Ir#qt y\34V$n@oLN =k"і"ЗW+\w|2~Blx$L-.\'g6̴b M)@)/@Pr05[ C/u}m.<=9t C%.brذh 58eF㍃ݮ,q, uJ+{oO]oЕGE^|Xkj;_*[4n}قVs:Gָwc…IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_debug_return.png0000666000000000000000000000101712662716363021272 0ustar 00000000000000PNG  IHDRasRGBgAMA a pHYsodIDAT8Oc`7HY*l{c]^8h&f/=ic]˶5!,woh=VٞK?O>dmjxk_pi'~n}]8 _rs/ M.=vDEcs/LX|mDW<O;?`;-?t@EAsOb6`6`O='ށhn<ߩlVGhs[k6S{3Z\.pn߶e1ND0IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_debug_step.png0000666000000000000000000000077412662716363020737 0ustar 00000000000000PNG  IHDRasRGBgAMA a pHYsodIDAT8Oc`pcsnׄO?e[]d :б[Ͻ03O:H$Y5Ķǚ:?Ͼ0sOXk6i?dm˯z^)](sٞbmH!Ȑ]S7D"Ⰵ`K^I ʢ3K.=v^/K?dpT*&R  ev뵛*-ۖ?zEA` &gN>| 85L>ۿ; 9tyOqnl=rէo<{Dlê ¦~IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_disk_as.png0000666000000000000000000000144112662716363020223 0ustar 00000000000000PNG  IHDRaIDATxmm,asG[lͻlFS-*Tڊ(^,)_zE"QZa/vqo k<{땅5,̓l53pȣS25h&]*lTgAjaiR 1`jVÔECcSjei׿l^S1}nB7;qX0gp+4. DYVԺgfw)gtʑ uQi(/b"vӑfn=KJ'T¬ V)##pAv'3P )1.@2(:]MbNO=QGH2clt$@@#RS3" < (yKT#/ʈ@]I*Tr~RAzL.\ ⽑^uw0ha\=(-@`%")?zK+~&Ѩ; 'G12-zEW šJh:xnx#dWYIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_filter.png0000666000000000000000000000116512662716363020076 0ustar 00000000000000PNG  IHDRasBIT|d pHYs:tEXtSoftwarewww.inkscape.org<IDAT8QϝhԀ"$x; e7.]pN[6\SRqMgzzOx<>}w8 NGx< s<ٶ}cm;DjHr ,`!-yTzJy"* _cf=˲w]uݝEQ>b.ty/5z/SY2d(([qƃ|}[Vr9lƗ`N>vS|^|>_SJC$QYo!"=@x]-˭ysN ø[oZ^Z-VӴG}\Tx<~s='i]?d>h4jG"i?7' B@!GC D$O\ȩ ?i+8l6d8]G+m UUPJ@X7A>ǻpWIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_folder_hg.png0000666000000000000000000000115212662716363020536 0ustar 00000000000000PNG  IHDRa1IDATx͓MhA&ifӂ^DB6zIERXA*Q (^z 'Q"ZV/ 5m R)~d_d7 ċx7of8s_eâwI7i جf Ql83`Fb99je48}ꊼ|۞7q p?^6+o@Wyw2 l^%33#bEֽOyiPP ٖuL28{[-@<9lT_µ&TpjU`oA&n 0ڡl:4dr&h9=A/j}4z3S܎'F-g&^{] 84)lzkQzY.03Q}5tt7x}3ȍ Z8ĥciC(,I)AEf4` ;__ݶ?(ߦx%)=̚915K xvu |655ݥ(0Hpp?&?VJ8IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_folder_parent.png0000666000000000000000000000123312662716363021431 0ustar 00000000000000PNG  IHDRasRGBgAMA a cHRMz&u0`:pQ<IDAT8OoHSQY1Z1 $aA(4 hX}0A B#..p$~{kZx8p\LUSȸˊ@ԼĻ#UXT8̴FsW{Cr 4ڐ ?$쓊"J]KivFP$!LR-%RX;N,$/f DkIF_HE -g]#I=KsKKtA0CxmfIByrW͸K3$eO_AfW&+ x^䶘-폻 :QEIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_folder_svn.png0000666000000000000000000000117112662716363020747 0ustar 00000000000000PNG  IHDRa@IDATx͓_hPƿtk֥-(CӢvd qS(c"(Ad/{e*>8 mZCj˘;ii7i+>/>腐s9M80hl8/(r鏳-%ߔ͸ۦ4#b먕8]gbM]@" T_U2a` pח۞;ɗ(*`z|m L^4DEcK;~~APsߐ$P`#Lgz8ۭApEE3!Ak}hVb!P /~?#ObNЀ3x6xk!_5S g+ $g2~e`)ۏ ~⿋:CP1#:W?`8wRzN7V`x3PO`X aϞ6 gSu;N?~1p1t/{p-ioa43CD2?{F;pZ mc?o 7ex72jKIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_overlay_hg.png0000666000000000000000000000020212662716363020737 0ustar 00000000000000PNG  IHDRaIIDATxcd00j̀q)`k #H3P 5!X B6`{hP @&I.7cIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_overlay_link.png0000666000000000000000000000061512662716363021306 0ustar 00000000000000PNG  IHDRaTIDATx1kp_HiK-. .C䤃nFK A'&M"&PM\N]ݏ{ pPl[$I8n{ $\E> T0y$ cc RijD"ݠ둗z|rt:B4a6,BXD\l6>yAEt:R)N'GF#Ȳz UU"vM^ MX,+DAT"?3sgd`b`;ÿ~y"n0ף~[3deW >cgeg ob-/`Q|>̱ߏ ~ r15ٳ8NbuԬ_oüf_Ӕ!IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_overlay_svn.png0000666000000000000000000000020412662716363021151 0ustar 00000000000000PNG  IHDRaKIDATxcd00j _24%a03b5$ A8 R#~lQ 4$jNfIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_overlay_thumbnail.png0000666000000000000000000000022112662716363022325 0ustar 00000000000000PNG  IHDRaXIDATxcd. P𾌁ҬUֽ$hb 0F0@N`22t1a)cDDjn1`Ԁj*{IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_page_delete_all.png0000666000000000000000000000141212662716363021672 0ustar 00000000000000PNG  IHDRaIDATxڍILQo:PE Š%Ń"hpAhL( F E6 &B^F"օVhm-T &N23,ޛ7 ON@|xx2g/X5=5GXec'e)?+^^9 40Ͻ0MMsW P]$64PdDx.(Rz07cCgd^KSYJ@L;Rө5{1?ڮEaPk!%nVl %vw;CY pYL- -Wޅy qpLЛ}e%9HnAǂـ2hU`IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_page_white.png0000666000000000000000000000054212662716363020723 0ustar 00000000000000PNG  IHDRa)IDATxڝ=n0m4)w% 8K$ZZZ"U6%.?2+ml4|)$_-7~,˗Rc}ߋ<ϳ4Mt{nFHQm+(UU,$I@n ARjNb0 3 9[QYǀf4M3*AOm›@?ka@p;`<(`8sUo$s90;mOJ@74mO3V\)yTIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_page_white_dirty.png0000666000000000000000000000051012662716363022131 0ustar 00000000000000PNG  IHDRaIDATxڝ_n mm_{/{ܟ3M;zjJP]Aitda5=o*\dZc_sE84 7]b{Z -9la4M bZWj0>pYmrLrw]WzH}0n>$W۸Aj{荼Ƙ j&]_~L?+3*$pgIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_page_white_py.png0000666000000000000000000000063412662716363021435 0ustar 00000000000000PNG  IHDRacIDATxڍN@B6k6^,l| ,| ; Z hd v>dy9;Yٕ?99\gTP+P2Y 'TH$Ƀ,ˆ$IY < <_4LUU,ˈm$cEnf=<2ee0Aȇ\8kd4MaJ a%@~X8cqOeF? Dn7Al5kj܌6y.#~".#[4|5Mh'D~(?jv״ Fǹ$.7/ `RIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_page_white_pyx.png0000666000000000000000000000063712662716363021630 0ustar 00000000000000PNG  IHDRafIDATxڍN@B6k6^,l| ,| ; Z hd v>D@x,C3{3?)d+ː*ߐ5u;M{UUMEQLY% 0}Bp^QYQDu]<ω8$Iqy^QZ.4OB(LUU1m۬(u`3llA,7%MFP^{^9@8AA]%?k70 9D躾.7N2!F죐o @ll[6 շEl ;pp`LE@r;*]gM/_zС\IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_plugin_refresh.png0000666000000000000000000000136412662716363021626 0ustar 00000000000000PNG  IHDRaIDATxڥKSQǿwyp75ajf/$^ % VDE?!z(HiCuZ-Lr{Q؛~;w0#V>%۾y#pi@/R-![)1L*z[[\jQ̺Z˨(!$A$,,fFG{jl-VSK\-s#eS 9b1!EIӃ7/lT"D"*^UsiqWufISϷ(dҁeY8gAeq+bq I$cj?NO75rz|PϋX[ci @F)YK;z*.L0f-yɲSBT#A "byx-8'A<*c ҂@(;#ۀz[-90{vc.9rH4Ri`w ɾĺwi(x'{ 3YW<ě⑚d}p̎wj:/=g4Z~PSOm)@ka;0yqϑX gbnoa4m,lv/`=8IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_run_file.png0000666000000000000000000000114412662716363020411 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕KQ(RJ}B.FSkJ7-"RQUPFQHP4UĂTJqBJB^s֯ h1so0|ܠTXPD"o<?J$(9z,T !I|><vl6 S-!#WJ P(zhsB~8s-%8@w%jFI{Hj-8Pb\kD="#XrѿK:-?JǗs+zڍ>viQ|,;-=|z;֚1s[x w߱DZeoqoq5fnC}RX <-A.ӷQ5W @Y2dU,OiI1v M(5y@( lYPR49T}vsIENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_run_file_script.png0000666000000000000000000000111512662716363021773 0ustar 00000000000000PNG  IHDRaIDATxڍkQǿ$HºURТ1؂Rz xE!顐)rT/hVc!V[)f[%}t`μQА.R 7)ʆ=bYcMŠ"cd2T*5H@8Z)s۶FQTdDـs p]D"u+et"MqYy^B0@?M K  4ր* o \aW 7C7[@ǾX|%l7\<،HuoN?YBq~`]i 9~Cp( V?a_Ѷ+wpr7>[CY0S'\T0N}ܳ%-B6\3JK'"!,̖`j vxm=p MmsXC8v /gK(WsUɀ^iZ -OE7^Y߇ct\N4ֹe1j^p͝2] C8,IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_run_lines.png0000666000000000000000000000112312662716363020601 0ustar 00000000000000PNG  IHDRaIDATx}kQǿ $!n"kLуXl AA CC/ !Gs)%zSE[rjbk5ݷyM7$f|;Hh!2{de2 Hi0ɲ$'ʴml6NBJir*,>42`&"οrL2/m/lWV-Fgh{p]vpWƕpu:UU:)u p8,=%wqo !E (BD3:Lа?[Ua:dNus֗Q6BG1{ u.6TQZykf[w_>"cb9,ddJq6%5 % vaǫsL[Z6.?8^0l =Olˋ(mя+z z})@p'=<0uuX05Ѝ8 :z̕[l *hve|.8 IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_run_mainfile.png0000666000000000000000000000134412662716363021260 0ustar 00000000000000PNG  IHDRaIDATx}_HSQǿwd-2I:2H{D"0 5衈(_zPh!ZiY-Ԅvν{s:A;~p>:;qTXR!np\ Qe  PWQQQ 2nwOzSd仚QQQaXt:˗!:ϻFKݳGi0l6kUsss]YY 9wE*LyfDB~@<ϯ*^Ϥ;]\m.=C2oRB$IwmZWj&ߺHmgpbR!ǭ4޶_..H}l4-d5ؘ#Z!Տלy ̢8v^/*>*ez= آvVdöi?#+4"]iɡid*w,x?zk9^P$ y "ilS~7Z% Z>@~MD$: )ou&J8H?6Lxa60Apo1့aH'OE,%$Ƥ`1`hb~zѺLt\,F7`ߒOh:>7.-Bu^znE6r. ·ھO8IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_run_mainfile_script.png0000666000000000000000000000135212662716363022643 0ustar 00000000000000PNG  IHDRaIDATx}]HQ3+jm+ZڇX-eH`XaE`jCQ"> ba>haI>hB̝ ځ?r?KHO̢x<]!uizPQF,2kk"!a@Έ{ʂ˜rW A "l6 nwmYY>sh{ lf9YXVkY555Ֆj:)QQd0 σ&Bx_Q E;a!uӾy0|CHKv2@[RiL4 QT!dIb4v^.:FY@BU`mzkn΄P޼zY21*Te⏃aՊf8oЇ14G)z@Y+PePشGځDu07`u@ƻTd?AY'/(?+ʬ(d>Bt~D%vV'z>&ـc]„V t!mL~:y`A3'}e8.pάx Dcۙ 94$.5ۆ©SpoL8§IENDB`pyzo-4.4.3/pyzo/resources/icons/pyzo_star2.png0000666000000000000000000000114412662716363017641 0ustar 00000000000000PNG  IHDRasRGBgAMA a pHYsodIDAT8O͒_HSQPQ>G` DI"H=B9G?RTk FVSD{pQwnͭѽMo*H3زda^P ڳ0%?Me5z2~F]Vp8>PJNjnM_5`wlX_L6|Q{0b#`e-ȇ`9t٧~E{HJ^@y1{b7)k8 uQMd۰(>}&6xh B]8V+65 [vƙoBW<3SV%TZ& &ɫH|*HR9@Mr*8fh/p?.k)h@F|E$ʰ %`Wcd`x=*s*6%kG33 H$~oLyŢB~^O;u}ϩ(Ǫ`0tw5MDVZ/#jՏiIENDB`pyzo-4.4.3/pyzo/resources/icons/README.md0000666000000000000000000000030612734232226016264 0ustar 00000000000000These icons are all from the famfamfam freely available icon set (http://www.famfamfam.com/), except the ones with the "pyzo_" prefix, which are created by us (usually based on an existing icon). pyzo-4.4.3/pyzo/resources/icons/report.png0000666000000000000000000000121112662716363017033 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8ˍSkQAC(T= BPlEɫJREog񣂕BoP c iiZ$ \ȶIENDB`pyzo-4.4.3/pyzo/resources/icons/script.png0000666000000000000000000000135412662716363017034 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<~IDAT1lTe߻^ w@J `4:Mٸ0:b4qb6QtpȊ\ۻB)\E|ҥE[v x]lkX]ʋg]`"ݣ|67ك'~{}׭Om'+5]~ f-OfG̎>促 >Х +?58]];> ,ƠFU+*AF SZ+΢)b,HAURk|q=\ίOfGE* S)<Ⱥ]v"@N3ӑy*B ADDB)tM#&C WUQYTPdHDtz Q I7?R BNrWG@avlnR~s6lȥ/1Yf{oKi)w~{a)\4m^{})֦S}템@wxa\hs:ayBnm:Ofu걱ɒՕ0{׍jvw}yI Ldelu?{;,EIENDB`pyzo-4.4.3/pyzo/resources/icons/star.png0000666000000000000000000000123612662716363016500 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<0IDAT8˕KQƟ{3ΌchT~4ha"mBUh?ЪEmZlQ`% uc͇|BMpts`E7!% 0OW@k2cc<'&Wx}g@qqj$9$5bA^U<|,gedSt?8:;_DBRF_7;$p@C"l-h3-( aqBتU~jLfp l-$ILRpRh׫ $`eC!nN&]6rUХHjDQ BN@ЪU[)vlaRC,= ʀ@ e^MXs;vO(KqF  a4J$υhõ^Oo*΂8{?ξ_ܪ(^䯖&쇻۝Fs ")IENDB`pyzo-4.4.3/pyzo/resources/icons/style.png0000666000000000000000000000145512662716363016672 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8c?%6.OڪG;x7YN˗^P h+;汧}̻kda$hR F 7'.ۋr>hy}Liap%qϿfۄ[/Yk6ߚc{I&wv:YzmBseՒ/,~@,]rgiwJ<,w0ooSf߽,p6#fmw/muL^pH/{Kz%t"QϽI3\̘u&{)uhkṖU/,бi_oK@"{{n\vI'.y_,l-X.w/l1й1O^̜}|܇.l:"[sy3z~:?]$d߉g>a hf_Z0'`₞G]*o|Ћ>F` )voWB>Y=(mmp6c>wwoϝ>w]<$grW͍@߫k?(0cQ:IENDB`pyzo-4.4.3/pyzo/resources/icons/sum.png0000666000000000000000000000044112662716363016330 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(cπ2ѡ3GrÄ)ɘ;CAA s XPP?FJ-ﮔבmDgΜD1 \ukf@E40 вn6O3[_NMջNaŕW3ݽ)L랞W`n3[>A·V)j cǂ|&@TN>|`  & 2HIENDB`pyzo-4.4.3/pyzo/resources/icons/text_indent_remove.png0000666000000000000000000000053712662716363021434 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8c?%B@,0Fyy1 ǿ~ w^F>FJÀӺݕj!;66:̙3pk*Hac;HfFλb4%M%HaXy]SRc-7ǿى`/xzz^O0o=|PׁVgW`&CȱĂ|&@TN>|` :);IENDB`pyzo-4.4.3/pyzo/resources/icons/text_padding_right.png0000666000000000000000000000041712662716363021376 0ustar 00000000000000PNG  IHDR7gAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT(cπ01PD7ۥvfd!&«@b#L?>"B,eĦI$W3_XLl= `<'ّI`W0|yFcj\!'r/9J*5R0FC߹gM80L\y}|BF(7dT%H NX,\.hgɟuq.- x7dǃ@ x<<;ц;arMK|n7~?,umgcrC> dB}>8(a| D"=#L `\. ^Gd2hp:BCjJQ07Vв?P vIENDB`pyzo-4.4.3/pyzo/resources/icons/wrench.png0000666000000000000000000000114212662716363017011 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕oQ_[c+W.]хi5  g mJ[Fq#_)qո;,@{;upLTʙH$(Z XMɤni"\nt:}yD 0 \ ږIU4(r HMk̈_4_ziy'"[ n1rM_A`b=$Ik_p-qS~=li~3Bv"qZAԧ̸r[G]<&e!'ڸ67 yq$OX!=_~1Gs~EZQx&qWK3!ޤunkzGrjQnIENDB`pyzo-4.4.3/pyzo/resources/icons/wrench_orange.png0000666000000000000000000000111012662716363020337 0ustar 00000000000000PNG  IHDRagAMA7tEXtSoftwareAdobe ImageReadyqe<IDAT8˕kSQ8H)N]uA,wjazcbb6& Qcn^b4b2e l xW }$9Xt:$n"0T8J)N!h4ʉF|p8|2X,f[͵`0S~OZ% } iz=nHXt:4 "BrLRtv_T'-lu&f_uRl6 u]e 5~lޢnz{ejjUiw|}?$&x r|TN%*4Ç l;'T^ٗG <"pFୌw!yhݔeE6  ܙצ PJ:C\DW^,<<4 A ѹ]s6ޑIENDB`pyzo-4.4.3/pyzo/resources/images/0000777000000000000000000000000013166630336015144 5ustar 00000000000000pyzo-4.4.3/pyzo/resources/images/pyzo_editor.png0000666000000000000000000015016712662716363020240 0ustar 00000000000000PNG  IHDR #! pHYs   IDATxwt\W~/U@!s@QR+wm>۳c{Ϟfz^:n;)F &$"BzP (jJ—,u_eXweB,wְ5 I0:+W%;Pv|[ְ52AoKn${6 w k:ְ5| @H@/TLO5ְ5|\!  #7' ^ְ5| %98r`6S d=Qc25a 1H gwHL2AM֐Zpְ5| $Sr BBAJrװ5Auꂿ?X̃&(  ૈ JcQ*q*Mn#|4PeCQU^EЮqU4]0!,Tq,_U5TMCU5\Ʋ,,TCayif`e",VQTlcxX׻M[ɠr²`mw<-8/v.K'3²Ll*2T-i:mcYf1M7<};v'Qe~, XO$iսf(M-aEY ضM*ZqP(ȪK^XlT /S OErNWf8FӴW $53LODپΊ8p˲) }4MA>twehpy˶>UĮlY937 <=rx@T*Eff&in0LDu,0 LӤT*q_q)Rx]h;E1<9FgϦ5V5:7!$ϣi|XEoo/Xl 8y$SSS$ɚ )%PKߡk&[:mM N(^/rE{0ʝ([wαS}x=NEj :;Q5uɾL&!MM w5`}rh(/z%6wʝT*q)BLݻw.[}98Ssر .\@[[Gz(De^c[`qW) lذ)eYYϕm~m8$$WRm󶅦n|2^6ӔzJ"_;]<0 0J)kB۶P|>Wfvv4ٴi#FjVURlnbjrȇ~&%~ \H5Y.ϢGI9?4P(D>' iR.H)q]f>Of}ӨZ(n0jK,_D*WgՎz8Kgu\4̽n@RIާum]8\4 ,ۦ'''] g!Lƕ+W0M&X{?s}׬E',,Ea``N\q@P* 4ټy39Rٲ]C<(HBTGضEGg'm8.B)ˁexؼy3۷oܹ󌏏z$oB9i|Eヨl]>tv,h,JR)0T6I)4P(j?MrgrrviR%Ԉ~SoVᬔ#i 7"Cϗ04eiYrD"РJғ3h0>]GWb(DQ&&&6rڀW*͢(Jm{J:X,ī סTQ=%5XQcu-Uޜp]fnw]!ʂDQ\EQ4M [nض]6ﹸeڿsEĶ̲ ضplDZ}ƶ-ױ+SĪ98ѣGinJTWm*{Y_9åR'wo]yӯ9?_|v &( v]NX+?5PNI]gΝ5ڑ吾rTU]kNڪW5ɮ斍q6σG]x멂Um=g f㬞j5 0rBkYRR1mi$R,8 LwOp}-<Xi[ezFP,榧25ɏIWY5|Dd25EU(㩙^^zPcŖ0=^ MAY@D#ǖZUTZ.ۼWyE&=]*WTjXAWPbcg&矯+'emͩn1ͱY6k!S)mq& X}Rcf|p m1?j]Rb3 %ѕnCkf9DGe||w"s_<9w狼Rj4hm%P:0@rQ4]NC7mtJE X4Bkـ)|.0*]=ĴclO"5HF"\oS;7~pUіKBg͉+ysJ~9aV~j0T^7Ȗ՗Su\׭u2wθU`spn`\aIbChDߠ;xl#36pSl[ #s|]}.YSe}Zh%l1\Ԯ&t\ye'7a\xckK';:utA΄=zf$$Lr!TEl߽3[/` 7s&xB8J AtKV:)^827b_r=Z\z,{7w{'5593 EzhO~en.X`͗5' z;ĥKp1j4I絳S6;oǗ,=rL?MsEAط{#WϼI[^?[`*hB֭[-HI81urdY֯_tu07+o !COk ˴Fx4TOs"׊I2LM\MM(Inf'JTO(A+Q1*D4BT" C1|C!=/Df!*Q3CU{B 4*/8C<_0)8ǩ!;:W',?>zeߣ/ٟ1knѷ91x O+|G%viO;~?>{89/{zuoiƇ̣Mcr9'};h}O7 ڛl{ vAg8iμ6cLϦ|\>zt0lU.iK$Ͷ]A:6vfQgM)VTc6K/^V33JWs/-Uv٧`Ϊ4Qs<&|_]Su2"_}3Q~|>CC3ųy?omò\if&&i% 31Ji&&f퓟H] />y/dwTV5 B>Ť]sy(wNļ:OgW\%I޷Ͽ-4zW4YH)riaUsj̟Dݠ Nq>|/rDsxk'fٵ e';~GHWFOkIX0qdu.yfWZum2߾dS{/\[pDnyN}s/BwTUYoqԃDŞ,q[\:68 q;5Ą%drpXGCrq(M{{/f]:x+z%5Nz;oswpe XS3%N4W1(k9.cc;4CCQFa:uUL\P,ld 5ǃ7Ы!ncDA|+Fƒ- ,#6 qrCx[HFj斕4OHn&Q[[6e8SNss:u%ea% 4_hl.SW_}ISr𳝬kvq;nS1̛V-,& JLd,E"5/zɠ ?*_{Нh Aq@ .fvjSK ̶o2hC2_*0 3V~+tn['8yק3wBO3~w$kBs艧iG^ҷhv-~i9\*u@ڜ}L ü^MZnciōpEd)}Xe|ʐR"MW^J3) P*^afH6fوkhBBP͙$F)%kupdZ )cU'j @ZV#C/WX;kB]\6F/?Xy$ڻXU Qazvů(y-D""Ȣ- &h^2R$[|㩘khpS6T-%ZS v 'FKe@f+Go6ޛ[#G_UwFg`6m42KʡH`;sqh#e6_7p㾍-hHkCjMM=Z@3t;4]6P-42=lq_rڬVkee=ljnG.:G.Әn8>W),nAB 6{0 #L']﯎U,#݈f=p _ [-n`B ں8aAAJt,Jgcϭ#>5HWzy>[+M(lf9a_(>l~ϻ qu.wсS9DeHlT+%^C7H w~n$˗x<+Vry>>kQQϻf$o>M ]t`xJA  ){wQw IDATkNb- _EJ)s\u$ᮟqjj.FgJ٘䩻mS &ۤry}3\k1Te[[[ ~N6^W}u%`]$=ȼû>XL,WR\JLMZteCpqLRd%XF[)alL˭,)F`a2w˭ES>.ȼz9w}5:hXxGuY\ϗ떊xlDII[LOrv0EfnmqHǞ~|zcYҚ0 YMnƸK'2&Զ-?3Mvʃ@| 넔׋.…]-^-i.+etm| NM шsf7wrHo w=wzxc|>Lqe"@mqn˔7Y#1%Bé,_e6L3E !T(X7Vę}ۄ(aDh~\U 3}#t=NFYOsr Z%Xm퐿{7Z nB>Pq*^5\'d|U_F"HNe1$)/TF䦻_7:X~ ?*mN,ȼ&SSO&= VILGb-Raa${Fw^]J&e9w%%UȧSϚjtwEW BG36mىy#t7at>GK?A[w3 \=W(Z'IA),Z| 3_"mz^O6`ï=BǮ/7Zz kh W/t6r Vih`Ă-F@ȟ[ ھ$P[m-7]$*Kw tz@( ״GC?斧!=tQʹV\u%MaI'>cHwQB'5z^f&5G,эMI5IvfF$u.> KL'+Iy@XȺϪ3UB@rEI]6))8ՇB[í!qekyrۅ3HZYCdqVK)3?+Ip_WD%sx)e{o"hœ޻d{_~݇LGg|K`Iac93Ԡ)6]r+w Ev4MN+y/(ݗ5Y{V9 5װr>9%AM߸\VU@Q!ܣޠg'-- U|G@s  -N>v.7)"QaS[R.^('5vp~0u 6#C\)oocC܋!._aT$[ؒc캅BIOWXYz﹏pE% KsBi^T^Y, &eIsd<|3@d6Cl5,a$]/wN7(씘? s6μ=6p癈9K{"to[Qk1Sy]o]6BJYGE{tmph3wmDfɍқ2˟:O)sv96nj'\l-QT! !`kWљ4_dݢL.eVhسPRR,'>rkX<ׯMY-55͒+~;ރ9}7dDe.OPxD!HyT?~M3 s$+dp: oU EC OYH=rEJ$37G_ n$жuໍOoPSO>Mi ?p7GmljC"N2y9a"7Z6قVtuBJŽhr.yUgDʪB,Y)o%f1A'\0E>zxMdbVƮ9UEGSU,ECR۲'qxb2%+r`?}J7n% zӒodH 6"Dyw\K8r[XYF~nwnGpu/FaX(B g KFc%qqqlwCLV0BU*ΙDctM'HR,Ɉ0PMGmK;~}EiM9\l,Y|okx\M[6Μ`P?C\vd-yYNhVLOQ9EQqk~ A{/z'?*((8j17#/q$)& NS(؎떿_M-?P%ꡫ9Qpm^yǪo*4-$ULf , mzkAi 8 "TzbLd-LWarr, ȧӘvBVzi)۽:ḟB?IJ0xzQcXv 3;9ä_%bX&k8 fP6$eIl܊3fri" ]#0`hSTp R 3a:!0="\uNM|0497鉆X)."Pu!ΐ5KLr U$!Ң\I 61gi<OoaC{| :,\pkY,@ca(:0FM>-ٲ| ɤ!VٲOǒy>|B}=OE Fx.Mh -3ve(ذc?w Z|3cۆGQ޸_Hق5l$gKDlvks( j%{x#j m%SBh+z2D83P1,{>o]'į h ?GgJI)%:w&e o"ꑬK ?H$`8)m5VT_󋇷@>ǯi&;鈆QuO=\ 8ƢhDiϑsT9ԂB_P}4mG?E;_Gb1*?crPyXiO r*ZJ(TRL !\T*ka;Z%GJ)k%?X`N\K64~,|j}ՠJ׋wN!a'(? ❟mUW!/XHz3=  Rc\!'E!h&ZwV$޴Ak= Dްy;v´=A:[gZWl0bt.Z[hn ѱpvd{7@r3 h]Db>ъKV|^A:;΄=HJI1;$ַ0!H.桽IV7u ۨi^RBqwAȣTy(Y,(+($)cD #)6.-$8)]j};$ \,/stWʩX[j-;vjZMiFmtmItT21+}iB4W΍7ވ<)Bg=uo5 -VDTJ;rQ&(bU'pș }_j+|)BV}~x ld|V>rÕVp9;ե7Zj͋n8Yr.}WY ;׶{x/iML\R?r*%YD\"S/܀%MwlbwHp$4.Q:±p"ıK ν%5|u /~_hin/Tx/,Z-ĢW¯]d-fH:1NKlm! &A壡*9%fMO(Jԧ]d2 $H2xB9ӱNVҕ\XkfmҜJ\&Mt"D}*3#x낏z7X*1;3WYX|bfUFpYGs@ϒ>"J\ D<☤3&De69=}.GONF*#["(Hrhf @8BXwHeȚP\.<3sy $\J,h6ex ;jѮfr,Q?oz܎O Rb;.+Q.ds ;@䕷)8`'P)rKc|v!3Uc;>byX\FNhWUwcwyT|Bbfhji&D*m򡉆 #1voGRLO3x(Nn83W>iAUjRWn{?;Mn:~eb,/ 9q Gd+DHl1;5piU^W's w>vM$lk)9ӟ+F>&_;cj"-)\k1ҡX#[zәN_&Ͻ1}ZPeKofֹ4mP:a<^|}[ޕl }yer`}q IDAT&>F"X7_3#(=rS?ss?_2nD";oȚf0éc30#y|k (hm E4;0$CЙ 6ElXhW46oJt~>![3_ F^q/'9z >cQĈ\>c<6n.] A<z L#}=@8t;VR$ o_xb~WԴ!`}$B̉HנTxr56U⾕_$j{N"iLZ`l謼Q2> b˕hQgp 6 w<9]k!O1F~"܇/4Q~$:-f h_+ BN6Ei vb"TPv= ؒt@ /th<ڇb-QT`$HPU jڢA^⃕$wM, Ո To+!id'扷k!h"huvJkpO .(f Jz*(zdV/Rf@ٷ/F1Bއ"aձ=ډfnѩ, BG\_\O5c钻L^7t@ uZ FGPHɍl֙mb"tmM6Z;<@[WE{&g)%,SYG"XXt X`SxVǷ1zt`1KDx}Mv:YJ9'po[6Jіcɶ%vJ39}&!KN-G2^,}4U5otwcmz\;ŪTS<37{o[[ vf\m Q,l;Q_Syo,k5˚y˚X`ɩsTIE %Mqǻў8_knŮڔ4 Bz6P,󮈝z$FP;Pl{n?V >N[l#(PIz"$P'7/BXai謝 KZy*WǹZp $XDw 0:;X6qBngLҷ~7*|w[Cpz,dN}ڗ>"*y|D|[[$`Mɽ!ǒY | "% NRx +*NW%K#wdCg~@x5n _S Qjp} 4&pJlʔ줴P fGj5EB,6X*@*w$xc 5}aں鎾ZhQӆOTQ=E>pЮK\4ڧ03T@ŕm(6D'r!#$/G\,bV({ ɨlJ Z!W';_Cx FW:b})k9)r1$8Uk!zٔ%R49 ab,rpe|â=bRRH& jL_Q; "1#Zhd!0TUha,^dGػ{ = sy眹rן{Q`j*txZhN56 {6pT]j-yXgI 3@/W2,.gw/'9g2k_aTg>d ̜ů=MI6ͅG nNɴCå7 aӹa8eլ9=g xs5 xo#H)q=* Ѡ X36z*Ƈ?MƯ6 dܽOݬO`@h0{מ? բOCOybǼX1+^K -2"+nY8f$X4N#gƑV󡃒r9Ǥ/QLrVӈt\ĵB\&Ѿ/N)F-Ջxy8t'mϿp[W3gK)9qtzh7 R2si -rqԁ%,eaq\N:2m? <~HлW;o4^g$AJmXe7IJ#L+Vᐛ›H7TFA OtT}w޾`}n㯢V FAuVJP 6>%6<^I#L}?_wiX@|3_ E-; sM\/tvPpݯ䘵5"\-7}RΞv],]U)8w~]ՆY`t|iňjlB#Ku2L'Ec :H@Gqm Iʹ,yG!].!K\57@GtVglpK UJiKQףU69DN*H(B2;Ŏ$c"S]`:[T22Qu]jlەhf΄I>_&)ό"[;NH _>pLLgPqO)#S c$C:}W39ƤSkR+WnqSAo513ؽ.koPXzsOCuzpLv-ܨk _v[NFBg%@ ź (©52MPKS<> S&ѽpqJ^~e?.$<Ņ2J;m8R13민ť\ <^ׁU+3r|* 붲{(ȷ.|o=ΕJk}+عs+}M d[xk@>w=GڕUGۅ]ۯ.2Ih3~"'2 V Y;P!ܙ=3y0'z69%/o36EƯ<{ 0}$oRfmEjv =ĹSmpuW*γ1,_TEGSUECR v|O'rԬ/ɮ]G֙~^C[:J6APeJb}4"JUrU-/VR.(JtDgj x6U!Pi@ DL:eJ3! YOɺ2v$-"'Ge2vdpy{ˆj)]kVQ𙟩:l\9wfV3d^OnLhx￾V3Bf=18/ck59vy)("jKTŌHsB F-UķˮKRMB6}QKBQ@Jl۩5.#hx҉(Zi 8yӇEiDJ@K@C.RzLNN,+88@)v uKLgDAʗ.qEɈ0&WN\flN{7Mՙa2ё` Mw5}oPR"4.fdp.VN+˕1FgJ`j f;6ӝ|&CZ"͑LNM0bTŧD8W((d81,k0IR95_hЖL\@U,`*o1In| \E-|5F+Ewφ_WJTǰ+& +| c@s'TlD5 ңPvP4΅@Dhx%T=>]+fEĈX$H%EHu x yE0(qө0X,y'N|.GQA EW"ZѰk31E-bfQ )a+&`E#h,e)4`UL|t]EhAL;kE0YĭZ9Sv `(BD0 Z2ɱ7Oұi (\rn11R EIX*[|'#KJ\TP8JX(s*F L2jf)WBJ)+ /A>,:HMOC˥Uѐִ흎Vk!i.M#8)55 s@ksE zPux)hzIQ4ĪH<1ZUۛrt6 z-~RLiLJ6يGЯ@-z*4Av]|C,'H/ZI&(Z3w.1)X$;6$k@hM6 8f Joኌ_6wlf=]$-'c)yh}W>n#ݽ>)cfyezTnϹ>Dq)z(1i)%oe+`D 6mNzD+J ΦTi}Tl-&`dG.aG:hݜ]18p},rS!ٴ}b;uS Dr 0 |{{L^70k'ٖr? 3^pE|VXhNb /6Έ~uN9~zƨdIvWѽ>yW0Ͼ!o c=1I0EcDu\6OD l6O٫-.V%|m; VuiG00m ,y[b#5w1LɦbXcpW"Ӆ*SӌddELpRvH$freʥ"tJT#Dgܤ[GD;Wvї _tsLP1C1O1#oB1!kLƅ9w9T/k:i`;%fEʎ$H$bl F4fy1IɴĢTwQZ:DI֏i?*WC 5Ò(w_ŷ&_aGӶh_Г'7?5ȧ ^gɡYgh|gC:"tؔpDsNAMi`31]+7~Y^ M,ɗ *.M"NW&tTś*Jj9K␽z^>IUig_̾mMc_T]'sϤسom]W8w͝x{G^^8S m WAؗSyvnw_'z4T0٩tkjB(υInsZ uy;ge?7jRqq=8O# .G9eE6{dqf4͊nҷHKSءl iLuBy}:Q ѿae2cqR+e#VWci F8́|O+KZCE@^-ƂwܿK9?R T!4q#   EMJU#HKk\6v.\FI' LLDt 3R&8x}6cq70%ȟX4LBIJas.SQlfrr*D=t CWJj0y #gi ƧSL̀*\ʘK Or%0 #or{0VH'rg0P$1줿;̵\7 A `K'M+H2f֏yil.CMW/(%nxmce`X\%ӈ| F 2o{\.ɊLǸ0ZLƨvXV$G]5( 6 0[v$,>rٻvvX IDATՕChm'W+dyA֤e o?m5Z<-̏>/mccClYc[B<̩S!G9=oqmzcOڧ+iVoÖ$GOg c5Z翇Ou`{0fOlRxF ~ə<o&Q.4>xSο_g>C]VM|vݸީIr_[p0Vpɦ EWշڅή6^̍SS.>& } RX>Nlq)-ep9re-#+L\P4@2) cΰ>k^Z<Fnv=>ADKHYr2XgpWfK1Jb3.hA:;උ9f.a Eje!GC{\˒Try{#V==CNT&GNs>!p*y&jĈh.|L DH, $qB5{tLaQHU3I5%GMkj^PDHgP=*@]W..Gdr+.f4A܀jnDt Qd*_EAC,]SP "(A%Y\r|X0Av"]bӃ{i h1[.ލKaNq|F헟Fޝ6 X, HhnuC{=D7Ctv.䄲 x"E71ٌ\ˬ15xݶ,}uFEI"3H.*[nR ޶){*i*"N,dT&w Z*oƻ5j 㤒fk\$"%:cosqfN_%{JH74Z,EmQu,QN$4ުl&JG`qI `5=W}:$h7WIݳ<ZxX65Tb>Hx]׆= g}QKJQUI{O`*~?RJjkhh4?h7u)л-R{` !FF=6hv!WYfU=FhV]#֜PӲ4Z*:!3,T|>B(Ag{z.}_-]9RǕLdKEAxHj9/L̼#Ce3--UzSI<n b . uq`FCH掶s Ld|#躂? kMrcpej"}P‰ػ2Ȇ5̠B{ĖbyB -|<+lZfh>o^p,~zBx0fjnCg{~#J2B -|0Ԣ]2EU;n%o9#(M%Q@);KĥgI/拌x'+6¬Z>|HYa>+chP]&EcS$)a9] $#VM4R`_إat-a[Mc[Gx?%/q=/h/IJw -|8SMIvStWf^8牍 mYe[`[Yb .3x`(FujɼJN3>:IX)z:T2< p536TTuu7kEFaiܜwza-sR{[@:N2^]M.%4l%%n ^.-FJ"g!+5=g5`zdew.fgṧʱim/G*I` ~7< lA{_}.s_繣(vžY: Ǔޒi,#`*()<0cXu.)kn/||y=-](̐}03vr{~BSPS4;FWoK]XXPNrW8[?wڟ.2] ~.>[6+.™_ E" b^Cw_fώQc ۓ8KŢqxO,$S -)}Y'Yϋ!27+MCRy鏱c@1#ĺVHѩfzvb(s27a?;=h6>/"n }(z]yg_"uk /)?1,Y(/˺8 V#@(zM/.ό0l%@}9Vs.F3k\x}I4hP\$>ݞn%r"XCW--Z̝adx 36==Etd9ϑH3`VF{Wb9LXD-A4Nub)W1z*Mӿl-wc#])+~2h(Q-R?u:S!]2<2ばX*kŧ&V|O6!tC|w 5g|ݐZ.Der3F< U`tvNnX7ex"G܈uV te4Vc0/m\$Ho>d '厐))DFsnZxNu^ zXs8z%.$Gshva. n3yg!.rpJU0C&/Rv#ARjkZhۇΤΘ_xOc$e/voa&v1iXgDkE% ixgj!f/pmrZ?sO])yjUC{<ְ2} ?~R.[?\_- (yp")X:[Ѓh6.4ڜ$ncZha)*jz80ɞX'.\7tvIA-{9}<"nn&nƑ ?k2[ d9ǥ . TX3s'qy9wg:܎!zM'H3qi[֠52;RxUH;Kj`W;^gү~)as$@/3sĠ㧶Q=#*H}B3L^ |%jV;[؛qװuEx4Oapv6tFrzڡ/" 4GOL =H`(&9v57({}E;GSieEF+f"2{u͛h35+ATӎcm#?⒧bk7FH.\Ωbxz}t@DkRyq.+)gg-۸(Әxf.&E*$LNrso~6PT6~ vHe={> 'xfrI|DBLF2r"gՕ{]^:pZ=M|Ov(h]m@Rd):.%ir.KޖHj6CY؎O 'I, 31'j׽:kDMTLA3$baTl8ZtBes#@<R R^>n:CYhug'itj >ƷN(UJdN?E-[w}䁮y~oKql~ۑK yٻҥs<ɞĺ3/^_Ҽ / i.[_Ż=!Y.RtL89"`hIJ> ;o3ݾM~}rA ]w+̌p2ragg9^v/~_3ϧ |M?%qd&N^ZMHSwo䌝D1#NUh&)AEJAy:õ6v_c/ +T/ƒ?E+gз ^HDGTJ䰯\o1ß+h*gnQ7Fxh3ضa ]1YܓJ w}\/~sBhd"`HdAh<;s>=d%,DS$@9 b׹d硻﫪[U?rBe;% ͱ97UQ++/sd;t<8پ]HLJ8|ʆjs5pHLH,_S',\bB#}e&PqG: \,_Ͽ/];S/Vc"+ X}p=-#Kq7GޕPdU m ϣnqk7\;<9k}8,}TtTB@s *ke s>;?Lxej L= I@ EQ UMHr؋^Y/?Bp޶J5VMmS_"2qtهMmM,LEfO!05k4s)aMBD4u`5t`&L)rV;ݹgzwQV%ۿ\&ExMϞM1وr QbQ'*^ӧxQuTM @6}xgGfJIFuƇuMI BIL$A#- CMN.0̑* *ʐ o#5u^[$/durdIܜK,]Iթqr"Qڨ]| ZX.xu+PqBVY'@G̹8ufrdu]u TWr * 'u|0_!I^@5PS7P%a.W4_­ƂB߬. z5qfwõ fqE1w% UJKmu3=W&x޳2`+ٰ^ {'?ږ-̻W7SQ[6I"HEsԤH,Oڅ+@E~$divP!P4K*ALVB 2䎿㩊⎟b/@=[^. IQ44 6?B}Y!CϾQby>J 0MF^x ߦ$F>M NyDqŔ[)ҹ7,u|Q B؄òluV!OAF_A:> zLͥw,re*k< SA3 Tiʧ_3B2jnt v_@=v|idL]E.EHTYs&66#C=|=s$Qhlp Ceܽ}9Ψ7\۷U$Ji"HNyq_$pm+7$^,4lXK#q=xJ\.Ci Y |¹HȲT|7d EKXUYWM"KH )1$[Wnpy,\?PPMHuř{lDyydK^,>.vgxe˶;h3.K͵ć.{-D]^#$ t"%$?$0_r] ?@ (z%gQ|~L__2D1—,6}n$99%1S$O5C{)M:J7+x:.n~S#*>T4IY|YeyՂc| ϽV{\8Z]+f?cu-Db-SRfxhKo>Nji w=Lmԋ%Wof;~0N\êpOC4DjI^:GswNq6jo? hjsϳ+Y:/Qv5غJ?wsIogםxWh{Qش*O0uoXA0tgyy +ϼW LLeF^*cyz($Rq}F0 IDATLý 6WU}y?_wЂݶV:PV.ëpr<ٶͷ)dR޺_)v~ &j/%k P<^6 0$|G?h,_œcd]Gl Χj4۟TF}hm0ƸX*B06k(K_?0_RML/nzY\xZ=d . ;'m dE1J9E6M)4#)ҖKl'DDFI\d,qrB z($GM[8L,OWF+g TB !2y$ lW^^ }p\%\-` l撃{ dhgq}&42pPpzpiAh@N!*'^꿀HHF-RLg|dr#+PՓ.5~.{H%6AkAx {$-kV&(Z;Ucc䄂 H <,A3 'Ȫ*D<6ق  z1U,r$q.O]c%&DD2Cz%,+xC!Lf" ' 'DZ=%XdR CQ$ߏ(mcHIZ iozHC-~Ny`k 5^4:p&:Ł v߻..ÃcASh(!Da1 )o,fB줺P(4lB?Ӊۻc?~3 rC37CJ%m!>**}30+TܶЬ5*kfq bbh^J sF<  ͼ3Bn}t` f_/]R:Mz݊ hUͳCcκVj`VUS-mq2dDV5dk#FH3Cܿ^GyH(@!9͞Q3EQ7{(̯o.E3*ٰ}''.(=:Ec:Y1iT9dE1HFd6N0ZA˚ Tj.^jf p$?+/'0ĕ)[6’PBg nf'rI»WK%w\l1Zk;^/xWw~ߒ$c.~[Yai{}Vlj$B9fo$!16n݂[ ILlL>+P| @$n_5Qjʢ9}=Y0MLU.!A7\/ȍµHZD ur%qa/ T( '^4jñr]^L69jRQ Kȷ\&FSSJCЖ\B|W/)%, A`/XhVw5deo!RUI{  Gb&ؙ1zcǷ/ZNY<`?zgE[4$$\2c}|˧7p}>+am $T+ iGٚMli)GbFҬ'Ot]WWw##p4/ciX֔->Sy.kYLm7ͥQCX8NCCO-Lw0CUȱdLq,dbfwʪ Rr|Qvx8qqaM1ډkE.Cga_89;hB.Is@2ôZw =ͰY~Ƣt78ǁDE^xT` >ژW^c0W; rî-<~=D=W_V š(st?G80gJYx%^z'~L8 r| $Qi >ڦ3Eʢ>TEp+cVXJyMܭ%9+ճ lFFMQH;8D }cZ3i*warj}(rvzXK r%(A,H2[hK"m 6(50BMFQ g4&3hI6%;hҢ"`+ PS\#9Q:d룦K6g15T1$3G9H ^dxtQN,h ]Idrr twv*kfU˖p\*09@={o fk  pY{KƝZSIMyU1ORMڧTF#ͮk8sɁtY1]/Hs`|q?)xQXT|ʹϰ܇zbn1&Ü5BƠ)Ҿn}MrZUi GH SS5i=uj'`}#F=+%,a #"Ŝ`ԯ4|o"ɫ&y5>²9h3 2oYBs$ĬSUh5E%~x_9Z>iFEӱL3IUBgr~,?3(*1rpQarQvVVfC2y1Sw?o!ϼOByWiߵu m}ͬNżHN-L0}|r^6P&3GFF %dK& e ,irnES>i7i?EQd !6Oc1C3(IULm@4MXl!c͠S/)2ٗꣶ̋*,[[-9ey^$ӵ i\2yB~hY-ו$ivR4eȆUq]K b Ku]hbRT2'I Todՠa)jЛ\L: f-L!+Q {+h畮5L>^tšwtN|(p i.{'tڽ֘`4/9Ab=njr^&ܾo=&,.L ]C 6! %룋A&1Sh+P@u%k.N>MbhM nlWBI)}dLƲ( a)HPG{5L~{?Q^gf_#;}u,1 )Ӭk&υ vBWdԬ2B*B[]SZxN] RZ d\ml:C,T<.8B F.ddA`ʈ P&a&Ne0L8^OS@A=*Q9XB5|(vdm#Cc)P :n ;OV,Vf"7g,/0A~t2k(r\t0ѐ*+I8iǣ"3d,k>ba?e,&k B(^$RՌ|],4sj-W]D"IN?#2%2dm?%`H84E +dR 34o)$ ˹L"|y#kcϿ6a4s%1iBroo ]?|Og{U^ _!S{ϟ9蕍 ܿUO9+AԺHU]ntL eA]d &~FeTEA[(&6p-Nu;4UG[so`p8Hv'O'}j<'IGC5̻sʈ~dwhf˂ 1>'ֱjTkLR??N&{^l2Lt<ƩQF ~qh`@jW9)ids;y==pAhAtGxZ]wo%s O3[Z%/;8zuwrw5dσK.5k{Ƴ.|tIT$<3xmͬKu1&aWre)&<ܵ U\joqwO.6v 7VM¡I=P>VAZgC«Ebuwpi4G]b=sy7r#pr5;dϽT.V6≘e-qEϟKѣNG]L١˄V=cW%O}Kg\fZL?+2U5D*} yx=Qzͳ 9OЗd׿ʛ*SXT^)bZ,:;)qwa_R ːQNbX 3R 1~ٻX)(nY%k- ufyBPFX\HJZ|])LUA4BB9\n'>CRY&ry\`PiH &| 6 .VK0JBlR '˥,nXFds3J%zsфeSnS}t f_ &ž>o,ȻSeD CEfKU&T]51be×<Ά\<~adڢr;`يF<47];$Ń.ϒLGBW"0JH%tSG (l܆Ru_$UA85%@9kŹγ8f˖6PìXIGm[QPJfʂb> duq2Qƶ*zLO+xUU$QѴ-+Qi U-f+!lFiiۇFR-OC=VG@Qќk#KH ^EK]U54 d 05VJ{IWMK0kTW">C,n5Bu yB+}`( uu]B j2V.CږHemTRm IWy r!5^d%4gxԏW Ӻ<ƥ:ȚkȈ*| x2H`)8X|C$bri.Xu~ֵ,UAvg'O(|z4,& 6msdL]H{L/Uf2`Db1JEyRMXi&ae1 ֖Ue<(J1;>@p1.V.h"L2ZͥpYẌȒIbxeU7Ƨ(yi!ΧI9U-Ry $I2}`E*'0L{ {dq8pۥP'Ӈ8v)GT,Z'-&l_ȨSt1!p I&X@krjEٔ I 8ǵ<jӜ?~JeTl([ۙJq-N8BB>AkF ]"~pX$(bFuw0U /""9.hJCW5L &P]Egy$lI?hDif$UbfQ%H;zuSٰ0$D8gHm('XFzŇ!ۤ 08;UHDMj$)1} 򎻩LWo j`ьK٦mUhvSaD]i0eұRWt䝔*5H2e H* ^A"~Xf U#\YE8 ƪ ]c%,л2жQzUINfpF T+C .cjR1Bg۹$/bv#7q-}ƻx3EtEm^ޛ9/asGhZVGB]Xd'γ1ӑ/ŶHU+wJ7taN e)SZGٲ+1ȥ$m+°L;33'l[YG_L pwX؇Z㌎ޚ pٹC/E@7usiNo9 IDATNe*QY,)>ZP1LLe/e_3̩8{gz|1f(o3&<\i!&3% 5m^/#Ėz ne i8xJĂƴdPټzMˌV&Z5fNQY,mY D#h%ʭߙx<!@RM$Y"&/Ό4%N9깥%,a 7E;~6̯ TPd»~n2 RD6M*k &&87QTs Kx_1(LC 8 ,S <,7Z@ R\͑w Ee8ʙx%,pB9+avz~f9 iEVƞ+XR=ךvi :߃^pLg KX­LDBNEva>zd lw:y1̈ WR)oL]+{NymXJ~oI9Dڡ0vG#ZUYL%\va'pCF IvuKGB!+d YҔp}==d,$Fm~RHOQ}6t<2D_E!M&%؄~#~Q` k@Kk M%xahCHo/㖗JTɥI2\Љt_f*'KpX.Oݗ\r4?ie Ԓ"㯢Z#\œ l2M( I2|^UѪ`Xs2'{YuP\ ;ۍ(D̹?DZoh8\bzdI1 ]rg =O|Q'yS|m}uPx5~e~W[n=:g7|_]C2w0w43G_~;dz`iۻXsC<]/>DaMm1m-7r}%k~eJvOEk"šn"d 5?dNA1gȐd>My KXµBBm%>0͍" \-Ax X2_@VN-\B&J/@,'{8kIJ&elá8#[#9f&La-7_lw{pbYu?H{W `2v>C9ѣS>̮Մ  rl@e.kO}jGX^BCZHyF3TF@6g_~}.9-݊9cZ9rڜdSɛgH2ԗ;V B8o0X^p7!`pt^|G: ܊]HCl/i4`L"΋-_؆8Վ%_.H #laAҫ 2Zt idIQѾo3_=ZKSrУXg߿֓d;3 /ēl)S[Xƣ>ik<B{ȯ0ڵ/@-loas%X0O:t0)k\]eXCoWDGyyy5Y+ TYCɮUa p4/IQF+>ګg_Y} =qME?/cMhs?'Zw}OkO/*y{^NE:Jx6~cQ31M"&xd\yWl&k|De&Ϝ#:\=``5n-5!3%YS9~s]*4(Cn;9s O~~wP<~E_C_pH soyQsnb%?òYW!l}/ b+f,ٳ9}fu}|ʌ{_V | W,~4OXb'Nj24muA2 #eM7z1E'yiԷn=bzf\@%K9p Ad 6d)X~&gd'ڒA9jXIMćpzu$Y@ U43!&$IȒƚG?LSаg7?Nds{>)/#.3֒}ZV7DAkZ5}(>jWWŲ&/Q a>Q[7H|>F@S%&alX5_fQ6VҸ;T$_;{~2",{~-f1-}JZjZWq'Cus#!ab0 Rr!05aFS)oŹ#H(@=h2#Є+,{W޷?%?ͲE8mA~>9T I/Ok>6IO.X=,{LQ^Npf\"bp4GL=4 5鶦('/!vN9e$fljG\<,9}ugglc=ۙ'S]Z6㱎sh(fa/qi;U!捘\ ߠv'*0m ^]YZYD"B!qW!h3H\_|sE1Eh+FX7P4jL2?_is~՜QF!f;IcQ,4$Xn}?Ӯ/DQ;<\On'no~~{Ǚ̳(IP_hP|d`%mFeLXa+7-?MC\z0Г@Q6'.E 9(-wE!u{J2IQ94:嶌Q݌ ;!JP% 4npENOc,<%;D(F(@RJ7gv՟de'fB]TNV( h/%,JYQg oL츮u}7 `#+HIZhK,*ٲJ9%WT+*}pJDN9l-,ZR"ɖ%J6nHp$e0}}͇[b0@UC{vs=Q$uۡ!vAHi?Eet7^k{Bl5򁋸ζJmG.XZK`kӂr6ݎ-Q n{ek*ЮXQ\ɣp3] \ 2y 1hP6( Ac~"~-H*1QHVq)ʤ.GORkI J9 GW)#*R\ |ݵ$4p '4|E:",SWHB'ib6C46C,QLdKT']PC~$ǡ`/Syx+ ]FGARU +P2!zϯbtIFT^jvfg=taĜ}xG3! n]XF o ]GkasG8\Dh1y~i>y!^=vWTs wAFbqF%@*EoHay}TVykLfFp71 H-#/7Pz 11kqU* a o?D)'822Q^0( <5F7U9h5}7EIlJ@y9tν(+i FN1ܣlIL!X3{(OMrr 6eN+~ GeYӹu\y [^_hcpEF4H_"Wrx9^waFdVߚ707=nd=kiy':[ozJ)繧V9͔3HӴ 6o %Nx' 63g9{oB! P5SXDNGdRp|DF3'PWkO}y~?y PY~q|Os8ɡ#Z'Uc>j|>$u>+JK ^Xb`FiZY;gK4ojv|+iiP2m3yRM8t F!$s7o"/-Gql"#|d_YO;mOg"F ph\`Rܙ[, dC18g9e<ϿRbM >z|30"S+5>XITE4gNIi&( y9NKŔ DgFb40}6188H8np-(0™3 P+!sTl’/JO94\ <Q< ؽW9`jl+o05=o9.ЂI9tqIUeQ# Ԫp@XeehP'Afxybڱ$%hDA.ɗTuERC0&RW9G%÷Wdd=Z!.Nl7BQTUR$d"$=)pToؚs> /c‹*!DJJZ $]ʡUcAn8<ʙӫRvEv89oc|~MFRdwo"!ud;S rM abh-0G?ksY }3H YeYJ%n]sdwuq]b$}}}R)Oa\GhgjJ &ޟB;<ǿoV(1R\4EF$$CVf+p h8pD\08?-1!h(PEvg7`M"H*DQtjƺl%5Nw^ H{F헞EVb[RkJDo'8 TW.%ì$p]|B &l#rJbq"*ܮňJE2jH$WƲ%K=?,X^Yқ# B.KpaI+ʤ'YQµX9•\!tBte"811YX beQ,B&K$-)xr <}NSߌ_mu!4,2ETREXE0-d LH䪛j@'Uєo 9 JuWe4Y"e- tJ,I.ٕ2 )?p(dL #:By jDr [5(+I *.U= Qc9&N)ka4䜉?PYhHF*ܒ K&ْL4V6mM$$Cr7-+?<A$˘J.zH'6lH 399xeY8C P> @QBʼ\b3@E3UWBaL _O!ڹ5wY 9,DCN1IDATCGԂ 4:E Do2[Ճ k{zR M6T⍇d}k[uTO= zJ Hܭc.VH~g""=WIUǒUkdEe`yh5azRjS;W֊HO]}O![Z"*>/PqY#tcp;mڬ($HVsK2H`P*!fMK۫%B WsIDX@&oC7(y90{}T5k|E~躲PhrpQ^1BcwOs6b6v/Y`]c CMa(|<G67u.t|z[곙:^xG3}IE؞;J$.ӵ,L`PWϬp ߪ6ޭiNle|M3޷CMAn<_OS8y[n&Z̬Na6kQΑ X:"o|7J;."V_焒:H5[k sV @,hBC*8=G#/oKb۞N׿ (r;{÷#e8I1_"UjpIJ !CQ,<\ٞ%Op1e7zoeȈ;7yj!M0r1 z6kă+>5+ld,b4e$|bk=ޚ8CPsU1&ՈPE">_wTHݘ/ JNDTsĹX^hme"(ZSS]`2Z P"_MOY`{8wd"rWP#Iy$ 69W9uUGYy.X̔AٕP5H(ɹ%lB:{aJ87k\|# @ӻNO_>x%7Vq ٴ>> 1kq$St#̼[Vqm ~?;fyyS!v,|>h=!3(GA*~.=ʏf 2OQ\F%\g"+ueΞ[`0iD@(8~z %a<]iXVcwxxB"m8UG`Gɖ| B(J?Ȯ(>A\bf%$49<\UIۓ[fV(=tBPBK鯬tOҁ^mhh~e*#Dptj>zd|Waw22Gt|F[0tTMzCp@1JYƄ;'åP 8cmY "5B5vʫj5;];^iUZ[A-J*].jm^rYFTvsY,W0a7ts9'$ue+ Jd:h/Z.^\-΍E~JQ,@\8*DJ*0zp =\uםbeaa D٢TN{6JY欪ot0sK0T\,W S!`.gW `(=yJigcEգt~"(W8EyUbӷv=+Ĥj1.,*X9 %tUIw.!L.K}[JyajjH XU~61:6E_.6h+NuЩueSOz4[]׭3& mX .FQ.z6 )cxK:S$ tݪ0)bwRK<Y&W:VGaZ0K}[tAA(D}9IZΠۊO^h*&]DײKwg]~䕏B:ڤxGrIP(";LQ_[S. QO-[,3cHRqJ |>anދ\UxC׭zǧstp=@4k-C-Sbz*I˴(Vo׺B0KpɊ%BOVVOe"2}}H↿"S&,7w<?z$+,({[9w)GTH?៟%GAeFxo}yʒpj׿;Cd0E|]D_O PL0 d8yY?zo{"Ew4S25Y TE,Ԅh2!k!x(R=sܶ(X{ W B`g˙j=feъzYUU 0Kpzн^ VeT6KSl&DrYD ˌ؅(WB#x{FxnlM%f";x>6e3+7d nS yǿ{ c/?A̮1 {|9d`&a˳qPY`aq:CkL6zL_.$;ؽ* oNFzUaq;ѥ"0!efMᦟꔽ:kץg4L>_g?m @Q.,=Ev02;7G'~ #8+?cpwN'y=~$IrRe*6bg"Lkʢ.´,Mk٥zn B-b.OhXLMA3o E㦇7KCN)gc#P5ύ ? .#~ݻS_"_~b7G2~NYIs[_ c}.̔܋<] }ۏ'L=x* 2=اn֯5BٱBW.L^ug\_>o>=0N$߭TX.jtLÊ\Bj֎Uu]|xۇe6ֽe&&&$G>vnCC O{pEy"@C`+xd3{c);-\zAc>+r_nxM=xPΖ$cp}u~{Zrt6jJ) <4N0 RCExUΡ@µ2y'r۪ E,"MR]4޿gkpZ$t_zvkc{_ nh(C`Ty:B 7x#{9?$).TIENDB`pyzo-4.4.3/pyzo/resources/images/pyzo_run1.png0000666000000000000000000014351212662716363017633 0ustar 00000000000000PNG  IHDRy@ pHYs   IDATxs\ɚ{xG AM;vkޙѬ6vRPH/}գ^Pl({{v w A*7CU}c(:/w(-*~S,UU TNn|JP/rSʉ,C]{mLV`IaP@b%Bm$^ #߸ i{OسV\7 jϣ4bo F5=NDB@}FVj+5L2MGQ Eϭb]}*ɍ3$o?-_ ^liz=PaK/֕mۗ}+%44_'BGօj̻z{7BZnt1X(LӻWgm۫Zn?8Rmc$loCHl_ξMbk=V"`U9G fRJKUx >@k;/&8n|b#f`aN6C!0|ۜv (:M΋&eyuxg` .*͟BH04Q~-M8@{6΋wFޑo/K%!X&({ԃQϔVS+Ve=UTuNMCkm{lO@\k HHGuy*M=QGtBxYD{wޑp/I%!DոZ Uا V+/J0 ωFFo_0joanOsn@m{{r(+pKyD 4 R`wf؆a(UP ]Nxkoi`wc젟[ibKB>؟~k?C}]FD A_;Nw `{IIտw;x@4{Euj)aw;x?4NXX;gR7'ƷEæcd[oO@7㼁θnalcX:f eп=z wDr]xC:F4\l|Ǻ.Fnn4TMX~'~xmɦwMdr]z6~wmmVېmء~7ЬNhuk^fЬ-],MqWaC\3Q򺎦;zjd8cT߳Ops?B bzp&x8gg; (Mp!\^!ƖT=|)}ڝ_<%rF0/XJ2UT&vctoڤfxpWv#0_~|Ar a%`4//ֿ/YD&mI@JK|q+E>}8t!۔:nB>Ͳ+j[Z}ɵQKqm'p:K58yOIXv!]XC^$}9W= mܾ~x1^ !h¯%?y>JWY>DUR!.6KCv{pKkLKM]%=}a>0k 9S m=d^q"%É+'Ďk-Uë(ƦIW6t,1ZP)b E6HYKQ}~^=Ƶ),"8<DJ .H-V$KV[)Q88ҥ^e1S@ċ5P.HkȦVشC@ $k?{\ "֚ځRRS+;Wv5ũ)XHٮ(dt5eZS5\]7BHWBWunWJ M1b lPyiYYIHBŭb(S,8B1f1U?CݩnB٦Js9 mA<2J\v2,ɀߋdV]Ikp]R#]cy)E`e=:TJeʮĶ $WV D~elcSIwUGɌ\6*K)V(d,fض$I2 4 !ڱ>Yfmu%*Zt/,Y\u*K,M4mm3r1 ]=٬JiN}Y^IXqiV29Dؕğ$hTv!ɪ!d2y4BQ(D:~OVJYQP0<bAM V3%@h^"!/갖a`H@0PiG 7p6GM+_Xa^ś)&L~:Qfxp' z]uBJ 7.;ux"BY{^)Nd9D"q LtE`xD|&,Ycth4'"KM,AQ|A^cdL>14IlzGOy3N<5{ȦVUqʿ<!svxސJ)&+U4E <:-Z7@ķL`}O6A|5f^N {C~bF3Z'A]۱Bh='ӳ1׿bh $O>Cpoɋma/ S i*[ʱ4󒑜%}];f+ %owi-\͡,IRS͢"yD-T"yD4h'`p3k|"hyBVͅ<45 ~q{8CGZz_槱\ZWTb-r)S |;GT'q)ç[N3|rG71kSsywGV+/aiSw _3gvZ~+s0@'ÛxZvs|> !A=y8H{x+ ҡ\Xcvveu;6q|./gDP"AWTEG8 +k`"\f&#'0+ɱ N!# ?8Q/F]n'ZWyW["TH.1fNz8WFM}=̱(L/37>n!-hqLMCi0UNg\tF,*@S鳸Nǭ(!x wKTUrS3_0P-'"Gȣl4CEUU0)rU[h^r)R>%9LgFZEC()eY\e泀;d(@إ S ĩ\9+̆|TT JסdtPU(e8N}#Ʃ'1~LSC<-s~ E:ޭ'èACdmi!5NjGu! WcR揿Naf_e|" \|5p_fD<_*O_Er}VJY AjaN]<䵗<p5Q"؛d5ƓG9!kWc.3= \}|v@j<|d O"Ib2suJ.e Z8Vmjtx5ERJ_CkQ*`Xxt 7`ux &ٟ6?͛?v]Gp )ƞ]E[7HR- J8-V\$93\WqbNovҌ[J2>tG)Xꚢr/xL3EAHw}PupQPKz!p-_Ee j;E/cߓA9ǟ0΅1"^ ̺!t@JQ8Y.e~=Wc>?%`sq#ƭXy=ia PkΛ.#.Q8Vpk4ݚM2)ԍlCKAnq{л)2y `e5M=,(w'%5bj >_6eKa+lnj[W_i&P-}I*wPB%_ z ݷQkH"yW]SHp0}H>,B4 ^U *A&h78,$gyw78;qT\z2' B.KAd󳈣4y\lFݛZi{3Z?௹B1eVWV1~S'ecCÌD{kx=sYHhQa-vlGIbv  fr^:: _%;':'i:NvB!ܒM<Tsxs.,%O2\!k LZZM%sK6!L/X(e*[BQQ}D4VZʦ!Q,@nvAUC7-,c^Y?0XI[*VaKC~J.ܢ Wt49`,n|y55ߋ ,5A[AڢaybA7omιIqmJTje^d"KDoF(kX\DCx]Ğ3;&hEk!!2Wܾ3D'DԲJ)!5DuKbV!ѕ] K1xb@>_xY㯅4G%h q䪍,K- F]Bؑ-[œ)cF?H{n:G>H0ul),-3H < t * rX:W(qe2,Y"d%][caKȒ5:m-$-Ȕ'5!X{ʓJ|,Pw>c0_b:SPq x_ ;ghl|41xeVg_8E3{ K/Pyx{;WS$%rUBB!?&jaW!G"ŰטONB/l*,R*X]uzד*o}&Zk+ casmBA$-j1D<̴r6WIrH_?eUp,{z1>s#s`^?~O'^S#~ς!PGGI;…MẂ #-y8MF~դ4. q6"/1vbQ !|rOC=Ç{|6. 3xGuw&?v;&[4C">hYU\ČI>sD˥-·ljx/Zc!|aH 疙Goc!L|̧qBd'n zw)2L-(ѼD}3v:ni%DO;@-"к!Rm90w/jD;ӓab=N|t THU'`J OC"ʛJbs.}(z^ k#ty L#Ľ*ՙf?%T<$i^{{^0-3Gk }-<ĉ>8ĵ x:6}ML oߔljZ%7hnRF8a{XM'H)n׹]F`"pz.xmFsksۖ7d~-ml?Q OoQPvqͧm36>1ĺV5c)85t[/WdT jr"L~WBКֹNd^t֫hx!ihy}[Jwnb8!6ީMb[SMb N7`;OOu6]]7HBdZzoIckTZfj0~&+yHv]uխ|7kRJJO2Kt>on5qo3ȍ65.YI-tF0 o4b-lio/o=Kb vÿ ŤEd8Q< Ph3nbovlDW =v|[cMB]迿Fghv/_C A^SER"CI$8y F>}jj7z5z5N^ֲ{l.w=ě^m;wRCuS;d۶qMg;d[7i}H)qY X ԣ9 aqhAn<?g~_w~oAT90y5ܚu x7aYk%;o岍#4`@E~9˝qN118{{nZus|~#Ӈ9iacTV{H<ՔrYי<ə8_1gy# |\1gS2e" zGyk,bx;{pxo#u.Hf1[YUy2 7~C||vCgf&3Uem[*f !~>?:3 7̯nRYr扛{jc&_u;ͯhIB䒌?w.Gd'm^Ş@ T93FaF8v"W}@ gc f2z«ܿK%~p win2RGii9g:UOsp5G.G9(B@THj:v?nYcgS1,OP%nS<6`7'|Lvo}iݲ$1 {em\oꁬ{*2!e^*\ U2eE9ڻ'18Xy[?ʳZqB1P /QbRAm;OOzHMc:?ẫ 4XFH*TȦxdñ81s6p+l 6}N- Շ$u|o xߍo-)).<# %- Iii/.*X~tJ)gΎ;" a!P'hՋV\'_E",zKa6k(8g$M@͓|zwq_zΓWyr˴;p]Kz)f>Sl oMZJ:[;xD m뷦m26FTRLv!0G$ %'q{O'/b0:p/7XP,+Sh,Pfivχ5ZYd99/5٩T-+3r>gp{ gT>j˧.ٙ|p{c.3Kω~<JK/O*%V䒌?%bW󀾉_RJ%GKG מPt^Y{,;:>t60WWXNXLW! >/@;|5,tGA$os}%*(\=$ͬ~\7?JEw(:`Sߪ̥YܟcKs\=%mZ5$|dG\a.Tͷ)V>G#߃y͝'\{:8K,3m x'P5Q(o`]'y߾L"w"TK#\3T=ȇgR ]Qtu]=1!~XH9D |3Jܽ 9z3r=yZkÒ3L wd hok'Z2 mS10T~?LT0]xq@EG,"dx1!Ss@`Nm:+ed> B'Ǩ Sf)YO!D}u Y|GNqE0~ K$ BG8_մMũɥWp c׋6Z@Y|1j35TU']jt+H1癱pp;Ae-t<XjgzxT~tfq%wᄁa#!|Fj^ov/ E9Oja kfSam4,ӓdqgzγC\wspL˗.*EdnDx^{(#~Ν>ٰah. Yeb߾n'e+N+W+BtGFO3J# UI>p)-1bG+}|u 'hH>dLqQ<=yIg ιxﲰRv5j}/eXa(zy[M'PCUu)ȥܘ7(+|4O<"!JUrˇ(.N0 ..'s-F4T//mxγ27ʝ/?8]dޗ<}̕ngw^RnS>c37 Xz{ /G_v!_yU ?FL0ci&=q ?}E{.7C!BWp]Y`x كC6[DY%"6^?|y H7EEW;2)R deo" JqsEeޔ5;mqCyPU[sJm *i23<}:y0Ǻ|o@1z ^P$$:Ƿ nD.M._I h!:N(Puȯ*mI; ţ(T݈^(xy#33>Q%P 8d3z߽8[5#]3o:P@q4MUJ d@`ՌsW$P4/\Gx"AWu7N>bi8O0 P0P;avO ݃{j'ۨjx:OK^zB 0CBEW( ު ZS2H lpz鋛iS]aX3@)S,$!vsG7 6 *U!44EZaU{T*z[FYH\)0Y4$e8v|r1" ݌R+zBۥ0qu\tc{$c5pA$Ru5U-1zӗA/ [Q|0AU*S.Q~tЃ@Qu oaKX*4h-TIHo<5gU`+$uqV" Csy(P*gRkR7?c( U՞<jNV4_+2%w@tST4/X)PR+ulC )&Eڍ+KE|>T*ԋKiXu Bj3:ODJ`bDa4 ΠCE/3>2*ˢ@ @˗{ε=T:uGzsĚbYKWPk٣9; ٫!yPu:A -ʞyP)b0'FUvT+E%] lmg! N!teng$JE]shzP4| $ZXsnNJ4 I k e,YdkJc%Iv=_pqjmjk~Oh_@##ABKT!dtYFWeDc:J>ːqk &Eu+<ʥlQ?LT.xz.O]+Eobm컖=kBmμ</%gy7h<֑= c|UFk'׮py)Ɗ$?Ip~l}Ϟ<$IDEN&лG;wiϙNsqV?UhA<&FGp n2㜻x돦9G)% 85I҈$yhqݜ s\<k78nћxq7*$@ Dy_ݯtFvΫ|~n|"8OfOge##!p*y}k>N[("Y%/kP"=4W/7j  N kuD?#Mt2c?'z "lnnVVbAqޢ'QƃĤ"sOoO$_Nuc%۶A| \5U#3gxvLȻ:Uَ^ɹE}I[m|?F0c81s"`#lwc"Qxtw.aD 'lN޳/o?^IGz޽+óXa T˲k`C_o~5@D"n-"e B5Il͛;@RjP"lqܾX}df![A P*YIد l243%,vL:bT=Mk 4QgVkKeP-Ie T\ҩ>O9HoKgmu|32zŵkAQ40Du8ElYCYDLE2Y2"nTȤsk!=HDYU ,]O$Ҕb\RȔğfN]Oyt||u{,]? 'ooasg|bƊSKoP_x1IÌDNk-'+쫚o_@o}vȗ0їo`@q6JM!,Nl-Nehu+>P}$پ/'>.kӫ՗zm"62Vi5.^~]c:kN.P\7@MWZ&Rpd4$6WUhɮA9םTI4&W;y %e9-ɰ(s䥆#ZK;W.,4]wL[&>E} |7r"Tr>onϓfH0=m0nZaxTDĮ:F|-O#BFN 8J=?'6K뜰 sŇl=s?$--|4V">z}ALrS <Ԝ$$/t43cm/&1\`~j|f_@Ui]~ywyΣCL~yӜޜ #5?_yzmH3>"ŷJU#"sX`sx|%2\Ϟ'ӟbgge&R2GYϲyxvljQTFLv}pY_-9Y-Uӳs0Gۈ*jUQ'O9Y#&ulyݿD(:"-oX. yn1 S#lMPYox=:^tYiFr GQ _Gv)EjJupO5QT_Ebd+d EґWtF&~SoְL,!<\B&EZD RHeJԐPtHȏ& GYd&5ܿ\ ea;.-aӥL?GxͩHg&D^vt'0$b.CTd$Y  V&-#"u]L:9DŽ"?ѰOp*yjB5T#|`jXCWV둇]+1;9ƭ) )hVR%4Ov٬Gy* ٻ. iX! Sp2L-n U͖u4M†, @Q_7?8DP06_z܎6N4>߇[t~B0$b*`(dsTeZqpVz-ҥ H2?H,ifN[*s%ɒΦgC#`#UHBj&7t66Ő2`d6PTpȏ33%7vV#8ٚ]i+Pq|l=z}]aVM)V|p/Γ8Ԑ vC'J(/?pq8˻ȊB ljS{v޿ϙ0C9ASI~wiLEav8y{.,JV}n P}G_cHe&r8y[2+=:&k$%HK/Ox/Vk<]D9atJol\s70=+.MH8ssG;"lc$9as >M1ύ;i:cXJMSmk=H:4Oo">:?tg$n _$휉E(Ďe7)^喳h^m$^Ӿj/| Hʽwq2G[E oݐ=\JyΦy{3tMbKB .dԷ~̩hcWl1.>K(9E?9g%޿Gc]`='G6Qr$;óïٹ9Fжf;-Jf~+*lr]YFCV`.o(3ҩ_+M&td\\&5>g2Ury>DH%~˧*#{%EdPqMل=;bܾI\Ŏt1yaR׹8ˮaW(G-027InwISN,o}~D*$1([:nM7Udfۣ׹1G LL*ahs/Y4 RȞ0Zk$iuT[ i8Fdv9 2UV*)J£TQpIv `芲rVdQ))]l(շu`pC \Np-0i$!yEʥy5AO@Aw|cw3QCQ}>cՓCOАS#;5E:"P#Pm#Vi\FLTD)2h7vBFEbw3.ݻE*>~.UjfʾkYJNQޚyj'1BC)].<\K㲝.xݥ$+:ɸUu85*\!47(F&שot%"ۭ2 V}U]d佊‹MMoM2=Vqܐ p+lICUBN1ґo䕯k3C:ݫR}T_$ץRh8D!@;|q;qO>{:k~ookQkoŇc!G-@r_Rr ]\!Ƶk&f$=L@o͞fM&j4wc5~{K*BdGu~u&S ;w!7!C}A*Ś2 2ѷt4_d4#[{q!!2WU.? nlx%]MG98':B Uۮ W?x v-s9[%i+% K_cK3Ct\(x8iF'&Ultʒv`>AIu6Ʋš\G6qF~暴y Y24Jj:LGf'& Ʃz :B6[dL&>@7ؘLE#.49iPaC4Kh (>ۤK2DZ(Lϕ)$uhD_wS:M>| g$)1,Yuw@V arrq 6T$;\~aB~Y)DALw%ai = z-Lo'5.  k Xvc ;|nvSjhJ|&ŜQd2]B!#Q,u=h+dbH2b.Mz6EA(:ٔ%[/i )fc:eӌj6(ɼܵ~0PknZZD ! Q11a|bgp*G &Xy39bdCWTV|#:bŢ x$cq.QSFߊө<%al!CsAks y.<*ILpKRNgxʾ&a*]|twc#T=0)$8ܑѠ &`H!+mTZL. n*jXQ?j`۲|tSnN Ɖ6/(Sh,_S ?Dس/ç[ whvQ :s̸6;%p%6ɕKcT]GzIX:kߌAP4_k%{9ė/pavE.|Dc #5@[T{ ihN37\*c!e;u5XvN(F^Bg~NY}0!4zc5.y#sH~ +טPe).v(LHW10>`Tŭ(K6o\M U3|8{<,Ai7dC#AD90+gG[%= u W?`GNp,ޝ||%xvNo S w5$5@U' bj*R8L һ{TS+@kCEV$h".#@[@4"F.>ǟ'xin^ólWt8H{,Lu7T$=q&LsϷ&!B͍:0@H@ mǂ*~0nK:uNmeͷcb c>fo4R,ϼOsrᘲi9j}A\* --[ox sĂNbkAΜɖqͻf_Y@Υ8~bp6&W/eUwˤ4.1=Wm5m_z\B-m^3nr&U/{Kxh_ IDATe셴\捂Z۽9y>Nma0}׼St--~_rb=]6_Xpa*V[ u2τ%]\,Uv<%oOR._x{P+Nds]-;˦ @bEꕯ %VGY5>[P<.8|::F_a8Y*YdMRHz]}Sȭ17:؋kk}SuEN1/Ye_z"nSk5//f<-ѯ!v/T9u瞵8Ac'*.䴹2]"bɩ~lVͥނ=&=#?TKPy"ox8,n2 yssmWC2:AXЗz_Gٝ1u[ i5cZl6UmR98i\~m-q1מuߗ؈s\v/#8syO+!ƶ!y]M.=ǽ%l7n7r8UO9{jna[ƭϮۅ SNrm;VLj^zœ̩[2`ދr2IX+s ~7iS)T%M/q%MgIg9@oPlY"$ /ܝ|vC;R 6\` |`?jgwɽ9I$ eOC6VvYkO,WGI!W)0rhk'H?gdbaeml,Nqq{@S ot{_pNǻZo΄kpY!kmV@BP|$5WKIi2&mQH?^l)q1OLyn\]ɩy‰0GF\F/NqQзNj/ϫ`=_ܘM6͟eLM 'DasHKB hDB2%Eh,Y>0Ɂcm)Y>@޸ @xP° q+S0RI5w04/y5}+HIÊpp_1.}<Վ۵v#<%ٳw&ٽZ؞N|vI:}:ۆ8O%.ߛ&>_z+IH}enܣRLs,=GKONlGe,>i!u$=H؀d*I:V\2O& b[& }94e +ȶ LQvbx<Ⱦc".ضmp**] ZfEB1zr218%=#?O8?;9gs9W;v=?˃W8ƛ't#\.-$?Ɩr[͇Kũt ($B**ec4R^WX8AQbz1g/cn~ #:4Lh@ĭ"2ǁ3*)! m 3Uvd$0'OZ We2W&)5U|Å ;dQw8LoN8 Qz2Ʌi.\#kpan"@?8me7ïg\jvӪsŮIa_KÍت*S;5&OxyO,,S}%DT ]VhI,ЌD<+obu/v_kzpa^*RLm Wd8M>G$Ei.1<cPdHf n#rALc1 Nu{c<{䉩gQMgN}L݈s.s\;S䡨R(Uє`33Cwi,7ۺ1!-__j~a SZfS0c mt U( J+!Tx@T/j6tI$sa4rD"ћZNyvBH+^$V[xBlovw.<#I/=0 GE 7κ-}M6*᠂IVԉLNQ_i>1˳Gܶ$4ˣfy(B?HO D- 1( s5ZT1>S_pֳGJ{AG‡9wJi8Ė>O+ aKb ]%ҸfU #@gҧ|n5}Ӥ$=7@z.,K??$ ;\z0]mUyWG1@U%v6',4)m! IШ E*Bבz=:7ѡOgldCCUd$vSOr>{ԸpE;4"K!-F0ە)tEgFؿ;V~B,XƁCVMFн,,凧:[%I4 i867 5BFpBAS=#0U Y +*e_?EgarsB66SY% ! @Hϔ=Y,-ֽH<ިҷ+ΰ:3M3Z:R'!6Eya "X5B*J\W꿼'Yfk[ˆHϩPϑT9t8N<2ꞜNv%; kX"uz)IބP%"tF0#(8*mΧvz7u:A TFSQk }qb, 4%RgIt&oB $k$5mfJp7ĢS.Cm>U 6by,Nrjٵ]%SG4 VV_q@4z2jUbe*'p{H!`He9JoW_}EjUC $՚*0]f@5ilB] ř A]iQ®ZC6܆U;/T&UU6k2eC]tY5~k*Oܜ9x,BX<%Ɠ5t*WQ,Uhbw+d㝄-eJl2]Rɓaw"5E=^ ?)KmJBy蚻H83 ]Jy~~+ 'o+b6tNFM-O&9v# ]'w P/p%&Jo(}z80~ݝb|<̓>^{y,l|(C3 澞F֐eKvw9scYK= 8y:?orrtoft+@&:.jԩ} /2[vIXf]~vCW!my"H1o;c$@ A8M|x5(Xa?4}Cmon)?{a*~SB! >u ODؚ` b 6mL&TaKŞKv6ùgx2'm0t$AB \1a5ieR[ShqrmwɧQy:_B f`H*f0V(i$b*p6%cHHԉFl~̛ޔ+x`IX4=NϨ5hQ v"/ " i7bwG3ӖM mP>yͣYUY\nB=?% &SwF8@:)E , (>5W V!WD{"Rn%^Yj9 d&SWui<ﶌ~$Kص_!_-|;[am;cvŰZFWmڍrqX+¶`'+ ޖi):?91^>L^8;O}J ;S.ͥn=vRX/[ j~C}\o\J,jn +**6|_*QWp(ZLDL+w{qXҊwjkEBx:}-ջ&[c1]w{E!y|Fd]/񐺆WzrWm,ϐxoZC+C."֯4=տ0/e`0k|PC[}-jxgXRT O0GכDЭ8C^R ZnBuWƖ[_ZkxԡG3|w to+UM#^Q@B&],G&󃿌6WI~61[{|n1dr>$KhtSn&]/yB% _ydfB袄u"E'SQڢ]j"E/@"fŏ/Y We$_#w]Ttefs}MdiSx뎤δ_}.I~G(ky!F!0czK)7ͥ ;vK\;wGfaow"棇Q?V ^4< "}1Æy!O9[go5$}:^n~9'(2l;ΧX9LSlLum(TƙmcGٛNG'̩&4QmecjLb/PЎ>d&Ǽ'E#jOMqjS (۝6bkֽ@U&=ï?JglSLʏi 2cNu8D2z3ovfA؎h&!Q-qq;Ij6̃YN_p=?`^EQ+4Ck1NfvS* aP~ӻ95ȱ=lyvlxZCk,-CL^rnT"B4hԯ`HBXzEEǎdR;6hrZ J._z  ci>l/BT,ɡR*cc8ȨFx40R\A1#":~@ 65\'2PdAun۷pH+0qKƭר8:I)ڸ,w!aaM mmzMLQS9Er:I 2]$SbŰHDBzsH'|X_l\͊yWlh-+cY*5md(l` n(|B)Cx5L]#bIT L(cln0^HV$ \ۥo2u;-K>xahPBC;Ē!C@תcZNIH]4̈A4HjF{6G]i|}|Y°4ID5,EŲ4BUM*nrKd;؜Iz}U~y]{ųKd7 2'Q') O< YƊ!w9A:a!cj3lCft`Ho!ܸ9VM:|#(b )?x?ZNBUTTUJ9w 2$$A!SGTLM(i"6O8wʖ@P!хOhHԉet܊Mޕ5%lN25)Mk |Z&[HT4]ok%M곒zGq湔!k; >f@WTeDB@+-^^%>fɌ2q;;2xBÄva)!<;׹eDSTSWf}Ot\'hBL~BnS;D*$7KM>R [Xl9*=5* IDATx|xWy.C}5E> DegopI|`/0>z}D+o?%}Ԕx?ͣ8'd >;'`*]wncpӋ/)>b mɛ姸y.MUp9o1xrϒ:CG/)HѶH+׀lYlڠOlqK! ?4]!yRsLU8aW|aצz{[.b.R^0txãOȹȮO͕?[Js9>yk({#P͔܈'_Ga",'?Γ=fȇQj?S_UI8fPĥS3ܞqui+\$g}dUq $T" n|62^Oү$#lRLFfc-ҸzXg5&E޺}b[SÞ|((catg3 G81N+ʱ2WR.HK_¤͆)S%X"9V4rlk] gGgn1Y@DFvƞG\vsh׮)rO|1  6D& Bba <X֕ 7xbAƎQT^3S9778.|9ЛHxO1#k*j<ʡWblHTnMI |B;x}[@37 'p=*x:ק8yۦpl3TrG&eJ[g~{|޷JsVl>2w9< 3a{\- ?e*S$ԉ[fvsÃlB +2~ݨ0cWNqz!LJ$k6Ksc#p.O*Ȋ s{~7gP+1>b"g^S +y~of){0?'zK\0˅8t@׆תyғ p/qeq&RWx;ffqb[z5alfk [ˠZxi/w@#΃'L=$C1en%ϓ{w8_A<?νFf2NeV!y0c)*5C}/x51GU6|& t)!)*$GJT6:Py$Z?g)1MV52ZU$8wHHzI@S!|ZjXY^'[ vm~u;r bqmR!~ A@=32^=K4xկ0~Yn\j!V1/K9>b)C\!qĝm @d"iMEz_ȧ^1~y[+>aouI !aF Bi `(0c07ҷ7@E=.P պv8;`zLVh|# i+40͟X C%KW`W#1 D=7Bxx o=@*o1UUedECvmAB ૯r`} EQcs$B͔bġVK#& Ϯx>BR$5O^,4}c?rH j 5{\'hF#۝ t&Ev־ %d=Qzk)m*٘>Rp훍S'C iɳCrQBg/bc1_ǥWonn<ۗP Mߐ2xu]PQ+t fYy+HH Ɗ(s" @Qd4! M=WÁ $HJ8թr/|^X$oH ܦb~MgԢPmldUcRk4" ߣ0Wm=kE.~8Գ\Fh-ߗݦ[s EӰ}YLFUU5fʘKuGKX$Bi<7:J'wVwk^yo 318!e J+Xm¨Γqn^ѯ?Wb@fSrtB=y^Ѿ0vm43F|[2"{Tn𤰞u JMjڋ>-ۼ| I$Y=#Bs#a璭LTHWnJpbQ-Xt^2TPDz`utDdPrϦTHVT*~ r#ܖ̔=*(A-q9N4Pp U}spq&enxZb1G,oAtjAcoX+]oA8m%ɟLB2\ XT6TCP PB'7!Qa%`P$|$ +< ȚjW)=ļK_6JخץF_2Y;Hb3PQ0vƩfM/l+O"Prdz^Co/~}8~ggؿl(`zN@iF=RdFooo7 *3%XP%n׶5r5ަOoXT!˛Ghf$5,%j4YަB5_Y nqiNO4d$%Ž[x9ԛǵLj&l(<ygc/;''?cJ`&|K %WIo]N$ p$aMDZO=xz7uӏ"-Y)j]TD7C mʼn}yHJdz$s'^[)6ıi>!ꌏd8xUU\L?}.Fǜ*3m3γ#+FG\Xb+CiO~|kL 2$-r)\%d*uޜOی^:6CݛDtz)˄j$NMouH߈Yf <ˇ.pZ'&'y{?cD 4M7"#*({F&9|"+D=ыcJ6ޥR+:֠O57r,5~?Qa%nqgBw9nr5aqX9lFKp:O8Ch:]<)P"PT3B&?;ÿ/DsA4 }1juN}[-X<~u A&5#X!,rXD_i~9PUC0 k Og+9i.~s슙 sߎ J:$\t۳A wws|E" 5g+eoѱ 6u0u,H@9N԰NǷZȝ=lg i&u?i|ɓ]H y޿_Vū1@t{Y.ي˭eyp7w75r"%C ƈRd4r&q |Y5bDthyd6n#jl Y蚌S0+*:A+BT [up]Y7PUh@ d 1@\ eR1' 6 I6|{wHm&:C\ WvVy\ubS*.b:V8)j(a9N]Ww)]AAR$2n&ST ]p%xXs#:ACBlk _cဂ^s)T$1d@ULS%ܐADv$LNӟV~~{ u\CB75<QKA|*yBU>*c& ̦wқC1seGkS)9a`DF.;.պ@VU@DPaCua癫D-Mc%60&(W )]k#_.1SwGPTz-@sf2f(J<7C.SB jnEfkdJ>$F>,c,UU \rFhW*>2j@#j)M3[_X7Ma7M4!J&_Uu )`U|Jy#z VdfG8\=檂ҫ KHJ4a`2s߼_aןm`wlӵ9Y}nEwsbzҖTr(L?ԩ+w~nK/( /xK&|D῾uإJ3 W%+F;xWtB50$$4/.j\ EZS |$vɡf[Q g"(BԘΣ naxp'/{Z^;y{:z>P|.3||оSJvE$}Z'Utc~"/U?NB3\%9"%?;ɹ)r76d¶?/s`Ǒ4tTg_$ӿ ʺb>eWt$]3/U@: ?JdDW瘬Hcy¢٭1j'UÄҴ[^v8e^_SƂ6y2N=]g X\ףwqBTMrZ .Kn:ǹ{_[dR$4cl7U0'WYǥY["iY NӖPlqnYFH/%yOj藹(Ԋ\tܹqkL\Gv;7to;z.ƫphis e8Ac[ɋFvND٩du: >:w9O#ڿ^ʀ)ENݚ"c}/Nb"L^<éLhw w;̋SF}oⷓ.v[qblQs\./4#~t"+ojEb%1w,'㑵?9.e.aBٕo筃ݼnQWXлt0ud@q2ܞ)惤}%/qi X_Er+|A!LJő7Ӭ LqZq`=Skzre}2i =ALE&5dʪ,ȾGƖo,7y[By>{'m i`Zt[9'2 7l^/ e %y.pjE܇ط}cc̤g)w֭^lcSti͛Xv:!(f}|?@Hkrݥm8k֍=|YFq5j~gI9^<ní'|bW83Mc&!tpU˔:2H ؖAr'6G(L816h$tsO8o'#y>t8Aضm%ful$jJ~i{.NVD8%&Qb+punL 59Q Ksbi2g 7S.\л0g<#J3X#ػ+d&o8F>sOps7WT\uД2ժ̭GٰnPK,Ykf!O[c +&fX$})kL5,0AujxO&e7`P%.Qu\\aSqDb zU!$Eh-]Eb&$y9CP?ssB-.)2UBqmbO0Q6m5x6eP\!+ITbhjÉ3hO+ETy,ރ,jh$ɋ 7B]%c5H ^ƥ(.7( wq EP4&w7JWϞ3yxR,dp tVng]`ԑQ-;ahʲ.\$ղLZrm&|+a[y:>(X$ U E d:հKS .kzk4mWK9$uo4, <FsrAV X7$AY4y Eoɰ ;_Žjt(}T\iz0[<<ͣɔRX[߭#2o<-*K- !5;4Iep~榧 !$|EԱ}}zPd 6)XhɜEr~ͥ+^ g!cMRIjh&<.'\χ?#f‚TAG)\5<u}LϧfԈPOexEc ɲ;Y?~#76o_㗗x#ԞN .^>&f.Rډ!}A- 6o:+F6(*)FS7<%ԉb ${+r 8{R % >ڝ ^IТ=z6y*N@VCuBQ,!0" k躊=7lpŚe6[Xdr UvuHœct:! Etx ӛW fdlr!켅T}"\Z|&KL`9AI yClHB(&x-tWP8EzR+ ,S[3?Ka̔H$Dq“0rU](*$CFbƽ<LLLbB"ХqlKfnNC ) rExOLzn#;W#Spq6s2a3@4 H>sW\׎%76n^̣'ej+3阬O*H A}X?E34nti|?l-✶-tSH$ֻ m7@)| aSoϑ'Ґ0;Q<}OO&ȦGHh.OeZO<e rW>[G g0qܾWpk2Bm||ӧc bbH)1uge6Wpt@lˀI \Agpv,$6kc ++Hx5S8uuQyrҡ=lT}&P}•+A,zB3ENJƉMV3qX&^?&B*&]'ՆRF$ޝ|xS&j|6 *H@T@_j@' e|M}Ǣgw8q+mcn"R5b=6蓜ےb'S| PM U R~ȕ y&`\ē'Xkߝ, lۤ}*"r2\QR?bj%Z(ܭYw%_g?;bA԰Ƨ4 ]h,9ߩѿ>M<ʗx#5~gؿ;A˛DG_/x[f39FaP 9$&Xv f߿,7(kEv\PWfzu~sN[c}^Lqvӟ\C}ͣ=¯uҞC[˾jAwTg\x-`s,ѭmSWdSf:vƲ=j|-K>9@I{Ԇ'Zק89ߋי |lHl:/d /E]!ݿZKj~C}\oتH 5Y;+tӳ bW#TTlr TQz;8h[XV3),}繉Sg..kr{ݚeW<^;-Փ}wd66J eb35Qbbʧ{h j^?6̗x?HM渥W̛ k$W/ûF?;g 1u/OċCtz a%tOWSX.2_;\z2=bMˬE͉W5j:kxԡ;|3|w L^JWmt3MX7h[kz6h{y|=g4KhFBWsr | XtJv[\'+xCT|_Ze<GIsj 6Z, i=צ+QIK_byf:X}/.RFÄ!yߵq% My1Tk3U!t]!22YÕTV’YS^3_qJ(%bvWVG`S4,ɥ,tzsx/Iyp|zuB  2jGG?aXt}0ؽy?O^Nyu%~?⃉*BB:*7?|YW9N{Cwìci@TKf+ 6jki>7><~1To c{3Rȗ"йX:)xXI%"DC&13YXKox8Y9 [Ga=.=$S%/Sq=l4Rh*P)IS±dUdT#H<l]ZHlRXf Wa (:7[Wbh`%kTэmrL]0&{T zIK3cD;r*X^9|dao̞㸲3gP S")VV~hp_py03/ ciFMj'ŭWV}뇪 `D Q1Tey={wHIj,YRn<39Ǖ8T3@2n$<̅Q?jcJc&@uWbx%8ͯ $c\.αaʉQ2PI8ȏ}L^Y<ʕB 7Iq9jisGũH2؝*fPqQ mNPBALtw =pGj dk0LJH(Y/sZj7hⳡ^}y2i}?LjY rɻ7ώDܹG/3-$wp<8?rC |ʳ-2{%E6mɬVK%9V؆_M}l<~󐫳)7S5њ]ZJR}t#דrC9-٭2HLL;P#}jv+CIQT%Ce5j ħa<榹 ճy7Jd=xk#0S8 EBB"ן$1j (  bf<"h6Uۓ 4 4&8(9 ;{d |-`[c*%)*){Ca R0<'Μ!~[].-YlF`_h?d46[ FXc{O*Edᰤe=wiϫSsd غhK8tH [3q1ene]uu"?ܺO] b Y37g97^T1?qӨä/a 2@Fzg*U2U#L8eR]"߫dc>m㚔 tB~Ax]ŷ=ʹi_)? Tdׯp 2:o' =[$ZRըj vdCH'(HXE 5<)0c9>RَBw[y( |rʔ5$Xy.Z @SN@PP4ssv> Bum;KVz4L,/MTP ,ϗ=bVifVWUŧ TQ|],VXj CRUPt< L9mUڪgn]V'OQ(_nB~9}&H ^f=#D"bV4GلQ0(V9 j\M@eJ')puيiIb_(Kf Qx0܆qG*CTkG̕Lf݄@(P+<<~e=$l,qPblf(ug=j1P7C+#kͣ[y(?FJZGS@8zԠM82ufe6hϕuP rq=&"ɧg(;.[RՐ>i@֜yf稤B i&!6>-bBkv$L3ywaW,J+8e}k~[6'~É?W ½yN6i.<˥.n`3+lZ,Wzgѿ%+_;Ca3卯pO0:ꍫ,e{5i.mcݏ/VƿGP;_C9y,]xfRMQ,Y>7V[wvE֘­^lj.ےc  RQSZhѣABu)bP/]+ZWjIFAz.X#]hq}35[Xcsmҕ浜rWU7H2P;错W\$ v8BĔ ]װQ,)A>R$)P@L!0t [\:OӈFK\dvtWYҥ&fh,iGob\]S[{ʙ,7&#ڐKj)\88zlRJ蚆R+ F"j"*|/JDuss#cQ#Α0 3@,ha6m9kNR`>$c6[&PR#ɑ4<=f\G#z(-P"zxhD~⺆zW36piA &d4 >Yc~. 3Zto9*MO )!/uK @!S![M@&aͣRyTh62Y0[V Ttu%5 8PaJMn`1IDAT &i 5sOfvEZ֡lrF-͙>ZxD_o 6~x|݁'W~vmȪf+uuyjU3C 1ΣlR4Uf[DEp@$ial9ǚUQw7tuepWtBD*]}Fss]B1Вd8=Aw'GY'U]H"+_bl'غp;ZH>.\_]?.B [5Vry||| JQŃvZX^T.3cTU KY m_aʔrߛGv*}ČcX`3b%&Eu|x-|]|>z@qoAG** ޸wE*n iˢI=/\j9ld ުPEcڊZųﺡźleC.zuW h55?LibI$9˄uxF|1vXb 4/Xd;G}Ya|[Uwc]|[:0袋.`k fDJya*НR-u뢋.xI A(S"L-h9rɎTYU;+Oev.#Hn!ѭmwӬ<-+֮"oE¶&` (~ZZ܋ x rA5E*N2 VM" m!<%B ;.]GE& !@:^Wn AQ:7_drk-n}]#BbFOx6"uJ#dopZ!5zA3ʯ@kP ;Bkuh4d0 dG׋N MJؿ@Yj{bPw"~>_߮g^e^GJe ?޾0~E٥p07 |~d/y}v)bT׋JWeH)p&&[l3m=o@P3BY]4&\#5چ,[biΤePF5ȭ FKA]# 5p{љIzHOYtςgjo:*Vb>{2֤u<'>8`f@Rc: :g1 ֩j(F'B *Iiدԟa4p w8]`Գyڍ#݇A, Qy[L ;>|lcccdIA"Pygv'wW}$-^#T=]]yz_lnBG1*GQ@\HQ}xg$Ww9D DŸx<=ALZ_MP9pB<5oYNBS1 a1P|)Chfr) :NR %xm@B: 0@AK2Ӯ .0]^#*>N$1$2 rJ#?[Kx4G6 1ɐ:t0iitސҘ aNt,a>Ecc1D<u| XL4Rv4O4P jR>xeRĤ?HajN{K?%Y`a PC$y#+rNNJ![>2A]JŤ?HO}M&j 1HMfT]=-IcMJw5PiDr;.z8;C#t9v`JP?LCRÁIF9M?&eP"1>aZ!=Sf6(e˄eJ!-JMrFF)LT4$RJW&EɻwL0׷3Jiœ)SesߦEV&a pO<i)RXlH(J)T"nbzhl7rB&O:T&N@t=UZ5>pХJBn8p&鏡49FR0a4aNHi0M+h*3A}Lbv?S*#LJ&K*\i$`9i)Ii*Od帄/_:ޱҘCG8a;‰c(9|ViħEOA*M~f&0!GlbR蜰>)}|Ř>3D@?[U H&Q1q H,;N^_ PpKØȎC rJ9T힖^T>-ԧP C9He araoIgĺD/c *&ƿKMp4#%_ +JAK*98.av+I=O!(**"??4M!u3BUjE1ELZ;M==Ôxb$!KI׾BCO;{:‘|L!BrJVZErŨI0&F f"YZs<޽дEB%H ܚqjr72橉-?B`/(\I1|b+`f?ġcǸ q8i'%+ؽ{7Bkupmq/I%JJ4QR!ɠ+7s5|ڽz;p5N{@2BI ܒ}xSO<hKxH2Tee")p럞wRRկؽHOODj֬Ě9ƛy ._ǁP9MI9.= J)F+04MfaHG6L J샮D*Sآԗ'84p}xOB;Q@#NWYSbV֗2#bRm1)+@if-Bc$$䴈5dqd 4'(T3h(o|Ck<T]fGWÛY?x+CwSatgO@4$^Ő1@d|z ]>Y)EG_ s95aNk%2E9>! D@X_/t~L}2$l8l[Z)&"Af sh|XtN말(HJ9QZGXLbʉ"Ɨ9JA,ew c1ȃ~#K) D,g _ʎe˃q8tn3-J)6vܳ sXobNpzySƩ*Iw[.6{M R~i)?҆~B:J)y~YSRCw]< ,W(doɡ<g-8"]azR?@q uZK<\ lB_)Lsl;I.\ǮYo9EJBv!g.o<%ܓl8B5 WX<ƶmQI*dx;;hڼF1b gW"~ #%0Q> Z[ZH9p"̡}4g[yn?L;BG)L0&M[&8_G,_^RO~R"v{qwa;|‰]_,]{0$aެ#/| Znm\vLJ)utuRv"/6?%rOaq[=߃/%X%AO8l(i'XZ9D1ưmS?%4MlJJo,lSaJi}La)~fAH^ x{wB[TD4D҈idA>ZZikmFלx] " @:{Al6O|[f T/ኙn;3t\Nu`Ș+gy^YYQ Nɕn&@i%!LJ%PJ꧵YT&Mø%O%]ش(72eJ^0N{yg7:d0>ECh)u2L5),_M)H;?Y>L$0h#7-)w\BWE>\2s-$s9KFVcl. *kQ렺ЁmCH({txJfsMa&< 7czXvzf=pŋO-x4Fy=+gOrT,6<ri1zӣ)]D2[ERk0-QP)iJRR%hڹ1jݼS}A:L̠7Xa6kZ~;|VݓXwJg"B?c3У#knJ+(% {6/δ}}nݺaJif&zkopՆ)ͯ=Ms* *ن8v/CHdD.0pg;Gxl篹lUӖ{!B 4}t"6挳6;w _2jlD*Jpj#Qaźt1Om\a ^~aBfEE^q]V^y *^GHvvKJsYn[mOR֐^@^;d7Ŀ}1|tϿ5 $&a !#L!=1d h~5IYi:R!۠(뛈ǹ`-KX){+e4 vSu0gR]Ykookf V"39J]*rJJu"fG=Js܋m;X+ߑm#2E;T; 5J+~?8ԉD@ 0t'zCiP_Xk6,XiHf͚KO#)d*2Ip};r S&QjzvZ=EC0H{H{e`hSO;TpY|-Iu5KQ/>2KHQD@cIS!2Psn!)"ϙ71gnE^ kĽ_w~;JJ%^BeVoE/ݡi"x$afG%c1b%!SДSr4-a: s"O6@*&T={D 'E!J7ARèntajѰ9$h:L`FL@*]^݅MZSGr%1S&ExI"-%INhq~c y ֱ`V16%$eWJQ__Okk+t*KCCI&i(ݗsekn0;8ũi"SHQĮ`A"ᛅ٧?LcvP$D,H,:|o> e] Ȅlf'/^ wUwqdۃFM*S u|>R`ؾE+.G݇n*bQ•ُ˞OCk8:>^?g1`|i6gu!MEnf@ T2Bovqt %AXs3;zM?ޗ8zЏ5 ..0Ƌ2OfZyɄW~ƕVZ5 "C)ܲmo9#l}ˋQė=^d^p-. < .ϯV#s. ^m.z bO;nP4Ab j& aP*1&#\N8%ό0HӠ;*렝v(P)k74r Wame즹IE ui *ڊ(j!% \`yT̏MAAW\_)ࢫ UtQt-է7gpaX_kXTv'y>F:*[+Յ#p' T z=RJif}Z3m!_?H"ك(S638x) vڗ^V;Ir$$"nc~SrDi4qa$2 اHF(u *;m UVSFl w~RНܾ G:;@MA .5s>oE:(hyMeusJs;3GA=k#49s*0=dEM-3䉗vcH4#NLGfC/X4#,7T2L7ht5p^u)BZvw>t-;.eE֬+7H*'/KxBǕ_BE~ 4|,ť *3nЀxwDk( *lکTR(c+.^dzSw_bo97?#fw.}ji ˿$Ȳ'Mhtͬ{.'{*2AeҌG0}>*zGBP]]M*:IGFtC.K X )`]dpGMuX{F3"at>Sc&fqEz(//#C %FJIS}_ugXi4446Izh XROu]5"=3 qLJ B]t@((#饥GmQ\ܺ" ?H7FW['nC![YMj4t41h6U.:zNAqAM6aewP䇎or3k͢v]=tv¤hO?fb"ΠD+ paP$ҜyQf8qh]9@(fz̧JkKE ]來/9S{û h㐉}F B$0B4'tex̏G>| .<:'o6{7ޞmp6]G$a2ۏS㯳+|-+x\kQZw=ý;]]2G~_r (y~l \I>4䵇~k ?۫kh O`J庅ّR&Gݫ>IE;fɿl?7o8<咊Aw?o(E^x}Z =r/jqtƌiNɭ+Lь v064!H`*90–˯eI˫{X,(q&Cd6o|@e-nv gw5>`,%呞mٸ^i ҧU Q>Kd帅$ zD7\@uRmsՕB̞Snjj&KP=8Lv0.¢WB")*š,U/K^g 8ճ5 86͉!%Rq7S֑f<:`j(v^iVfETҰi.{?o1)]RL]#tig9ːx̭ۙ( 0$54 p>fÉ@t<:A xoɪ|W\.^q;1[4꫁P\`uçyJ:fmyuA4JJ IDAT\U*no~ †bUG.vhrfu#Oy:odU| .^|#WT;A:Q4-MX[^l!k/uDBۋPK?ߪ.#ڋL4&BV2a~ߥFgd>NV/ws}?-Y!VϡoK l6ܲ|-s4h6Ժ75w&R ķ}J6+4/[V?srIRŁ%D4Q\m*?Om>²lؽ~ݭs̿l(囔RJ vct=DO$E T4b+L*ZC8(+Av%lyq<<d|`܅v8;+$(54S?@B9\Ta tG 'rvڎ))!us8K::>'*b~APG#G?}G{GhDVRlOPZC$=F%N@ᦴB[o RIK)oo;w!8TΘ8߃͈2 aPB;(,-iG(/j:JWFαnʋ)yJ)ǎ=ᇩ_ sCrM78r VvgN*B$"l$?Go_{"KũŒvqӧeʇEzts8$LA2?{yO7O:%nIiT%MEUh62xO,ž{\)~I|9^A#k,FsP߭h*&ioh;%#ǞO!?cJwL翍r0s Lh||n;7lk'ҙ̯Sn}$yZ{s!E8FwG%Rġ 4yqqZjK ( h.A nJ& K tjG$N#9z(<~aH6rIE Kk(u 29{A^ykڹ5xS\ET7m}ܺ8]1e$Ӎ GlM۠fwSVȝRCݤLw8/-r(ǴNB.%//|G4'G @T1^71n(ۭ1C(a;9PloJp:ҫ#wӬ+{[X48G9CӲq[5@A~lzϛG]Pt=4EyqNxScgݮ^Ud٣wYS2(h5PCOcS d}9.lʌyO_ r4MtRj i !X0{E%Tc}XYȂ+v7 ZyIݕ#Yņ Pు2i>pdAE_gqJ24oa7C5kM iһ5r`[͂kݝ=)l%Ey0 {.`LD2Dg#kko0:EUD<blvrx>RWXJ; ~ ݻ>NS۷@ @qq1vPęQ wc wqg4 frFE9Ou(Q0ifһ T&FYShGj@:ITcV,ŷnL9;e8'_ćO{x*~>tu_߻7ŹաrF0ābP+NjW9|zk|\Pً9>Hˋ6V"^~IvV0cK`QU&дwiJYaJ%cR0 85<#V2])QQOz-۶c9ΖBws= 9YpRεP@@9 \w_\"w7HyK ݺ\uӇ&Lnr>?φS,8|+ tZ& OAI rtX!/;LyM.P;0rkrKGCCCv(=sv61ͪ؇ۖ`eIBTLS(m[i; PʹKxHS%SdjıxP<1=‛.?VRo_pFuFٿ5*奤Q k)R^7YTϜ=~+_Lȕ4L%F g`%3rK "&wA.~]԰zEC{x|.YŞqcV8cٙu*ETe0R}dž0nlMQ~ x0SlXiexzK9gA*߉.F4y]Qʍ;(cI<h!$ 14ۋ~ySC +̓6eUcr!k|c~P Q]b}1@S^* ӭ.\B]7i rʅLs(u;1f>r59ä+)// /W]_;th89ܼ~8㼋3%N 1qYܒo9GØUө9~vSDxs !G9CiX{@K0s!wӍC)kOQ0a3G9û P]ޛ#O|<ra*(غ{c̘3N_T28Fo9!5!(avU%Şt"N[js[Q:q+!r =k-8ga VSqn9A7矙C9X>URsA:iawDbQVVi Qi)$ ~ņߩFKrGφ~B =2=}z5q1I22hP!F告f&S6$ʈ̞4|cax xJ/\] w>8@y!stXx~>TJP#6o K)GV Ph\r#VZLwM0'z@$w}7P/}Kc˘1Rs!C wff"z $7 Q|^@*4r??o#Q}|+f_zMˮ_POq8qٲYElϴx8a-E.mB]<}ye\p-\v.2o[vr n}  0=|8荒u˛M%s噳Go2%ɉC):wǩ+q[[*Ey8帴s(@BGϲY8#&ږDZ/_at|嵅pIQJavnbRNf'wש_'y'|˯aET%M5{:%$6:hFKR1u))P7`/*!sX8v#/Y\$}hv`,-"Eα T)('c+#]ERBHO1%"NgG'IlBJ 8tkd p6=@yױsRWBHdK/s>U啗x|f˫G5.G?K^RJ5p* B9|1摍ذ~={;h m{ U{Ъ&: ;4f?]]]bۉFl޼^{c;B ܈(j0<{@WGv%6Z>uZ8c\[(w?ý3&Y3O?%6^ba v@[/ c㺮{){ `*Eu9ndKεk;Ny'yyM˻Nc'~[؉ؖmY%*T#) zs~  %JZ߇$3gvkZe lcQei%2"@VTFAG|V1}vbUTVnbmMS;ekr)otles8 WHe5 fDj;o7IaV U-aj=~%d щ_Uwt"9S`>ꗹ[|^&VMr$1x>7dždSS 1]Xx}/~8pox|޺HR7NC.˱@-+6mG Z$ fh*rLX=z=gSUrҭ,=U|»3{]7iҲ&sП.=wSfHȏ/s]7r޷0$>wOW&]r6WY0#1Cm坂di~-W7S7yoߨAXؽ g݌}AwqcEbk}/Xd%YQ|t IRA~ 8 IDAT{ ˨ٺF@-Ȓ@CXO}ff,TPYѱVpCl^SXh~^$p$QMͦX^Do?f:$ wۍԛ{&.1O4>MVK$PfLw0`lI 9BXAᏔs 3$d_L>kO-)tqȖFBr9լغGF9ڣE8t:Ϯ[6z|um`y*?#Ğ"s[XSmt "H%tn&M8XB,bO7?Y"I/|nXth&Lh4JMM nwR&Z.Ťf뜋(%>mR)L]TF:'NixPrLL&w*°Gp۠SP|x֙KAzr42NHu01^U6r鬆e$Y!P[!{ÌFU58ý}h !T#PU5U>'ũAbOuFo/E]B%d[hN`M-˹)"Yx08#c EQ>"Ps e SqC3}&k!+77P!7v?i>liFg)`VYS(zINN6&NdLRB"Roܸu >;e0SP>r*"~;!2y!qEQF?(Y*0q*:+Ty4є<ޭ=To R4FT#O:E4m_!3/nk;f\Kd{MYd>c%\ .ezUu+R>-5]c8baFCezdR[c_@6.{ :ӚZ6I4&Re::Mh؃D|V<VC˒/}20H&1$+a5SWE8c7g9>矵m'OX:͓<_o3Dv7\Јo]p׏y;ʼnNKرQ&ъaw!?'U"drvO8m;ƷyH/`lz5{_[>~?7>OOp'>ի LAϾ_2̟ sߡG7fwsvO2, 4GZ]Ntz=5N)>EzT~8D.qK:0sAϓ3!<6;@d{:'ֻVP}؉͍Х`Ǧ,&$9i#ki_7&RlY+Tez. S'8w <ÕYRy&n+ 5 vBh΁UqWձfk ]#{8kGR<:n&|}`zGr{TcM>O%oᚆ(g;@)t 6 P-a?z}3u5~e moGOf0굫h[OGٲmu<Ι,o0 F8vBSjv>Bcjd v֬\n9{GL3. dɖyrbA `]#aa<(ORsmM-p3tu7E\g4Uї0ì:Wa :/=à,ٶn[I۲h ZٟMmZW:;ili£gf` uk:logc2lwĉǣzD3=J$* ,7 . H;6T|V;zxR(V69S tw̼_ri.oU I{N\IZٱ^15F\׉2gWN).@k19,*.[KX09-S?cV>|}'A% y* c?m]`dE6C了ԷoGNP=6ڏTL=˫P$QQv+.N˛19zރn:YA&2\Un'YaE+8ce\ouvb/Ǩ"(޺A?O\=( 2CyH )Ԯ]J .dHһ]&w4ͮD2wp]p城1Ԅ8~{ٵ,Б]>j s/.b) Y$+n__$ zi݂wCU$"#[e à$dRz7ۍ"Ie7@U$L|p . ǁ*,6^;f KVrpRUe#ɓ32L'xLTz7#bLT( SOdN$ P&Q)kq9 ?.kuĂREHx͊$Y.Bom >ͧ>,#w\՟2qл~fq6ͽk)>|qz7c|+Np&)>y|8aG9^A>p\_Pyb"j{~c1';hm'|K$Z_|'w.NX}|wo,- }#d߷BYֻogdL+z/C?'W߰/@7!?+O<i~#?g2>T0=d_{_f ^\Ar A9&һ r!NSmh[4c#Q%}Iz757X4F$F1}^$6tD;|D)̓Eռs!x߻単]2\[w׈f6 ̋_wRHˮ]g:q -au^יӛBrPjf~/5ÿuY$,Nvh,"_ X|g o7>&g-'&Xc0)+@ ^ Jޱ Z9,` )\.Gooow qWp TZ,wd")$:i滹o7.g۽+&;vXsXh>cU;o۹<\Fim"p`^m<'\~̎~P'oF+ ߖ\WpW_bֽu> sI?g$~E`^\yKw> MY(`V`^<xЎj)r%Tgߟ5oT+ ҟ:Mo 2 i,3[%xZ/,ad(뫒2dw y*<_),BCDɕY2r_,&~uaǙ>o|őm2E70Ǽ3[Ln}1b2Tw¶bQ:s4|g7ptyyO we̋ "0t?}8Ù޺nFNq]+քQ,#5lR8.4I!g{^"^P|wnj@wY3p.]YM߸z>/e4ciup/4v]##x׮ᴬ?`D)/hoM[ȅU^l\oS;Ƙk|XKY<{U\ 0=ј[67b+ nKR &\4>BZ_ AbQ:%Ҷ,Y&_fa]`,׳1Yu Ue{)ΟyGaK3у(?VIK#[L rZGeR%*N" v^Z>C,T}THL/EZ+V7۷ =5ƙSd[;)Դ#RH2Es>5Xi m){Α*|gM݃ BVb^$ORLL'PMA1$%I31:̸%D{c=->|:N2_@*=O2Rd(Z]hJ9BT`jtq0AWFV-~40I" Z|sNaHmOm[$qwLj7Q2(fx9榽K~dְkYc}L5GUܴV:Ş3Ҥ U;UV71+klұ~m)DS}ٱs ^6rm-&]-KttMA&-PuMzdbS ,LGG9pjv($c^£݌gڐgVXIyǏ*d0rp0UYҢQ"2x00 F 䥔ϐԭص 8ݸ,(tm-Jտ8Q̲K݋]m4]S&w.HzOɴ.!Y"" cQ$iOOQ:먯Z&$2I׎U qo_` 5ViAhK}m;L!Vښn4C].%A}WtL1lԊC/rA4D/[0ugOaCl߶Z&x H]pם`9 LCuʋ(vV5SHdGwlTm$M4aA]4@ՕZA'xza$Pk9} Gs˭F9t4qN]KŞt>Jp-bw'z}^|NR{!3'8:e04 IDATmc6^9& t KG&Hj5ϱ$ndy^:ҍ4bжj#ֱ0eo훬<3]n S(xE&.湛Z0q 0ȧӔ"YVb~Lks'.Ofszim`dLzQշ5K$'J M2,qw60,Xtn;Ie5rمIohpn2m5KϾL֭ln b Ma4,+VխfbaZbpp/hX==Opv2q-W/s0[h5| çgO'ރ=seKnM%)*qNOH+;t&QFIIqf>17Yevօgx󏲳?-]Ρ[N+t:ه:Q(lf"l2N |o $:#W'VgyzytL`wXIٵ|97]4.s}[ a`%(A*M+P&gN2Nja.xʂS$luBLpr S`yPROY0sd2E8@6,[SƈL ӚvU@|,fqS8-QIXNa$,$med"#<%!J .Ro?CɂNъCA/^m&]`%4CbQ$ HO($ AvYLjlX~Lo}[g'Bϑ́]a2yaڹ}JȦu׉jdy TS:V@g>lTaz (DYbw mHD+dH ~I/0:dY% P4dhO%0؝T]3"gpy68xTnnub) X*2)$- P8]F^UdK '$J5 Sx*3c\>|;̙]QK)b#wHSI9 CV &Z!T%-0W똩G"N$]=69%ƢӔ@d|0N N$<>/>{9V̓A8@˧eb'< >uuyy 69IF3@P>Bn g2p9Qb>C"/Qtor:iUAH%Ҥ4_ ˪ cλѵ"Tֲ*Q+AzjN+̎YSiR6M&Q49,ȆF^êwvgL$Ih͆㤐yXdx< ="^Bղ.7.U03p&D"bxWf)xMli ! T2Eb$t4߬H%$iMBH2vZ$ʓ/jHV~U&(5I, ./ U6=4ǵvn^WF& aȼ#Ry3)s&g\>yIsQ¦N>KGĶ֪OZafcI<:PVսȌMΝm~d*9&6 fgHԭa{ ̷KC2VhO Jb,.uyRE]P1҅5s2Y\WKZg(@oX-)6_Q`fOkѩ8;LSs+\ D3[XS05އ^Mȩ иf~S:-+,i3 QdO. J* /vX0)2++Ѿ|5[[]g((*v[1?&UG}] e4 w\VEȩ|;3L+WM;y aD"xEuHJ$MZ@h dj({NpJZm+y/>m&dQ=^#cϜs Cyi\B]+^0sܺZ6{M鏹wAt {khgj3od+ 6a1&cxq[$[l4X"B$H2"o(x^| Բq0*b0n_C[ YfYɗ[:D!Kc&z2 Q t) I&\{<,&esϏ].G4\$Q0>7vd0XVzum^{xm2ө%J@­ob(dln.QX aL@&^8Å# wq߿6rDKo S)_%4|Htpxe\Xjs=9d `" E&x|^F"![2M4dFIHK/>D)"LG1s]&b&` յYt]-[N^]˥"Sb2U~g !x2=9鸓)fnٽ/asX(;0/#I|zs#MNͬkg"_Fh,bY6 $2zBJ0׿V,Z#Z,2~M@K؇K3.Yw$HZܱ#r,c{H`KnSH5`h8FS>OA}'Y8U Ww૰ϝ;59g\^nFf%l~2$نÕGmm ֪FLz(˧l/exyZ豧H^Ck؍058;\h!v {'UՄ7q#ܲB&nAߙh6>K:+$4$r 2bkpfܱAZ/ꖹV*Tp JfյzMqMĉD6D?΍W9FClݰY62gE_k\&Z1cTJ8yaN:;|ҼV#8`eYCeV܉n\n–癗ƹyöwZ6qЈO2ZZʀ9 paJssZ~0m ,':NmZu S: 5T; Q|-Nѝ(msgy9\ՁK$YYQ.cs&xF}=s)LcwucV(V̪R_p,C L6v dC gQQ0*H!00e+;E.s5d{(lŢEspTt, tk 2Uxvy;w2 C6 BWsly;|((%Ăʩ &&MTia׃_^"} ; 2P>ߋ_W0r9(j4) i)QGd PU!k<+WYwe2bC&'xYv&)D*zjұԜ3LtoαJQ63V!Շ̅:9,&f)ƃ*. !x~Z[ryOXӧiaTyĚfGDXYK\}?ehN6a~RŒucA"<ˇ^DL[İq۴q7zx}eMž!N~#a>A<3%nzjoms bqc(Rp{mI ?x(',}hsŒ҇󠹇CnOS'clu_$W|%[N+TIZx}3_l#=\?NgAw(]kRY4;j?>u?ŽD[il};D*W |r3Ξѽ5^m2H]T=%Wci>n##T. Sn1wkr {9?;n"rWA\{pن|oFwym{JV<}:Dk=Hq.Ȯ=CsyktY]آ#_kڹ/W*PW~`n,s])_l^> $KcjʠG#4}O ]!6rܻho~vux-rsFqy[Eg٧^<}U[r LWˈȐEQlt yhNpX2 ĮfY \X1=4%IˇwKEBقfVk33V .gNSH^ HNLI)ɤ$ \fHf%B(x~\hJTR$YZ躎a'+p[5\%ق”̂^xRA%ɯ4oDW ϏU5@5!8=^632d1!!UD.ED~` ӂnV*gCez>iAinFpdtۉ.BDҒߋLljb*D%~/"3XkGf'//sq:="l# ,^"QuƧ 2 XF00ed\ TEc-\Ckb,͑ӜVDEK#Oْz]U^3t`(B,p3$YT~/)L;^~IijMd/;hT1o9d>~_Z9j W+Zm3xZ!]y)*bKEXW|=2ǢIRiFF$*2k)'CsyqZjvL(B\,2fz!x5J WOT/sAFZy\]2fUJWQU2U执 IEɥBkq* D$*:,BMbq?RFֈ0Tˆ"ᔁiتv-RJKdNytF*%it߲QJ,JjΡ i0p [ʜ䅓gIhd (6{:WnToE~Vz&oךFF&xp.,@څz<}k|(bs?bWt:ᾦqW 9bi;~}g[N,|)Vq<xua*9//y~cXtȬUL2XLcS˪V&X2$$KsOVw `ӻ|{(8x_|_|v1-|@_yiM';sٍnD AWY*+s(geFAS p.mb1De^2kbe) DI5#wh&G~5jﲪLOQϦMK_mZ8ObٰUtSd;| ff{2 PHS C2"wJĩON0`knȾ0g9w.Y;Gwt4a#EU`Ud)prܿpZ{iΞ@(kUR4S_17#&ɕe4f 1Ύiw9qTCǜ?w1+;3"\Ife*iqԈK,^[֐.YOoũW=aǁrL]iWms(/ wђ$#Єd +\ᛇp!Fpftr9{Ts[aę{xwvɁxrFǙ489._1TaFr3g.pq֝G8e#5y.OVN_L4C8Lm dy1MǩdQ<\ʰuklLNq74N<'N_b2Gliw'.t [rDY\A>yx&;qf"I}-8@l][s~:Œ\lօl&#,:Yck) I~ޛ|Y43}"GAߌli% Tϗē8lȬ_cgw ntSA?CfLRy1 ?΄4VSCf&Opql8-66݇P jj7w7EA|JlwXg49,6f_S+7 Q̪Y*ZIoG[kDР+:z虙' pW%S\?r7{2J`w/hٵv'݆tE ~bx: 4 0l$I:*rAw Oo,ΙX;Z9:jct33׈ cld%K^C&<(caro,Dt$c*.uB(bw61ogGhT/.1pM2 \:z6Y'4,޶^4lѧ TzH?\b>G}-}J'0C{c3եcч&l|ڹ|"iaVZL/$x EՋp}47H3t"M׍ۥ=;~%KmC.0Mu4b q &lJm08ډOLp/g|vS$]SzyV*ӽ4i5뜸$_0M&ǍSZhq$[{L^X$HqH#brbɄNV7{ jpWˆ"ZMv3k`r3niY rJMŤionį+5%F'Xf"`u(ÎH0Yx|ܘp v'n G[/pK sI\ ]d6az>5`jncpLzrL ?G+܄[͡jf dH3" *9HJB#cA|,(qEJ6!"_l~u]n,0E/ )%4v|Zյ`L/\T&G.'fw'gV ˣmn2iA+qQ觧|NZr=H4DRrZhB!F\uYԪkWij_FC^}ݚ-̬l*3$ٴ%YGY[烂րYQ]|&;$|AUfKaX;ٿ˵ lnְO0=0ŭ`8Y.Ft ي+dvHy;H^_v<(q[y`T$_J Q4E:NeI_ ?~g 7o>w^z|5?& ՆSd͘i&r$DGWb2i.',gx3ȉ'Buu|T6Ic=od":㵭Lq 58-f/ʣ kT>=x7"J^eV#WYKֲd)>*gM9yYٗVJ:ωvӾw.e;*߲w8**({*~w-a}rBYVs)%F4ď3L_߹xVi5s%oL3X})+[JSxA|Nέ^SzʧO#(Ar/LJPt'ylVXr!Df,(NJ U|&yK_ v+F 8^0GN)o7Mz+i ŎuԆaG&> O)QGxn8}PsA4se4]F[IXk-$2\Day26NkX+.e2P,S8b?k"<*-Mσs\(yHp-gK#r$ <eY먣o%r`H''kB%E(A4rzG.JjOzyU~^/(O^dzc]Km+(2y/>=E-w<.D]c[>(@ 2aSbz:1=#zK'QJg͘!rV@A9۪(B|czò%j*$溓K2SaC}ð巖m >IENDB`pyzo-4.4.3/pyzo/resources/images/pyzo_shell2.png0000666000000000000000000004763312662716363020146 0ustar 00000000000000PNG  IHDR!!W pHYs   IDATxwpWe7(_(.Nq7k1,B,#e(gVYcdU]K Ub ޒ:a2Yq= ܺ&+6ٹw*_(HD&!i|}X6l)G#j8vӍ>)L:Bd7΄Ca@2iRA!Bcqa yqj  0xYtvOa6L:3<>t:>{Ak3dYP=~eAY|a[JEaVʷPWR=?ёey KHĹct&;P2zkDG%F=O0\7r&A'kx `ouIԜd:[[HFl$zGFյA"(H ʠ͉^/z.Υiŝ# P=)r6>#:3.+PD=n4uIHzz$!LW21x7W$chb5n[cT/K畢: H[?I$!4U q};+O>E;Z $I cǙkV4k6"bš1hFۺa'xl! Vk`e}EAՠv e$N%~(ƙbҚ95,Zֿ 5@P+N7# MgyoƬ6d"AzIt79n#m]rEkơ odYS]LxX΂7׃qs:J.n?._Y4w6r3:їˊ~ ~kpiض,KVC/UB/N4We[j.a9pކcz*VnޥKSfE]w7'Xt> sNX4pX!Dg'$G8$1??3/a1iv '9|^ʹ[BaQ旊z,. i j$B }\>:~A,̽>s7PxmiJ G-ګ_i[7xIk u9z}d{DVJJ&#%D^kc$ P'I$2iLvV]ǞGMHx̚I8~$ ͥcUL&sI x/*b]#SΘ\ibd|NttϝHwT˲||V#NMRR\#޵h!cDIv͇wp^01gArKk8m@Yi!\ S{Yaä@Tn?,؄$* $BY`[䙋}/vI=O]<.qsZ~6%DLy"ʀA*:X%,z)&~+,IXu0T?4l3onUP]dtuuue^zI|gƴb+=Nv Arss:L<Cpχ .w c!Ǩ?z.S!s+g/r#3TΚ,]^U$"̪e{lL\:G/1PwQD#Op:2>*kf}3U(mmm|>VE8:͡*LOGI|ԍr!h%@;X /I7j_6zoW!(0o"AsHXWP=P0loy|VN%sd22(|P N&#)r]_6Z%˖0}j2 N(sWQS`ssUpoLJmKu6rRQv Y2ҭol[T̝: d݂(Mu;zB_Q6!QE!AeV^ V]H Uue_LD"qϬ2P(D<~,0/^L^^pwn\xβD"c,P ~DݕAtqoc#e_F;|ML&%aCmr븮1ۅ*B***ʄÁ^a ФmoBP:fݴ"r5xb4+'X&E"l6V **^mmmDᐴ#P"),,D!;(Q G2Dhh\: @ "OFV u§E+𲰲#ȏmVcL8xL,2iIkD3]?RG΂gyhb> Tq^z4F(dQΟ=C`-pB̞όR&=xѵ9|0 x<8ĢHÆݵM r=,pQ@ƋmM ʄ1Aǔb}I=L.rxi .eh73*'{j 4EL ]l,5 vOcGEq+6/vHtj] 3ť h6&(/POrv>b'oåq**w 6χ[8NLfA hD ߷wL*)uy#&5TV5(uئ,,ׅ-}#sshD=9~3eΒ04ep~|y4z^On92 |(s|,-QQ#| uuuUp8,VTT| &iTȒVWX*x a8{X :|G%6sf3s**FED"fW D*- xtb+ ?ը<*$_΄!Q$*=|52$Ij@ %BYYY? ;N6\΄!A0 wUTTTF0aDHEEdˆзuOE΄!UTTTUTTTUTTTUTTTn;#}& Ie[>NEN ˜A믶/p'B!N^ѝ{ַQ|=MIZ|*dn { gU/(zG,ױlVDבQTR wTUUqijjjXr(w Z`QuANvDD$zXLk5zWeuo17ˈ(I$4Y9< y!X :b=l9F`F@$lo%=u&mL:$Ȳ}}>s[ъ]Z`i14e1ɄVVXsӷ/ 6Vqpluu5QEH`Ҵp?.ddE9GL]=Ǫ{˸>CsqEpv,^7LO&e- Eq,ֆTNZ1Ɵm39~-Θ!nBcZ#]Ӆh:;:?LQ* 1N-+uNOa硭5'cC 1O!g#|3Տ0${lZ#,gGy,Y Rh42 (gƌdJQwP6p!8tsv;1hƶ " 3):I,gޮ{  ]wgɳ1cLU~c*wEQ8}4.k 4:nɲVd2MeCj(._ϯER.)j <Wy[=%<`P\DW_(pOh2N_ R3l;`6ǻ***h2 xkc2|BhG~k**Ap:ÿ_:nl{!9|TTTn[IUTT[rBsM+++++JdY&Hw5TT& `0tP"iooV/UQQc.P"(**p"rp8L8VEHe$IBokoua%/{]D~~>AeYEHJI'kwC7B%RM Yn?^ˆx>hm]Ȣ?c:h` icN^+ba8b=AZ=z v6ACS;HA!!u`3]ۤd#il4ֆx_B!qKT`F+^h A'V!bI% :,w缎N/pXclIJt>6z P@ K/=ڵkEq8XܴCnyd]tmdYFdJ)CMM3?m>`(ޏ!JM(zG Ҙ(R Yo|,:[5 bsZ\bjPB762F5BWK_tuj4R| F_ P3j?BNO($m,yYboafddR^EEcJ_lʉ0zw+TcV)ܓfP8bh+Y<ñiI[I>r^g0Bd~4w ±-X%tf6)Tʤ8N5!xgpJo|&]6: m~f}6zV'tOEX>>?bɊdO> ui=!r*Т4Aq~ a(~e>dj'>#o1:0 IDAT}_#0w9p9+Գp=i׬8It5CO>8\ Ȑ㢥w(H-[ף`kl@}X4|Gψ^Rn5²r@j[$#V PӧOcن{E73ShM邇S1 'uI3LKC9CM+[=4c#,3c'7adi*+b q*Hp1Ѓz)b҆9;R[$Ȳ@xw#od6s+eɪxeP@˗dE& 't|'gLEEGɉds'n09Mp3ݜ?~ͅ sRk]FCUECȂ%\>6 ՗f5p{!=^2HgHЫ'qݗIX"Ve<z@| 3g?A-̿,JM"}8hE m4q/f/,()Z/g%״T""q^1QX(^)8.cy^uLK3M_?𹿎?.h9!c[dgl؂;y<8-g!/ uc9Fۅ +͊=;C^3нTc=.@(l…Tmn:#IH2=<[ȩ^o|p3ٵ ]$d%)>Jk,s5XRf@hHR2ƅsg50؋ 9J =4%2}#<!E}C %)'ˠL֨ wΝƜ,"X=U(HK:AHVdfspV* (v:k&T>:Z1X&sط~ŏتAo2c?ʴs8T %˙ӧ&)(9SD[QR5tTf,r- 1ذ)!N:MցNddyѬ$RvK9 fյ3 d '@aʲ8{ 3:<߰gq9)+xӑ~\"tV-΄UEI Q4cZ9=b=s|n- ЀA5 7"qQ\l/av -{DtL?iA+}ܐ]k#wX= E}ianlld2E*GwgW^>z ílIzAWo`qILN=AiЈ2.o.r`wDݽ:Yv76}>Ij%C/DsAВSP@QIIa3dV4B=H:3>D2z U OB4KGbL!BI6v=`b 73hV A)܉Ε!Gs[ g^wzlȩmm$ш qgZSZ;z0ؼ8 :{rqۆz mmM4MC,̦ӇfBBD|3aN@*=,hqd0IQ:B,n?SPܼØt>0$)0$ ×"F}**_϶#T?Yz]BNŹԌ,eYHZ$qgf*?"!k0XX:n.LO7 FT:0yr0_/ ۽β͌As(D4!a5I);౛PEoH,GV2>uj/SrMJ{ "(l*&-.L$`/2Pde^69.3-%dN/LI0ܘgXVe ?\[Cg{Egg<0.XVY//A{K޺g7Зk6e^ -ͭ(z~'-A%EL4c!FKs;z!ݏs퍑kײ?3%# Vb:br2J2#mRh v"rgrEctl.8-^N0wpw+ Af1Agޤ]-yx):Ca Y^=Ze_>չVDl/f!IT60Bo&/ylI9H5E~?bvEe9t˙ڻ-vHdmsBLk.V`Wm/K`TVvnHhj|i/h8%ECgs^>7+4rHG'9 C~Be3Kx~uv__4$;γpa//#ٺ/>(DZjpEZ?gڵٷ'tF8Qg,祭M<|bV"9\2ݓf7ɫjt\w6SްE+汫N[jfdI|~ܳie*2rXEIqlk[1Ag;vpz~N}t&o;ΪElj]YQh;`.]MHdJh.O3uߋD1AGɔ" h1mⱳO\&ޓ%C"*}8@x 7!0i$ MXH\‡ n-qyeڍ?/mJ)LN!td)1LM'y3zSȘ9}]39S w2}K(DSS"d:'` rk 8(++Mg݇!ZAtSk ʊ*}\|}-Br3'O3BU%n{]ūyeBc=fYtQ,SgrV:Ũj( "ja>HYg- "ܒhR1M/EAA7˙Z;ț]բEDFD5hDӧR7I Yjf_lc?g=Y㐈n0VED4@8Јf%OLkVLsYJM'p,ړ $W&m7i; A;~3:=f)Erur:KgP\h c͝R]+;6nefqyObק5Q WVj-._spF k9uGc]y 2!f|42(݋˔f;STZ!OK{z 6Nn%lBN2q@Hu@!#v]`o Á14 Jxf 7r-IJiE$!$GB0cH56<.TYDLjimi##Hwby??}z:vci4~:$ avQDDCvhZc^[wU9s 1ɽz'=;"X[P6B$/m ۄ5NlB8,+G|/ѥU82>}0` P7Tp&UBQΞ=;ftkP*Gpt>8qtH_v<:M:؍#AںpdFx]Gj1ecTh3AEhP ,>'fmz<IwSw"k i/'ٽ<=OHN =pB[g0O_2iU6;ۨݵEZ='J@6jydJh敷>eYpwq/ƍ{ȲB 50^*DfBR[bS1IոG;(^ZF8OG_?g5d5/Ǥ?i[u>rO6,tSJ4A9s)USCc0R9SK>d ?œ 'I,9r+x }=h>g#a)s'S:TƳp-#_isKvvÿsr).*OjVR6I<[!>A~P4lj͡Q\TVVp$t+N*++8>z2(@~"RGtVf=}t$ LHJtwήQ"zASu}ȮR'>6Eqh?Qc`%揿A*U[O'TStqA_R <3abH~,\Sg>lrfoS,__-^h%scofk"9SWx~ID>HՈkV5{wW t-]lWo??I;Tx' QyX 1s~BrM?!AAkb,須brcLHfp Sur+QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\QEHEEe\p"mM쨢r7UnB$I={V͛rQY6D(??rJR]DAQئrp"ˑ#GHWlzTVVb0ơf** 'BHYfGنdY_&77K1:QldHZ'x*$OX,7^f?LZcjԍT!Nw\KυpZ tIS7Ua‰(h4DQ@醇cC(ӼAn<Ζ;O+-h BR!i $h&D Q$Bg1" סY SrHAA:@tvZ"-e_KLS/}IKh8qqXC(dx}ܿ~B5ݫ)& ~BӍMQr'{ޫ/NP8c ?zr)zAvIfgio >|I)q`;E1ܸz92!AF]F!ȁ ^8] 㟶"kEVsm8H>ɱ3g+ohgG[U˱n:~:U&|{?hm~~x7ob{oq4(I8yO?9.F^Iqlݳ'xL$-K*D0Ma n8DeD$[-+%Nc\r\nܽ&q\8Ve5SJj$p$I,˖HGx>o8-]?쳻],HWyB#Ͱ~ Q%ʨc 6ⶅ8rsS\idR$MnARrVsn:K3*o݁M]ߣdu9J6D[8AEE u 64b4*XLfJ뭷,!B*n*j^prR$ƣN9֊l0b9M]纨IId{ifrqh F:}3}G2eW<FtwjjjZda$c%BD$7B+6YA2P;DY1ؿe)+ڇ=҇MJ,ɀf!DK$p\1,^t4Flf$ ua' Hp=q$Y $N]%q28'$ !t:M.uhD}f,.BekO\^5FZfa͉7# \AU#ɫ#_BhiTLot?ʑH\fH?Jk(X;Ǯ;]anK#%b5>ڋI߽k=B 5dž?eպXTz@qXg'r2AV[Y'{ȦIdW%4\_z ߝ(4gb-lti")4DoY bhz#Zl9`J}!9BH|drQ!џ$F(Ϟ"8g) ̼Gǐ?$yc|O?@}q-2AmXh#dZQom`NF5IDATYi>zl3+^#vSJۧV(BH&ju6PQX}G-eF?TOE䡂JD``F /~3LIKע$cYٽl=|kOz '_-dHÅ)y~} o5;~^PCu^"ab|w5S}½ vj ^W&H$d2Q\\ Hn,Fil6Dvېpx}מjs더$,fEWA'#ߵl6{{fEF(GB0^ERބs&zUADބЕj##AyB#*pUUGMp8Ȳ, 8ɛd2\Qvɂ (,,5w!x<B(ˡ*\I6z{]~6VUްƎr謞)ĬS2qMj KIEalzM˱6f|"Bj<$IB׏>/K428]}:S}wDM6|"$2Yl.?>doP4zn hfDQ".dCm)tE/TU V;\DqI1hd3IB UTgPgШ?a7Рr9tf>J.хJ(%}nE>ʃ.D %/Cj' Ø4RšfLl=KLө|mPٽ2Oet|jbUy3gf̘݅:6Բ)lx50 e jݿY.eo_trt+;ًoZDl܍@;%uwI2:Y"3&Ng%foVϦuɋQ<&[yB,y%Qtu*`KA ŵt9Ɏwwp{EkLR9i V %qtV^ lk/>Δ3G./awZh 0g^nc5MjS;3et<"ɤ;kPt$ 3?)s[71蘉Cwl&UI&22)q!$IҨ_}M !TMN|p&-q)sjh}k356J&M-˅S'iME̞VN_N4ba-ҾͻNq}sع{:j,x> F;CELrfz-;AMvO\q/;8o:I#N*vb4P-L.%3hsa4۩zV-g#m}1$I:;;rm$If:D͒đ \"$IbP]]}qxH\N7ǟ,[E^Јl6;ѫ ° [)*A!$„!$„!$„!$„!$„!$„ʻ"1Rb_>gR)!$`kjeܹ QUEQ/a|\px<>f !M0LcWP4 B:s|^\YYQcU&17Ҽi}zzuH =<6<Kg^]dn:^cYJ4GZ179mRF`uUwf,[skM;ONxei c?2(9wjHH$yUbj$igq{^D]-Ϟ,IcxCYξ"͕xz4֓Q H#Ⴤ  F B~ sx}k:gw}y^o!6py`TܺU+Mx;"`Tǧi8o#KSXQKp~FvY}S.[;y>z|3S;^Dg?`aaϿ֎lb[x5|-B.XŢۋ8w 5Kg % Y\ ԓe?\J*1@QI0=FQ.JYZxTLp.U=7_DaeުZ:m&VX:[rك[ywIL*YΆ]l9p:Di3=_OBvr:+ n{׻m#uk8s޻Q1lu\!J \; s;HׯrU/ ]y %eV|PMqr鎈w^a~n[ r^:E|Z~?1kEA+~~n͕ L-wsS1v;+8j y{u h~> x崁uv#THFNgi7̛/40k 6Sr=Μ{BM,Sh9AEa֓<ߣckfTN-â6?SZe,mnɯ}\h1E !o23tp=M [[p!L2ݜS͡WL2rNAQ{hf0SĤ~4XS̀b|E,r0 0(zmق$$W܆X`40 &#7%Ee̝_p֚Zґd0 pTVN1 ͫ0(=K1Ò#ä(؜2v kqdzNsX%!jPLVΏNs,Hɥ8foSmG8q%@˹&>3Ɔ}ԴBx?'O am:շWO3Vq؄[ >4ϯ^<{rQ*+ɀ F#mq B1Z(qن>3ip2JF'((_]eV?b4vz#ΈURVC<4]&àlAIvn7%E_֯e~E~ȕ=D9ewk4N{bn j&\?MӈFR)v諻BH<_X^ :y}|9/b _8HRla0l6OB^t9N'ߢ?p8X,o٣=A$ 5f1 4[HSh4^6,uU~mvf$JIENDB`pyzo-4.4.3/pyzo/resources/images/pyzo_tools1.png0000666000000000000000000004727412662716363020177 0ustar 00000000000000PNG  IHDRΦ pHYs   IDATxwřvf6礜 @w;;w Eq !$@B,Vg'Ow]jg$@b?-f{z骧zSȑ#;S Z8_k^,a]7,LӼt)%RJE2&`D׊1ѵRa DT t+!Ŏ;X@R[ Xk![ێO3MEQ.ѐ$RFhw;)E.T)6Js߮D8퍝(^ U\;/յhpud+7Φ""r%GX8gǼFQ|geDi#2"Y^m?{v*^dS;e|ORbsw/ǧ]?|<6/M56a+/gԳ((]>ՆPTl?9Np'@*$41Mj+#V nM x$%RH߹0ML)M4a QyN,H6dÂK@ Pdbik.QH`&ômaͻ{iI*d"AfVưKl9{#DpI}! EgJ`k߿^\)=Q_b2cN:ܴΚE<{Of6xBШYxWt ŘH&=l1cC;,4]9>Pd2Lۉwy]L cDêGpM9YWW;Fmiq0}jka}Hr0lFO+i@:hn+eKq̜<*kTè\| MT0B]k7x^OiNY\HAI9UX]ZI)t_&ŚۺC67ẙRyf2]H,8('!N,W_ cP,I<#%5d*I,b`Uh4I013<* Bjf=4 '9v[^L}$9mҭ+Яv_ 7ͽFPL%1U ZdB TH3NJզ@'5*wbv戭OrA۪WG먳%gjdL];Ca(.!-Đr_K5&qDzRג Z8qޕDeA#L3ڇ[+ɯ?iad2O/c 0;xgo-߉jR2.\rg$fG´S8~&3jqkB6Dc\V|Sm\i"/벤XqRb8- bͧЏݏw&Xrj_FFӊ:Ev. K;rSB{BSTUYK~2VJ)I&x<TUrT G/0}O.;T}/CXn JqjR_w&&;;[>>7IltyCA}a'bҮdtt-iXI^^^z8E$%$9N4M#??e8';ƈ ^*VwXעZ[[RK)I钓8yNN%A^-3SԩzRJ‘qXhd'CR7lD0 T*;zGtuhk O$S}!ihad M45wuҥ9 rF :ܭۦa:9<9iPYJ3cs셻A^ܵ_!z4]abz)z5fi, =ɹ:,885XisIbػLH~iXs ]y˘:{> f!J$~ďL`oG\q&= ?>B'J/B"4J}`,K0#ٿr>f%畿מ ["!`]4n$:t oXDxF"=D=DQJ47Svc̝RDӑͬ~^miTfZb(h6rX@JXG3=$ A*Jck (V[ԱAH7?i,B}\AS4|շ0Ps/-LcLbGp+Q17soPqJ8s,zt]nD]Ѽq>ΦSP[';yT)v)GNٓɩ]wdnu(>6ζ3;^~ϙ9K[Z.e,.i?5+^a͌,p`[ MYE D߸9=z5_Sv?ZK?E#P"Ϗblt^YOgYN !IckZL^Jp3L;7̡b0g;쌼q$>3q=l8tneҌb~b3x4#VaFVw3zXOBM;9]2Ms ~NC܉nb/ aH4Х"Rw,PgS38%~9~{n #*/O#ת M:Ȏ0!-C!hYߎx׳6?~E)P4 v K!Hu 3= ^6*6$JX\,wLJw'6+`el?sAygpN{-4#a{t,8NĔ` !LV<0#BQz =co-ĔgP>q$UkXyF2IbX):=س+EƝ$n ?7@šNe_K} ՙǗ}TiCW]4N [ZRJZ+WQ^^>F;3߿ĉ"Z]Z7EeiH-nOFlb$y%N$D8x!ltwzޗ7·^;Rr9'ʦ"wg:Ohr_%PpZzIv8ꋏ62?څ]Esx-s1~~%$#VXbJmmp?[l}C:ޖkљb09a].:ְ.Eipw6+bhhXúT }5O5%MJ>_n6ODdsαj*::::Ù'b> 1Job1bDZ夽8Q]%RndkRTTDiiUa}Rfw;;;!//łХvtZ)OIL (4&d}ᰮ_I)ٲe G?a*qK%PA 4.$ pJ֊}s+TUEQ[fp.;<@AShXZFRz ~#} #qZN=@  ͍WK`VRdKLT/1#4jYO+;=h`pՁf說fW ϚEXH t3E$& I2{F8i^_VB$zOԳezcbGw~gS/?gj!2  Iљh3ց.{!wEЂs Kph@M0ٯ#z9{=de50Q7MCna>>5᧦NG bœ[@A@DMT@A˙$ olC%|vK9NhK[ [ZI䔒Eion;aP:s(,"BʹtKHaǗ_@ǎlLSE/0y]d֝m~ 'u\B`^ i`4]AZM,'fqSauϝ! bE4_p)VLi8fD Ӛ59/HGwsk^cKY)O.w}l,N 12UNV>zs -}_hxi/GGkf^i?~bu3z;Vy4~wv=@oxL~2) st u`EQ@Dzn7áM40dƈAW/ٌ4"XNla R3aA)2cQhv; WzO'ѱ5K0En]ZqWW;w|[o쨊TF|::DGÅU}~NZnz'%N fqc/ /f~j+6KP|"VKЦ\wZԐr$!Mti2tK$ ti3+`E7{ O|IDB-s˸445KXZȨ8.Ռ+-waLR{TNƭ b(!j8V϶GX+'Sؽzu%̘˱z(̝¨3o,?}7TTBGju T U@ןTVVfz3A@G'Qh/ML bfz)$Hꧤg/۶aQtuTKo,}I݈z @O XK泈.uCRRPJw2V&tl4p,@Up*͆' Á=)<9ۢ) C孋)$~DqR߇ܰ~74ȈV++*p8>tݙ>]dDx.Wh*hcU",i)Z x찮k]tQ5i$$)#E:Ic* #쾣AY"փ#ؤ4T~?/DʑD/ n4ՈzуaCCÔњiJ,\o4&4w5!]979%|dj:M3As$R&m jxz;Ċw{nyPK͠^־9'QMH0̅%$X/co4tZ(DR!]i‚Kato K0I3F't66У$ +HaB]C96̞֋"` U 73BK(@tjvq\;ՙK]+_̦0%E-A*8E3Ѓe5$j[,%HjH|FGIi5IC4>AKi'ϗPZm4chlH AFN~9.5@im)c)g?y Ⱥys<:u.R6v*/㵽$R:-13ZlϽu)TUll 9RĘ{ {/JPM\s=?X76r-vq|L}$Gt OӖ0:^c#ǿH_$No@aaAG;01?SZXDžIVǫI|HΗۋ`)q $HKI\*ҥes?~3F*P1>7a5{w^*@Xqg0%kW#6d2IyMe? &lS3kvb_2:xX'M퇮 I%>+"FΘU/,,g

n|]TtyOyÂɯFpff [G?Gx٫;w4jT=XW?AJѰkתpWSpbSA =Y}"g!b׾N۔x 8kdiTpsUo.E!wU}_^$`y9DD"P>(IEl)_Kɻ'og\d rpL-膁iaJWCVT)"t҇dc(~{2x~ϏrCL qH1`kʄMmP<,نt :i rT?K?%^_P03`4()ljEcmRJ^/˕l,?]vmO 5Mnc˖-B+*[n!''CWnXX{<fΜɕ!@44hև0;a!rARⱦNwOՑZ&,&I}&>Ηh;[ޗ8& eXϬuFI&R:nbc:^|q3bt=û#1u_k:΍/JW 焕' yAú|]u.ttf<6W  #G%=)xl(]9_ 9JL̤1yEԾr"=G~ F|v"&b:9f#BgnּdN[AJPp}`DFIm\IjH#<8II,E ɉSXV<҇z1݈6u;=D0;(k+do_|צe@bL灧Ɠ2 '7yTzqX[}ISvongj b!o=|/Tx\:(5dw᱗%7ue CL,. Ҳ߾ӭ)>Ό\-}8F eğ޲]#+|>gyXWWY<{)XZѐx֭[ BWD6㱟.ecs<{l*a]dDiijjb,??`08lHee<ڵk BiHUĔ&&HH+vBnff͚sm,ǫAFd555X8${LɛⱫWfĉF)QVVt. N@kՒ Oq)1N|嗸k?WX׈$E\ V'tn9;}?4vAnMLfK3@v{^2B\lנ}~7 !,\c5%PK@W9t uEIZ;2=x=[e(4qf 6j"}n#mwnexwhq}!c,fMf{5^!DgB鴻{72Ԝs[s :^ '7nλy~SxzxJ;(hdMRb$t5!dgNrd/3P#}Ke_&O?Š^bE7mm9#hcp39Ou"2?8oNJ)  3`-c)+!/% Bg=|#͛s'K_|晏g':Z_6O`de;Kxe92B{S3yrm g/QT9GE$? KV1%m&5}1_ce9Q"hc{V,e">; ԻfF\UVU V5z$4ƪ9RIQ2x;ǕQE;t M9cORinl Mq:Pshj 0-v<7-ːapX4U` O`yh1HZcR KE[PrW%$G,V9'7!=!2plFO OU?7${0R&$ !$hͥYi~5,d̘("_ vܾ" ݴAM{S7cpW1 ۊ "Bm;m?˸;JID,)R$n`on ? R'%lc#e\8Jʜ93;Jt9Zb&v\˶Ý+vP(h|;2HiA~͋vga2S'vF zĢ1̔V숬@̲jd`$f9)Gl^MC\@ʹ*LEY7g./Z ]~@(|BLW~LkM)ek@~)*}Vb1v5}k~~sFSwob*,&IK۷ofRV68{6F,^ )$F$6ϝ'ZGia)HpsU}^#:t .=Vi="|zL 㤴ԟ^f$n | z Swp]f,,$`5Iĺhij'(gN!E~ӧ{B>ZtǕOυ ܆_NC{h@旑%ik5"جbr" ˰*&3(A*ɮ9`͎Cm OgwC Z{R"ނb<V W c}%rn:[A55fD B 1MH ;yY.fřSBUHѰy12u+Wq=Ft(EBr)tfD_EhJK/nwW;ٽWx3UP J*2lHh=lx$q)(mM('׮ _P^;ks/{/S:ŰcĬ?f;]5u2a PrևTQҐtGAlaC¡H_vaKLmwMaw(( `\ZLzvB1,ǫ$bMk^yMT"J89?l]8lCDd) E=J$xWwz]łWxikH# h3[w?ΛfsrdΓM‹[f= *BP9s%+ lXo){#g^a-eMp{G0q4nƕ:cƔJ:C7c(;N__OK1G2 wa?9cXr4Dgj6'=%ʱNuH`T?ߜ|9ts&f|k,!7S]U5vv5􎬭ƥ&9]]w)7 h&˓OEu%e0}Q0~B&Jl߱IvYLN:.r;R*ЮtXa%i"FM¹C[Xs2Ńۦ  Í}dCJYqرj z tRq">`V4-J qD {QGG?uaS> ҫϽK2멤afѨ"2B*o WD^LJXp]̷3W>Knߋ`[dβck)m:w 34)p-QZB1b '{(CQ'òVеs#Gf3'8IR{\nHJJ(qcIϊ UGBŝ"h,'9vp 5Oz8rUT<z)1f'SI&\|~q؊'1r:`ڨD2F*'5h>Zsc 2NӮ7xHHL- _FMtie,~VX:"$487ϲ7+XpW~WGC䏼OLH};oEQHtZ۸=d;Y5nZN/ћ=,ZR"0mMt&͎'GAj&v&%K] FGAhTEuiǖOێL%ik;!T,67BJp{3͝Qt,\V5NB"efڻ P,8A8TdvŽo vRYO FBNAGS#]1P5l.?EEdm tTEd(jEKFHK(ʱh:M?Xvbyt\}?ohF3ѾXl11 iBNӴ=I[rZN&!=MP`6@1,Zm3f{kЂd-Ơbc޼w՛~.n^i39q:=={2,ں۱m0AJ(aʤyulӦD,gvfd?J?iob4*8ՁHyo7D-)Q7ZmbF/W ƫN{+6p,dJd`08yY +Ȳի?L@OFtd >y]f NE#Og0vĉjW ɬЋnqpHؖ=",I#*Hg(cLT 3(+NfD8<n{D+cK;o:}wfs(&IBx\>E- zt)1NM)b;ˁ։334KJSuP k5GeLdY6j)[EL g7s㯽1&)&/[-7]ia=w7ߐ̛P{ ſ76O];7PկzҸ@v҉zr?;ǵK LH攏u5l0IL74WDa9B(vߛ@uI-n=6 <AQ* F~\.b?DE|ch{e;1ʢ<ļ`>߳-"V/w# 6c!ʈL}&#)q )z60b!MwG?fR }8IOZ&h'gX$A"Eq${̎hd_XS yCɴ\0OpaFFƯjؽE9+?mC,NJyBJ)4*N8Uȿ]j6Z e\ek]dt@\q, 9`ys<W/cy]dR2ͩVIDAT4>ͯ .Bpkyh%hqd6lJ$dҲ! TRl9ƙ_\{~w/_5aeaosi N?cN.=ټ"? DN-8uR4VRb䮻~b)e~Az&6U*'B/s#(\kv(L J_]c?Jr0I6;[bS]]覀n5W\y!'Wʎ fD4K찶ݘZ;zh9HW6:? hfA5z7:|Vk+[ÙC;Ŧ*m|5WUEAvn{ EVEس +PU%mU p?>KDOa%DN-`9WW[H-`۷ A!;HM<%u|ŷy'غʨF?Bf}e4CNl K sEݘւX4,-c*_MSԇȲyWui%<gDD0YY̢ }Kojih;̂*j,fqjEhIfuclͰ-"d3T m%_Y~$Q@P%<<]t<>DIʸ+Č 9/S G@ [:`9c# vAN@s4d䩙HU$/KN'(h{|2 Uw#3 6%zaKƥHHֽv'-eqTjOhݷ]/mᗿ= lt".IzXsM= k* !$}L"ppjQ3߾K/b*|be!eo˶˜XXS!zh>&ӳs&Dw њmɳcD n_dˈD ކԔj"8L=vu`=ùv2|"4=)TWEPFd{R9LA ~{1i3ҋ+h"^$#M_VJP۟tQex{?r?- ljAThv<+bN~0# 2Bf # *>"eQB^c#RTzcdJ)pH(ujQ[30pcJ?:q (*!:i,E U| ѺzK4L=ur=xGNOcèyؘvhM3 ˲*IM3:&'`Eohd<(b vAG˖-CӴqX>+_ cIi- ]RIENDB`pyzo-4.4.3/pyzo/resources/images/pyzo_tools2.png0000666000000000000000000006320012662716363020163 0ustar 00000000000000PNG  IHDRE- pHYs   IDATxwxוΠNw)JVnq&&Mdk|dMI~&սŖTjBR"%Ht|"Q(Kr>fΙ{9I&K,YA~ ɒ%K+P3ǑeAP* N%˕F,cpЋ,''SݩlvjuR(=gxDв|9M34{_gױNP]xFմxCǺ@)@sz=ttzD#tz=9`b244Hpdd\qe/ƨ7аfrMqo{vQx&qR,v Y0'ϒ|س{'%A0 L{vĕF 8ĊkV%bQjͫfptf XH3GYDϩe/뢠l._#-!f7$q ^ܳfQ&Fpk}Ϳ~!k'J$YWk},.o1SW3ONo`#@5nkfeر0E8^X2݆x,ӕ0ØLfj X|\۩3GѰqiƕ 㞷e^?t}a+, ߐLլ63݉-e߫oHPrf vnǖWAϡsۆU2ýx歃9n}M!vReJLT5x*l\O.B$odsW^GoK/Yb &K0]g^-:0P7e_G3}u<ryZZö$>BD,]`)+ 3獌AVaz1<.bn\ޱ+VbX'L֗ Ybۺ<07w=]΅x< Qpӭ\*%ub5"  !8Y_6\j2Fen}j DgU)c$ =zxm~WܗDK1D,V.C ,>,\7;E`Ѣ<Xn P`80X!Nl!b59ݍ"vNz3m* ܹ]FG8}0(+ c]MOVIDQj™aF"2.GikQ%0Dp3GmU@jf+26 㦻> Gb=rɄjHQIݚ}I%RJ1A˩S t29̟Sb唷֖ڬ5ݽTu(UQ  *Km<:#*k{)5}(n=M/H9wsJPٜ(m Z@|!t-þۓrc鑢$#{L+Z}/.W}z'DbR?eI$~|bZ'\2x{gU*lBͼ8Sm#CLWF"HwT;n\mhiiA샩g(S(NCNl6D"ل%Ph4Juu5zp8L{{; "ϙMx#!:;@m0BL;N:)nt^Odbor"`hhp8kZV댮ͽ̄PL$?~+ViX_o@dv̥TKb 많{RgZgz6 ꞉㗊BlxR煶3$Ituu]p+aTx>\MYAz{{I2@LQ`Wjٕ@:DMMM̚5Y`|>뮛f:ltUg,tuu]]jB۩P(L_,YΗ̥zB1I!"iwS0*jұiW]h+H\#|>U#0f3fr7#Kqb1޵"H [296L)Nahw,W=Ż1ʒj㬊xߤfu|3SKTdzq-R"$U.&nf~OpA/GYN E)<ΠJryb.x9JH09O"z1b0{:9{{~ 7.hKY8ƹR_~‘H+J EހJcFVpf9?f@(wsg֞x \75 1 R rb34  W4܄b gE3ga-cCf屲,EG# cS%DQ*-nhvrK׶[Àj*9*CC`޺jjo(S b=-mh)Bي-?`jW;]^>VC}~N–|"𱻽rTBjTEaN%w(t8hԺ\X:Ѝ:>|&@ @"8("5(  D" PF鴲^x oT[odc,YV "dA$z{{etVIvX Z&87,T@@&9Ixy/9j+_s <" \l $ZiG=Yt&םٮQ_e9Aq QnqPT$G$iLr&Ty7wKaa!7i/J?U$Mk- NQ*kGM2$ʍvO~`p>3_I)vtt$lF"h4A-tq!YEotL"ND_K=iQ/YLC.]O}S IvXFDO>)bR, #`ɱ'80969۔e)o]-S1Yn qy/*Td3ω (ɝ>a Fӱv̗Z4 % K,HĉdL[  w^;#04mngQYPRYH`۱Sh)d8e@l/)t@2Z*Y^c";}?NRųXvyZ ]Fcج/FAĕbV·CA YdYZ" Igd(I _A@m*v@XP`Ɠf$P AxL}~c!`q b"Xs ;/Ύj D.EAΨ/`) Rqds pMp=-7 6gF7PUU=mbdoxxjt C|+.p¼yZ57B] d.n2;^X cKO =) !yvfe9tb$ V-Ky{`3,~ajh:>k /@ӃPb+ȟoP ;jI Wo8/.|h4~O$ عuon?K 3Ae yr;Νe8ߗ )&[O`Jˠ JJ @s< # 8J=TrEE`@)(.3I#d8r8Qw{LL)^h'N뿸!'''okkRAiI9.)mB"MP^6uPjK~w0 Ek ,˱Ӄ6R\FbtwU=iKyKq-!K(JZ-#4W(y (*iAQXRJ) { +fOR&EQMT9)<6 {z{)*[CEi.-ϙC,=;|,ͮQ,3genR0s[ݹØd!gcnZ'[KCyy9ַ&i (Pэۼ9zWH_Xy]Hβ9exi~vͪO"EtsPUax2ylYX(5y<-7_E,eU玢~x2c°/l7ás*'믰/߯[KN >Z1w9*1[WA _Wnڐt+&g.$j:9- z-JBE"  pt fHHd, J%zV!r Jcx'f5 d壟8XƬ̡rFdYbxǦ kˆ\q2 l#RN__/.2K>ԕA! Ďc;αBG4c%(1-Ǧ8 IDAT/Nvc0+im | CʗP:ʯN%z6k!Xވ}7X|y1zyc͝Dv^vHr>UiX~_/s89VQdmf:l^^>[oD 7s!Xy}>5 ܋~i G?28~sIAfω<_3 6>Ӱ|H"[ob͊ |@y-U%[N7J)jنXFG<|y(Ln쯭%|׿e}%-y`sxHD??m|~aU5veK'GjLO>bdYbWƹ=q(`&BCC7?]P|tV[;k\n.H@ ǚt] @#bcUQLF4 V% jj-:^k:Al0]P0/!+qJx7PUWZZGN̞O U ۛ8qI%ىp89YY5<;9|^²hl?ţ#QV(:rWЊY^r6m/(tyo45Ls>}"nR1Zbd*AިZbmG݁Y)2nw wPf!IƠ5Pn .34jCVaJ#9baD*#\1™ Y;]|jAlh LAA/HBPܜ&+&tMr)lyQ(ɨG` XO nHLW |hV)wȴSqB~F3vɅgBE;oܑ {MbmR"ʉpG Z;fsI$N;qY3Eʣ=.`qNP;P3hOeyEE}xYfNY& qbӎ*%Gv zԸ\{)2 )'iЍ uĠPmeNTn.f+=܃(c/XI@&;p6?#wZ9 6H,I^"4lCՅdY@EI!uKcUP)Y^[a"xg$W(Br up̠vJhЩU2#˕Œ)-v4?x; 1΀SǞoubP 4F+@aQ8"(da_—6^5ɸsYT~|=p`:M8@P$]Z⫂kh(O4EcQ1-,%)Npdd\Ygk#,(=Ʈ",7D]W;Joz d5pӊۑ:|@aDmusMN XV#(9'˕ΌL7#oaM_7柶$?lAa'9r!4a]t3?}8K,z920wR(GD3lJo:6M@l>0T"'.H|5qzǏՂ B124atzQ2|8ΘPGeL!XPNMHk2P\6f$Sssowsg֞x \75 1 R rb34  W4܄b gEG7FjF*fSOueq*sr)П]+dJRp9s~'DOgU^1B: auy/9j+_s <" \lH)V;Ɽv31yx2a}GsH}$ǟ%xp$ʂy )|TJե)C~1l"H;|l+c>kd񿕋ev˕BH߲vYFeg}SXXB'$}C cT"eAY0 JY~w۟|R^y2v YC+ sT!c)f"2/O?4s0ǘ ԣT*P(dpzZV+I$o?Ϟہ˨lRc/Z^#GO <MْXU੧^\rSGECx,d\2.e|%3d6Ev4e:DQ$3) jdLv$;/H1o.wv~"S[+;"|C/Jy;8Ԧ¾NarQf;faNYX"k1p~\>':^t ;՚`q뗣 s,;mvrՓzzV^ͦMƍ Q|yMed$ h,yf׀3 j(o*5ZR .+Y&/,Ei +dv_~pAco/]ۙUZAݮ\GG?w*f1LG}|buzvXas`]؂W7*2$r$VNmBͽVsK!E/bXF X,Z7C;lAwY`N~գc`ט[]Uƹ~ӟRd NM'`w`??zKAUz|4JKN>904B') 'g|^lWFҊAyOwVp/" yn/=\S]s$Q{|iu|S]?VsE 4چlwFkھ1>͆hn(<1 ZJH<%A=^ԴC oEBfVZ>u,=g-`a {@GIngh;J\*5gЗɬP3i&%7fNI[{G8>ߢ7ST EX,S rj-V &ZcnƵEFDU]՜k%k֬gF* X gsA3Z klViR,3M0,dB}$%V3=~? RGD'prxÎI!" %b4(8=8j3 ãbV",!f+ņ 9"bH&t\v"5TLtQw!mq>'NQljFDQS|NfuZZm&'b^Y9};FX0{ .NzUKf(\܌t%;\6&SrU2]({ޢ($ٰVfr+nZ+8b|Cd{($qfYEqʑep2{Z,)^oZђ"e xX"H83A;]6-HxDF ,2A FsGw$;kh4~?сWNL_)fN1,Gd@<b枖qo^B.Sdf=:<@SG}?`61uQ[LYH.O8E9/i@ɲWUJJJشiEEE0zű!I-H`ŤX^b^~icy6]:vnl8흽(f." (]j3y6z:{^4&KT*qV$X,FEE~r0ʄF ,wT5;~pjK?29=-,]JH)>UR ɣ3V^ ˑJ?&dcY?cԚmH4)ͱ|eu(2mFo'8KZLk# 2AW>MS?D>V:;l7y K6Zg{ }?*BΏ/>.=Id'/6Y B;ٳ}sקku:אa东~EbҸF)2o7*Jik~nZW!qm `Xo?;娆;)[-Ow晗vv@ <8ޭrv-bK ,^ß9~A:;anm*8P>Ra61LD"DQh4V H18r/Y^IDGf wۏW)^)b 0j#%`QW=n1uK߳]23/WqSϷށ&r|,`(xY:/K[é*x]z Jim ;:/k>Ŷ?,` G7};bKCA ߚO氂y%7kr.a[yx-tx( CIڡߍ{(ėߗd@_V>1YlOOt&t=8^TX[׾LA>RSс ߷=Q"׽,?#c7@k-_^06.RӟH1x,P  2#D"vHa:95ֵb3[spj4;}:js`(*]A^}US)z#2җ|=Cx%/%y%EThE:a$(HX 9pM=1ʊhn eBjX3Gaİ{+䗧^gr<9< 3q/x[_)l֑_p-_ zэ)\_o#K?Oe乩js q?HX)h;xݽk94pbǹ7o? =K)b3*B#鍿d+MF91}[zT\g茕Z_96=D~9қ$K0KK ?,(ygcGi(jɧ8Zv Q% Qe QH AyULgx@icny[~5Ֆ}9j\c`Žv 7&@!uihSFW)usApǭE F;8yӽ8 nkLiFb$mkHq<, C0=Umn*/v'?Fw4%Ipj4ђZ˫$Pg6pӞ2j +aUJt*7y [DZz)8y"X1*'^  @Vô/?Bc̖>nNy`, .FoRe"yUr{ *e|N{DQn@+­ H #?Fu̡^O9FIф GqZ]Uwav 7ȳ_77Z!>wlj|򖟲5ޚJ\hx,VG "Xh4ӏ\XP2o[;I/:T~"e-PvW*믛P@|!޻ F kSmrh13x=%V Ɯ(1л!zSʵԛ:QWD ,ʭDE֙pj2igցͼgeUFqzZksȽ;90G Z^c5 ]}9,7v,{{+vs`7 ypڤ/¢/B)jraW9@Y(3nj#Hg)K2n{X[ ffğb!D/8N$0`Lƨ3xg)(kDEXu ,KT8<%(|gW-jJ 6M[E<{}<,_t-;,17=*ȤD]  g1 |K%&5S1Wzb?d{2D@Ti'yhIPPbw('b,3@N7QXϿ̰`w^˿%r~&s>-LrZ<'{Yq=vm1&=xko\[;o;@H)Ӓ+lY"Ų#[؎]g7ݧݶO}ӧ&8c;>ؖۺ:ysHIY%Q$3!~837tUN8*a Ͽ{ LefMxg:8^k:ǂu[yc{Z< rf0{Dҥ´K?حyw;>VH'zy'Zx<^ym7_ª@9k#wea_P(+tgÞw^g ,]O+UJWǶM@Ap&#I'vr'3]^LZ?Uē݆g ?d9;GYT *i6|};=( sTߵ]If{x+{["swc-Y,ƈ|ܳ`e2~Cx򝋖lJDksn}-VϣYS/#lbWѩ'@G-睲˚VUy }9:Y IDATKocHqq YʦZ>h]e+1]TG96bݒ,)r@Qo>y"0qn;MSzG8|֤ W1Wzl ˍKK*ش~%? wRS]Ǻ _mFw: V-dN~%zy%o"We5 ׳}OT接ic_k;;tD"5 M- 5\qQQV|l>Eo:_ _,;Hai2`IMXT0n k. N*BU, xќ^~{<~p(_["Bĭ,Rd>ǎݏ1/T{ln|^#*Bm,߂kNQv1@ @>gЧEQAQm۶eo(`{:JW4| GwwMI mP[SIӜ#JB5|>QY)fs/j&ŕ{up:<6'lgB0t2GH!#@(f9^;8>rR&^/u7yY۶K"9mv<ϐEow?hWܟ,)SkѲ6~|z@ ?7;EQؽKoWN'8TE'/}>]:VVfuK<nI҉j}mRXBQH?5RzXMCG'LR^^>c/ƞanEBQJ(+ Ǟg9ͷvށS0 㒡` f?yP/;eCO/L E o$s4  Eh_W W$,ZqFMVu_  m8@K l*K_Ѷg3[TPWW3?jd\ 6R2M741ܯvei|=*h)8t/IYe ]Sq^eJDw8BEz/;']OBDf/DmlJE:IW E( !0 MH5%U=tO/eϧtt-̴B R: E,\:gY;cѪ;ص.tUīncr"CQ*JP__ѣGG{BLtt9hv#1: '#=^:[o,ax(:"`ʤÁ 2w^S}OZWQ.72RѺTv<=/P\1(T*E(=p;N}÷v݉霱x[&EWy&+;Y EhYH:i3̢[ dU:㨳gߑ8-DUcGxSy׭BΔGg\*ȶ7 RO}׏Of=H/}]oOm Q͓;NYJVExܡ 6gO>Ͱ o]; HOb*-nA9ӯ]pəo 2eDjeS=̧fz3.nSG$qzo^XVvXwe,up@iP>M.npL+ 6,lő$SOiG0>2oN K|K#C&͹#q8T5EEM9VmfP Sp{Kw6/9,d(JEix{٢JVX.? g!)wZVcx쉯32),^jjc,H H%.&X1@r\V{hIf7ҧ?:ht:D]ga5H$2#G*Kw,BYfpz`4Lmh '+EeeulGs(CQT\K7JC`΂ #Xyaj/4A#“%~ܿi|VtU7$ }yH2d(JL % %9K)U+],y^[伎2]2OW  vW+SBuFRYOLh8'J:d?jP,rWz E隰e[Y EiY$9pwd * -mbN#pUKle*P a[؊9{_0L8؞ȥAgۻC;lA4|{1^Ԕ-Is'-ǫZtr硑ui骤l.Y`$yaL5Dɹ^IהPV+c$CQ&lac ;6RLD[OSEb9lB(ϊm&8>JHמR^5a6֬2 `y nD(il-N_!LBQ`qI8:sAkkG?rU=s^ -dGq|S6"劏e(Jׄ,:ȁ Ax4tE|&|Te;9Ň34qgY#hf(ܹ!^Ym\vфv(8Tn{Xh'/ 5QRZZ:k͙?IdBd2I?<T\dYz= EPtR@L&&4TUÁ4>BXvFЮӝ8Bi̧3NFrIe|!\.EQƃqxax? <!hvP N!zt{]W{g 3S&זƣk^?!G +y{,W,(vB^)FQ)Gǣg; k/*̄GUU…۟?H^Ď ︟*Gs¶ݼTbK6U0_حO7g%CQ*ZW&OZUU`}-}?Ze7PIcPV&!NoVB-ߏ}_V\NfJב EhNey XuKI&d2JKK KxJyf!(['/;gxM2I:OwD>=0s4l9F/.,,,}WR,v1h9sh/J&w؇?xhBGHc'LF9㭽ث)[ځ=<{J]^:&}ᷞUæ[}{4Н.֮\&Ć9$yhJTל EhA<|s{{ve0)GlΒrx^v }<dMU¶ݏrGob.A61y:k>o``]۹Ao*ZʆuGxy7 vB:V_|6%;d ^'2e&WmlY؊#I. q}-rAw9q9 3C$ω'8?bܺ˥bcm\>f g(^)6N]gtr5 p*980\'SKMT6~~cʁȖ>#RQ+^k 1OBY{DfR(*<_* :fr@{A+ Hx%%8|A<N;Gd^#Me8\# y:G*jIYi v?DGC8}e:c6XߥQMETD"zAEQp8V>d(JEkHQJbY6R%x<(%i EhR)B-9p3qΩׁl=ps(-˲IT'-qYt+lòJ/Ԅ-4edu.=%$G'P.{?RO=֎ 0s,uYFbЎ@X)?%kJ@ݲ (ضp՝hN'Nشz)mX"Aδ8M0rԠ_ژҀF~4+N|ə'hZKYUOSOYz$(Oݎgc~l7%~'#,| WDPvѪ7AgeCy[ A9lxk 5eQ~(ԉDBd{\,$2U{k/֣sKCTEtى>XhG-E''0 RUx*6Fb% yGS.̌mf5/hajb~4 7N XGhNaU9Y!]>Rl]܆IO7,'ɊFUm%בRd2i`r%@>iWWJLލMTz{{b FR\Pͺ%$I2̌=\`nQ0v}O EhyGRu<Oͭu*5a]-*]DCǃrsʷe1ETEn<2eY&!gc tfGZ.,N9֜nt]ǣ{ @u>],)0,,e#i[ئyYP}1Wl'Om*ݸd(JE˲,pBȕJk  DPyQ;XC#t8"`8 8BY}[^e<;9z)%A|{ /<7YKy E(Ѣ3л$*l_ۅ(|vbI[ 96/3ƒ}G{ (Yn24֎ѣWyud;+- ,P2v?ąp7RѺ>FN]|%CQ*Z!MTRee!Z:س,gԗ;pjڄvbmB,3!4<.gsf Eh[v{dX-1;G2/9Ew<$yGBls e^t8pzC<ǣ*DFJT< kj:5b#ڹA/X~Wٲyټ#Ǯo*6孧uKe l]!CXPiu:uA¢Gn\ށi)N|!,\Ȼ%o Q"qhu5P&o}2% -Dۍs8.ebZpWc ]}󴷶1j tڪ=ǪRjb%tA&FvhPy> #OYe-a2swT[d(J3(I7Pϒ$IP$sir*5>"}8O]*Ph2NAKK da<ϴA7aL^.CQa4MIל>lۦW544495WtumXuE\LtM8 N F}i:5,hFTm3u >wE ϣ=csavttuX,vϓ E隸!y7^\vj5A_ SgmKLDyfVαIlo/Euh^/>˲R۷ǏpBv= E麙޲M,ŭ!H jC:_TSxϪ46_yǾ明:.$BXH~?~!6lԆ@t\E`O`(L0\=+$'N VCK"SWifcGc8|p y/܏.CQN|e1.焞'K$|AbuKvS[U‰e[wPsEow?ifO0y3Msґ(lٲ!c8683 Ep8Ȕ5O Q .,@RWFMjb׾kcA96;a(pLxw('4rv1t>g-.&^OPfI?irRInf2v*xqQI(;wn“n mb&DHRR)VƎ|>e-?zL|vě)})$]mc~M/AC|bIENDB`pyzo-4.4.3/pyzo/resources/images/pyzo_two_components.png0000666000000000000000000013033512662716363022023 0ustar 00000000000000PNG  IHDR`/ pHYs   IDATxw\u:3L$@`HI [ieIN\ْeշ~^??{]{}>Y>zVNh&LHD"`:=3= $snUݺuϩ:u\SN:uԩSN:uԩSN:uԩSN:uԩSNˌFMm!fXSb+DK2i\:ux"Sɱ3a[O&/vNXD vzc<(s?x0z{ gˎw޳Xns,^~{#^Ov ʔSL%=~!~{l*^s?BScc8z=nm-w|b_ m.?{NOuBzhk/Ry79<}y*ÉkCXpLZugR`4!)N D(ɘL ĖX"q{,z !BHR{$^}/w[BlY=5> iÃ'޸A( H9&94'֎տgxbcd)'c_~rw~tʴN+hҁ+7#K!DJbթ[SBtz?_Vf! \:=] pQZIa SRj"y#jseR NYB}BR)1aa:!*v4QbYŕ`z-2=L~ݶRxn2 PS?FF**WVuCT3ݥ'@uԹw=FMǏ>e9W{ú7C{~4t:wB G<́3=ozl:,TRC? Cv±?K2tPS[# D6%k!:,]DVյQ)lzD{Džm;vyS?X);֮[mLlH&xSlߤVS[nڝ\3UQ`Rȇ$D[ 4eK8w|O{=jf-{O}N;^h= {鹟5B[ R{Vog>gtgmyݸ1ğ -˲,*Db}8_:ܾuvo8cx<֯_}ٖ@RT2Y`l:u~6&::\='>p=nxvYhcGdG2c=gpY{Ph)iПw_ wiO>Gnxr35>Dp.ېEͿgz.Ӯ ύxHpr_OS;꺱3;v~_N՛7^7ͱDgH$Rg|fi%YͷZ:u!0xDoꆛgwGDug;˺kvXbUƄA<o`gϝ:R"?nx<;bD:oCUiOη:׮;m Hv6zsJ}?3No]Q"_8ܾlܲ5O4N'RZ9߄;nBl9?,])=}G_wD!sG<큞3'ltuI 緦ԩfb֠Y6ax'XQ4Sɱ_}̌ a4:XXYK[xӇCwh߶}וj7FTM=ws'|=:2幓;w_9nc_£==eHJ%o߸XRɉ| X֩S痑XGO[=| &gvpHnQbDVIDR|뮲οP1HέqEP;[y&+`fˊ[E OX__hضs׎u>ƻtsn9'~5s@O&HI,tѲP.[꼙\Y WPy\r =/DuqiWjqj+m;?\rBZ@lA8BSG87@&G}/圳e' *Kʢ*\5*v h#\p\|[pj.|AE5JX~+aF^F.Oﭤ0OĀHz1 J7H[3R|nߋx㫐ҵl: %Rs.ddBϙڽyÚaܴ< SJg#JYXX:K1T'%2b!`%p^YFUL L Kf!g<ɕWry^hbJTj2/ߔ[@QO ~ oMTr,G~;X}:%&rjh⥴* WC ׈W!UWt֐⶛T/\QisC]vnRgʇ4Wl(+%H-< "ԩJ"O] :#Ʋe@ĎRDV#RQ+rxu+Õ*+K1]ѽ~Eu Yu/+tePI) ťymyP;Sә[WWz{U"`A[0S]ѽAU_]m^u鳀EzOT_R{\ZZVؔeֵ ,=\J%H4ͩ/&''X%{*%@{̶]JIu1V<.a _+:u</ YX1AE|nͪ]}P"Bu\j_-eV_tm*sSeؗ1: o[UUheXuneT "~[8nkZYs\f/ )*Tk~n1seRW7]EsChשS@0|Q<̱@J0W5IJ hz骬)Rj}]ߣ*c~b"H*(ot4!+ /%G  q%x4T!)˘{X8 S\ñg!]  ,Z"eHJ-/oYJh&M!vCXyP? [ʀF14KX%G( iJβl+JEo#~)2ݍ] !U)&f yi m(*zX&|`o~JގmQtZbY&+Rᐏc9 BUG(!+b>> m+t%/3yyE|ɢD*XCY*ڱ]CcU8^n@ tpL%uP%r>GIƼyk;w\\4noݼgu(e)KPTLg8cKO%մ /o!39|L7Y*u[6Ҳ m{J*]M:z X\d*!KN;9zύ2d4rݪ1 E6jѷf`Jx=׵q/`xK&}>DtD ,R">睨jmLljU?C+$nx;.+AuC4ܳyE(`V~49L_[sY.8?cm"lgyy2 M$&Nsq oh`SZDpYaPD9hJǦjxbsb\csԊs r4 ZaAs5 ʹ4nј*q%Jg]J|)YM\.Qd!\iݾLg\"2J#g8iᎭMҡXxFegdO)px .vwe핐q^e>^ek[QҲ2(/AʪR]Ca K=6Bk˂|m {tZ%l챑EKVUfo/8sY w3Uo#ғWӽ>LfDT^L\pjc^A-3=*;ljEfvJg:nc\/]c^lY͚wnAyc.[銀 Cd=k+H'ݻp=x(vi+!'XkhTdot-H8,@h[8fQJ"8kiMaʲMQôy<556z5</q9Wj2+qmR bqPplu˩b3O1Ǣml⺆RSL-"@*HpX nhfsq㇎24;޼p?=˶[58 )I8%da =vjBkn@'q-EA=g#K%؂QØxa BonDe5,`U/F; /q'h~Fpl,LG?a SzS{UlC$`\/wo`htW4y~i`j:jմ&tŵ)KMW(M^T7KcW=JweƬ"SB1PKZX \ BܹIi&G])6w1|tY?HP,TN.E?A]mGRȞN_GZ[ٵ:F<) ֆT><96U3[uuEψ@QTMe"r浘tI K!.ЂYrO)3B`J]3*bvwRu,<"JBd8֛d}X# Pl3&\d*O̲([ǴH[.Q) W `Kx4 FBoxP6wx}jGx%BP8y ׇ[nE 6"sq !p˸)_(㨾`e5AI(Nb #$d͛̀|XGj< o")=űρD iPahb}mhyl ã$ 9 PF87Nhb -AH :wIJi#R} 4IO[؋~EܯѬSM rzKR u:"t B(=*׵=?Lxaj*U!;=vnn=_< IDATˁ e^z:hN+iV5FI:ЃcXC8H E} SBx1%vVxe;c(Q^rQc-.'=B ܞGW16O]1J?$ڴG~܁p4QF烄c]L.ۉ_G&Q1iKo ں=k:~}x:nOGz1jߪ$'LXK7=ar!cDnM<pAtж.<)rkY@P/O]^~lG'|;4({'Ȼ)TGc+0@1|džsXv23c,8-BWOqA"%d'8|qW-^G E ha}Sa4XY6u( =9Bi"ds5x/ ܺf/_;C)D$d`zz˱vQ8?=ڵꎡ%&RyVt)2::ЕQ0V2\WXs@~o綎O}yZp7-!}NB*[{_ըzń/|w߸Y,\V19Zc\-,gb 1P&6޶s|NHj=;S[&3 !VqR״yH8g̘p#BtQ@/G5^ wgN9O=wɋ5??&Nbhq{.f8@3]>N5E: ].G޸/Q1Tz[OÿԹT"D '(|`z*AGr}L#+ۧ ,-)FH_!AzuTjeumJ׍7i_M _T+FEsƻѠx4+ׅz7\d۠SS2lb,MjY~e-eyb;W@L[ cZ+'ͽGeH֘fCEH9/}c%,kcux@43}aNr,~E-!Fs˫xi޽^U\fM['kLg]q T57dZXRV)28W7Pg825DبT0L ϋGiR,~v$gJ_3Y$UMSP ޽U#9Nvzc”U2i(%H;xH'U,e &-~XРŧ0)3U:->u8>A:o2Rtt*!J%MQ Z":Q S&&_+,s}"Áҕdm U j%+MV)l6}WJr6O'^T_47i+LMRD+eYpA"mݣR8K`1wx4:#:[| %0˴lΧl 4Ld"UF@H.LJ.x<: Z=E6f[XX) at*w>ɴϹe9sGHqXB`ѰGLԤi0(tFuBY  ZŜIb Ϝ@ӏY:[IT1p߬7~r)-| TB.2j);woG`vo6B2rΛ()\1O( YoOrI"{Yeێ!7±/.o'פ4'7 );G*Ng(F'RhO)8B#E8EF^AWTK)Zبb`I5b\$ڹUqz6p2ɡ~^ll9!t#4\ n;g sTeOE[p@oI G9["Qihdw$Y+SlEPmVP%;;|JtMi^B{̃cd]/ ΔE _?wToNs,nsmW<:kp, Ssk6V?O61?'X/uMqryO a~wGYASvn̓Utwc<6<;:-IlWI|yAFGH%щbs\o `0 w_ȻZuZC$ap,7(C1b47oKaxtj/>qG+X^K1[<{pg\Cooe9uyǯJ3-lε,nE} 3,0 V,WPL&OnM8ly]XITֵKJ$}'xSmk X&Fs<5 wvCK@SUy)]wV)U1Xl+YEU|9;Ӹa+Ix Wg3Xb<_{8_U )A!*3NL1P^Ox}޽=HO :Zw݅xtq[+gÌM f:P;76psAP€BԤ?#D"e*b6i?iG]Wѧ-bT:U#kU&MRJSl[{σ 7lj :^k/MqߵQv$6!CSU q]7$X=V &6jtUcEZ-?Ned޿jnm̬)Yr3Z<-Y!T|.4bINFTEQ bxEvG`E hhis!쾡1NK#1BQ:t`:~]wU"EW`znܱBAUP !VOeFR\GLj>p܁2^ԙG ZY%r0a<)+I\39>հv[17*㙲@^X);euװ*q'-ʎETf%06tWϤg^_H=8wj53Ӆ,1\}=tJBp0VQBƯֳ U&@(*CS6VX4kI&'KqR4yWe\+% xh ڜo :+r$1) z$".Q# ƨKEOQg{PFJ\9B!=lS\@̿4aФ80PduҢHW2Hi׃ d0y5.8*UJeZ#(%jF_'qdL%fhF5Fr6ceR6]lG+ژH钵H9EtZn{X2 &Ӵ-Ṋh\f(6SJ'P [xeCUEq\6SEOY4D5ڣCCT!F{DoUsYJT) AkJKœkRVJ1tn|˕X  ՟+F2력y\+OT3{n#` @w?[w/j, BA c}A{6ي_F-2SçS'pTlkH;XA^׳g}#3ןsMwuPKn|=[[i- D?{zq ݾjmm'9Ֆ|4x/~͛n!<ۗ㓯3)h^t)(;Hdɟ54HyπsO}y52= nw(,Vߧ HItӦoX歼O6/e(4(Q㒴3N&ԚXO.o2bJFWa(eR DHEus$ H-Ɗ.BSi b~ŒpDW&|:->A6o1ZtkݧdF3eUaųtZW2R&c+k((JO0Y.BSh{iӟs+LdLҖDT::e3Vtk۪%[ $C-26ə.JGQi -u.Χm\!xu qZAOMF+,[uB1s]P9BA, KUi d0cQU:"ʌ#}:qC{JHR>L8sYPؖh P1ؙR2*qtgߧTL{V<ᛝ) aݗss !hE{+  gR:nB m\6E҈Fn +Lp c2SQ}"~tiab$f1Fbvք*!_ Dc@H#+}D}:%Ʀ*"E%I]fd<+T oD_QkNq9R5L&GUEB(4S2TH 6IYhB cJS\4Y[U@QHS(R%j(MrbG5;Jû>+3{]jߨ A 1X;βvaffv1;#FAΌHBRKԶLʬ62Û?""3\feUWuP|De{B!\?߸d/hB4Ybm1!-Y"spU@tm.-z;Ŭlh:H;CX GKztyںu5覟C9 [v76.Mow[W`rz GF0tHU?P% mM T}jUꂃjx90h1+[TG{G$tվ飫.]zfPu|N|u`_] м+DqbҡQrd\p>4Syi!!fZuky8 )Y{ΨT-}f!)6;m*y͉aޡoʾrW- BC[L߇p% q䅅B;vTqUYv{ 24"VMۄt]|mf8(%[̳f+,-2WSd]zD:mB4ֆRLCX IDAT(*`D4k3(Xй.%ihۭZj"WCSpt3H@&F+FyPQؙ8)3g\]uk&I|% IRԌdSI&d%F%)_Jdwr4e6B9r>Ǟ}!Fna\+ Յ4+>>!\^>r_oQ*(%k"o>/ML."!z5֤ߏzE%C:cG{vC+.9OOP/tR5, QKݲٳژ5kV AH⭹<#AoOh+M^et=^bt \g` Jȫvy^y߁_s,W~PjT1FMSL!FUJ$X80i*\] ܤ 7P1j" ~N"skt4L\& \Yf9J\RBL 84E)zu4t%(j1֚zMd;1ϓe/||G*1y,߹zJB:Ηh>>-- o {˒bի$D;977_Bl]8|UpE1b|!S\M{8 N Fo";x&YvvcZd/?^:|( \] q~/q!5ƃuntko\j+YLA>"JN>8M} nMyC̛, tYfY4=p2jGZINzgy \[y'-0~ex1AW7ͫ/͙4c'DIfg/Ý0?rl0{WR>\2j g83'boߤS%s7s^G=7:WiYy/$9.V.~x9pMpVZ#ε\>u5_W_KNUCY[apy[>;ǺY.%%*o]I 94voCj$-r~%3s;Px<(zy=׶E+Yq%s8?Wd }n^s2Fb;طkbwo;1%ߟ^v1&rr3c=T|i|{f<.2j{'ސo(,e a39z=XNv{ !F҅4W] j˧q]PLa`+]t  Ԏ&N4`xaPlUײ;uuDŽ;Smc U>bD"JHOaPUoŋ q0k 1C 4<~6/_tX:ͨ$JjR%l])MN(U_gWQ "I^%~)+!138%mH$*%:KQ_ƢJ<@QU!5dn7eK S#Mr}N w7-[4Q)]V!4m](kdo\B'h*7@~"sI,Z&SJ, 0>JW-W|D6>A:`B"brr鬉m"66ۦ8^vvZ!Ic 4qK'B6|o}]6m#5?p#~ WQ |;tUͤހtsHa33dµi Y֓=2EQT| ^A{4Lrab V2{:gd)gGE0'\ iFh , pg7;z:)`z_3Ji[]%u"Ea֤85ٌz;tTڂQ{gQd51HCXo9% ?*cB  h d TUA-3P a(hlzsE Dq]D-ˍtCCWxyaH!M~ |*a<ŤT}7eakTR␔BZ~X^~/ȏҮJ29G2-mTl5j$(9TJ{Z'G?W޷^'ķ8^Kvq_ ]U2„R"Mq3 wXu=OQ,y*{F)%aD;@U6P F udm"8 դcC'86"*heR ;b2C/TQtz>>㻻.q*]>y8YK2(|,佖I[(Kh,'5QnهD)NJ枿5]VV($:yHͽ=2Ea]T-BVHM[qS]TИv FUl0Q 64'O~uuCrn^2 !E9~kړ] yA,:mdy2R~~=_$\]7_Avg&I2CFYvIlO?{6 ,?r5w+dhnb#^<>9vv4SYxɴ಩sbGL_/iq1TOΠ.]TW$X?mQ/:1誁9^Y{sh!']/(R? jcXft(kUܴ~[^_j}1ZԺ7 k^QSzUJQأ'8iВip|jK5<"n}Ve?3YmgjǣJ1pzOUJ?uç3ӕZ}C>eB+\r \7ttQ*T=mxx|3AH3`M&"i:IGE'VCH[5ӍJ%=\aQ^ 3P:r3sݭ{UGQZ{D^hx/4;$v&|S/dom][|n}jo5K687GGuNoijmkM6v>۬ds⇴Qc3-u Y;!PLiD 2vRZE`Itrme5My8m%[BN}}<i]YJvPOPFY#N-܊C6g(z+qTQ3BLd6E u5XXb}-Јtm%M閰3k,&,C]brmfW2X$_P$E``:2Kkf-ɦ8CmN P@]0C_ˡY:ASɧXX͕  XKQ,)̦aE`(Hɾ4TKu&JgSILIyB:EK˜Z8>"b*WW6$Ρ^|{[hi-},g$Lq88ijlɅM̽n1}]N{ԇ;yivD#u.Wg}NߦE*JN)EQ:>J5 \Ű|Eʤ^:sgm0n?4)[ޠeyJ ܸ"T' YGZhᖐj/>{št$XP=覟'c(rtbS!0f<#հte{emx,E1=drRI lEP,qSORXӾpJ@,ԕ ' E#cxe:TNYHHY Y9rα'N k8y&g8ZP?]tY\Z TF֖* &Z[@d=޸O>|=+)L̐_\a؛|>OvP1(hz ڴSP @Y25@Qjudm=WK ADž{%kR t?&]!ZZ:#4BN8ک[FZhᝲ baTMy65̞K`\zѽ%w[yIsކn]S0C~BԷy,j=eU^8EJ. cff΀@&@=dɃX8J6~lg)Fj g8ro:×O>ŀ©7/!E_qC7.5䇟OP֯ȁ'Éh,_c=p0AУAV kD5KGOsH鰼ǓbfXDb!@>zw7Q7 &cOZan&T ٙBa~PDn -!6Uكt֬,_TKpOZhۆWg沕k7gO{^}A,jG'}"o|kG.Kzt'YSi5d#ɷuق6tI-Nen\]Z:Do#w}<ucbjv kߎGv2Y@,7xAz]6[xUN{(I^Uȏ3&s5zQW{YĞgSY(ؔ~ҕdkUˌJu@TMJ -p_"kdGsby4n-N+1Ϗ?1P-8Y?Dedp_ d'q;,ק$I HP ]=vZUS?۰JA)H*HVr4-OλHTmU?=%N IDATԧݖP%y KtM4-lQ+6 i $݄UC&܈Q<1"e̊; ?H!4Ӈ{X+Uiv3ԗB oRRلA^o$T.V7_iRr#@kJg.DZr}>tىҵfiZhLPpqzMEhUk|ifRd"hb*H8.MǸ6'Rn6|i[v5?O6L{k4K{B W!tܢ$ʕ˗$yY]tC;{0ެJFd -&m,^ťfOݻm"XO'I5"zZh+rq T;i"N>)|:pprQRU33bY*E@(〫aE39أ)r'4}B -W.j+^Bx|(w4B DpW 4(Q7#Ttj3Rh&-ZC}C+v n؄PZg -|C6{'o n*2duߡP{W-pHN,doOس3 k\sw;T$oQu׸6'hHfX;H߭oQ]Iѭ, 72zO[C,$y -{(ΔQS%'/0.s vZc)'HQ)DC5R~ Zhw?Pҧ'HIqjFS%⇉nJih8@/l5B( [r@9t[*Rnԣ~! x5p͂+6qJnͨ6+Z==>|MRkmmU M9Z-|s<͈mlƨj:m<.-*wENA-½@+fkiʻ)JqMqgVJB04EWQP-o -oh[ik~h~[GQXL6+[3* L4zZh1,_̿=F3\7l ~ͧw[_FMe3K鷛6!u'4ϼmݬ-f(i^Ujw+~MWs{c]lhZm|y2rSQ'A:JW]ϸLu7%H) ٜ M(b>GvAx{u2E ڎQM-K9i2\t,i'Ed}!mrk8BS%.E|(w+tVH!4Bi]Nɮ`(v"8e\%מhT*-G~m|bhvSv|b nǖh6C1i-6!Y9x{,H@ 9 ]->@w.e'Ի1tz.B ` w< 7Zj $nh=0ftvKS:^A6d-S$̩UC!"<3"E~ox1ysDg? ȍM_~'~]I>p@~̑nJ@%ҭK+j&RUo 35R>Ui6Pd8lZIijhk gˌ|>o9SdTn"23M (;td<ӯ>+Y;-Bᶆ4gK}PH]cuAcdtr_ &9}S(wi>\Hl]UT?qf{C@((6 S_gCσ?Gΰr[h;?EW7M1yW߅VbS-Ui,_RY6l۳3[hy0{yNS\{w,ei2~+8<*7ŅeGQ~2⯱# z) g[_#/O{Խ3o|}U:Wڍ9M$vLb4e~ffTg?%>wڟ1u቗9)">{φ~Uf.!үߢ$nn&霂yhWH@6?-r3lT,**qSWxdH}2O)gq>F>oϮ%2}S$^C <O&}י>P`.Yh|`-&YYHdU$^ -D1S(Ll5f5QZרI+M3F xf((2򤒠drוRӄpC$򳴍~l2D0dG pL¡O5pȯ09 gQ6*/M|0Yxh(\o2}0Kr!Tձ2?Er3XObn?Sh{_Š&_FNfB۾_$ 8La| ǴuCm0Qh=t?M'yf5ϲ25o0B|2N ch&q;uU!:(&ҵ1jC8>t Mb m.2cq$OQ½lj;c !F}0G> ůlٍ?#?Xz3m 3y5S0޵>O3qOѳX<"#$}uSXDwwΒXǞ:_`fP|Q o>-%b}e/jہ`6KQo2ϙ!x',O'F˳.ޡWgXg|fhw.J-4i{ -O( T\)ӟ^XPou ym&3ovi\;:E4=(X]9.}#}_3 'H1_xawXΣCj/K$g_' 6F(pOˬ.ջqknnoRCUTk B"VH'^':4CE:@Jt>"H{:ىnv 4y> B G0R[מ!tw-VZ&qGQe3U#Tx]ɬ&>s&.,)+%sz(&3?|Z`эE|h+Ig]@o::`;ng6N~Ks238D w'Ɨ)u~p0Fq^N{DAg|{'LPA!tuZX6dx@;~=HZJa|e TѬ S_$r޻ې-H0ՀjR"s&t%U;JPh"2;-,`Rx&9ŪlWMb 3\!PD.Ox/o0AF "H\tKB@%$ ղSI5t^}x!L7^zID!(,3ok]'8Qz$n@hT?!ulB *v6QFs#⛿ RTJ9vi(z\bb HGV[]gm2Etqpr8m*RK{BWKGi*7jU}FEVQeV_%ogn|E៣S9K\Uۅbh*-Evqm~phf'f'>>!8_&_)vK.*A"(([{ s (R϶fUڈ)8MM'?~'ygcj{H*fəҮV;&t?+c!VvX[b% 0=%P(VF^ry (X/'2EǞ$95&27Q4oޮ} 0! {/ W\}Q1zwT=Oљ[x O{;XCa?fMbc9&4z(BG1L/=,Z0 a=WY8;)@U xa4u44(pBю1b'_AJb_źH\#V}{Q.ڭ ,^xx(,?RL~Vp/+L5 ^Vf`X׷ގTِ']sryLc=9wU `0XvY ChZUE ϰht&qO ubf/D:ʅ Vf/)97YJe<-AD楍2 |F) >NUJgrpq9WHɯB᷅Vp|JTa$j6x:,mjB!TsNXX/&MdY-uK^ڻEuЪ|cs{j$Q"ai;3Sw_眾>y6pyxj4WIźѩC&>xFZz#* 7+o2Ϲ0'C.GXL] 62lhYYxUtHRK:H)qz ʛ·yl]^U1k|Ur@|C'Ȍs4rg@QSRJ9Whx&yIIf5/̳0œ|- (T3O -pXwT,'H dډ̭e)WW FsrļzB:,-s$n2?ͥs.~ JOHRz+W835} iB(A|z_vE^}-Nglj[pwɾo.1ouQy(YiQv +MhH*7۲z_%~0} *PS)Ϥ!\ݥJ<;p{K:RgW ^5l^k=spMlAṧo{ )d8;;fds,Yc:c8͛c 4*9=ü{<'r )Բ[64/"Pu^F/Q9E.4xx%Φ\S/!S*E<ťQ#ᇚugxP13GK{;pVe0/lz3Y*kDܿ)Jsd5^VQ5\j4Ua&,9hI EAPtĨtyH))-+{M^SeUDtk=1+ݙΕ&(M6Tġ >KGr>=9랓 xK;6, K.Dl5::кO99coq" gͭk$|f ii\ɲ0Q,jS[P7=RJ\w],d97TH}\i޹MJ??{KAhnkgS+lJC@l(|xGx;O"{ғtҩ_UC)ۢ֨8ݼrdGþK!vߞ@J^D `%|fOVůlaݕf.?Qk&ܛyzE^?_1^c.lIlIcMA-B%5Q4ÊF=q-G7Nm]zrk*'~P_ ºB8bJXWLJPL/ W5rg>GATT(IǽkΧ#P1z_B:hSXqz=w7cyb:} $GKJ2'թB%q]4Ø)kRVubʏ@ZISTyd勪hڤ[,16 Yh RG.ĚyRRpWb%74o9sm>oH=UU DIveڼ A(>U]ί*[?Ů"%D ĸ񛪱,Wj(6A1x6]wvmʂG8Vci"X*fc_Jk*nDL~/^E IDATiV^~#u࠙*>'G&#Qďp(9xӧrP :؃y ^`X ,!PS#\p1YxJ6B"%ܲW"6{[?M!$/Y;7A)V+'R޾=#NG,u_7t/h2P-<#`1^ J}o},<|74|&`|32j:JGݴ3.MuA\\|*?@4`eMbhX(~ Υ9}npjx/D &<8T40=M<.`$NpS6**jj0, qfMi@$򷜳105^|YƠEnt:O}g6]R0ctDtX|1$P5: ЈNzߜqV&B(^ɫ(c^- 1nu|M'<78jU#+-DwsШW 0jxQCPXP,!Ywc.5PHq,r1 k*nXLVڬvi(4._K]A(Te_)P:֕ET\U5}._r{EcJ9DϕgI\ihn|(wG4di\v.1W`f#5at6=/CWLY'ْӊy˘`zO9y3>jˮΓ>޳ &.8iUխcQB57:BWr¨oa%$0:LWjt"( Rn%0//ൣ8ByU=AX5S"$+$&F0뽄r:{d0c/5A QYQw7gj0P7 -W_%a:P*+t6R͆+g<nV[=q<5 m5wo>6VqbBUJa6[3%4x'!bYdk*>,IPB ]RXR/@s(eGx܄bM$L^e-GmJhN4j1V~\GUDmte%5Ǒ҉4R׈4TKIwho}KN24κ~C^RY$j򖮺ଡ଼' LJ > l7H(="Eݥ8Ǣ>6b3ɇ&@Kt]fvIl򎅦kz(B`y !z%459ם'T,R*Jn٪24"xؘSj*KE$6ϤOrqa$u@ΧP+9,_ 襱Vr eCct^CNBN д$@j-1û6ʺFRKInD #0,z/~ c /-># 1N1x`i[>gkSDz?3tjZHY2N&! L/.[G{t} ՒS` &s&{P#$]<9RJEK7XYǶ& $hsn$TH+]0q3'2[U(B\W`Rd_REPBefBp)4'IR9?Bo Ȋ:"4U܌a}κ ,\. I2//=oKni{h3&Gmc5z!c|c*y9.<<.#}[DX >ں/oV[U|;0 [Рby6hf oɏYE%dxok"GAttM ( Aī@KO.ohZ& 3tMNlzZe㮐OEB|j …9dGYoۼmWs[ Q*\_LN;uJgWQhJޫ2*HSMT3u_=z%BV"PaI2)b53XʔUU0}j~}E嘇*.AePcMJ`ReN_E+KUbP& 3 Yx2Li:{r0Q<SA&0-j_gz9t,9hcl4 !f$X~Ӷ/EB(Kռrq*$tef(yDѱIɑ/q`d4ALE3*b_fBv ) PXʄEOO?mJ+ʹJↅ,io֠@E{/ؾ-AX<=&1MMK3OpA0_`=abJF6(}珌YR ٟ]`5|vұ;݋S71oV<̧_#bC)Wdzؤsdžp:rF#,Nșf 1?=ۂx(N?|6E%9ľ=ܵnk 2}axMͺ7%4 Ax쑊wTʜ]'E{x30#ԏ}W ͨmfMWP#I$`0ZyfZllᣩ04$kjVݠ6!j7zx2S䵟_D +S0; o۔G/c#ZT31 ;0 T]gl7Y2վ1{s8Ád?iq~dI'e͎6E/1 "T"b+حFp|Aޗ- yM5<]S.2q?n_NrO(KVN,Ry/u iXO%9xn¾%Ȉ~CW?m̼Xi|w z 9@ս4R$G@D4qfIYV^A%,0Z޽q{4x0O!,N rncv~ػh?(Pf9\Cu&$Bbi¨$K f K+TqbQ 0Eth@!] eIAp8?V"$T Sd,"Շ I *5S% ik *%ŜMwV.%2B-y ~Cz9l\ȔH,3i0lbg( \Ɗ%-(gz\ha04.uXQogXXkX^o sY. *4XRb <*6I1>EdjN)|Ko"qɒʕJ8A ׭{]B{]7\be_}o ]ͳ Ã星S!Fk1IJIvB_+.]tY״b(˹\bI sψUOFSO3w+ rt7ۖ\eyv|1㻜޷ݘ;Lii.sRuR<7pnt(xs iM6cANu[YDQVɕwƥ(ouK=v.?*xtX`Ȝۖ_ Df5a± R?NQL+eefr$tR?zѡTb8sH׬{ߧ>*g>9Cs3SE<,Ԋ'P1SX/y<ٮ9L3W_(-6 ~h0h5+}T%šUjnƎ=H淄4~|Us̥1Ԩ%+TtGxk(՟,ڳp?rp,dNq߮=t*9SnBq .n}^rTʙE&sBUI &ᩌB&⛕95S cp<@{y*KJ64[v"=ąRUN \tVsj9U|s$9g?[ tڶa )e f{Pdtz 9I0R&zk4RRE)} ei@Ӎ @ѱG*pgS^^mˮ?Ծ+'tөcw;OG]{hh\tȏ_EJw'_زsEO D}ybמ7F s󮇿ܾw4]z̩'Oo.\y^,SeDJP*y}y;pֿ}#'>Y>m0=3kwm6/Uh]]?/}wB?% TF+(Ri:/U4SKWkU|Ș}R*?Zybw?J{ `wZu$s\0ZnOls{B7|sGn޵{W5AQ j"'ϥSɬbbbdzӧ?: `G~eY9@ ֶUkI~=1o$lwuD Ryhv׎=y|쉣oꆡܽM^ŖbS#Q7 mG8ZSp04Ӽuժo}CgO{Ή UU5 DbI*~sAiղL]Rt t&UTQŢ$viƾT,nLDbps{ 0ċO= -~w;~7K<س=U7:}"bv>P/̫<5wG!D)_ͤoۼZږaoܾrߓ1 2c2>[;>| )Ͼ?l(k) ,c^xRoמ~Dp>|}m*O7G3t8^|S(ʒ-vtfum~]_|_YmO/i_7݃"/!8䟾7wˮ=d}7o38A`ֿBA=k$-Ы2*+%9MTo&hޠF;{Ν8T7%{.t9}m]10VH)ǺOݮ3-LfH7@vu˳'GJ>c٢c_ _C9Ƕ3GPd:̴Z !ǟ 8Ejjѡzp|f7]H"0U@:{Q$J]垍߉$jnwꦹt녢Ԃߓ^{g74/=0W Xw;""Hο{0ߍ&Eux6yb"~k ժKU$A1Gȁ@Po߼m<ظ*ڻ6=qRӏ?Is$Q[}j7^}"@Jy/v!s։`/!XwƖ ;w~S&(R;] &=rptcmU)+P $7I'+J%3Tj88YүWU| g-(OR}]$2=:2.$ZP޺g9CZ@ !Եwn h%BNR?rBrj뺎m_L90:2)۬`H^rp5IjT~J'xhdNJN$>cIh fHڀ6NaR"P=Wq=#Wt{d1Y3Ix ۱v~zu8޺yn]?ga@!mٹ/3'[^$~h$MEǶe˗mJNQ9α?*ǭpJ'MmkDbVoj#_>)'0R}H`˃7G5KzL2㓜O;|L5#\iCgOslin]jwBJyIJȡիsB(mcǖMm橽ɤ߬ih*T|iߏΜ8zA]7Gtx4+ |x/. l,uт"GhڼrqRLM bpU;ql\Y&@6'ҩdP3Do{GzsʕN76|7p&G>I|V t>u|Ͱwm?^<өԼKSD#ėodKs<OP[4Cu9}Lt!6ꆹlGթ#o^i! aˮ=C/j oϷ?~l ݝN%g̷N_3{ (:ΑHpK{)4WnٹvN]bWuXְO~ᅵN_(@A~ xcۥ֕knݾ'"B,9}!rߝxH?u>@&/]ޓږݺ}d>ݳ/G\(jljhٱѿ5L>*{Q:7Zu;v?ݴVc'PİN cVXv+4BsO=_ZyS ߴA(t?!=r:snUBZ)9KpUK"tr|^7L}·?? C_|)ub5SSW2O;+7n}ah:_|Ba6Mhoxብ+WEb%Shn_b~ !Z@doVjUܸXPLJo\7:}>*Y]<-o>Ӷʀŗs-t M!@LfĆ>P/Ya!#fUxwq;gq~dEJy[|] ^˸YH{.+b;gN/LQ p]VEh ã,&, 3/0&ZU lQvSZč.2S(PfR7R q΂q6r7fjr7-?UTquͯKga=Z{?gw_{E-0zE.^* 5U,hbQ`-ܨ&@_ՠ !6&7CE/҆0 debug Stack Pila editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter Filtre de noms arxius Search in files Cercar en arxius Projects: Projectes: Click star to bookmark current dir Cliqueu l'estrella per senyalitzar el directori actual Remove project Esborrar projecte Change project name Canviar nom projecte Add path to Python path Afegeix ruta a rutes de Python Project name Nom projecte New project name: Nou nom projecte: Match case Coincidir entre majúscules i minúscules RegExp Expressió Regular Search in subdirs Cerca en subdirs Unstar this directory Desmarcar directori Star this directory Marcar directori Open Obrir Reveal in Finder Mostra al Finder Copy path Copia ruta Rename Rebatejar Delete Esborrar Create new file Crear arxiu Create new directory Crear directori Give the new name for the file Dona nom al arxiu Give the name for the new directory Dona nom al directori Duplicate Duplicar Give the name for the new file Dona nom al arxiu Are you sure that you want to delete Segur que vols esborrar-ho Go to this directory in the current shell Anar a aquest directori en el terminal actual Import data... Importa dades... Run as script Run Jupyter notebook Open outside Pyzo importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu File Arxiu Edit Editar View Visualitza Settings Opcions Shell Consola Run Executar Tools Eines Help Ajuda Use tabs Utilitza Tabuladors Use spaces espais spaces plural of spacebar character espais Indentation ::: The indentation used of the current file. Sangria ::: Sangria emprada arxiu actual. Syntax parser ::: The syntax parser of the current file. Analitzador sintaxi ::: Analitzador sintaxi arxiu actual. Line endings ::: The line ending character of the current file. Final línia ::: Caràcter final línia arxiu actual. Encoding ::: The character encoding of the current file. Codificació ::: Codificació de caràcters en l'arxiu actual. New ::: Create a new (or temporary) file. Nou ::: Crear arxiu nou (o temporal). Open... ::: Open an existing file from disk. Obrir ::: Obrir un arxiu del disc. Save ::: Save the current file to disk. Desa ::: Desa arxiu disc. Save as... ::: Save the current file under another name. Anomena i desa... ::: Desa l'arxiu actual amb un nom diferent. Save all ::: Save all open files. Desa-ho tot ::: Desa tots el arxius oberts. Close ::: Close the current file. Tanca ::: Tanca l'arxiu actual. Close all ::: Close all files. Tanca tots ::: Tanca tots els arxius. Undo ::: Undo the latest editing action. Desfés ::: Desfés el darrer canvi d'edició. Redo ::: Redo the last undone editong action. Refés ::: Refés el darrer canvi desfet. Cut ::: Cut the selected text. Talla ::: Talla el text seleccionat. Copy ::: Copy the selected text to the clipboard. Copia ::: Copia el text seleccionat al portapapers. Paste ::: Paste the text that is now on the clipboard. Enganxa ::: Enganxa el text present en el portapapers. Select all ::: Select all text. Selecciona-ho tot ::: Selecciona tot el text. Indent ::: Indent the selected line. Sagnar ::: Sagnar la línia seleccionada. Dedent ::: Unindent the selected line. Dessagnar ::: Dessagnar línia seleccionada. Comment ::: Comment the selected line. Comentar ::: Comenteu la línia seleccionada. Uncomment ::: Uncomment the selected line. Descomentar ::: Descomenteu la línia seleccionada. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Justificar comentari / docstring ::: Remodelar el text seleccionat perquè s'alineï a uns 70 caràcters. Go to line ::: Go to a specific line number. Vés línia ::: Vés a un número especific línia. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Cercar o reemplaçar ::: Mostra giny cercar/reemplaçar. Inicialitzar amb el text seleccionat. Find selection ::: Find the next occurrence of the selected text. Cercar selecció ::: Cercar la següent ocurrència del text seleccionat. Find selection backward ::: Find the previous occurrence of the selected text. Cercar selecció cap enrere ::: Cerca la ocurrència anterior del text seleccionat. Find next ::: Find the next occurrence of the search string. Cercar següent ::: Cerca la següent ocurrència de la cadena de cerca. Find previous ::: Find the previous occurrence of the search string. Cercar anterior ::: Cerca l'anterior ocurrència de la cadena de cerca. Accept autocompletion with: Zoom in Acostar Zoom out Allunyar Zoom reset Zoom original Location of long line indicator ::: The location of the long-line-indicator. Ubicació indicador línia llarga ::: Seleccionar ubicació de l'indicador de línia llarga. Qt theme ::: The styling of the user interface widgets. Tema Qt ::: Estil dels ginys de la interfície d'usuari. Select shell ::: Focus the cursor on the current shell. Selecciona consola ::: Selecciona la consola actual. Select editor ::: Focus the cursor on the current editor. Selecciona editor ::: Selecciona l'editor actual. Select previous file ::: Select the previously selected file. Selecciona l'arxiu anterior ::: Selecciona l'arxiu seleccionat anteriorment. Show whitespace ::: Show spaces and tabs. Mostra espais en blanc ::: Mostra espais i tabuladors. Show line endings ::: Show the end of each line. Mostra els finals de línia ::: Mostra final de cada línia. Show indentation guides ::: Show vertical lines to indicate indentation. Mostra guies de sangria ::: Mostra línies verticals per indicar la sagria. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Ajustar línies llargues ::: Ajustar línies llargues que no caben a la pantalla. Highlight current line ::: Highlight the line where the cursor is. Destacar línia actual ::: Remarca la línia on es troba el cursor. Font Font Zooming Zoom Clear screen ::: Clear the screen. Esborrar pantalla ::: Esborrar la pantalla. Interrupt ::: Interrupt the current running code (does not work for extension code). Interrompre ::: Interrompre el codi en execució. No funciona per extensions codi). Restart ::: Terminate and restart the interpreter. Reiniciar ::: Finalitza l'execució i reinicia l'intèrpret. Terminate ::: Terminate the interpreter, leaving the shell open. Finalitzar ::: Finalitza l'intèrpret deixant la consola oberta. Close ::: Terminate the interpreter and close the shell. Tanca ::: Finalitza l'intèrpret i tanca la consola. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Edita configuració consola... ::: Afegeix configuracions a la consola i edita les propietats de l'intèrpret. New shell ... ::: Create new shell to run code in. Consola nova... ::: Crear una consola nova per executar codi. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Executa selecció ::: Executa les línies seleccionades a l'editor o la selecció de paraules en la línia actual o la línia actual si no s'ha fet cap selecció. Close others::: Close all files but this one. Tanca altres ::: Tanca tots els arxius excepte aquest. Rename ::: Rename this file. Rebatejat ::: Rebateja aquest arxiu. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Pinçar/desprendre ::: Arxius pinçats són més difícils de tancar. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Marcar/desmarcar com arxiu mestre ::: L'arxiu mestre es pot executar mentre un altre està seleccionat. Run file ::: Run the code in this file. Executar arxiu ::: Executa el codi en aquest arxiu. Run file as script ::: Run this file as a script (restarts the interpreter). Executa l'arxiu com script ::: Executa aquest arxiu com script (reinicia l'intèrpret). Reload tools ::: For people who develop tools. Recarregar eines ::: Per programadors que desenvolupen eines. Ask a question ::: Need help? Fes una pregunta ::: Necessites ajuda? Automatically indent ::: Indent when pressing enter after a colon. Sangria automàtica ::: Sangrar codi automaticament quan pressiones tecla retorn després de dos punts. Enable calltips ::: Show calltips with function signatures. Habilitar ajuda funcions ::: Ensenyar ajuda prototip funcions. Autocomplete keywords ::: The autocompletion list includes keywords. Autocompletet paraules clau ::: La llista d'autocopletat inclou paraules clau. Edit key mappings... ::: Edit the shortcuts for menu items. Edita assignacions de tecles... ::: Edita els accessos directes per elements de menú. Edit syntax styles... ::: Change the coloring of your code. Edició estil sintàxis... ::: Canvia colors sintaxi texte del teu códic. Debug next: proceed until next line Depuració següent: continuar fins a la següent línia Debug return: proceed until returns Depuració retorn: Depuració fins retorn Debug continue: proceed to next breakpoint Depuració continuar: procedir al següent punt d'interrupció Stop debugging Aturar depuració Clear all {} breakpoints Esborrar tots els punts d'interrupció {} Debug step into: proceed one step Etapa de depuració a dins: procedir un pas Run file as script ::: Restart and run the current file as a script. Executar l'arxiu com un script ::: Reiniciar i executar el fitxer actual com un script. Run main file as script ::: Restart and run the main file as a script. Executar arxiu principal com a script ::: Reiniciar i executar el fitxer principal com un script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Executar selecció ::: Executar línies de l'editor seleccionades, paraules seleccionades en la línia actual, o la línia actual si no hi ha cap selecció. Execute cell ::: Execute the current editors's cell in the current shell. Executar cel la ::: Executar la cel la de l'editor actual en el terminal actual. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Executar cel la i avançar ::: Executar cel la actual i avançar a la següent. Execute file ::: Execute the current file in the current shell. Executar fitxer ::: Executeu el fitxer actual al terminal actual. Execute main file ::: Execute the main file in the current shell. Executar fitxer principal ::: Executear el fitxer principal al terminal actual. Export to PDF ::: Export current file to PDF (e.g. for printing). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Goto Definition ::: Go to definition of word under cursor. Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. None Postmortem: debug from last traceback Create new Python environment... ::: Install miniconda. Create shell %s: (%s) Execute selection and advance Help on running code ::: Open the pyzo wizard at the page about running code. (as script) No autocompletion Automatic popup Only show popup when pressing Tab Autocompletion Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run No és pot executar Could not run script. No es pot executar l'script. Check for the latest version. Comproveu si és la versió més recent. Edit syntax styling Edita estil sintaxis Advanced settings Configuració avançada Language changed Idioma canviat Edit shortcut mapping Edita assignació d'accessos directes Shortcut mappings Assignacions d'accés directe About Pyzo Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Amaga giny cerca (Escape) Find pattern Busca patró Previous ::: Find previous occurrence of the pattern. Anterior ::: Cerca l'anterior ocurrència del patró. Next ::: Find next occurrence of the pattern. Següent ::: Cerca la següent ocurrència del patró. Replace pattern Reemplaçar patró Repl. all ::: Replace all matches in current document. Reemplaçar tots ::: Reemplaçar totes les coincidències en aquest document. Replace ::: Replace this match. Reemplaça ::: Reemplaça aquesta occurrència. Match case ::: Find words that match case. Sensible majúsculas ::: Sensible entre majúsculas y minúsculas. RegExp ::: Find using regular expressions. Expressió regular ::: Cercar utilitzant expressions regulars. Whole words ::: Find only whole words. Paraules senceres ::: Cercar només paraules senceres. Auto hide ::: Hide search/replace when unused for 10 s. Auto amagar ::: Amaga automàticament cerca/reemplaça quan no es fa servir durant 10 s. shell Use system default Empra opcions per defecte del sistema name ::: The name of this configuration. Nom ::: El nom d'aquesta configuració. exe ::: The Python executable. exe ::: L'executable de Python. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: Kit d'eines de GUI per integrar (per traçat interactiu, etc.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath ::: Llista de directoris on cercar mòduls i paquets. Introdueix cada ruta en una nova línia, o separa-les mitjançant el separador per defecto del teu sistema operatiu. startupScript ::: The script to run at startup (not in script mode). ScriptInici ::: Script a executar al inici (no en mode script). startDir ::: The start directory (not in script mode). DirInici ::: Directori al inici ( no en mode script). Delete ::: Delete this shell configuration Esborrar ::: Esborra la configuració d'aquesta consola Shell configurations Configuració consola Add config Afegeix configuració ipython ::: Use IPython shell if available. ipython ::: Emprar terminal IPython si disponible. File to run at startup Arxiu a executar a l'inici Code to run at startup Codi a executar a l'inici argv ::: The command line arguments (sys.argv). argv ::: Arguments linía de comandes (sys.argv). environ ::: Extra environment variables (os.environ). environ ::: Variables d'entorn addicionals (os.environ). shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Step Pas Welcome to the Interactive Editor for Python! Benvingut a l'(E)ditor (I)nteractiu de (P)ython - IEP! Select language Selecciona idioma Language changed Idioma canviat You can execute commands directly in the *shell*, Podeu executar comandaments directament a la *consola*, or you can write code in the *editor* and execute that. o pot escriure codi en l'*editor* i posteriorment executar-ho. The editor is where you write your code L'editor és on s'escriur el codi In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. A l'*editor*, cada arxiu obert es representa com una pestanya. En fer clic dret sobre una pestanya, els arxius es poden executar, guardar, tancar, etc. The shell is where your code gets executed La consola és on el teu codi s'executa Configuring shells Configurar les consoles Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Mediante 'Consola > Edita propietats consola', es pot editar i afegir *propietats a la consola*. Això per expemple permet seleccionar un un directori d'inici nou, o emprar un Pythonpath personalitzat. Running code Executant codi *Run cell:* a cell is everything between two lines starting with '##'. *Executar cel·la:* Una cel·la és tot el que hi ha entre dues línies que comencen amb '##'. *Run file:* run all the code in the current file. *Executar arxiu:* executar tot el codi en el fitxer actual. *Run project main file:* run the code in the current project's main file. *Executar arxiu mestre de projecte:* executar el codi de l'arxiu mestre del projecte actual. Interactive mode vs running as script Mode interactiu vs execució com a script In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. En el mode interactiu, sys.path [0] és una cadena buida (és a dir, el directori actual), i sys.argv s'estableix com ['']. Tools for your convenience Eines per a la seva comoditat Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. A través del menú * Eines, es pot seleccionar quines eines utilitzar. Les eines es poden col·locar en la forma més convenient, fins i tot es poden desacobla. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Tingueu en compte que el sistema d'eines està dissenyat de tal manera que és fàcil de crear les seves pròpies eines. Mira la wiki en línia per obtenir més informació, o utilitzar una de les eines existents, com a exemple. Recommended tools Eines recomanades We especially recommend the following tools: Recomanem especialment les següents eines: The *Source structure tool* gives an outline of the source code. L'*eina estructura codi* ofereix un esbós del codi font. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. El *Navegador d'arxius* ajuda a mantenir una visió general de tots els arxius en un directori. Per gestionar els projectes, feu clic a la icona d'estrella. Get coding! Comença a programar! Getting started with Pyzo This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_ca_ES.tr.qm0000666000000000000000000007366313156166666021466 0ustar 00000000000000,Z[f3B`/De-Cn%4G~j!9x > q Хe?N2ET3eEf@'FUp^(SGZMc>O!k2I͎1[e<3@,T[JnuPp}.Bq^0} uCIl~"TmH߱W@3pHc<5 c'r>QxGEbR. A t^X8N2F{o L .&L9K,|IX~m^k.M!F>$l$S&EnDSnU6a@>bh+nnjWYOH5s  R>!',<>FN\I<5$L.Y &_pEJ*T6Pm>V"5f//e%VI VbV~0[#>[:gOCCnqDyF}"LѯQR(jj98N?MA/%i=f?Fj^o +i @ Ix wSD. 5 nw ר+ @ .\ B^Z JHH c V F/ l[ >'B 3j eR thr nA T  : 0n6+ t) "< @ s= )Di ˳- n\! i  i b 0m GnYv MNL; VKx f ?SX S]!o4[>Z0\`5>T>Ch~>Xn _G (J:;VH~ߔFYEގ8vDR. _irPilaStackdebug<Afegeix ruta a rutes de PythonAdd path to Python path filebrowser4Segur que vols esborrar-ho$Are you sure that you want to delete filebrowser(Canviar nom projecteChange project name filebrowserlCliqueu l'estrella per senyalitzar el directori actual"Click star to bookmark current dir filebrowserCopia ruta Copy path filebrowserCrear directoriCreate new directory filebrowserCrear arxiuCreate new file filebrowserEsborrarDelete filebrowserDuplicar Duplicate filebrowser*Filtre de noms arxiusFilename filter filebrowser*Dona nom al directori#Give the name for the new directory filebrowser"Dona nom al arxiuGive the name for the new file filebrowser"Dona nom al arxiuGive the new name for the file filebrowserZAnar a aquest directori en el terminal actual)Go to this directory in the current shell filebrowser Importa dades...Import data... filebrowserNCoincidir entre majscules i minscules Match case filebrowser"Nou nom projecte:New project name: filebrowser ObrirOpen filebrowserNom projecte Project name filebrowserProjectes: Projects: filebrowser"Expressi RegularRegExp filebrowser"Esborrar projecteRemove project filebrowserRebatejarRename filebrowser Mostra al FinderReveal in Finder filebrowser Cercar en arxiusSearch in files filebrowser Cerca en subdirsSearch in subdirs filebrowser Marcar directoriStar this directory filebrowser&Desmarcar directoriUnstar this directory filebrowserNFes una pregunta ::: Necessites ajuda? Ask a question ::: Need help?menuAutocompletet paraules clau ::: La llista d'autocopletat inclou paraules clau.DAutocomplete keywords ::: The autocompletion list includes keywords.menuSangria automtica ::: Sangrar codi automaticament quan pressiones tecla retorn desprs de dos punts.BAutomatically indent ::: Indent when pressing enter after a colon.menuPEsborrar tots els punts d'interrupci {}Clear all {} breakpointsmenuVEsborrar pantalla ::: Esborrar la pantalla."Clear screen ::: Clear the screen.menu>Tanca ::: Tanca l'arxiu actual.!Close ::: Close the current file.menufTanca ::: Finalitza l'intrpret i tanca la consola.8Close ::: Terminate the interpreter and close the shell.menuJTanca tots ::: Tanca tots els arxius.Close all ::: Close all files.menulTanca altres ::: Tanca tots els arxius excepte aquest.-Close others::: Close all files but this one.menuXComentar ::: Comenteu la lnia seleccionada.&Comment ::: Comment the selected line.menufCopia ::: Copia el text seleccionat al portapapers.1Copy ::: Copy the selected text to the clipboard.menuHTalla ::: Talla el text seleccionat.Cut ::: Cut the selected text.menuvDepuraci continuar: procedir al segent punt d'interrupci*Debug continue: proceed to next breakpointmenuhDepuraci segent: continuar fins a la segent lnia#Debug next: proceed until next linemenuNDepuraci retorn: Depuraci fins retorn#Debug return: proceed until returnsmenuTEtapa de depuraci a dins: procedir un pas!Debug step into: proceed one stepmenuVDessagnar ::: Dessagnar lnia seleccionada.&Dedent ::: Unindent the selected line.menu EditarEditmenuEdita assignacions de tecles... ::: Edita els accessos directes per elements de men.;Edit key mappings... ::: Edit the shortcuts for menu items.menuEdita configuraci consola... ::: Afegeix configuracions a la consola i edita les propietats de l'intrpret. WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menuEdici estil sintxis... ::: Canvia colors sintaxi texte del teu cdic.;Edit syntax styles... ::: Change the coloring of your code.menu|Habilitar ajuda funcions ::: Ensenyar ajuda prototip funcions.;Enable calltips ::: Show calltips with function signatures.menuvCodificaci ::: Codificaci de carcters en l'arxiu actual.8Encoding ::: The character encoding of the current file.menuExecutar cel la ::: Executar la cel la de l'editor actual en el terminal actual.IExecute cell ::: Execute the current editors's cell in the current shell.menuExecutar cel la i avanar ::: Executar cel la actual i avanar a la segent.]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menuExecutar fitxer ::: Executeu el fitxer actual al terminal actual.?Execute file ::: Execute the current file in the current shell.menuExecutar fitxer principal ::: Executear el fitxer principal al terminal actual.AExecute main file ::: Execute the main file in the current shell.menu.Executar selecci ::: Executar lnies de l'editor seleccionades, paraules seleccionades en la lnia actual, o la lnia actual si no hi ha cap selecci.Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menu ArxiuFilemenuCercar segent ::: Cerca la segent ocurrncia de la cadena de cerca.exe ::: L'executable de Python.exe ::: The Python executable.shellgui ::: Kit d'eines de GUI per integrar (per traat interactiu, etc.).Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shelldipython ::: Emprar terminal IPython si disponible.+ipython ::: Use IPython shell if available.shellLNom ::: El nom d'aquesta configuraci.(name ::: The name of this configuration.shellhpythonPath ::: Llista de directoris on cercar mduls i paquets. Introdueix cada ruta en una nova lnia, o separa-les mitjanant el separador per defecto del teu sistema operatiu. pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shelljDirInici ::: Directori al inici ( no en mode script).6startDir ::: The start directory (not in script mode).shell~ScriptInici ::: Script a executar al inici (no en mode script).DstartupScript ::: The script to run at startup (not in script mode).shell*Executar cella:* Una cella s tot el que hi ha entre dues lnies que comencen amb '##'.F*Run cell:* a cell is everything between two lines starting with '##'.wizardv*Executar arxiu:* executar tot el codi en el fitxer actual.1*Run file:* run all the code in the current file.wizard*Executar arxiu mestre de projecte:* executar el codi de l'arxiu mestre del projecte actual.I*Run project main file:* run the code in the current project's main file.wizard.Configurar les consolesConfiguring shellswizard(Comena a programar! Get coding!wizardEn el mode interactiu, sys.path [0] s una cadena buida (s a dir, el directori actual), i sys.argv s'estableix com [''].pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizard>A l'*editor*, cada arxiu obert es representa com una pestanya. En fer clic dret sobre una pestanya, els arxius es poden executar, guardar, tancar, etc.In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizardPMode interactiu vs execuci com a script%Interactive mode vs running as scriptwizardIdioma canviatLanguage changedwizardTingueu en compte que el sistema d'eines est dissenyat de tal manera que s fcil de crear les seves prpies eines. Mira la wiki en lnia per obtenir ms informaci, o utilitzar una de les eines existents, com a exemple.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizard"Eines recomanadesRecommended toolswizardExecutant codi Running codewizard"Selecciona idiomaSelect languagewizardPasStepwizard6El *Navegador d'arxius* ajuda a mantenir una visi general de tots els arxius en un directori. Per gestionar els projectes, feu clic a la icona d'estrella.The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizardpL'*eina estructura codi* ofereix un esbs del codi font.@The *Source structure tool* gives an outline of the source code.wizard@L'editor s on s'escriur el codi'The editor is where you write your codewizardLLa consola s on el teu codi s'executa*The shell is where your code gets executedwizard:Eines per a la seva comoditatTools for your conveniencewizardMediante 'Consola > Edita propietats consola', es pot editar i afegir *propietats a la consola*. Aix per expemple permet seleccionar un un directori d'inici nou, o emprar un Pythonpath personalitzat.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardLA travs del men * Eines, es pot seleccionar quines eines utilitzar. Les eines es poden collocar en la forma ms convenient, fins i tot es poden desacobla.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardTRecomanem especialment les segents eines:,We especially recommend the following tools:wizardlBenvingut a l'(E)ditor (I)nteractiu de (P)ython - IEP!-Welcome to the Interactive Editor for Python!wizardnPodeu executar comandaments directament a la *consola*,1You can execute commands directly in the *shell*,wizard|o pot escriure codi en l'*editor* i posteriorment executar-ho.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_de_DE.tr0000666000000000000000000020702013156166662021016 0ustar 00000000000000 debug Stack Stapel editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter Dateinamenfilter Search in files Suche in Dateien Projects: Projekte: Click star to bookmark current dir Stern klicken um das aktuelle Verzeichnis als Lesezeichen zu markieren Remove project Projekt entfernen Change project name Projektname aendern Add path to Python path Zu Python Pfad hinzufuegen Project name Projektname New project name: Neuer Projektname: Match case Groß-/Kleinschreibung RegExp Regulaerer Ausdruck Search in subdirs Suche in Unterverzeichnissen Unstar this directory Markierung dieses Verzeichnisses entfernen Star this directory Dieses Verzeichnis markieren Open Oeffnen Reveal in Finder Im Finder anzeigen Copy path Pfad kopieren Rename Umbenennen Delete Loeschen Create new file Datei neu Create new directory Neues Verzeichnis erstellen Give the new name for the file Neuer Dateiname Give the name for the new directory Neuer Verzeichnisname Duplicate Duplikat Give the name for the new file Name der neuen Datei Are you sure that you want to delete Loeschen - Sind Sie sicher ? Go to this directory in the current shell In dieses Verzeichnis der aktuellen Shell wechseln Import data... Daten werden importiert.... Run as script Run Jupyter notebook Open outside Pyzo importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu File Datei Edit Edit View Ansicht Settings Einstellungen Shell Shell Run Ausfuehren Tools Tools Help Hilfe Use tabs Tabstopps benutzen Use spaces Leerzeichen benutzen spaces plural of spacebar character Leerzeichen Indentation ::: The indentation used of the current file. Das Einrücken ::: Der von der aktuellen Datei verwendete Einzug. Syntax parser ::: The syntax parser of the current file. Syntax-Parser ::: Syntax-Parser der aktuellen Datei. Line endings ::: The line ending character of the current file. Zeilenenden ::: Das Zeilenendzeichen der aktuellen Datei. Encoding ::: The character encoding of the current file. Kodierung ::: Die Zeichensatzkodierung der aktuellen Datei. New ::: Create a new (or temporary) file. Neu ::: Erzeugen einer neuen (oder temporaeren) Datei. Open... ::: Open an existing file from disk. Oeffnen... ::: Oeffnen einer bestehenden Datei. Save ::: Save the current file to disk. Speichern ::: Speichern der aktuellen Datei auf der Festplatte. Save as... ::: Save the current file under another name. Speichern unter... ::: Speichert die aktuelle Datei unter einem anderen Namen. Save all ::: Save all open files. Alles speichern ::: Alle geoeffneten Dateien speichern. Close ::: Close the current file. Schliessen ::: Schliessen der aktuellen Datei. Close all ::: Close all files. Alles schliessen ::: Alle Dateien schliessen. Undo ::: Undo the latest editing action. Undo/Zurück ::: Letzte Bearbeitung rückgaengig machen. Redo ::: Redo the last undone editong action. Wiederholen ::: Wiederholen des letzten unerledigten Bearbeitungsschrittes. Cut ::: Cut the selected text. Ausschneiden ::: Ausgewaehlten Text ausschneiden. Copy ::: Copy the selected text to the clipboard. Kopieren ::: Ausgewaehlten Text in die Zwischenablage kopieren. Paste ::: Paste the text that is now on the clipboard. Einfuegen ::: Einfügen von Text aus der Zwischenablage. Select all ::: Select all text. Alles markieren ::: Ganzen Text markieren. Indent ::: Indent the selected line. Einrücken ::: Einrücken der markierten Zeile. Dedent ::: Unindent the selected line. Einrücken ::: Einrücken der ausgewaehlten Zeile rückgängig machen. Comment ::: Comment the selected line. Kommentar ::: Ausgewaehlte Zeile kommentieren. Uncomment ::: Uncomment the selected line. Kommentierung bearbeiten ::: Kommentierung der gewaehlten Zeile entfernen. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Berichtigung Kommentar/DocString ::: Markierten Text so umformen, dass auf ca. 70 Zeichen ausgerichtet. Go to line ::: Go to a specific line number. Gehe zur Zeile ::: Gehe zu einer spezifischen Liniennummer. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Finden oder Ersetzen ::: Zeige Finden- oder Ersetzen-Widget. Mit ausgewähltem Text initialisieren. Find selection ::: Find the next occurrence of the selected text. Finde Auswahl ::: Finde das naechste Vorkommen des markierten Textes. Find selection backward ::: Find the previous occurrence of the selected text. Finde Auswahl rückwaerts ::: Finde das vorherige Vorkommen des markierten Textes. Find next ::: Find the next occurrence of the search string. Weiter suchen ::: Finde das naechste Auftreten des Suchbegriffs. Find previous ::: Find the previous occurrence of the search string. Finde vorherige ::: Finde das vorherige Vorkommen der Suchzeichenfolge. Accept autocompletion with: Zoom in Hereinzoomen / Zoom in Zoom out Herauszoomen / Zoom out Zoom reset Zoom zurücksetzen / Zoom reset Location of long line indicator ::: The location of the long-line-indicator. Position der Zeilenlängenanzeige ::: Position der Zeilenlängeanzeige. Qt theme ::: The styling of the user interface widgets. Qt Thema ::: Das Aussehen von UI-Widgets. Select shell ::: Focus the cursor on the current shell. Shell auswaehlen ::: Cursor auf die aktuelle Shell setzen. Select editor ::: Focus the cursor on the current editor. Editor auswaehlen ::: Cursor auf den aktuellen Editor setzen. Select previous file ::: Select the previously selected file. Vorhergehende Datei auswaehlen ::: Die vorher gewaehlte Datei auswaehlen. Show whitespace ::: Show spaces and tabs. Zeige Leerzeichen ::: Leerzeichen und Tabulatoren anzeigen. Show line endings ::: Show the end of each line. Zeige Zeilenenden ::: Zeige das Ende jeder Zeile. Show indentation guides ::: Show vertical lines to indicate indentation. Zeige Einzug Einstellungen ::: Zeige vertikale Linien um die Einzuege anzuzeigen. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Lange Zeilen umbrechen ::: Lange Zeilen, die nicht auf den Bildschirm passen, umbrechen (z.B. kein horizontales scrollen). Highlight current line ::: Highlight the line where the cursor is. Markieren der aktuellen Zeile ::: Markieren der Zeile in der sich der Cursor befindet. Font Schriftart Zooming Zoomen Clear screen ::: Clear the screen. Bildschirm löschen. Interrupt ::: Interrupt the current running code (does not work for extension code). Unterbrechen ::: Unterbrechung des des momentan ausgeführten Codes (funktioniert nicht bei Erweiterungscode). Restart ::: Terminate and restart the interpreter. Neustart ::: Interpreter schliessen und neu starten. Terminate ::: Terminate the interpreter, leaving the shell open. Abbrechen ::: Interpreter abbrechen, shell bleibt offen. Close ::: Terminate the interpreter and close the shell. Schliessen ::: Interpreter beenden und Shell schliessen. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Shell-Konfigurationen bearbeiten... ::: Neue Shell-Konfigurationen und Interpreter-Eigenschaften bearbeiten. New shell ... ::: Create new shell to run code in. Neue Shell ... ::: Erzeugen einer neuen Shell zur Ausführung von Code. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Markierten Bereich ausfuehren ::: Ausführen der im aktuellen Editor markierten Zeilen, die selktierten Wörter in der aktuellen Zeile oder die aktuelle Zeile, wenn nichts markiert ist. Close others::: Close all files but this one. Alle anderen schliessen ::: Alle Dateien ausser dieser schliessen. Rename ::: Rename this file. Umbenennen ::: Diese Datei umbenennen. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Markieren / Unmarkieren von Dateienr ::: Markierte Dateien können weniger leicht geschlossen werden. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Setzen/Rücksetzen als MAIN-Datei ::: Die MAIN-Datei kann ausgefuehrt werden, waehrend eine andere Datei ausgewaehlt ist. Run file ::: Run the code in this file. Datei ausfuehren ::: Code in dieser Datei ausfuehren. Run file as script ::: Run this file as a script (restarts the interpreter). Datei als Script ausfuehren ::: Ausführen der aktuellen Datei als Script (Interpreter wird neu gestartet). Reload tools ::: For people who develop tools. Werkzeuge neu laden ::: Für Nutzer die eigen Werkzeuge entwickeln. Ask a question ::: Need help? Eine Frage stellen ::: Brauchen Sie Hilfe ? Automatically indent ::: Indent when pressing enter after a colon. Automatischer Einzug ::: Einzug wenn "Enter" nach einem Doppelpunkt gedrückt wird. Enable calltips ::: Show calltips with function signatures. Methodentipps aktivieren ::: Zeige Methodentipps mit Funktionssignaturen. Autocomplete keywords ::: The autocompletion list includes keywords. Auto-Vervollständigen Schlüsselwörter ::: Die "Auto-Vervollständigen" Schlüsselwortliste. Edit key mappings... ::: Edit the shortcuts for menu items. Tastenzuordnungen bearbeiten ::: Tastaturkuerzel für die Menuepunkte bearbeiten. Edit syntax styles... ::: Change the coloring of your code. Syntax-Stil bearbeiten... ::: Code-Farben verändern. Debug next: proceed until next line Debug weiter: Bis zur naechsten Zeile Debug return: proceed until returns Debug zurück: Bis zur Umkehr Debug continue: proceed to next breakpoint Debug weiter: Sprung zum naechsten Haltepunkt Stop debugging Debug stoppen Clear all {} breakpoints Alle {} Haltepunkte deaktivieren Postmortem: debug from last traceback Postmortal: Debug vom letzten Traceback Debug step into: proceed one step Debug Einsprung: Ein Schritt Run file as script ::: Restart and run the current file as a script. Datei als Script ausfuehren ::: Neu starten und Ausführen der aktuellen Datei als Script. Run main file as script ::: Restart and run the main file as a script. Main-Datei als Script ausführen ::: Neu starten und Main-Datei als Script ausführen. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Markierte Elemente ausführen ::: Ausführen der im aktuellen Editor markierten Zeilen, die selktierten Wörter in der aktuellen Zeile oder die aktuelle Zeile, wenn nichts markiert ist. Execute cell ::: Execute the current editors's cell in the current shell. Element ausführen ::: Aktuelles Element des Editors in der Shell ausführen. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Element ausführen und weiter ::: Aktuelles Element des Editors ausführen und dann zum nächsten Element wechseln. Execute file ::: Execute the current file in the current shell. Datei ausführen ::: Datei in der aktuellen Shell ausführen. Execute main file ::: Execute the main file in the current shell. Main-Datei ausfuehren ::: Main-Datei in der aktuellen Shell ausfuehren. Export to PDF ::: Export current file to PDF (e.g. for printing). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Goto Definition ::: Go to definition of word under cursor. Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. None Create new Python environment... ::: Install miniconda. Create shell %s: (%s) Execute selection and advance Help on running code ::: Open the pyzo wizard at the page about running code. (as script) No autocompletion Automatic popup Only show popup when pressing Tab Autocompletion Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run Konnte nicht ausgeführt werden Could not run script. Script konnte nicht ausgefuehrt werden. Check for the latest version. Auf aktualisierte Version prüfen. Edit syntax styling Syntax-Stil (Codefarben) bearbeiten Advanced settings Erweiterte Einstellungen Language changed Sprache geändert Edit shortcut mapping Tastaturkürzel bearbeiten Shortcut mappings Tastaturkürzel About Pyzo Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Such-Widget ausblenden (Escape) Find pattern Pattern/Suchmuster finden Previous ::: Find previous occurrence of the pattern. Vorheriges ::: Finde vorheriges Vorkommen des Patterns/Suchmusters. Next ::: Find next occurrence of the pattern. Nächstes ::: Finde nächstes Vorkommen des Patterns/Suchmusters. Replace pattern Pattern/Suchmuster ersetzen Repl. all ::: Replace all matches in current document. Alle ersetzen ::: Ersetze alle Treffer in aktuellem Dokument. Replace ::: Replace this match. Ersetzen ::: Ersetze diesen Treffer. Match case ::: Find words that match case. Groß-/Kleinschreibung ::: Passende Worte finden. RegExp ::: Find using regular expressions. Regulärer Ausdruck / Regex ::: Finden mit Regulärem Ausdruck. Whole words ::: Find only whole words. Ganze Wörter ::: Nur ganze Wörter suchen. Auto hide ::: Hide search/replace when unused for 10 s. Automatisch ausblenden ::: Suchen/Ersetzen ausblenden, wenn 10 s unbenutzt. shell Use system default System-Standard benutzen name ::: The name of this configuration. name ::: Der Name dieser Konfiguration. exe ::: The Python executable. exe ::: Die Python-Ausführungsdatei. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: GUI-Toolkit zu integrieren (für interaktives plotten o.ä.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath :::Eine Liste der Verzeichnisse um Module und Pakete zu suchen. Schreiben Sie jeden Pfad in eine neue Zeile, oder trennen Sie sie mit den Standardtrennzeichen für dieses Betriebssystem. startupScript ::: The script to run at startup (not in script mode). startupScript ::: Das Script welches beim Start ausgeführt wird (nicht im Script-Modus). startDir ::: The start directory (not in script mode). startDir ::: Das Start-Verzeichnis (nicht im Script-Modus). Delete ::: Delete this shell configuration Löschen ::: Diese Shell-Konfiguration löschen Shell configurations Shell Konfiguration Add config Konfiguration hinzufügen ipython ::: Use IPython shell if available. ipython ::: Verwende iPython-Shell falls verfügbar. File to run at startup Datei beim Start auszuführen Code to run at startup Code beim Start auszuführen argv ::: The command line arguments (sys.argv). arg ::: Kommandozeilen-Argumente (sys.argv). environ ::: Extra environment variables (os.environ). Umgebung ::: Extra Umgebungsvariablen (os.environ). shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Step Step Welcome to the Interactive Editor for Python! Willkommen imr Interactive-Editor für Python! Select language Sprache wählen Language changed Sprache geändert You can execute commands directly in the *shell*, Sie können Befehle direkt in der *Shell* ausführen or you can write code in the *editor* and execute that. oder Sie können Code im Editor schreiben und diesen dann ausführen. The editor is where you write your code Im Editor schreiben Sie Ihren Code In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. In der *Editor* wird jede geöffnete Datei als Registerkarte dargestellt. Mit einem Rechtsklick auf eine Registerkarte, können Dateien ausgeführt, gespeichert und geschlossen usw. werden. The shell is where your code gets executed In der Sell wird Ihr Code ausgeführt Configuring shells Shell Konfiguration Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Über Shell > Shell Konfiguration bearbeiten können Sie eine Shell-Konfiguration hinzufügen oder bearbeiten. Sie können ein Heimverzeichnis wählen oder einen benutzerdefinierten Python-Pfad anlegen. Running code Laufender Code *Run cell:* a cell is everything between two lines starting with '##'. *Ausführen Zelle:* eine Zelle ist alles zwischen zwei Zeilen, startend mit '##'. *Run file:* run all the code in the current file. *Ausführen Datei:* führt den kompletten Code innerhalb einer Datei aus. *Run project main file:* run the code in the current project's main file. *Ausführen Prjekt MAIN-Datei:* führt den Code innerhalb der MAIN-Datei aus. Interactive mode vs running as script "Interaktiver Modus" gegenüber "Script-Modus" In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. Im interaktiven Modus ist "sys.path" eine leere Zeichenfolge [0] (z.B. das aktuelle Verzeichnis), und sys.argv wird auf [''] eingestellt. Tools for your convenience Extras für Ihren Komfort Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Über das "*Werkzeug-Menü*", kann man auswählen, welche Tools genutzt werden. Die Werkzeuge können frei positioniert werden und angedockt als auch un-angedockt genutzt werden. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Das Werkzeug (Tool)-System ist so ausgelegt, dass es sehr einfach ist, Ihre eigenen Werkzeuge (Tools) zu erstellen. Infos dazu finden Sie in der Online-Wiki, oder verwenden Sie ein vorhandenes Tool als Vorlage. Recommended tools Empfohlene Werkzeuge We especially recommend the following tools: Wir empfehlen vor allem die folgenden Tools: The *Source structure tool* gives an outline of the source code. Das Tool *Quell-Struktur* gibt einen Überblick über den Quellcode. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. Das * Datei-Browser-Tool * hilft den Überblick über alle Dateien eines Projekts zu behalten. Zum Bearbeiten Ihrer Projekte, klicken Sie auf das Sternsymbol. Get coding! Auf geht's ! Getting started with Pyzo This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_de_DE.tr.qm0000666000000000000000000007634713156166666021456 0ustar 00000000000000  Хk4?N5ET6eJKf@'KEp^)LZyc>T"S2Nz͎4Kj<3@,Y[OnuUQp}.Fq^3} SuCN~#>rr߱WD3uHc@8 c+r>VLEgzW.  tX8N5.{!( .'GL9P0,'|I|~ Qck.R!F>%^$S&EnH3 0SnZ*a@>h_h+nnj\O}H5s `F BR>'.<>[FSI<5RL1Y '_pJJ, 6U m>[5kw/e]VNRVhV~2[$$[>ToH'nvfyKq"9RѯV8W<j^j=;8NCQA/%nf?Kj^sb , C IxW wSHr 5% n- ר- @ .b= Bc JMr c [ F2! l`b >( 3p e tm nE T  0n9' t+@ "? D sA )Do ˳/ nac i _ i 0k Gn^ MNQI VP k ?XT Sc9!u4[>_T\ei>X>Gz~BNn _L (J:>VM~ߔJYESގ;DW. iw> StapelStackdebug4Zu Python Pfad hinzufuegenAdd path to Python path filebrowser8Loeschen - Sind Sie sicher ?$Are you sure that you want to delete filebrowser&Projektname aendernChange project name filebrowserStern klicken um das aktuelle Verzeichnis als Lesezeichen zu markieren"Click star to bookmark current dir filebrowserPfad kopieren Copy path filebrowser6Neues Verzeichnis erstellenCreate new directory filebrowserDatei neuCreate new file filebrowserLoeschenDelete filebrowserDuplikat Duplicate filebrowser DateinamenfilterFilename filter filebrowser*Neuer Verzeichnisname#Give the name for the new directory filebrowser(Name der neuen DateiGive the name for the new file filebrowserNeuer DateinameGive the new name for the file filebrowserdIn dieses Verzeichnis der aktuellen Shell wechseln)Go to this directory in the current shell filebrowser6Daten werden importiert....Import data... filebrowser*Gro-/Kleinschreibung Match case filebrowser$Neuer Projektname:New project name: filebrowserOeffnenOpen filebrowserProjektname Project name filebrowserProjekte: Projects: filebrowser&Regulaerer AusdruckRegExp filebrowser"Projekt entfernenRemove project filebrowserUmbenennenRename filebrowser$Im Finder anzeigenReveal in Finder filebrowser Suche in DateienSearch in files filebrowser8Suche in UnterverzeichnissenSearch in subdirs filebrowser8Dieses Verzeichnis markierenStar this directory filebrowserTMarkierung dieses Verzeichnisses entfernenUnstar this directory filebrowserVEine Frage stellen ::: Brauchen Sie Hilfe ?Ask a question ::: Need help?menuAuto-Vervollstndigen Schlsselwrter ::: Die "Auto-Vervollstndigen" Schlsselwortliste.DAutocomplete keywords ::: The autocompletion list includes keywords.menuAutomatischer Einzug ::: Einzug wenn "Enter" nach einem Doppelpunkt gedrckt wird. BAutomatically indent ::: Indent when pressing enter after a colon.menu@Alle {} Haltepunkte deaktivierenClear all {} breakpointsmenu&Bildschirm lschen."Clear screen ::: Clear the screen.menu\Schliessen ::: Schliessen der aktuellen Datei.!Close ::: Close the current file.menupSchliessen ::: Interpreter beenden und Shell schliessen.8Close ::: Terminate the interpreter and close the shell.menuZAlles schliessen ::: Alle Dateien schliessen.Close all ::: Close all files.menuAlle anderen schliessen ::: Alle Dateien ausser dieser schliessen.-Close others::: Close all files but this one.menu\Kommentar ::: Ausgewaehlte Zeile kommentieren.&Comment ::: Comment the selected line.menu~Kopieren ::: Ausgewaehlten Text in die Zwischenablage kopieren.1Copy ::: Copy the selected text to the clipboard.menubAusschneiden ::: Ausgewaehlten Text ausschneiden.Cut ::: Cut the selected text.menuZDebug weiter: Sprung zum naechsten Haltepunkt*Debug continue: proceed to next breakpointmenuLDebug weiter: Bis zur naechsten Zeile #Debug next: proceed until next linemenu8Debug zurck: Bis zur Umkehr#Debug return: proceed until returnsmenu8Debug Einsprung: Ein Schritt!Debug step into: proceed one stepmenuEinrcken ::: Einrcken der ausgewaehlten Zeile rckgngig machen.&Dedent ::: Unindent the selected line.menuEditEditmenuTastenzuordnungen bearbeiten ::: Tastaturkuerzel fr die Menuepunkte bearbeiten. ;Edit key mappings... ::: Edit the shortcuts for menu items.menuShell-Konfigurationen bearbeiten... ::: Neue Shell-Konfigurationen und Interpreter-Eigenschaften bearbeiten. WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menuhSyntax-Stil bearbeiten... ::: Code-Farben verndern.;Edit syntax styles... ::: Change the coloring of your code.menuMethodentipps aktivieren ::: Zeige Methodentipps mit Funktionssignaturen. ;Enable calltips ::: Show calltips with function signatures.menuvKodierung ::: Die Zeichensatzkodierung der aktuellen Datei.8Encoding ::: The character encoding of the current file.menuElement ausfhren ::: Aktuelles Element des Editors in der Shell ausfhren.IExecute cell ::: Execute the current editors's cell in the current shell.menuElement ausfhren und weiter ::: Aktuelles Element des Editors ausfhren und dann zum nchsten Element wechseln.]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menuzDatei ausfhren ::: Datei in der aktuellen Shell ausfhren. ?Execute file ::: Execute the current file in the current shell.menuMain-Datei ausfuehren ::: Main-Datei in der aktuellen Shell ausfuehren.AExecute main file ::: Execute the main file in the current shell.menulMarkierte Elemente ausfhren ::: Ausfhren der im aktuellen Editor markierten Zeilen, die selktierten Wrter in der aktuellen Zeile oder die aktuelle Zeile, wenn nichts markiert ist.Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menu DateiFilemenuWeiter suchen ::: Finde das naechste Auftreten des Suchbegriffs. Such-Widget ausblenden (Escape)Hide search widget (Escape)search`Gro-/Kleinschreibung ::: Passende Worte finden.*Match case ::: Find words that match case.search~Nchstes ::: Finde nchstes Vorkommen des Patterns/Suchmusters.-Next ::: Find next occurrence of the pattern.searchVorheriges ::: Finde vorheriges Vorkommen des Patterns/Suchmusters.5Previous ::: Find previous occurrence of the pattern.searchzRegulrer Ausdruck / Regex ::: Finden mit Regulrem Ausdruck.*RegExp ::: Find using regular expressions.searchzAlle ersetzen ::: Ersetze alle Treffer in aktuellem Dokument.6Repl. all ::: Replace all matches in current document.searchHErsetzen ::: Ersetze diesen Treffer.Replace ::: Replace this match.search6Pattern/Suchmuster ersetzenReplace patternsearchRGanze Wrter ::: Nur ganze Wrter suchen.&Whole words ::: Find only whole words.search0Konfiguration hinzufgen Add configshell6Code beim Start auszufhrenCode to run at startupshellZLschen ::: Diese Shell-Konfiguration lschen*Delete ::: Delete this shell configurationshell8Datei beim Start auszufhrenFile to run at startupshell&Shell KonfigurationShell configurationsshell0System-Standard benutzenUse system defaultshellXarg ::: Kommandozeilen-Argumente (sys.argv)./argv ::: The command line arguments (sys.argv).shellfUmgebung ::: Extra Umgebungsvariablen (os.environ).5environ ::: Extra environment variables (os.environ).shellHexe ::: Die Python-Ausfhrungsdatei.exe ::: The Python executable.shellgui ::: GUI-Toolkit zu integrieren (fr interaktives plotten o..). Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shellhipython ::: Verwende iPython-Shell falls verfgbar. +ipython ::: Use IPython shell if available.shellNname ::: Der Name dieser Konfiguration.(name ::: The name of this configuration.shellpythonPath :::Eine Liste der Verzeichnisse um Module und Pakete zu suchen. Schreiben Sie jeden Pfad in eine neue Zeile, oder trennen Sie sie mit den Standardtrennzeichen fr dieses Betriebssystem. pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shellvstartDir ::: Das Start-Verzeichnis (nicht im Script-Modus).6startDir ::: The start directory (not in script mode).shellstartupScript ::: Das Script welches beim Start ausgefhrt wird (nicht im Script-Modus).DstartupScript ::: The script to run at startup (not in script mode).shell*Ausfhren Zelle:* eine Zelle ist alles zwischen zwei Zeilen, startend mit '##'.F*Run cell:* a cell is everything between two lines starting with '##'.wizard*Ausfhren Datei:* fhrt den kompletten Code innerhalb einer Datei aus.1*Run file:* run all the code in the current file.wizard*Ausfhren Prjekt MAIN-Datei:* fhrt den Code innerhalb der MAIN-Datei aus. I*Run project main file:* run the code in the current project's main file.wizard&Shell KonfigurationConfiguring shellswizardAuf geht's ! Get coding!wizardIm interaktiven Modus ist "sys.path" eine leere Zeichenfolge [0] (z.B. das aktuelle Verzeichnis), und sys.argv wird auf [''] eingestellt.pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizardtIn der *Editor* wird jede geffnete Datei als Registerkarte dargestellt. Mit einem Rechtsklick auf eine Registerkarte, knnen Dateien ausgefhrt, gespeichert und geschlossen usw. werden.In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizardZ"Interaktiver Modus" gegenber "Script-Modus"%Interactive mode vs running as scriptwizard Sprache gendertLanguage changedwizardDas Werkzeug (Tool)-System ist so ausgelegt, dass es sehr einfach ist, Ihre eigenen Werkzeuge (Tools) zu erstellen. Infos dazu finden Sie in der Online-Wiki, oder verwenden Sie ein vorhandenes Tool als Vorlage.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizard(Empfohlene WerkzeugeRecommended toolswizardLaufender Code Running codewizardSprache whlenSelect languagewizardStepStepwizard8Das * Datei-Browser-Tool * hilft den berblick ber alle Dateien eines Projekts zu behalten. Zum Bearbeiten Ihrer Projekte, klicken Sie auf das Sternsymbol.The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizardDas Tool *Quell-Struktur* gibt einen berblick ber den Quellcode.@The *Source structure tool* gives an outline of the source code.wizardDIm Editor schreiben Sie Ihren Code'The editor is where you write your codewizardHIn der Sell wird Ihr Code ausgefhrt*The shell is where your code gets executedwizard0Extras fr Ihren KomfortTools for your conveniencewizardber Shell > Shell Konfiguration bearbeiten knnen Sie eine Shell-Konfiguration hinzufgen oder bearbeiten. Sie knnen ein Heimverzeichnis whlen oder einen benutzerdefinierten Python-Pfad anlegen.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizard\ber das "*Werkzeug-Men*", kann man auswhlen, welche Tools genutzt werden. Die Werkzeuge knnen frei positioniert werden und angedockt als auch un-angedockt genutzt werden.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardXWir empfehlen vor allem die folgenden Tools:,We especially recommend the following tools:wizardZWillkommen imr Interactive-Editor fr Python!-Welcome to the Interactive Editor for Python!wizarddSie knnen Befehle direkt in der *Shell* ausfhren1You can execute commands directly in the *shell*,wizardoder Sie knnen Code im Editor schreiben und diesen dann ausfhren.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_es_ES.tr0000666000000000000000000020645613156166663021071 0ustar 00000000000000 debug Stack Pila editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter Filtros de nombres archivos Search in files Busca en archivos Projects: Proyectos: Click star to bookmark current dir Apretar la estrella para marcar el directorio actual Remove project Borrar proyecto Change project name Cambiar nombre proyecto Add path to Python path Añade ruta a rutas de Python Project name Nombre proyecto New project name: Nuevo nomre proyecto: Match case Sensible entre mayúsculas y minúsculas RegExp Expresión Regular Search in subdirs Buscar en subdirs Unstar this directory Desmarcar directorio Star this directory Marcar directorio Open Abrir Reveal in Finder Muestra en Finder Copy path Copia ruta Rename Renombrar Delete Eliminar Create new file Crear archivo Create new directory Crear directorio Give the new name for the file Da nomre al archivo Give the name for the new directory Da nombre al directorio Duplicate Duplicar Give the name for the new file Da nomre al archivo Are you sure that you want to delete Seguro que quieres borrarlo Go to this directory in the current shell Ir a este directorio en el terminal actual Import data... Importar datos... Run as script Run Jupyter notebook Open outside Pyzo importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu File Archivo Edit Editar View Ver Settings Opciones Shell Consola Run Ejecutar Tools Herramientas Help Ayuda Use tabs Utiliza tabuladores Use spaces Utiliza espacios spaces plural of spacebar character espacios Indentation ::: The indentation used of the current file. Sangría ::: Sangría usada en el archivo actual. Syntax parser ::: The syntax parser of the current file. Analizador sintaxis ::: Analizador sintaxis archivo actual. Line endings ::: The line ending character of the current file. Final línea ::: Carácter final línea archivo actual. Encoding ::: The character encoding of the current file. Codificación ::: Codificacion carácteres archivo actual. New ::: Create a new (or temporary) file. Nuevo ::: Crear archivo nuevo (o temporal). Open... ::: Open an existing file from disk. Abrir... ::: Abrir archivo del disco. Save ::: Save the current file to disk. Guardar ::: Guardar archivo en disco. Save as... ::: Save the current file under another name. Guardar como... ::: Guardar archivo actual con otro nombre. Save all ::: Save all open files. Guardar todos ::: Guardar todos los archivos abiertos. Close ::: Close the current file. Cerrar ::: Cerrar archivo actual. Close all ::: Close all files. Cerrar todos ::: Cerrar todos los archivos abiertos. Undo ::: Undo the latest editing action. Deshacer ::: Desacer última edición. Redo ::: Redo the last undone editong action. Rehacer ::: Rehacer la última edición deshecha. Cut ::: Cut the selected text. Cortar ::: Cortar el texto seleccionado. Copy ::: Copy the selected text to the clipboard. Copiar ::: Copiar el texto seleccionado. Paste ::: Paste the text that is now on the clipboard. Pegar ::: Pegar el texto del portapapeles. Select all ::: Select all text. Selecionar todo ::: Seleccionar todo el texto. Indent ::: Indent the selected line. Sangrar ::: Sangrar línea seleccionada. Dedent ::: Unindent the selected line. Desangrar ::: Desangrar línea seleccionada. Comment ::: Comment the selected line. Comentar ::: Comentar líneas selección. Uncomment ::: Uncomment the selected line. Descomentar ::: descomentar líneas selección. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Justifica cometario/docstring ::: Remodela el texto seleccionado para que se alinee a 70 carácteres. Go to line ::: Go to a specific line number. Ir línea ::: Ir a una línea specifica. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Buscar o remplazar ::: Muestra widget buscar/remplazar. Se inicializa con el texto seleccionado. Find selection ::: Find the next occurrence of the selected text. Buscar selección ::: Busca la siguiente ocurrencia del texto seleccionado. Find selection backward ::: Find the previous occurrence of the selected text. Buscar selección hacia atrás ::: Busca la ocurrencia anterior del texto seleccionado. Find next ::: Find the next occurrence of the search string. Buscar siguiente ::: Buscar siguiente ocurrencia de la cadena de busca. Find previous ::: Find the previous occurrence of the search string. Buscar anterior ::: Buscar anterior ocurrencia de la cadena de busca. Accept autocompletion with: Zoom in Acercar Zoom out Alejar Zoom reset Zoom original Location of long line indicator ::: The location of the long-line-indicator. Ubicación indicador línea larga ::: Seleccionar ubicacción indicador de línea larga. Qt theme ::: The styling of the user interface widgets. Tema Qt ::: Estilo de los widgets de la interfaz de usuario. Select shell ::: Focus the cursor on the current shell. Seleccionar consola ::: Selecciona consola actual. Select editor ::: Focus the cursor on the current editor. Seleccionar editor ::: Selecciona el editor actual. Select previous file ::: Select the previously selected file. Selecciona el archivo anterior ::: Selecciona el archivo seleccionado anteriormente. Show whitespace ::: Show spaces and tabs. Muestra espacios en blanco ::: Muestra espacios y tabuladores. Show line endings ::: Show the end of each line. Muestra los finales de línea ::: Muestra el final de cada línia. Show indentation guides ::: Show vertical lines to indicate indentation. Muestra guías sangría ::: Muestra líneas verticales para indicar el sangrado. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Ajustar líneas largas ::: Ajustar líneas largas que no caben el la pantalla. Highlight current line ::: Highlight the line where the cursor is. Destacar línea actual ::: Remarca la línia donde se encuentra el cursor. Font Fuente Zooming Zoom Clear screen ::: Clear the screen. Borrar pantalla ::: Borra la pantalla. Interrupt ::: Interrupt the current running code (does not work for extension code). Interrumpir ::: Interrumpe el código en ejecución. No funciona para extensiones código. Restart ::: Terminate and restart the interpreter. Reiniciar ::: Finaliza la ejecución y reinicia el intérprete. Terminate ::: Terminate the interpreter, leaving the shell open. Finalizar ::: Finaliza el intérprete dejando la consola abierta. Close ::: Terminate the interpreter and close the shell. Cerrar ::: Finaliza el intérprete y cierra la consola. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Edita configuración consola... ::: Añade configuraciones a la consola y edita las propiedades del intérprete. New shell ... ::: Create new shell to run code in. Consola nueva... ::: Crea una consola nueva donde ejecutar código. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Ejecuta selección ::: Ejecuta las líneas selecionadas en el editor o la selección de palabras en la línia actual o la línia actual si no se ha efectuado ninguna selección. Close others::: Close all files but this one. Cerrar otros ::: Cierra todos los archivos a excepción de este. Rename ::: Rename this file. Renombra ::: Renombra este archivo. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Pinzar/desprender ::: Archivos pinzados son más difíciles de cerrar. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Marcar/desmarcar como archivo MAESTRO ::: El archivo maestro se puede ejecutar mientras otro está seleccionado. Run file ::: Run the code in this file. Ejecuta archivo ::: Ejecuta el código en este archivo. Run file as script ::: Run this file as a script (restarts the interpreter). Ejecuta el archivo como guión ::: Ejecuta el archivo com un guión (reinicia el intérprete). Reload tools ::: For people who develop tools. Recarga herramientas ::: Para programadores que desarrollan herramientas. Ask a question ::: Need help? Haz una pregunta ::: Necesitas ayuda? Automatically indent ::: Indent when pressing enter after a colon. Sangría automática ::: Sangrar código automaticamente cuando presiones tecla retorno después de dos puntos. Enable calltips ::: Show calltips with function signatures. Habilitar ayuda funcions ::: Enseñar ayudas prototipo funciones. Autocomplete keywords ::: The autocompletion list includes keywords. Autocompletado palabras clave ::: La lista de autocompletado incluye palabras clave. Edit key mappings... ::: Edit the shortcuts for menu items. Editar asignaciones de teclas... ::: Editar los accesos directos para elementos de menú. Edit syntax styles... ::: Change the coloring of your code. Edita estilo sintaxis... ::: Cambiar colores sintaxis texto de tu código. Debug next: proceed until next line Depuración siguiente: proceder hasta la siguiente línea Debug return: proceed until returns Depuración retorno: Depurar hasta el retorno Debug continue: proceed to next breakpoint Depuración continuar: proceder al siguiente punto de interrupción Stop debugging Detener depuración Clear all {} breakpoints Borrar todos los puntos de interrupción {} Postmortem: debug from last traceback Postmortem: Depurar desde el último traceback Debug step into: proceed one step Etapa de depuración a dentro: proceder a un paso Run file as script ::: Restart and run the current file as a script. Ejecutar el archivo como un script ::: Reiniciar ejecutar el archivo actual como un script. Run main file as script ::: Restart and run the main file as a script. Ejecutar el archivo principal como script ::: Reiniciar y ejecutar el archivo principal como un script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Ejecutar selección ::: Ejecutar líneas del editor seleccionadas, palabras seleccionados en la línea actual, o la línea actual si no hay ninguna selección. Execute cell ::: Execute the current editors's cell in the current shell. Ejecutar celda ::: Ejecutar la celda del editor actual en el terminal actual. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Ejecutar celda y avanzar ::: Ejecutar celda actual y avance a la siguiente. Execute file ::: Execute the current file in the current shell. Ejecutar fichero ::: Ejecute el archivo actual en el terminal actual. Execute main file ::: Execute the main file in the current shell. Ejecutar fichero principal ::: Ejecute el archivo principal en el terminal actual. Export to PDF ::: Export current file to PDF (e.g. for printing). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Goto Definition ::: Go to definition of word under cursor. Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. None Create new Python environment... ::: Install miniconda. Create shell %s: (%s) Execute selection and advance Help on running code ::: Open the pyzo wizard at the page about running code. (as script) No autocompletion Automatic popup Only show popup when pressing Tab Autocompletion Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run No se puede ejecutar Could not run script. No se puede ejecutar el guión. Check for the latest version. Compruebe si es la versión más reciente. Edit syntax styling Edita estilo sintaxis Advanced settings Configuración avanzada Language changed Idioma cambiado Edit shortcut mapping Editar asignación de accesos directos Shortcut mappings Asignaciones de acceso directo About Pyzo Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Oculta widget busqueda (Escape) Find pattern Busca patrón Previous ::: Find previous occurrence of the pattern. Anterior ::: Buscar anterior ocurrencia del patrón. Next ::: Find next occurrence of the pattern. Siguiente ::: Buscar siguiente ocurrencia del patrón. Replace pattern Reemplazar patrón Repl. all ::: Replace all matches in current document. Reemplazar todos ::: reemplazar todas las coincidencias en este documento. Replace ::: Replace this match. Reemplaza ::: Reemplaza esta occurencia. Match case ::: Find words that match case. Sensible mayúsculas ::: Sensible entre mayúsculas y minúsculas. RegExp ::: Find using regular expressions. Expresión regular ::: Buscar usando expresiones regulares. Whole words ::: Find only whole words. Palabras enteras ::: Buscar sólo palabras enteras. Auto hide ::: Hide search/replace when unused for 10 s. Auto ocultar ::: Ocultar automáticamente buscar/reemplazar cuando no se usa durante 10s. shell Use system default Usa opcions por defecto del sistema name ::: The name of this configuration. Nombre ::: El nombre de esta configuración. exe ::: The Python executable. exe ::: El ejecutable de Python. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: Kit de herramientas de GUI para integrar (para trazado interactivo, etc.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath ::: Lista de directorios donde buscar módulos y paquetes. Introduce cada ruta en un nueva línea, o separalas mediante el separador por defecto de tu sistema operativo. startupScript ::: The script to run at startup (not in script mode). GuiónInicio ::: Guión a ejecutar al inicio (no en modo guión). startDir ::: The start directory (not in script mode). DirInicio ::: Directorio al inició (no en modo guión). Delete ::: Delete this shell configuration Borrar ::: Borra la configuracion de esta consola Shell configurations Configuración consola Add config Añadir configuración ipython ::: Use IPython shell if available. ipython ::: Usar terminal IPython si disponible. File to run at startup Archivo a ejecutar al inicio Code to run at startup Código a ejecutar al inicio argv ::: The command line arguments (sys.argv). argv ::: Argumentos línea de comandos (sys.argv). environ ::: Extra environment variables (os.environ). environ ::: Variables de entorno addicionales (os.environ). shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Step Paso Welcome to the Interactive Editor for Python! Bienvenido al (E)ditor (I)nteractivo de (P)ython - IEP! Select language Selecciona idioma Language changed Idioma cambiado You can execute commands directly in the *shell*, Puede ejecutar comandos directamente en *la consola*, or you can write code in the *editor* and execute that. o puede escribir código en el *editor* y posterioment ejecutarlo. The editor is where you write your code El editor es donde se escribe el código In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. En el *editor*, cada archivo abierto se representa como una pestaña. Al hacer clic derecho sobre una pestaña, los archivos se pueden ejecutar, guardar, cerrar, etc. The shell is where your code gets executed La consola es donde tu código se ejecuta Configuring shells Configurar las consolas Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Mediante 'Consola > Edita propiedades consola', se puede editar y añadir *propiedades a la consola*. Esto permite por ejemplo seleccionar un directorio de inicio nuevo, o usar un Pythonpath personalizado. Running code Ejecutando código *Run cell:* a cell is everything between two lines starting with '##'. *Ejecutar celda:* una celda es todo lo que hay entre dos líneas que comienzan con '##'. *Run file:* run all the code in the current file. *Ejecutar archivo:* ejecutar todo el código en el archivo actual. *Run project main file:* run the code in the current project's main file. *Ejecutar archivo maestro de proyecto:* ejecutar el código en el archivo maestro del proyecto actual. Interactive mode vs running as script Modo interactivo vs ejecución como guión In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. En el modo interactivo, sys.path [0] es una cadena vacía (es decir, el directorio actual), y sys.argv se establece como ['']. Tools for your convenience Herramientas para su comodidad Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. A través del menú *Herramientas*, se puede seleccionar qué herramientas utilizar. Las herramientas se pueden colocar en cualquier forma que desee. También pueden desacoplarse. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Tenga en cuenta que el sistema de herramientas está diseñado de tal manera que es fácil de crear sus propias herramientas. Mira la wiki en línea para obtener más información, o utilizar una de las herramientas existentes, como ejemplo. Recommended tools Herramientas recomendadas We especially recommend the following tools: Recomendamos especialmente las siguientes herramientas: The *Source structure tool* gives an outline of the source code. La *herramienta estructura código* ofrece un esbozo del código fuente. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. El *Navegador de archivos* ayuda a mantener una visión general de todos los archivos en un directorio. Para gestionar sus proyectos, haga clic en el icono de estrella. Get coding! Comienza a programar! Getting started with Pyzo This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_es_ES.tr.qm0000666000000000000000000007550313156166666021505 0ustar 00000000000000  Хh?N4ET5eGf@'Hp^)IKZgc>R="2L5͎2h<3@,W([M7nuRp}.Eq^2v} uCK~"p߱WB3tHc>6c'r>SJ-EdT. k tfX8N3{  .&L9Mv,|IZ~ aJk.P!F>%"$Sf&EnF3 /SnWa@>eh+nnjYwH5 s &Z :R>'-<>#FPI<5<L01Y 'P_pH-J+6S.m>X5i'/e-VKzVedV~1[#[( 3n e tk nD T  p 0n7 t*\ ">! CI s@ )Dl ˳. n^ i 9 i 0q Gn[ MNN VM i ?U S`!sZ4[>\\b>Vc>E~@|n _I (J:=#VK~ߔHZYECގ:jDUF. iuPilaStackdebug8Aade ruta a rutas de PythonAdd path to Python path filebrowser6Seguro que quieres borrarlo$Are you sure that you want to delete filebrowser.Cambiar nombre proyectoChange project name filebrowserjApretar la estrella para marcar el directorio actual "Click star to bookmark current dir filebrowserCopia ruta Copy path filebrowser Crear directorioCreate new directory filebrowserCrear archivoCreate new file filebrowserEliminarDelete filebrowserDuplicar Duplicate filebrowser6Filtros de nombres archivosFilename filter filebrowser.Da nombre al directorio#Give the name for the new directory filebrowser&Da nomre al archivoGive the name for the new file filebrowser&Da nomre al archivoGive the new name for the file filebrowserTIr a este directorio en el terminal actual)Go to this directory in the current shell filebrowser"Importar datos...Import data... filebrowserLSensible entre maysculas y minsculas Match case filebrowser*Nuevo nomre proyecto:New project name: filebrowser AbrirOpen filebrowserNombre proyecto Project name filebrowserProyectos: Projects: filebrowser"Expresin RegularRegExp filebrowserBorrar proyectoRemove project filebrowserRenombrarRename filebrowser"Muestra en FinderReveal in Finder filebrowser"Busca en archivosSearch in files filebrowser"Buscar en subdirsSearch in subdirs filebrowser"Marcar directorioStar this directory filebrowser(Desmarcar directorioUnstar this directory filebrowserJHaz una pregunta ::: Necesitas ayuda?Ask a question ::: Need help?menuAutocompletado palabras clave ::: La lista de autocompletado incluye palabras clave.DAutocomplete keywords ::: The autocompletion list includes keywords.menuSangra automtica ::: Sangrar cdigo automaticamente cuando presiones tecla retorno despus de dos puntos.BAutomatically indent ::: Indent when pressing enter after a colon.menuTBorrar todos los puntos de interrupcin {}Clear all {} breakpointsmenuLBorrar pantalla ::: Borra la pantalla."Clear screen ::: Clear the screen.menuBCerrar ::: Cerrar archivo actual.!Close ::: Close the current file.menulCerrar ::: Finaliza el intrprete y cierra la consola.8Close ::: Terminate the interpreter and close the shell.menuhCerrar todos ::: Cerrar todos los archivos abiertos.Close all ::: Close all files.menu~Cerrar otros ::: Cierra todos los archivos a excepcin de este.-Close others::: Close all files but this one.menuNComentar ::: Comentar lneas seleccin.&Comment ::: Comment the selected line.menuPCopiar ::: Copiar el texto seleccionado.1Copy ::: Copy the selected text to the clipboard.menuPCortar ::: Cortar el texto seleccionado.Cut ::: Cut the selected text.menuDepuracin continuar: proceder al siguiente punto de interrupcin*Debug continue: proceed to next breakpointmenunDepuracin siguiente: proceder hasta la siguiente lnea#Debug next: proceed until next linemenuXDepuracin retorno: Depurar hasta el retorno#Debug return: proceed until returnsmenu`Etapa de depuracin a dentro: proceder a un paso!Debug step into: proceed one stepmenuVDesangrar ::: Desangrar lnea seleccionada.&Dedent ::: Unindent the selected line.menu EditarEditmenuEditar asignaciones de teclas... ::: Editar los accesos directos para elementos de men.;Edit key mappings... ::: Edit the shortcuts for menu items.menuEdita configuracin consola... ::: Aade configuraciones a la consola y edita las propiedades del intrprete.WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menuEdita estilo sintaxis... ::: Cambiar colores sintaxis texto de tu cdigo.;Edit syntax styles... ::: Change the coloring of your code.menuHabilitar ayuda funcions ::: Ensear ayudas prototipo funciones.;Enable calltips ::: Show calltips with function signatures.menupCodificacin ::: Codificacion carcteres archivo actual.8Encoding ::: The character encoding of the current file.menuEjecutar celda ::: Ejecutar la celda del editor actual en el terminal actual.IExecute cell ::: Execute the current editors's cell in the current shell.menuEjecutar celda y avanzar ::: Ejecutar celda actual y avance a la siguiente.]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menuEjecutar fichero ::: Ejecute el archivo actual en el terminal actual.?Execute file ::: Execute the current file in the current shell.menuEjecutar fichero principal ::: Ejecute el archivo principal en el terminal actual.AExecute main file ::: Execute the main file in the current shell.menu4Ejecutar seleccin ::: Ejecutar lneas del editor seleccionadas, palabras seleccionados en la lnea actual, o la lnea actual si no hay ninguna seleccin.Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menuArchivoFilemenuBuscar siguiente ::: Buscar siguiente ocurrencia de la cadena de busca.Oculta widget busqueda (Escape)Hide search widget (Escape)search~Sensible maysculas ::: Sensible entre maysculas y minsculas.*Match case ::: Find words that match case.searchjSiguiente ::: Buscar siguiente ocurrencia del patrn.-Next ::: Find next occurrence of the pattern.searchfAnterior ::: Buscar anterior ocurrencia del patrn.5Previous ::: Find previous occurrence of the pattern.searchtExpresin regular ::: Buscar usando expresiones regulares.*RegExp ::: Find using regular expressions.searchReemplazar todos ::: reemplazar todas las coincidencias en este documento.6Repl. all ::: Replace all matches in current document.searchPReemplaza ::: Reemplaza esta occurencia.Replace ::: Replace this match.search"Reemplazar patrnReplace patternsearchdPalabras enteras ::: Buscar slo palabras enteras.&Whole words ::: Find only whole words.search(Aadir configuracin Add configshell6Cdigo a ejecutar al inicioCode to run at startupshellbBorrar ::: Borra la configuracion de esta consola*Delete ::: Delete this shell configurationshell8Archivo a ejecutar al inicioFile to run at startupshell*Configuracin consolaShell configurationsshellFUsa opcions por defecto del sistemaUse system defaultshellbargv ::: Argumentos lnea de comandos (sys.argv)./argv ::: The command line arguments (sys.argv).shellvenviron ::: Variables de entorno addicionales (os.environ).5environ ::: Extra environment variables (os.environ).shell@exe ::: El ejecutable de Python.exe ::: The Python executable.shellgui ::: Kit de herramientas de GUI para integrar (para trazado interactivo, etc.).Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shell`ipython ::: Usar terminal IPython si disponible.+ipython ::: Use IPython shell if available.shellVNombre ::: El nombre de esta configuracin.(name ::: The name of this configuration.shellhpythonPath ::: Lista de directorios donde buscar mdulos y paquetes. Introduce cada ruta en un nueva lnea, o separalas mediante el separador por defecto de tu sistema operativo. pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shelllDirInicio ::: Directorio al inici (no en modo guin).6startDir ::: The start directory (not in script mode).shell|GuinInicio ::: Guin a ejecutar al inicio (no en modo guin).DstartupScript ::: The script to run at startup (not in script mode).shell*Ejecutar celda:* una celda es todo lo que hay entre dos lneas que comienzan con '##'.F*Run cell:* a cell is everything between two lines starting with '##'.wizard*Ejecutar archivo:* ejecutar todo el cdigo en el archivo actual.1*Run file:* run all the code in the current file.wizard*Ejecutar archivo maestro de proyecto:* ejecutar el cdigo en el archivo maestro del proyecto actual.I*Run project main file:* run the code in the current project's main file.wizard.Configurar las consolasConfiguring shellswizard*Comienza a programar! Get coding!wizard En el modo interactivo, sys.path [0] es una cadena vaca (es decir, el directorio actual), y sys.argv se establece como [''].pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizardXEn el *editor*, cada archivo abierto se representa como una pestaa. Al hacer clic derecho sobre una pestaa, los archivos se pueden ejecutar, guardar, cerrar, etc.In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizardPModo interactivo vs ejecucin como guin%Interactive mode vs running as scriptwizardIdioma cambiadoLanguage changedwizardTenga en cuenta que el sistema de herramientas est diseado de tal manera que es fcil de crear sus propias herramientas. Mira la wiki en lnea para obtener ms informacin, o utilizar una de las herramientas existentes, como ejemplo.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizard2Herramientas recomendadasRecommended toolswizard"Ejecutando cdigo Running codewizard"Selecciona idiomaSelect languagewizardPasoStepwizardNEl *Navegador de archivos* ayuda a mantener una visin general de todos los archivos en un directorio. Para gestionar sus proyectos, haga clic en el icono de estrella.The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizardLa *herramienta estructura cdigo* ofrece un esbozo del cdigo fuente.@The *Source structure tool* gives an outline of the source code.wizardNEl editor es donde se escribe el cdigo'The editor is where you write your codewizardPLa consola es donde tu cdigo se ejecuta*The shell is where your code gets executedwizard<Herramientas para su comodidadTools for your conveniencewizardMediante 'Consola > Edita propiedades consola', se puede editar y aadir *propiedades a la consola*. Esto permite por ejemplo seleccionar un directorio de inicio nuevo, o usar un Pythonpath personalizado.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardlA travs del men *Herramientas*, se puede seleccionar qu herramientas utilizar. Las herramientas se pueden colocar en cualquier forma que desee. Tambin pueden desacoplarse.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardnRecomendamos especialmente las siguientes herramientas:,We especially recommend the following tools:wizardnBienvenido al (E)ditor (I)nteractivo de (P)ython - IEP!-Welcome to the Interactive Editor for Python!wizardjPuede ejecutar comandos directamente en *la consola*,1You can execute commands directly in the *shell*,wizardo puede escribir cdigo en el *editor* y posterioment ejecutarlo.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_fr_FR.tr0000666000000000000000000022242513156166663021063 0ustar 00000000000000 debug Stack Pile editor Close, Discard, Cancel, Save Fermer, Fermer sans enregistrer, Annuler, Enregistrer les modifications et fermer Closing Fermer Save modified file? Sauvegarder le fichier modifié ? Closing pinned Fermeture du fichier épinglé Are you sure you want to close this pinned file? Êtes vous sûr(e) de vouloir fermer le fichier épinglé ? editorTabs Select one or more files to open Sélectionner un ou plusieurs fichiers à ouvrir Select the file to save to Sélectionner le nom du fichier à sauvegarder filebrowser Filename filter Filtre Search in files Rechercher dans les fichiers Projects: Projets : Click star to bookmark current dir Cliquez sur l'étoile pour ajouter le répertoire à la liste des projets Remove project Enlever de la liste des projets Change project name Renommer le projet Add path to Python path Ajouter le chemin au path de Python Project name Nom du projet New project name: Nouveau nom du projet : Match case Respecter la casse RegExp Recherche avec une expression rationelle Search in subdirs Rechercher dans les sous-répertoires Unstar this directory Enlever ce répertoire de la liste des projets Star this directory Ajouter ce répertoire à la liste des projets Open Ouvrir Reveal in Finder Ouvrir dans le Finder Copy path Copier le nom complet (chemin) du fichier Rename Renommer Delete Effacer Create new file Nouveau fichier Create new directory Nouveau dossier Give the new name for the file Nouveau nom du fichier Give the name for the new directory Nom du nouveau dossier Duplicate Faire une copie Give the name for the new file Nom du nouveau fichier Are you sure that you want to delete Êtes-vous sûr de vouloir effacer Go to this directory in the current shell Aller dans ce dossier (shell courant) Import data... Importer un fichier de données... Run as script Exécuter en tant que script Open outside Pyzo Ouvrir avec un éditeur externe Run Jupyter notebook Démarrer un notebook Jupyter importwizard Select file Sélectionnez le fichier Browse... Choisir... Preview: Aperçu : Select columns to import: Sélectionnez les colonnes à importer : Close Finir Import data as single array Importer les données dans un seul objet array Import data into one variable per column Importer les données en utilisant une variable par colonne Raise error upon invalid data Lever une erreur en cas de données invalides Import data wizard Assistant d'import de données No current shell active Pas de shell courant actif No current file open Pas de fichier courant ouvert Import data Importer des données The import data wizard is already open L'assistant d'import de données est déjà ouvert main unsaved non sauvegardé menu File Fichier Edit Édition View Affichage Settings Paramètres Shell Shell Run Exécuter Tools Outils Help Aide Use tabs Utiliser des tabulations Use spaces Utiliser des espaces spaces plural of spacebar character espaces Indentation ::: The indentation used of the current file. Indentation ::: Indentation utilisée pour le fichier courant. Syntax parser ::: The syntax parser of the current file. Analyse syntaxique ::: Analyseur du fichier courant. Line endings ::: The line ending character of the current file. Fins de ligne ::: Caractère de fin de ligne pour le fichier courant. Encoding ::: The character encoding of the current file. Encodage ::: Encodage du fichier courant. New ::: Create a new (or temporary) file. Nouveau ::: Crée un nouveau fichier (ou un fichier temporaire). Open... ::: Open an existing file from disk. Ouvrir... ::: Ouvre un fichier sur le disque. Save ::: Save the current file to disk. Enregistrer ::: Enregistre le fichier courant sur le disque. Save as... ::: Save the current file under another name. Enregistrer sous... ::: Enregistre le fichier sous un autre nom. Save all ::: Save all open files. Tout Enregistrer ::: Enregistre tous les fichiers ouverts. Close ::: Close the current file. Fermer ::: Ferme le fichier courant. Close all ::: Close all files. Tout fermer ::: Ferme tous les fichiers. Undo ::: Undo the latest editing action. Annuler ::: Annule la dernière opération d'édition. Redo ::: Redo the last undone editong action. Recommencer ::: Refait la dernière action d'édition annulée. Cut ::: Cut the selected text. Couper ::: Coupe le texte sélectionné. Copy ::: Copy the selected text to the clipboard. Copier ::: Copie le texte sélectionné dans le presse-papier. Paste ::: Paste the text that is now on the clipboard. Coller ::: Colle le texte qui est dans le presse-papier. Select all ::: Select all text. Tout sélectionner ::: Sélectionne tout le texte. Indent ::: Indent the selected line. Indenter ::: Indente la ligne sélectionnée. Dedent ::: Unindent the selected line. Désindenter ::: Supprime l'indentation de la ligne sélectionnée. Comment ::: Comment the selected line. Commenter ::: Commente la ligne sélectionnée. Uncomment ::: Uncomment the selected line. Décommenter ::: Décommente la ligne sélectionnée. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Justifier les commentaires/docstrings ::: Remet en forme le texte sélectionné en lignes de 70 caractères environ. Go to line ::: Go to a specific line number. Aller à la ligne ::: Va à un numéro de ligne spécifique. Toggle breakpoint ::: Toggle breakpoint on the current line. Ajouter/Enlever un point d'arrêt ::: Ajouter/Enlever un point d'arrêt sur la ligne courante. Find or replace ::: Show find/replace widget. Initialize with selected text. Rechercher et remplacer ::: Affiche la boîte rechercher/remplacer. L'initialise avec le texte sélectionné. Find selection ::: Find the next occurrence of the selected text. Rechercher la sélection ::: Recherche la prochaine occurence du texte sélectionné. Find selection backward ::: Find the previous occurrence of the selected text. Rechercher la sélection en arrière ::: Recherche l'occurence précédente du texte sélectionné. Find next ::: Find the next occurrence of the search string. Rechercher suivant ::: Recherche la prochaine occurence de la chaîne. Find previous ::: Find the previous occurrence of the search string. Rechercher précédent ::: Recherche l'occurence précédente de la chaîne. Accept autocompletion with: Zoom in Zoomer Zoom out Dézoomer Zoom reset Réinitialiser le zoom Location of long line indicator ::: The location of the long-line-indicator. Emplacement de l'indicateur de ligne trop longue ::: Emplacement du long-line-indicator. Qt theme ::: The styling of the user interface widgets. Thème QT ::: Style des widgets de l'interface. Select shell ::: Focus the cursor on the current shell. Aller dans le Shell ::: Donne le focus au shell courant. Select editor ::: Focus the cursor on the current editor. Aller dans l'éditeur ::: Donne le focus à l'éditeur courant. Select previous file ::: Select the previously selected file. Aller au fichier précédent ::: Donne le focus au fichier précédent. Show whitespace ::: Show spaces and tabs. Afficher les espaces ::: Affiche les espaces et les caractères de tabulation. Show line endings ::: Show the end of each line. Afficher les fins de lignes ::: Affiche la fin de chaque ligne. Show indentation guides ::: Show vertical lines to indicate indentation. Afficher les guide d'indentation ::: Affiche des lignes verticales pour visualiser l'indentation. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Couper les lignes trop longues ::: Coupe les lignes qui ne tiennent pas sur l'écran (pas de défilement horizontal). Highlight current line ::: Highlight the line where the cursor is. Mettre en évidence la ligne courante ::: Met en surbrillance la ligne qui contient le curseur. Font Police de caractères Zooming Zoom Clear screen ::: Clear the screen. Effacer l'écran ::: Efface l'écran. Interrupt ::: Interrupt the current running code (does not work for extension code). Interrompre ::: Arrêter l'exécution (ne fonctionne pas pour les extensions). Restart ::: Terminate and restart the interpreter. Redémarrer ::: Arrête puis redémarre l'interpréteur. Terminate ::: Terminate the interpreter, leaving the shell open. Terminer ::: Arrête l'interpréteur et laisse le shell ouvert. Close ::: Terminate the interpreter and close the shell. Fermer ::: Arrête l'interpréteur et ferme le shell. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Configuration des shells ::: Ajoute des shells, et modifie les paramètres de l'interpréteur. New shell ... ::: Create new shell to run code in. Nouveau shell... ::: Crée un nouveau shell. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Exécuter la sélection ::: Exécute les lignes sélectionnées dans l'éditeur, une partie de la ligne courante, ou la ligne courante entière si la sélection est vide. Close others::: Close all files but this one. Fermer les autres onglets ::: Ferme tous les fichiers sauf celui-ci. Rename ::: Rename this file. Renommer ::: Renomme ce fichier. Copy path ::: Copy the full path of this file. Copier le chemin ::: Copier le chemin complet désignant ce fichier. Pin/Unpin ::: Pinned files get closed less easily. Ajouter/Enlever un épingle ::: Les fichiers épinglés sont moins facilement fermés. Set/Unset as MAIN file ::: The main file can be run while another file is selected. (Dé)sélectionner comme fichier principal (MAIN) ::: Le fichier principal peu être exécuté alors qu'un autre fichier est sélectionné. Run file ::: Run the code in this file. Exécuter cet onglet ::: Exécute le contenu de l'onglet dans le shell courant. Run file as script ::: Run this file as a script (restarts the interpreter). Exécuter en tant que script ::: Exécute ce fichier comme un script (redémarre l'interpréteur). Reload tools ::: For people who develop tools. Recharger les outils ::: Pour ceux qui développent des outils. Ask a question ::: Need help? Poser une question ::: Besoin d'aide ? Automatically indent ::: Indent when pressing enter after a colon. Indentation automatique ::: Indente automatiquement lors d'un passage à la ligne après deux point. Enable calltips ::: Show calltips with function signatures. Activer les bulles d'aides ::: Affiche les bulles d'aide avec le prototype des fonctions. Autocomplete keywords ::: The autocompletion list includes keywords. Complétion automatique des mots clés ::: La liste de complétion automatique contient aussi les mots clés. Edit key mappings... ::: Edit the shortcuts for menu items. Éditer les raccourcis... ::: Modifie les raccourcis pour accéder aux éléments des menus. Edit syntax styles... ::: Change the coloring of your code. Éditer les styles du code... ::: Modifie la coloration syntaxique du code. Debug next: proceed until next line Débugage pas à pas principal (next) Debug return: proceed until returns Débugage pas à pas sortant Debug continue: proceed to next breakpoint Débugage : exécuter jusqu'à prochain point d'arrêt Stop debugging Arrêter le débugage Clear all {} breakpoints Effacer tous les points d'arrêt Postmortem: debug from last traceback Postmortem : débuguer à partir de la dernière pile des appels Debug step into: proceed one step Débugage pas à pas entrant (step) Run file as script ::: Restart and run the current file as a script. Démarrer le script ::: Redémarre le shell et exécute le fichier courant en tant que script. Run main file as script ::: Restart and run the main file as a script. Démarrer le script principal::: Redémarre le shell et exécute le fichier principal en tant que script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Exécuter la sélection ::: Exécute les instructions, les lignes sélectionnées dans l'éditeur ou la ligne courante (en l'absence de sélection). Execute cell ::: Execute the current editors's cell in the current shell. Exécuter la cellule ::: Exécute la cellule courante dans le shell courant. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Exécuter la cellule et avancer ::: Exécute la cellule courante dans le shell courant et passe à la cellule suivante. Execute file ::: Execute the current file in the current shell. Exécuter le contenu de l'onglet courant ::: Exécute le contenu de l'onglet courant dans le shell courant. Execute main file ::: Execute the main file in the current shell. Exécuter le contenu de l'onglet principal ::: Exécute le contenu de l'onglet principal dans le shell courant. Export to PDF ::: Export current file to PDF (e.g. for printing). Exporter au format PDF ::: Exporte le fichier courant au format PDF (pour imprimer par exemple). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Coller et sélectionner ::: Colle le contenu du presse-papier et conserve la sélection pour en modifier l'indentation. Previous cell ::: Go back to the previous cell. Cellule précédente ::: Retourne à la cellule précédente. Next cell ::: Advance to the next cell. Cellule suivante ::: Passe à la cellule suivante. Previous object ::: Go back to the previous top-level structure. Objet précédent ::: Retourne à l'élément de plus haut niveau précédent. Next object ::: Advance to the next top-level structure. Objet suivant ::: Passe à l'élement de plus haut niveau suivant. Goto Definition ::: Go to definition of word under cursor. Aller à la définition ::: Va à la définition du symbole situé sous le curseur. Restart Pyzo ::: Restart the application. Redémarrer Pyzo ::: Redémarre l'application. Quit Pyzo ::: Close the application. Quitter Pyzo ::: Ferme l'application. Create new Python environment... ::: Install miniconda. Créer un nouvel environnement Python ::: Installe miniconda. Help on running code ::: Open the pyzo wizard at the page about running code. Aide sur l'exécution du code ::: Ouvre le guide pyzo à la page sur l'exécution de code. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Signaler un problème ::: Vous avez touvé un bug dans Pyzo ? Vous voulez demander l'ajout d'une fonctionnalité ? Check for updates ::: Are you using the latest version? Rechercher des mises à jour ::: Utilisez-vous la dernière version ? About Pyzo ::: More information about Pyzo. À propos de Pyzo ::: Plus d'information à propos de Pyzo. Select language ::: The language used by Pyzo. Sélectionner la langue ::: Langue utilisée par Pyzo. Advanced settings... ::: Configure Pyzo even further. Paramètres avancés ::: Configure Pyzo un peu plus en détail. Pyzo website ::: Open the Pyzo website in your browser. Site Web de Pyzo ::: Ouvre le site Web de Pyzo dans votre navigateur. Pyzo guide ::: Open the Pyzo guide in your browser. Guide en ligne de Pyzo ::: Ouvre le guide Pyzo dans votre navigateur. Local documentation ::: Documentation on Python and the Scipy Stack. Documentation locale ::: Documentation sur Python et la couche Scipy. Highlight brackets ::: Highlight matched and unmatched brackets. Surligner les crochets ::: Montrer la correspondance entre les crochets ouvrants et fermants. There is no main file selected. Vous n'avez pas choisi de fichier principal. Could not save the file. Impossible d'enregistrer le fichier. Can only run scripts that are in the file system. Impossible d'exécuter un script s'il n'a pas été enregistré. Pyzo wizard ::: Get started quickly. Guide de démarrage rapide ::: Comment démarrer rapidement avec Pyzo. None None Create shell %s: (%s) Création du shell %s (%s) Execute selection and advance Exécuter la sélection et avancer (as script) (en tant que script) No autocompletion Pas de complétion automatique Automatic popup Bulle d'aide automatique Only show popup when pressing Tab Voir la bulle d'aide uniquement en pressant Tab Autocompletion Complétion automatique Search Rechercher History Historique Copy ::: Copy selected lines Copier ::: Copier les lignes sélectionnées Run ::: Run selected lines in current shell Exécuter ::: Exécuter les lignes sélectionnées dans le shell courant Remove ::: Remove selected history items(s) Effacer ::: Effacer les entrées sélectionnées de l'historique Duplicate line ::: Duplicate the selected line(s). Dupliquer la/les ligne(s) ::: Dupliquer la/les ligne(s) sélectionnée(s). Delete line ::: Delete the selected line(s). Effacer la/les ligne(s) ::: Effacer la/les ligne(s) sélectionnée(s). default Défaut Open current directory in file browser Ouvrir le répertoire courant dans le gestionnaire de fichiers Change current directory to the file browser's path Aller dans le répertoire sélectionné dans le gestionnaire de fichiers Open directory in file browser Ouvrir le répertoire dans le gestionnaire de fichiers No shell to run code in. Aucun shell dans lequel exécuter le code. No editor selected. Aucun éditeur sélectionné. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. menu dialog Could not run Impossible d'exécuter Could not run script. Impossible de lancer l'exécution du script. Check for the latest version. Rechercher la dernière version. Edit syntax styling Modifier la coloration syntaxique Advanced settings Paramètres avancés Language changed Langue modifiée Edit shortcut mapping Édition des raccourcis clavier Shortcut mappings Raccourcis clavier Could not run notebook Impossible de démarrer le notebook About Pyzo À propos de Pyzo The language has been changed. Pyzo needs to restart for the change to take effect. Le langage a été modifié. Pyzo doit être redémarré pour que les modifications prennent effet. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Cacher le widget de recherche (Escape) Find pattern Recherche un motif Previous ::: Find previous occurrence of the pattern. Précédent ::: Recherche l'occurence précédente du motif. Next ::: Find next occurrence of the pattern. Suivant ::: Recherche la prochaine occurence du motif. Replace pattern Remplacer le motif Repl. all ::: Replace all matches in current document. Tout remplacer ::: Remplace toutes les occurences dans le document courant. Replace ::: Replace this match. Remplacer ::: Remplace cette occurrence. Match case ::: Find words that match case. Respecter la casse ::: Recherche des mots en respectant la casse. RegExp ::: Find using regular expressions. Expression Rationnelle ::: Rechercher avec des expressions rationnelles. Whole words ::: Find only whole words. Mots complets ::: Recherche uniquement des mots complets. Auto hide ::: Hide search/replace when unused for 10 s. Affichage/Disparition automatique ::: Cache Rechercher/Remplacer s'il n'ets pas utilisé pendant 10s. shell Use system default Configuration par défaut du système name ::: The name of this configuration. nom ::: Nom de la configuration. exe ::: The Python executable. exe ::: Exécutable Python. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). Gui ::: Intégration d'un toolkit graphique (GUI toolkit) (pour tracer des courbes de manière interactive etc.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath ::: Liste des répertoires dans lesquels rechercher des module et paquetages. Chaque chemin doit figurer sur une ligne, ou être séparé des autres par le séparateur par défaut du système. startupScript ::: The script to run at startup (not in script mode). startupScript ::: Script à exécuter au démarrage (ne s'applique pas lors de l'exécution en tant que script). startDir ::: The start directory (not in script mode). startDir ::: Répertoire de démarrage (ne s'applique pas lors de l'exécution en tant que script). Delete ::: Delete this shell configuration Supprimer ::: Supprimer ce shell Shell configurations Configurations du shell Add config Ajouter une configuration ipython ::: Use IPython shell if available. IPython ::: Utilise IPython s'il est disponible. File to run at startup Fichier à exécuter au démarrage Code to run at startup Code à exécuter au démarrage argv ::: The command line arguments (sys.argv). argv ;:; Arguments en ligne de commande (sys.argv). environ ::: Extra environment variables (os.environ). environ ::: Variables d'environnement (os.environ). shells Click to select shell. Cliquez pour sélectionner le shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Voici <b>Pyzo</b> <br />l'IDE Python pour l'informatique scientifique Version Version Pyzo is open source software and freely available for everyone. Pyzo est un logiciel open source libre disponible pour tous. wizard Step Étape Welcome to the Interactive Editor for Python! Bienvenue dans Pyzo ! Select language Choisir une langue Language changed Langue modifiée You can execute commands directly in the *shell*, Vous pouvez exécuter des commandes directement dans le *shell*, or you can write code in the *editor* and execute that. ou vous pouvez écrire du code dans l'*éditeur* et l'exécuter. The editor is where you write your code L'éditeur est l'endroit où vous écrivez le code In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. Dans l'*éditeur*, chaque fichier ouvert est représenté par un onglet. En faisant un clic droit sur l'onglet, le fichier correspondant peut être exécuté, sauvegardé, fermé etc. The shell is where your code gets executed Le shell est l'endroit où le code est exécuté Configuring shells Configuration des shells Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Via 'Shell > Configuration des shells', vous pouvez éditer et ajouter *des configurations de shells*. Ceci vous permet par exemple de paramétrer le répertoire de démarrage, ou d'utiliser un chemin python (PythonPath) de votre choix. Running code Exécuter du code *Run cell:* a cell is everything between two lines starting with '##'. *Exécuter une cellule :* une cellule est tout ce qui se trouve entre deux lignes commençant par '##'. *Run file:* run all the code in the current file. *Exécuter le contenu de l'onglet courant :* Exécute tout le code contenu dans l'onglet courant, même s'il n'a pas été sauvegardé. *Run project main file:* run the code in the current project's main file. *Exécuter le contenu de l'onglet principal :* Exécute tout le code contenu dans l'onglet principal, même s'il n'a pas été sauvegardé. Interactive mode vs running as script Mode interactif vs exécution en tant que script In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. En mode interactif, sys.path[0] est une chaîne vide (i.e. le répertoire courant), et sys.argv vaut ['']. Tools for your convenience Des outils pratiques Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Via the menu *Outils*, il est possible de sélectionner quels outils utiliser. Les outils peuvent être placés dans la fenêtre de la manière que vous voulez, et peuvent aussi être détachés de la fenêtre principale. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Notez que le système d'outils est réalisé de telle manière qu'il est facile de créer vos propres outils. Jetez un oeil au wiki pour avoir plus d'informations, ou utilisez un des outils existants comme exemple. Recommended tools Outils recommandés We especially recommend the following tools: Nous recommandons tout particulièrement les outils suivants : The *Source structure tool* gives an outline of the source code. L'outil *Source structure* (Structure du fichier source) fournit un *plan* du code source. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. L'outil *File browser* (Gestionnaire de Fichiers) permet de garder un oeil sur tous les fichiers d'un répertoire. Pour gérer vos projets, cliquez sur le symbole étoile. Get coding! En route pour le code ! Getting started with Pyzo Démarrer avec Pyzo This wizard helps you get familiarized with the workings of Pyzo. Ce guide vous aide à vous familiariser avec le fonctionnement de Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. Pyzo est un environnement de développement multi plate-forme qui met l'accent sur l'*interactivité* et l'*introspection*, ce qui le rend particulièrement efficace pour la programmation à vocation scientifique. Son design se veut *simple* et *efficace*. This wizard can be opened using 'Help > Pyzo wizard' Ce guide peut être ouvert en utilisant le menu 'Aide -> Guide Pyzo' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. La langue a été modifiée. Pyzo doit redémarrer pour que les modifications prennent effet dans toute l'application. Pyzo consists of two main components Pyzo contient deux composants principaux When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Lorsque Pyzo démarre, un *shell* par défaut est créé. Vous pouvez ajouter d'autres shells qui s'exécutent simultanément, et qui peuvent correspondre à des versions différentes de Python. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Un shell s'exécute dans un processus fils. Ainsi, s'il est occupé, Pyzo lui même continue d'être réactif. Vous pouvez continuer à coder et même exécuter du code dans un autre shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo intègre la boucle des événements de cinq bibliothèques graphiques différentes, vous permettant ainsi de tracer des graphes de manière interactive avec par exemple Visvis ou Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). Pyzo permet d'exécuter du code de plusieurs manières depuis l'éditeur (voir le menu Exécuter). This concludes the Pyzo wizard. Now, get coding and have fun! Ceci termine le Guide Pyzo. Maintenant, en route pour le code, et amusez-vous bien ! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. Le bouton droit de la souris permet de choisir un fichier comme *fichier principal* (*main file*) dans un projet. Ce fichier est repéré par le symbole étoile, et il permet de lancer l'exécution du projet plus facilement. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. *Exécuter la sélection:* s'il n'y a pas de texte sélectionné, la ligne courante est exécutée ; si la sélection appartient à une seule ligne, la sélection seule est évaluée ; si la sélection s'étend sur plusieurs lignes, Pyzo exécutera la totalité des lignes sélectionnées. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. Vous pouvez exécuter le fichier courant ou le fichier principal normalement, ou en tant que script. Si vous exécutez un fichier en tant que script, le shell redémarre et l'environnement est vierge. Le shell est aussi initialisé d'une manière différente de manière à se comporter comme dans le cas d'une exécution normale de script Python. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. Dans le mode script, __file__ et sys.argv[0] contiennent le nom du fichier, sys.path[0] et le répertoire de travail seront réglés au nom du répertoire contenant le script. pyzo-4.4.3/pyzo/resources/translations/pyzo_fr_FR.tr.qm0000666000000000000000000015032113156166667021476 0ustar 00000000000000VEM6f u"zE6ZJ6bY,m5Z[f3s`/D&J_eOnA56T~T'%G~ "!fx+ > Х?N`1ETaDew5f@'wp^DM[zzZ lc>B92}jn)(_͎]a^^ <30@,%[~nup}.sq^[Tt>p}uC}~:iLS߱Wp#;^>3¦HcM(KR{Lks%gbecr>{EsT. t$a$оKX8N_Wz~W{4`8'{ .BL9?,"`x`|I~7$9N=k.!F>=E$S's&Enu3 SSnX>qa@>Zh+n-nj H5 sfg/ M| R> %r'OM13<>2FI<5GLWY C_pwazZ PJH!6C/lnm>J$5`^/eVV}7VV~Y[;[hwQ0unlyx3"i ^<ѯj.jgeu+Nn#nXA/%f?z%i|Pj^!ukUm** 0 I oG 6 Ixi KƎ wSuZ qJ| u6# 5  g~x n3, BN רI @  [ . B' iH 6 ;~] ? J|Q OZOH c' t FY q Tx lz >C 3 e& t nq)   4iA T _ j . ,<K 0nc Y [=n$& aoy tF? "j pp sm )Dd  ɠ ˳R' n 3VC r[ i i ) 0: 7 Q Bn~L GnI MN O Qun V  Ͽ^S ? SD'V)!4[>Y:>#YZt \`>p>th;?~m]n  _z BL IJ:iV|~ߔwNG>$#Mx^YE ގf-DO.i8PileStackdebugntes vous sr(e) de vouloir fermer le fichier pingl ?0Are you sure you want to close this pinned file?editorFermer, Fermer sans enregistrer, Annuler, Enregistrer les modifications et fermerClose, Discard, Cancel, Saveeditor FermerClosingeditor8Fermeture du fichier pinglClosing pinnededitor@Sauvegarder le fichier modifi ?Save modified file?editor\Slectionner un ou plusieurs fichiers ouvrir Select one or more files to open editorTabsXSlectionner le nom du fichier sauvegarderSelect the file to save to editorTabsFAjouter le chemin au path de PythonAdd path to Python path filebrowser@tes-vous sr de vouloir effacer$Are you sure that you want to delete filebrowser$Renommer le projetChange project name filebrowserCliquez sur l'toile pour ajouter le rpertoire la liste des projets"Click star to bookmark current dir filebrowserRCopier le nom complet (chemin) du fichier Copy path filebrowserNouveau dossierCreate new directory filebrowserNouveau fichierCreate new file filebrowserEffacerDelete filebrowserFaire une copie Duplicate filebrowser FiltreFilename filter filebrowser,Nom du nouveau dossier#Give the name for the new directory filebrowser,Nom du nouveau fichierGive the name for the new file filebrowser,Nouveau nom du fichierGive the new name for the file filebrowserJAller dans ce dossier (shell courant))Go to this directory in the current shell filebrowserBImporter un fichier de donnes...Import data... filebrowser$Respecter la casse Match case filebrowser.Nouveau nom du projet :New project name: filebrowser OuvrirOpen filebrowser<Ouvrir avec un diteur externeOpen outside Pyzo filebrowserNom du projet Project name filebrowserProjets : Projects: filebrowserPRecherche avec une expression rationelleRegExp filebrowser>Enlever de la liste des projetsRemove project filebrowserRenommerRename filebrowser*Ouvrir dans le FinderReveal in Finder filebrowser8Dmarrer un notebook JupyterRun Jupyter notebook filebrowser6Excuter en tant que script Run as script filebrowser8Rechercher dans les fichiersSearch in files filebrowserHRechercher dans les sous-rpertoiresSearch in subdirs filebrowserXAjouter ce rpertoire la liste des projetsStar this directory filebrowserZEnlever ce rpertoire de la liste des projetsUnstar this directory filebrowserChoisir... Browse... importwizard FinirClose importwizard(Importer des donnes Import data importwizardZImporter les donnes dans un seul objet arrayImport data as single array importwizardtImporter les donnes en utilisant une variable par colonne(Import data into one variable per column importwizard:Assistant d'import de donnesImport data wizard importwizard:Pas de fichier courant ouvertNo current file open importwizard4Pas de shell courant actifNo current shell active importwizardAperu :Preview: importwizardXLever une erreur en cas de donnes invalidesRaise error upon invalid data importwizardLSlectionnez les colonnes importer :Select columns to import: importwizard.Slectionnez le fichier Select file importwizard^L'assistant d'import de donnes est dj ouvert&The import data wizard is already open importwizardnon sauvegardunsavedmain((en tant que script) (as script)menur propos de Pyzo ::: Plus d'information propos de Pyzo.+About Pyzo ::: More information about Pyzo.menuAccept autocompletion with:menuxParamtres avancs ::: Configure Pyzo un peu plus en dtail.5Advanced settings... ::: Configure Pyzo even further.menuLPoser une question ::: Besoin d'aide ?Ask a question ::: Need help?menuCompltion automatique des mots cls ::: La liste de compltion automatique contient aussi les mots cls.DAutocomplete keywords ::: The autocompletion list includes keywords.menu,Compltion automatiqueAutocompletionmenu0Bulle d'aide automatiqueAutomatic popupmenuIndentation automatique ::: Indente automatiquement lors d'un passage la ligne aprs deux point.BAutomatically indent ::: Indent when pressing enter after a colon.menuxImpossible d'excuter un script s'il n'a pas t enregistr.1Can only run scripts that are in the file system.menuAller dans le rpertoire slectionn dans le gestionnaire de fichiers3Change current directory to the file browser's pathmenuRechercher des mises jour ::: Utilisez-vous la dernire version ?7Check for updates ::: Are you using the latest version?menu>Effacer tous les points d'arrtClear all {} breakpointsmenuFEffacer l'cran ::: Efface l'cran."Clear screen ::: Clear the screen.menuHFermer ::: Ferme le fichier courant.!Close ::: Close the current file.menufFermer ::: Arrte l'interprteur et ferme le shell.8Close ::: Terminate the interpreter and close the shell.menuPTout fermer ::: Ferme tous les fichiers.Close all ::: Close all files.menuFermer les autres onglets ::: Ferme tous les fichiers sauf celui-ci.-Close others::: Close all files but this one.menuZCommenter ::: Commente la ligne slectionne.&Comment ::: Comment the selected line.menuTCopier ::: Copier les lignes slectionnesCopy ::: Copy selected linesmenuxCopier ::: Copie le texte slectionn dans le presse-papier.1Copy ::: Copy the selected text to the clipboard.menuCopier le chemin ::: Copier le chemin complet dsignant ce fichier..Copy path ::: Copy the full path of this file.menuHImpossible d'enregistrer le fichier.Could not save the file.menuxCrer un nouvel environnement Python ::: Installe miniconda.7Create new Python environment... ::: Install miniconda.menu2Cration du shell %s (%s)Create shell %s: (%s)menuLCouper ::: Coupe le texte slectionn.Cut ::: Cut the selected text.menudDbugage : excuter jusqu' prochain point d'arrt*Debug continue: proceed to next breakpointmenuFDbugage pas pas principal (next)#Debug next: proceed until next linemenu4Dbugage pas pas sortant#Debug return: proceed until returnsmenuBDbugage pas pas entrant (step)!Debug step into: proceed one stepmenuDsindenter ::: Supprime l'indentation de la ligne slectionne.&Dedent ::: Unindent the selected line.menuEffacer la/les ligne(s) ::: Effacer la/les ligne(s) slectionne(s).,Delete line ::: Delete the selected line(s).menuDupliquer la/les ligne(s) ::: Dupliquer la/les ligne(s) slectionne(s).2Duplicate line ::: Duplicate the selected line(s).menuditionEditmenuditer les raccourcis... ::: Modifie les raccourcis pour accder aux lments des menus.;Edit key mappings... ::: Edit the shortcuts for menu items.menuConfiguration des shells ::: Ajoute des shells, et modifie les paramtres de l'interprteur.WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menuditer les styles du code... ::: Modifie la coloration syntaxique du code.;Edit syntax styles... ::: Change the coloring of your code.menuActiver les bulles d'aides ::: Affiche les bulles d'aide avec le prototype des fonctions.;Enable calltips ::: Show calltips with function signatures.menuREncodage ::: Encodage du fichier courant.8Encoding ::: The character encoding of the current file.menuExcuter la cellule ::: Excute la cellule courante dans le shell courant.IExecute cell ::: Execute the current editors's cell in the current shell.menuExcuter la cellule et avancer ::: Excute la cellule courante dans le shell courant et passe la cellule suivante.]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menuExcuter le contenu de l'onglet courant ::: Excute le contenu de l'onglet courant dans le shell courant.?Execute file ::: Execute the current file in the current shell.menuExcuter le contenu de l'onglet principal ::: Excute le contenu de l'onglet principal dans le shell courant.AExecute main file ::: Execute the main file in the current shell.menuExcuter la slection ::: Excute les instructions, les lignes slectionnes dans l'diteur ou la ligne courante (en l'absence de slection).Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menu@Excuter la slection et avancerExecute selection and advancemenuExporter au format PDF ::: Exporte le fichier courant au format PDF (pour imprimer par exemple).AExport to PDF ::: Export current file to PDF (e.g. for printing).menuFichierFilemenuRechercher suivant ::: Recherche la prochaine occurence de la chane.Rechercher la dernire version.Check for the latest version. menu dialog*Impossible d'excuter Could not run menu dialogDImpossible de dmarrer le notebookCould not run notebook menu dialogVImpossible de lancer l'excution du script.Could not run script. menu dialog<dition des raccourcis clavierEdit shortcut mapping menu dialogBModifier la coloration syntaxiqueEdit syntax styling menu dialogLangue modifieLanguage changed menu dialog$Raccourcis clavierShortcut mappings menu dialogAffichage/Disparition automatique ::: Cache Rechercher/Remplacer s'il n'ets pas utilis pendant 10s.7Auto hide ::: Hide search/replace when unused for 10 s.search$Recherche un motif Find patternsearchLCacher le widget de recherche (Escape)Hide search widget (Escape)searchRespecter la casse ::: Recherche des mots en respectant la casse.*Match case ::: Find words that match case.searchlSuivant ::: Recherche la prochaine occurence du motif.-Next ::: Find next occurrence of the pattern.searchpPrcdent ::: Recherche l'occurence prcdente du motif.5Previous ::: Find previous occurrence of the pattern.searchExpression Rationnelle ::: Rechercher avec des expressions rationnelles.*RegExp ::: Find using regular expressions.searchTout remplacer ::: Remplace toutes les occurences dans le document courant.6Repl. all ::: Replace all matches in current document.searchPRemplacer ::: Remplace cette occurrence.Replace ::: Replace this match.search$Remplacer le motifReplace patternsearchrMots complets ::: Recherche uniquement des mots complets.&Whole words ::: Find only whole words.search2Ajouter une configuration Add configshell8Code excuter au dmarrageCode to run at startupshell@Supprimer ::: Supprimer ce shell*Delete ::: Delete this shell configurationshell>Fichier excuter au dmarrageFile to run at startupshell.Configurations du shellShell configurationsshellFConfiguration par dfaut du systmeUse system defaultshellfargv ;:; Arguments en ligne de commande (sys.argv)./argv ::: The command line arguments (sys.argv).shellfenviron ::: Variables d'environnement (os.environ).5environ ::: Extra environment variables (os.environ).shell4exe ::: Excutable Python.exe ::: The Python executable.shellGui ::: Intgration d'un toolkit graphique (GUI toolkit) (pour tracer des courbes de manire interactive etc.).Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shell`IPython ::: Utilise IPython s'il est disponible.+ipython ::: Use IPython shell if available.shellBnom ::: Nom de la configuration. (name ::: The name of this configuration.shellpythonPath ::: Liste des rpertoires dans lesquels rechercher des module et paquetages. Chaque chemin doit figurer sur une ligne, ou tre spar des autres par le sparateur par dfaut du systme.pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shellstartDir ::: Rpertoire de dmarrage (ne s'applique pas lors de l'excution en tant que script).6startDir ::: The start directory (not in script mode).shellstartupScript ::: Script excuter au dmarrage (ne s'applique pas lors de l'excution en tant que script).DstartupScript ::: The script to run at startup (not in script mode).shellFCliquez pour slectionner le shell.Click to select shell.shellsxPyzo est un logiciel open source libre disponible pour tous.?Pyzo is open source software and freely available for everyone.splashVoici <b>Pyzo</b> <br />l'IDE Python pour l'informatique scientifique@This is Pyzo
the Python IDE for scientific computingsplashVersionVersionsplashLa langue a t modifie. Pyzo doit redmarrer pour que les modifications prennent effet dans toute l'application. The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. wizard*Excuter une cellule :* une cellule est tout ce qui se trouve entre deux lignes commenant par '##'.F*Run cell:* a cell is everything between two lines starting with '##'.wizard*Excuter le contenu de l'onglet courant :* Excute tout le code contenu dans l'onglet courant, mme s'il n'a pas t sauvegard.1*Run file:* run all the code in the current file.wizard *Excuter le contenu de l'onglet principal :* Excute tout le code contenu dans l'onglet principal, mme s'il n'a pas t sauvegard.I*Run project main file:* run the code in the current project's main file.wizardP*Excuter la slection:* s'il n'y a pas de texte slectionn, la ligne courante est excute ; si la slection appartient une seule ligne, la slection seule est value ; si la slection s'tend sur plusieurs lignes, Pyzo excutera la totalit des lignes slectionnes.*Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines.wizard0Configuration des shellsConfiguring shellswizard.En route pour le code ! Get coding!wizard$Dmarrer avec PyzoGetting started with PyzowizardEn mode interactif, sys.path[0] est une chane vide (i.e. le rpertoire courant), et sys.argv vaut [''].pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizardfDans le mode script, __file__ et sys.argv[0] contiennent le nom du fichier, sys.path[0] et le rpertoire de travail seront rgls au nom du rpertoire contenant le script.In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script.wizardvDans l'*diteur*, chaque fichier ouvert est reprsent par un onglet. En faisant un clic droit sur l'onglet, le fichier correspondant peut tre excut, sauvegard, ferm etc. In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizard^Mode interactif vs excution en tant que script%Interactive mode vs running as scriptwizardLangue modifieLanguage changedwizardNotez que le systme d'outils est ralis de telle manire qu'il est facile de crer vos propres outils. Jetez un oeil au wiki pour avoir plus d'informations, ou utilisez un des outils existants comme exemple.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizardzPyzo intgre la boucle des vnements de cinq bibliothques graphiques diffrentes, vous permettant ainsi de tracer des graphes de manire interactive avec par exemple Visvis ou Matplotlib.Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib.wizardPPyzo contient deux composants principaux$Pyzo consists of two main componentswizardPyzo est un environnement de dveloppement multi plate-forme qui met l'accent sur l'*interactivit* et l'*introspection*, ce qui le rend particulirement efficace pour la programmation vocation scientifique. Son design se veut *simple* et *efficace*.Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*.wizardPyzo permet d'excuter du code de plusieurs manires depuis l'diteur (voir le menu Excuter).RPyzo supports several ways to run source code in the editor. (see the 'Run' menu).wizard$Outils recommandsRecommended toolswizard Excuter du code Running codewizard$Choisir une langueSelect languagewizardjUn shell s'excute dans un processus fils. Ainsi, s'il est occup, Pyzo lui mme continue d'tre ractif. Vous pouvez continuer coder et mme excuter du code dans un autre shell.Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell.wizard tape Stepwizard`L'outil *File browser* (Gestionnaire de Fichiers) permet de garder un oeil sur tous les fichiers d'un rpertoire. Pour grer vos projets, cliquez sur le symbole toile.The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizardL'outil *Source structure* (Structure du fichier source) fournit un *plan* du code source.@The *Source structure tool* gives an outline of the source code.wizard^L'diteur est l'endroit o vous crivez le code'The editor is where you write your codewizardLe bouton droit de la souris permet de choisir un fichier comme *fichier principal* (*main file*) dans un projet. Ce fichier est repr par le symbole toile, et il permet de lancer l'excution du projet plus facilement.The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily.wizardZLe shell est l'endroit o le code est excut*The shell is where your code gets executedwizardCeci termine le Guide Pyzo. Maintenant, en route pour le code, et amusez-vous bien !=This concludes the Pyzo wizard. Now, get coding and have fun!wizardCe guide peut tre ouvert en utilisant le menu 'Aide -> Guide Pyzo'4This wizard can be opened using 'Help > Pyzo wizard'wizardCe guide vous aide vous familiariser avec le fonctionnement de Pyzo.AThis wizard helps you get familiarized with the workings of Pyzo.wizard(Des outils pratiquesTools for your conveniencewizardVia 'Shell > Configuration des shells', vous pouvez diter et ajouter *des configurations de shells*. Ceci vous permet par exemple de paramtrer le rpertoire de dmarrage, ou d'utiliser un chemin python (PythonPath) de votre choix.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardVia the menu *Outils*, il est possible de slectionner quels outils utiliser. Les outils peuvent tre placs dans la fentre de la manire que vous voulez, et peuvent aussi tre dtachs de la fentre principale.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardzNous recommandons tout particulirement les outils suivants :,We especially recommend the following tools:wizard*Bienvenue dans Pyzo !-Welcome to the Interactive Editor for Python!wizardtLorsque Pyzo dmarre, un *shell* par dfaut est cr. Vous pouvez ajouter d'autres shells qui s'excutent simultanment, et qui peuvent correspondre des versions diffrentes de Python.When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions.wizard~Vous pouvez excuter des commandes directement dans le *shell*,1You can execute commands directly in the *shell*,wizardVous pouvez excuter le fichier courant ou le fichier principal normalement, ou en tant que script. Si vous excutez un fichier en tant que script, le shell redmarre et l'environnement est vierge. Le shell est aussi initialis d'une manire diffrente de manire se comporter comme dans le cas d'une excution normale de script Python. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution.wizardzou vous pouvez crire du code dans l'*diteur* et l'excuter.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_nl_NL.tr0000666000000000000000000020621413156166662021064 0ustar 00000000000000 debug Stack Stack editor Close, Discard, Cancel, Save Sluiten, Verwerpen, Annuleren, Opslaan Closing Sluiten Save modified file? Gewijzigd bestand sluiten? Closing pinned Sluiten van gepint bestand Are you sure you want to close this pinned file? Weet je zeker dat je het vastgepinde bestand wilt sluiten? editorTabs Select one or more files to open Selecteer een of meer bestanden om te openen Select the file to save to Selecteer het bestand om de code in op te slaan filebrowser Filename filter Bestandsnaam filter Search in files Zoek in bestanden Projects: Projecten: Remove project Verwijder project Change project name Verander de naam van dit project Add path to Python path Voeg directory toe aan Pythonpath Project name Project naam New project name: Nieuwe project naam: Match case Hoofdlettergevoelig RegExp RegExp Search in subdirs Zoek in subdirectories Open Openen Reveal in Finder Laat zien in Finder Rename Hernoemen Delete Verwijderen Create new file Maak nieuw bestand Create new directory Maak nieuwe directorie Give the new name for the file Geef de nieuwe naam voor het bestand Give the name for the new directory Geef de naam voor de directorie Duplicate Dupliceren Give the name for the new file Geef de naam van het nieuwe bestand Are you sure that you want to delete Weet je zeker dat je wilt verwijderen Copy path Kopier directorie pad Click star to bookmark current dir Klik de ster om de huidige dir op te slaan Unstar this directory Verwijder deze dir uit de projecten Star this directory Voeg deze dir toe aan de projecten Go to this directory in the current shell Ga naar deze directorie in de huidige shell Import data... Importeer data... Run as script Uitvoeren als script Open outside Pyzo Open buiten Pyzo Run Jupyter notebook Voer uit in de Jupyter notebook importwizard Select file Selecteer bestand Browse... Browse ... Preview: Voorbeeld: Select columns to import: Selecteer colummen om te importeren: Close Sluiten Import data as single array Importeer data als een enkelvoudig array Import data into one variable per column Importeer data in een variabele per kollom Raise error upon invalid data Geef foutmelding als de data niet juist is Import data wizard Data importeer hulp No current shell active Er is nu geen shell aktief No current file open Er is nu geen bestand open Import data Importeed data The import data wizard is already open The importeerhulp is al open main unsaved niet opgeslagen menu File Bestand Edit Bewerken View Uiterlijk Settings Instellingen Shell Shell Run Uitvoeren Tools Gereedschap Help Help Use tabs Gebruik tabs Use spaces Gebruik spaties spaces plural of spacebar character spaties Indentation ::: The indentation used of the current file. Inspring niveau ::: The mate van inspringen voor het huidige bestand. Syntax parser ::: The syntax parser of the current file. Syntax parser ::: De syntax parser voor het huidige bestand. Line endings ::: The line ending character of the current file. Regel uiteinden ::: Het soort regel uiteinden van het huidige bestand. Encoding ::: The character encoding of the current file. Codering ::: The karakter codering voor het huidige bestand. New ::: Create a new (or temporary) file. Nieuw ::: Maak een nieuw (of tijdelijk) bestand. Open... ::: Open an existing file from disk. Open ::: Open een bestaand bestand. Save ::: Save the current file to disk. Opslaan ::: Sla het huidige bestand op. Save as... ::: Save the current file under another name. Opslaan als ... ::: Sla het huidige bestand op onder een andere naam. Save all ::: Save all open files. Alles opslaan ::: Sla alle geopende bestanden op. Close ::: Close the current file. Sluiten ::: Sluit het huidige bestand. Close all ::: Close all files. Alles sluiten ::: Sluit alle geopende bestanden. Undo ::: Undo the latest editing action. Ongedaan maken ::: Maak de laatste bewerking ongedaan. Redo ::: Redo the last undone editong action. Opnieuw doen ::: Doe de laatste ongedaan gemaakte bewerking opnieuw. Cut ::: Cut the selected text. Knippen Copy ::: Copy the selected text to the clipboard. Kopieren Paste ::: Paste the text that is now on the clipboard. Plakken Select all ::: Select all text. Alles selecteren Indent ::: Indent the selected line. Inspringen ::: Spring de geselecteerde regel in. Dedent ::: Unindent the selected line. Terugspringen ::: Zet het inspringen een stap kleiner. Comment ::: Comment the selected line. Commentaar ::: Voeg een commentaar teken toe aan de geselecteerde regel. Uncomment ::: Uncomment the selected line. Verwijder commentaar ::: Verwijder een commentaar teken van de huidige regel. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Uitlijnen commentaar of docstring ::: Hervorm de geselecteerde tekst door het uit te lijnen op 70 karakters. Go to line ::: Go to a specific line number. Ga naar regel ::: Ga naar een specifiek regel nummer. Toggle breakpoint ::: Toggle breakpoint on the current line. Zet breakpoint aan of uit Find or replace ::: Show find/replace widget. Initialize with selected text. Vind of vervang ::: Laat het vind/vervang paneel zien. Initializeer met de geselecteerde tekst. Find selection ::: Find the next occurrence of the selected text. Vind selectie ::: Vind het volgende voorkomen van de geselecteerde tekst. Find selection backward ::: Find the previous occurrence of the selected text. Vind vorige selectie ::: Vind het vorige voorkomen van de geselecteerde tekst. Find next ::: Find the next occurrence of the search string. Vind volgende ::: Vind het volgende voorkomen van de zoek tekst. Find previous ::: Find the previous occurrence of the search string. Vind vorige ::: Vind het vorige voorkomen van de zoek tekst. Accept autocompletion with: Accepteer autocompletion met: Zoom in Inzoomen Zoom out Uitzoomen Zoom reset Reset zoom Qt theme ::: The styling of the user interface widgets. Qt thema ::: De stijl van de gebruikersinterface. Select shell ::: Focus the cursor on the current shell. Selecteer shell ::: Focus de cursor op de huidige shell. Select editor ::: Focus the cursor on the current editor. Selecteer editor ::: Focus de cursor op de huidige editor. Select previous file ::: Select the previously selected file. Selecteer vorige bestand Show whitespace ::: Show spaces and tabs. Laat whitespace zien ::: Laat spaties en tabs zien. Show line endings ::: Show the end of each line. Laat regeluiteindes zien Show indentation guides ::: Show vertical lines to indicate indentation. Laat inspring hulplijnen zien Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Vouw lange regels ::: Vouw lange regels als ze niet op het scherm passen. Highlight current line ::: Highlight the line where the cursor is. Markeer huidige regel ::: Geef de huidige regel een andere achtergrondkleur. Zooming Zoomen Clear screen ::: Clear the screen. Maak scherm schoon Interrupt ::: Interrupt the current running code (does not work for extension code). Onderbreken ::: Onderbreek de nu uitgevoerde code (werkt niet voor extension-code). Restart ::: Terminate and restart the interpreter. Herstart ::: Stop en herstart de interpreter. Terminate ::: Terminate the interpreter, leaving the shell open. Stoppen ::: Stop de interpreter, maar laat de shell open. Close ::: Terminate the interpreter and close the shell. Afsluiten ::: Stop de interpreter en sluit de shell af. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Aanpassen shell configuraties... ::: Voeg shell configuraties toe en pas de eigenschappen aan. New shell ... ::: Create new shell to run code in. Nieuwe shell ... ::: Maak een nieuwe shell aan om code in uit te voeren. Close others::: Close all files but this one. Anderen sluiten ::: Sluit alle bestanden behalve deze. Rename ::: Rename this file. Hernoemen ::: Hernoem dit bestand. Copy path ::: Copy the full path of this file. Kopieer path Pin/Unpin ::: Pinned files get closed less easily. Vastpinnen/ontpinnen :::Pin dit bestand vast. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Stel dit bestand in als MAIN bestand/gewoon bestand. Run file ::: Run the code in this file. Voer bestand uit ::: Voer de code in dit bestand uit. Run file as script ::: Run this file as a script (restarts the interpreter). Voer bestand uit als script ::: Voer de code in dit bestand uit als script (dit herstart de interpreter). Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Voer selectie uit ::: Uitvoeren van: huidig geselecteerde regels, geselecteerde woorden op een regel, of de huidige regel als er geen selectie is. Reload tools ::: For people who develop tools. Herlaad gereedschap. Ask a question ::: Need help? Stel een vraag Automatically indent ::: Indent when pressing enter after a colon. Automatisch inspringen ::: Inspringen bij een nieuwe regel na een dubbele punt. Enable calltips ::: Show calltips with function signatures. Pas uitvoertips toe ::: Laat uitvoertips zien. Autocomplete keywords ::: The autocompletion list includes keywords. Autocomplete keywords ::: De autocomple lijst laat ook keywords zien. Edit key mappings... ::: Edit the shortcuts for menu items. Bewerk sneltoetsen... Edit syntax styles... ::: Change the coloring of your code. Bewerk syntax stijlen... Location of long line indicator ::: The location of the long-line-indicator. Locatie van de lange-regel indicator Font Lettertype Debug next: proceed until next line Debug volgende: ga een regel verder Debug return: proceed until returns Debug return: ga verder tot function return Debug continue: proceed to next breakpoint Debug verder: ga verder tot volgend breakpoint Stop debugging Stop debuggen Clear all {} breakpoints Verwijder alle {} breakpoints Postmortem: debug from last traceback Postmortem: debuggen met laatste traceback Debug step into: proceed one step Debug instappen: ga een stap verder Run file as script ::: Restart and run the current file as a script. Voer bestand uit als script ::: Herstart en voer het huidige bestand uit als script. Run main file as script ::: Restart and run the main file as a script. Voer main bestand uit als script ::: Herstart en voer het main bestand uit als script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Executeer selectie ::: Executeer de geselecteerde regels, de geselecteerde text op de huidige regel, of the huidige regel als er geen selectie is. Execute cell ::: Execute the current editors's cell in the current shell. Executeer cell ::: Executeer de huidige cell. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Executeer cell en ga verder ::: Executeer de huidige cell en ga verder naar de volgende cell. Execute file ::: Execute the current file in the current shell. Executeer bestand ::: Executeer het huidige bestand. Execute main file ::: Execute the main file in the current shell. Executeer main bestand ::: Executeer het main bestand in de huidige shell. Export to PDF ::: Export current file to PDF (e.g. for printing). Exporteer naar PDF Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Plakken en selecteren ::: Plak the tekst op het clipbord en behoud de selectie om de indentatie aan te kunnen passen. Previous cell ::: Go back to the previous cell. Vorige cell ::: ga terug naar de vorige cell. Next cell ::: Advance to the next cell. Volgende cell ::: ga verder naar de volgende cell. Previous object ::: Go back to the previous top-level structure. Vorige object ::: ga terug naar het vorige top-level object. Next object ::: Advance to the next top-level structure. Volgende object ::: ga verder naar het volgende top-level object. Goto Definition ::: Go to definition of word under cursor. Ga naar definitie ::: Ga naar de definitie onder de cursor. Restart Pyzo ::: Restart the application. Pyzo herstarten Quit Pyzo ::: Close the application. Pyzo afsluiten Create new Python environment... ::: Install miniconda. Creeer een nieuwe Python omgeving ::: Installeer miniconda Help on running code ::: Open the pyzo wizard at the page about running code. Help over het uitvoeren van code Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Rapporteer een probleem Check for updates ::: Are you using the latest version? Kijk of er updates zijn About Pyzo ::: More information about Pyzo. Over Pyzo Select language ::: The language used by Pyzo. Selecteer taal Advanced settings... ::: Configure Pyzo even further. Geavanceerde instellingen Pyzo website ::: Open the Pyzo website in your browser. Pyzo's website Pyzo guide ::: Open the Pyzo guide in your browser. Pyzo's instructies Local documentation ::: Documentation on Python and the Scipy Stack. Locale documentatie Highlight brackets ::: Highlight matched and unmatched brackets. Geef corresponderende haken aan There is no main file selected. Er is geen main bestand geselecteerd (as script) (als script) Could not save the file. Can het bestand niet opslaan Can only run scripts that are in the file system. Kan alleen bestanden uitvoeren die op het bestandssysteem staan Pyzo wizard ::: Get started quickly. Pyzo's wizard None Niets Create shell %s: (%s) Maak shell %s: (%s) Execute selection and advance Voer selectie uit en ga verder No autocompletion Geen autocompletion Automatic popup Automatische popup Only show popup when pressing Tab Laal alleen popup zien door Tab te gebruiken Autocompletion Autocompletion Search Zoeken History Geschiedenis Copy ::: Copy selected lines Kopieer Run ::: Run selected lines in current shell Uitvoeren ::: geselecteerde regels uitvoeren in huidige shell Remove ::: Remove selected history items(s) Verwijderen ::: verwijder de geselecteerde items uit de geschiedenis Duplicate line ::: Duplicate the selected line(s). Dupliceer regel ::: Dupliceer de geselecteerde regel(s) Delete line ::: Delete the selected line(s). Verwijder regel ::: Verwijder de huidige regel(s) default standaard Open current directory in file browser Open huidige directory in de file browser Change current directory to the file browser's path Verander huidige directoru naar huidige pad in file browser Open directory in file browser Open directory in file browser No shell to run code in. Geen shell om code in uit te voeren No editor selected. Geen editor geselecteerd Change current directory to editor file path Verander huidige directory naar directory van bestand in editor Change directory when executing file ::: like Run File As Script does Verander directory als een file uitgevoerd wordt. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. menu dialog Could not run Kon niet uitvoeren Could not run script. Kon script niet uitvoeren. Check for the latest version. Check laatste versie. Edit syntax styling Pas syntax stijlen aan Advanced settings Geavanceerde instellingen Edit shortcut mapping Pas sneltoetsen aan Shortcut mappings Sneltoets lijst Language changed Taal aangepast Could not run notebook Kan niet uitvoeren in de notebook About Pyzo Over pyzo The language has been changed. Pyzo needs to restart for the change to take effect. De taal is aangepast Herstart Pyzo om de verandering door te voeren. Could not change dir Kon de directory niet veranderen pyzoHistoryViewer History viewer Geschiedenis pyzoSourceStructure Source structure Code structuur Parsing Parsen van search Hide search widget (Escape) Verber zoek scherm (Escape) Find pattern Zoekpatroon Previous ::: Find previous occurrence of the pattern. Vorige ::: Vind de vorige instantie van het zoekpatroon. Next ::: Find next occurrence of the pattern. Volgende :::Vind de volgende instantie van het zoekpatroon. Replace pattern Vervangpatroon Repl. all ::: Replace all matches in current document. Verv. alles ::: Vervang alle instanties in het huidige document. Replace ::: Replace this match. Vervang ::: Vervang de huidige instantie. Match case ::: Find words that match case. Hoofdlettergevoelig ::: Houd rekening met hoofdletters bij het zoeken. RegExp ::: Find using regular expressions. RegExp ::: Zoek met regular expressions. Whole words ::: Find only whole words. Hele woorden ::: Vind alleen hele woorden. Auto hide ::: Hide search/replace when unused for 10 s. Automatisch verbergen ::: Verberg het zoek/vervang scherm als het 10 s ongebruikt is. shell Use system default Gebruik standaard van het systeem name ::: The name of this configuration. naam ::: De naam van deze configuratie. exe ::: The Python executable. exe ::: Het Python executable bestand. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: Welke GUI toolkit geintegreerd wordt (b.v. voor interactief plotten). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath ::: Een lijst met directories om te zoeken naar modules. Elke directorie moet op een nieuwe regel. startupScript ::: The script to run at startup (not in script mode). opstartscript ::: Het script dat uitgevoerd wordt bij het opstarten (niet in script modus). startDir ::: The start directory (not in script mode). start directorie ::: De directorie waar gestart wordt (niet in script modus). Delete ::: Delete this shell configuration Verwijderen ::: Verwijder deze shell configuratie Shell configurations Shell configuraties Add config Config toevoegen ipython ::: Use IPython shell if available. ipython ::: Gebruik IPython shell als deze beschikbaar is. File to run at startup Bestand dat uitgevoerd wordt bij het opstarten Code to run at startup Code die uitgevoerd wordt bij het opstarten argv ::: The command line arguments (sys.argv). argv ::: Command line argumenten (sys.argv). environ ::: Extra environment variables (os.environ). environ ::: Extra omgevings variabelen (os.environ). shells Click to select shell. Klik om de shell te selecteren splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Dit is <b>Pyzo</b><br />de Python omgeving voor wetenschappelijk coderen Version Versie Pyzo is open source software and freely available for everyone. Pyzo is open source software en voor iedereen gratis beschikbaar. wizard Welcome to the Interactive Editor for Python! Welkom bij de Interactieve Editor voor Python! The editor is where you write your code De editor is waar je je code schrijft The shell is where your code gets executed De shell is waar de code uitgevoerd wordt Configuring shells Shells configureren Running code Code uitvoeren Interactive mode vs running as script Interactieve modus versus script modus Tools for your convenience Gereedschap Recommended tools Aanbevolen gereedschap Get coding! Begin met coden! Step Stap Select language Selecteer taal Language changed Taal aangepast or you can write code in the *editor* and execute that. of je kan je code in the *editor* schrijven en deze vervolgens uitvoeren. In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. In the *editor* wordt elk geopend bestand gerepresenteerd met een tab. Door met de rechtermuisknop op een tab te klikken, kunnen bestanden worden uitgevoerd, opgeslagen, afgesloten, etc. Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Via 'Shell > Aanpasses shell configuraties' kan je *shell configuraties* aanpassen en toevoegen. Zo kun je bijvoorbeeld de start directorie insteller, of het Pythonpath. *Run cell:* a cell is everything between two lines starting with '##'. *Voer cell uit:* een cell is alle code tussen twee regels die met '##' beginnen. *Run file:* run all the code in the current file. *Voer bestand uit:* alle code in het huidige bestand wordt uitgevoerd. *Run project main file:* run the code in the current project's main file. *Voer project main bestand uit:* alle code in de huidige main file wordt uitgevoerd. In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. In interactieve modus is sys.path[0] een lege string (ofwel de huidige dir), en sys.argv wordt ['']. Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Via het *gereedschap menu* kan je eenvoudig selecteren wel gereedschap je wilt gebruiken. Het gereedschap besaat uit kleine schermen die naar eigen voorkeur gepositioneerd kunnen worden. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Het gereedschapsysteem is zo ontworpen dat het vrij eenvoudig is om ook zelf gereedschap te maken. Kijk op de online wiki.voor meer informatie. We especially recommend the following tools: We raden met name de volgende gereedschappen aan: The *Source structure tool* gives an outline of the source code. De *Sourc structure* geeft de structuur van de broncode aan. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. De *File browser* geeft een overzicht van alle bestanden in een directorie. Klik op het ster-icoon om je project te markeren en managen. You can execute commands directly in the *shell*, Je kan commandos direct in the *shell* uitvoeren. Getting started with Pyzo Wegwijs worden met Pyzo This wizard helps you get familiarized with the workings of Pyzo. Deze wizard helpt je om bekend te worden met de werking van Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_nl_NL.tr.qm0000666000000000000000000012507213156166666021506 0ustar 00000000000000  Х(`m?NS~ETTeff@'gVp^=ADiZ 6c>s32mjn&%^͎P^V<3+@,x[nnut p}.cq^Ot>g}uCl~4iC߱W`r;^g3Hc M(BRj]s"Ucr>ujk9E4vT.H t$a$оBX8NRsiz~Lf{.2' .:L9o,`g)|I~1$N7_k.q!F>6$S$L&Ene3 ISny<X>bGa@>h+n)Bnj{|y H5 sZ*vMN TR>%b'Fz13<>-+FrhI<5LLY ;R_pfzZ J?6t+?ln7m>z8J5K^/eVl\VV~N[5|[[dls9Qe nyg"i Rqѯtvj)jZX+N_Q#nMoA/%f?i2i|Gbj^kKmCu' h 0r @ _ 6z Ix KƎD wSeM qB# u0 5 g~ n- BF רAH @l  . Bl iv 6k ;~Q ?j Jk OZO c$ z FM qV Tg l ><. 3l e# t na| u / 4[ T  .C ,<C 0nV Y7 [=n!e aoh t> "\ ` s^U )D Z ɠD ˳H n 3K rPJ i i ) 0 7  Bn~DF Gn} MNpQ O Qun Vo  Ͽ^J ?w^ SrD)V& ![4[>}:>!Zt \#>w>d_;8M~^n  _jp :} IJ:\;Vk~ߔgN?v>!#Evx^}YEq KގYDv.Biq StackStackdebugtWeet je zeker dat je het vastgepinde bestand wilt sluiten?0Are you sure you want to close this pinned file?editorLSluiten, Verwerpen, Annuleren, OpslaanClose, Discard, Cancel, SaveeditorSluitenClosingeditor4Sluiten van gepint bestandClosing pinnededitor4Gewijzigd bestand sluiten?Save modified file?editorXSelecteer een of meer bestanden om te openen Select one or more files to open editorTabs^Selecteer het bestand om de code in op te slaanSelect the file to save to editorTabsBVoeg directory toe aan PythonpathAdd path to Python path filebrowserJWeet je zeker dat je wilt verwijderen$Are you sure that you want to delete filebrowser@Verander de naam van dit projectChange project name filebrowserTKlik de ster om de huidige dir op te slaan"Click star to bookmark current dir filebrowser*Kopier directorie pad Copy path filebrowser,Maak nieuwe directorieCreate new directory filebrowser$Maak nieuw bestandCreate new file filebrowserVerwijderenDelete filebrowserDupliceren Duplicate filebrowser&Bestandsnaam filterFilename filter filebrowser>Geef de naam voor de directorie#Give the name for the new directory filebrowserFGeef de naam van het nieuwe bestandGive the name for the new file filebrowserHGeef de nieuwe naam voor het bestandGive the new name for the file filebrowserVGa naar deze directorie in de huidige shell)Go to this directory in the current shell filebrowser"Importeer data...Import data... filebrowser&Hoofdlettergevoelig Match case filebrowser(Nieuwe project naam:New project name: filebrowser OpenenOpen filebrowser Open buiten PyzoOpen outside Pyzo filebrowserProject naam Project name filebrowserProjecten: Projects: filebrowser RegExpRegExp filebrowser"Verwijder projectRemove project filebrowserHernoemenRename filebrowser&Laat zien in FinderReveal in Finder filebrowser>Voer uit in de Jupyter notebookRun Jupyter notebook filebrowser(Uitvoeren als script Run as script filebrowser"Zoek in bestandenSearch in files filebrowser,Zoek in subdirectoriesSearch in subdirs filebrowserDVoeg deze dir toe aan de projectenStar this directory filebrowserFVerwijder deze dir uit de projectenUnstar this directory filebrowserBrowse ... Browse... importwizardSluitenClose importwizardImporteed data Import data importwizardPImporteer data als een enkelvoudig arrayImport data as single array importwizardTImporteer data in een variabele per kollom(Import data into one variable per column importwizard&Data importeer hulpImport data wizard importwizard4Er is nu geen bestand openNo current file open importwizard4Er is nu geen shell aktiefNo current shell active importwizardVoorbeeld:Preview: importwizardTGeef foutmelding als de data niet juist isRaise error upon invalid data importwizardHSelecteer colummen om te importeren:Select columns to import: importwizard"Selecteer bestand Select file importwizard8The importeerhulp is al open&The import data wizard is already open importwizardniet opgeslagenunsavedmain(als script) (as script)menuOver Pyzo+About Pyzo ::: More information about Pyzo.menu:Accepteer autocompletion met:Accept autocompletion with:menu2Geavanceerde instellingen5Advanced settings... ::: Configure Pyzo even further.menuStel een vraagAsk a question ::: Need help?menuAutocomplete keywords ::: De autocomple lijst laat ook keywords zien.DAutocomplete keywords ::: The autocompletion list includes keywords.menuAutocompletionAutocompletionmenu$Automatische popupAutomatic popupmenuAutomatisch inspringen ::: Inspringen bij een nieuwe regel na een dubbele punt.BAutomatically indent ::: Indent when pressing enter after a colon.menu~Kan alleen bestanden uitvoeren die op het bestandssysteem staan1Can only run scripts that are in the file system.menu~Verander huidige directory naar directory van bestand in editor,Change current directory to editor file pathmenuvVerander huidige directoru naar huidige pad in file browser3Change current directory to the file browser's pathmenubVerander directory als een file uitgevoerd wordt.EChange directory when executing file ::: like Run File As Script doesmenu.Kijk of er updates zijn7Check for updates ::: Are you using the latest version?menu:Verwijder alle {} breakpointsClear all {} breakpointsmenu$Maak scherm schoon"Clear screen ::: Clear the screen.menuLSluiten ::: Sluit het huidige bestand.!Close ::: Close the current file.menunAfsluiten ::: Stop de interpreter en sluit de shell af.8Close ::: Terminate the interpreter and close the shell.menu`Alles sluiten ::: Sluit alle geopende bestanden.Close all ::: Close all files.menulAnderen sluiten ::: Sluit alle bestanden behalve deze.-Close others::: Close all files but this one.menuCommentaar ::: Voeg een commentaar teken toe aan de geselecteerde regel.&Comment ::: Comment the selected line.menuKopieerCopy ::: Copy selected linesmenuKopieren1Copy ::: Copy the selected text to the clipboard.menuKopieer path.Copy path ::: Copy the full path of this file.menu8Can het bestand niet opslaanCould not save the file.menutCreeer een nieuwe Python omgeving ::: Installeer miniconda7Create new Python environment... ::: Install miniconda.menu&Maak shell %s: (%s)Create shell %s: (%s)menuKnippenCut ::: Cut the selected text.menu\Debug verder: ga verder tot volgend breakpoint*Debug continue: proceed to next breakpointmenuFDebug volgende: ga een regel verder#Debug next: proceed until next linemenuVDebug return: ga verder tot function return#Debug return: proceed until returnsmenuFDebug instappen: ga een stap verder!Debug step into: proceed one stepmenulTerugspringen ::: Zet het inspringen een stap kleiner.&Dedent ::: Unindent the selected line.menubVerwijder regel ::: Verwijder de huidige regel(s),Delete line ::: Delete the selected line(s).menunDupliceer regel ::: Dupliceer de geselecteerde regel(s)2Duplicate line ::: Duplicate the selected line(s).menuBewerkenEditmenu,Bewerk sneltoetsen...;Edit key mappings... ::: Edit the shortcuts for menu items.menuAanpassen shell configuraties... ::: Voeg shell configuraties toe en pas de eigenschappen aan.WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menu0Bewerk syntax stijlen...;Edit syntax styles... ::: Change the coloring of your code.menu\Pas uitvoertips toe ::: Laat uitvoertips zien.;Enable calltips ::: Show calltips with function signatures.menuxCodering ::: The karakter codering voor het huidige bestand.8Encoding ::: The character encoding of the current file.menuZExecuteer cell ::: Executeer de huidige cell.IExecute cell ::: Execute the current editors's cell in the current shell.menuExecuteer cell en ga verder ::: Executeer de huidige cell en ga verder naar de volgende cell.]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menuhExecuteer bestand ::: Executeer het huidige bestand.?Execute file ::: Execute the current file in the current shell.menuExecuteer main bestand ::: Executeer het main bestand in de huidige shell. AExecute main file ::: Execute the main file in the current shell.menu$Executeer selectie ::: Executeer de geselecteerde regels, de geselecteerde text op de huidige regel, of the huidige regel als er geen selectie is.Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menu<Voer selectie uit en ga verderExecute selection and advancemenu$Exporteer naar PDFAExport to PDF ::: Export current file to PDF (e.g. for printing).menuBestandFilemenuVind volgende ::: Vind het volgende voorkomen van de zoek tekst.Geef corresponderende haken aan@Highlight brackets ::: Highlight matched and unmatched brackets.menuMarkeer huidige regel ::: Geef de huidige regel een andere achtergrondkleur.BHighlight current line ::: Highlight the line where the cursor is.menuGeschiedenisHistorymenu`Inspringen ::: Spring de geselecteerde regel in.$Indent ::: Indent the selected line.menuInspring niveau ::: The mate van inspringen voor het huidige bestand.9Indentation ::: The indentation used of the current file.menuOnderbreken ::: Onderbreek de nu uitgevoerde code (werkt niet voor extension-code).TInterrupt ::: Interrupt the current running code (does not work for extension code).menuUitlijnen commentaar of docstring ::: Hervorm de geselecteerde tekst door het uit te lijnen op 70 karakters.`Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters.menuRegel uiteinden ::: Het soort regel uiteinden van het huidige bestand.?Line endings ::: The line ending character of the current file.menu&Locale documentatieDLocal documentation ::: Documentation on Python and the Scipy Stack.menuHLocatie van de lange-regel indicatorLLocation of long line indicator ::: The location of the long-line-indicator.menu`Nieuw ::: Maak een nieuw (of tijdelijk) bestand.)New ::: Create a new (or temporary) file.menuNieuwe shell ... ::: Maak een nieuwe shell aan om code in uit te voeren.2New shell ... ::: Create new shell to run code in.menudVolgende cell ::: ga verder naar de volgende cell.'Next cell ::: Advance to the next cell.menuVolgende object ::: ga verder naar het volgende top-level object.8Next object ::: Advance to the next top-level structure.menu&Geen autocompletionNo autocompletionmenu0Geen editor geselecteerdNo editor selected.menuFGeen shell om code in uit te voerenNo shell to run code in.menu NietsNonemenuXLaal alleen popup zien door Tab te gebruiken!Only show popup when pressing TabmenuROpen huidige directory in de file browser&Open current directory in file browsermenu<Open directory in file browserOpen directory in file browsermenuFOpen ::: Open een bestaand bestand.,Open... ::: Open an existing file from disk.menuPlakken6Paste ::: Paste the text that is now on the clipboard.menuPlakken en selecteren ::: Plak the tekst op het clipbord en behoud de selectie om de indentatie aan te kunnen passen.yPaste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation.menuZVastpinnen/ontpinnen :::Pin dit bestand vast.2Pin/Unpin ::: Pinned files get closed less easily.menuVPostmortem: debuggen met laatste traceback %Postmortem: debug from last tracebackmenuZVorige cell ::: ga terug naar de vorige cell./Previous cell ::: Go back to the previous cell.menuxVorige object ::: ga terug naar het vorige top-level object.@Previous object ::: Go back to the previous top-level structure.menu$Pyzo's instructies3Pyzo guide ::: Open the Pyzo guide in your browser.menuPyzo's website7Pyzo website ::: Open the Pyzo website in your browser.menuPyzo's wizard$Pyzo wizard ::: Get started quickly.menubQt thema ::: De stijl van de gebruikersinterface.7Qt theme ::: The styling of the user interface widgets.menuPyzo afsluiten$Quit Pyzo ::: Close the application.menuOpnieuw doen ::: Doe de laatste ongedaan gemaakte bewerking opnieuw.-Redo ::: Redo the last undone editong action.menu(Herlaad gereedschap..Reload tools ::: For people who develop tools.menuVerwijderen ::: verwijder de geselecteerde items uit de geschiedenis+Remove ::: Remove selected history items(s)menuDHernoemen ::: Hernoem dit bestand.Rename ::: Rename this file.menu.Rapporteer een probleemRReport an issue ::: Did you found a bug in Pyzo, or do you have a feature request?menuZHerstart ::: Stop en herstart de interpreter.2Restart ::: Terminate and restart the interpreter.menuPyzo herstarten)Restart Pyzo ::: Restart the application.menuUitvoerenRunmenuzUitvoeren ::: geselecteerde regels uitvoeren in huidige shell+Run ::: Run selected lines in current shellmenujVoer bestand uit ::: Voer de code in dit bestand uit.'Run file ::: Run the code in this file.menuVoer bestand uit als script ::: Herstart en voer het huidige bestand uit als script.DRun file as script ::: Restart and run the current file as a script.menuVoer bestand uit als script ::: Voer de code in dit bestand uit als script (dit herstart de interpreter).LRun file as script ::: Run this file as a script (restarts the interpreter).menuVoer main bestand uit als script ::: Herstart en voer het main bestand uit als script.FRun main file as script ::: Restart and run the main file as a script.menu$Voer selectie uit ::: Uitvoeren van: huidig geselecteerde regels, geselecteerde woorden op een regel, of de huidige regel als er geen selectie is.Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection.menuNOpslaan ::: Sla het huidige bestand op.'Save ::: Save the current file to disk.menubAlles opslaan ::: Sla alle geopende bestanden op.!Save all ::: Save all open files.menuOpslaan als ... ::: Sla het huidige bestand op onder een andere naam.8Save as... ::: Save the current file under another name.menu ZoekenSearchmenu Alles selecterenSelect all ::: Select all text.menutSelecteer editor ::: Focus de cursor op de huidige editor.9Select editor ::: Focus the cursor on the current editor.menuSelecteer taal.Select language ::: The language used by Pyzo.menu0Selecteer vorige bestand=Select previous file ::: Select the previously selected file.menupSelecteer shell ::: Focus de cursor op de huidige shell.7Select shell ::: Focus the cursor on the current shell.menuhStel dit bestand in als MAIN bestand/gewoon bestand.SSet/Unset as MAIN file ::: The main file can be run while another file is selected.menuInstellingenSettingsmenu ShellShellmenu:Laat inspring hulplijnen zienHShow indentation guides ::: Show vertical lines to indicate indentation.menu0Laat regeluiteindes zien0Show line endings ::: Show the end of each line.menufLaat whitespace zien ::: Laat spaties en tabs zien.)Show whitespace ::: Show spaces and tabs.menuStop debuggenStop debuggingmenuxSyntax parser ::: De syntax parser voor het huidige bestand.8Syntax parser ::: The syntax parser of the current file.menurStoppen ::: Stop de interpreter, maar laat de shell open.@Terminate ::: Terminate the interpreter, leaving the shell open.menuHEr is geen main bestand geselecteerdThere is no main file selected.menu2Zet breakpoint aan of uitPyzo</b><br />de Python omgeving voor wetenschappelijk coderen@This is Pyzo
the Python IDE for scientific computingsplash VersieVersionsplash The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. wizard*Voer cell uit:* een cell is alle code tussen twee regels die met '##' beginnen.F*Run cell:* a cell is everything between two lines starting with '##'.wizard*Voer bestand uit:* alle code in het huidige bestand wordt uitgevoerd.1*Run file:* run all the code in the current file.wizard*Voer project main bestand uit:* alle code in de huidige main file wordt uitgevoerd.I*Run project main file:* run the code in the current project's main file.wizard*Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines.wizard&Shells configurerenConfiguring shellswizard Begin met coden! Get coding!wizard.Wegwijs worden met PyzoGetting started with PyzowizardIn interactieve modus is sys.path[0] een lege string (ofwel de huidige dir), en sys.argv wordt [''].pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizardIn script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script.wizardtIn the *editor* wordt elk geopend bestand gerepresenteerd met een tab. Door met de rechtermuisknop op een tab te klikken, kunnen bestanden worden uitgevoerd, opgeslagen, afgesloten, etc.In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizardLInteractieve modus versus script modus%Interactive mode vs running as scriptwizardTaal aangepastLanguage changedwizard Het gereedschapsysteem is zo ontworpen dat het vrij eenvoudig is om ook zelf gereedschap te maken. Kijk op de online wiki.voor meer informatie.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizardPyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib.wizard$Pyzo consists of two main componentswizardPyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*.wizardRPyzo supports several ways to run source code in the editor. (see the 'Run' menu).wizard,Aanbevolen gereedschapRecommended toolswizardCode uitvoeren Running codewizardSelecteer taalSelect languagewizardShells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell.wizardStapStepwizardDe *File browser* geeft een overzicht van alle bestanden in een directorie. Klik op het ster-icoon om je project te markeren en managen.The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizardxDe *Sourc structure* geeft de structuur van de broncode aan.@The *Source structure tool* gives an outline of the source code.wizardJDe editor is waar je je code schrijft'The editor is where you write your codewizardThe right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily.wizardRDe shell is waar de code uitgevoerd wordt*The shell is where your code gets executedwizard=This concludes the Pyzo wizard. Now, get coding and have fun!wizard4This wizard can be opened using 'Help > Pyzo wizard'wizardDeze wizard helpt je om bekend te worden met de werking van Pyzo.AThis wizard helps you get familiarized with the workings of Pyzo.wizardGereedschapTools for your conveniencewizardRVia 'Shell > Aanpasses shell configuraties' kan je *shell configuraties* aanpassen en toevoegen. Zo kun je bijvoorbeeld de start directorie insteller, of het Pythonpath.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardtVia het *gereedschap menu* kan je eenvoudig selecteren wel gereedschap je wilt gebruiken. Het gereedschap besaat uit kleine schermen die naar eigen voorkeur gepositioneerd kunnen worden.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardbWe raden met name de volgende gereedschappen aan:,We especially recommend the following tools:wizard\Welkom bij de Interactieve Editor voor Python!-Welcome to the Interactive Editor for Python!wizardWhen Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions.wizardbJe kan commandos direct in the *shell* uitvoeren.1You can execute commands directly in the *shell*,wizard You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution.wizardof je kan je code in the *editor* schrijven en deze vervolgens uitvoeren.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_pt_BR.tr0000666000000000000000000017305213156166663021074 0ustar 00000000000000 debug Stack editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter Search in files Projects: Click star to bookmark current dir Remove project Change project name Add path to Python path Go to this directory in the current shell Project name New project name: Match case RegExp Search in subdirs Unstar this directory Star this directory Open Run as script Run Jupyter notebook Import data... Open outside Pyzo Reveal in Finder Copy path Rename Delete Create new file Create new directory Give the new name for the file Give the name for the new directory Duplicate Give the name for the new file Are you sure that you want to delete importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu spaces plural of spacebar character Indentation ::: The indentation used of the current file. Syntax parser ::: The syntax parser of the current file. Line endings ::: The line ending character of the current file. Encoding ::: The character encoding of the current file. Open... ::: Open an existing file from disk. Save ::: Save the current file to disk. Save as... ::: Save the current file under another name. Save all ::: Save all open files. Close ::: Close the current file. Close all ::: Close all files. Export to PDF ::: Export current file to PDF (e.g. for printing). Undo ::: Undo the latest editing action. Redo ::: Redo the last undone editong action. Cut ::: Cut the selected text. Copy ::: Copy the selected text to the clipboard. Paste ::: Paste the text that is now on the clipboard. Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Select all ::: Select all text. Indent ::: Indent the selected line. Dedent ::: Unindent the selected line. Comment ::: Comment the selected line. Uncomment ::: Uncomment the selected line. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Go to line ::: Go to a specific line number. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Find selection ::: Find the next occurrence of the selected text. Find selection backward ::: Find the previous occurrence of the selected text. Find next ::: Find the next occurrence of the search string. Find previous ::: Find the previous occurrence of the search string. Location of long line indicator ::: The location of the long-line-indicator. Qt theme ::: The styling of the user interface widgets. Select shell ::: Focus the cursor on the current shell. Select editor ::: Focus the cursor on the current editor. Select previous file ::: Select the previously selected file. Show whitespace ::: Show spaces and tabs. Show line endings ::: Show the end of each line. Show indentation guides ::: Show vertical lines to indicate indentation. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Highlight current line ::: Highlight the line where the cursor is. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Font Zooming Goto Definition ::: Go to definition of word under cursor. Close others::: Close all files but this one. Rename ::: Rename this file. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Run file ::: Run the code in this file. Run file as script ::: Run this file as a script (restarts the interpreter). Ask a question ::: Need help? Accept autocompletion with: Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. File Edit View Settings Shell Run Tools Help Use tabs Use spaces New ::: Create a new (or temporary) file. Zoom in Zoom out Zoom reset None Clear screen ::: Clear the screen. Interrupt ::: Interrupt the current running code (does not work for extension code). Restart ::: Terminate and restart the interpreter. Terminate ::: Terminate the interpreter, leaving the shell open. Close ::: Terminate the interpreter and close the shell. Debug next: proceed until next line Debug step into: proceed one step Debug return: proceed until returns Debug continue: proceed to next breakpoint Stop debugging Clear all {} breakpoints Postmortem: debug from last traceback Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Create new Python environment... ::: Install miniconda. Create shell %s: (%s) New shell ... ::: Create new shell to run code in. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Run file as script ::: Restart and run the current file as a script. Run main file as script ::: Restart and run the main file as a script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Execute cell ::: Execute the current editors's cell in the current shell. Execute file ::: Execute the current file in the current shell. Execute main file ::: Execute the main file in the current shell. Execute selection and advance Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Help on running code ::: Open the pyzo wizard at the page about running code. (as script) Reload tools ::: For people who develop tools. No autocompletion Automatic popup Only show popup when pressing Tab Autocomplete keywords ::: The autocompletion list includes keywords. Automatically indent ::: Indent when pressing enter after a colon. Enable calltips ::: Show calltips with function signatures. Autocompletion Edit key mappings... ::: Edit the shortcuts for menu items. Edit syntax styles... ::: Change the coloring of your code. Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run Could not run script. Check for the latest version. Edit syntax styling Advanced settings Language changed About Pyzo Edit shortcut mapping Shortcut mappings Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Find pattern Previous ::: Find previous occurrence of the pattern. Next ::: Find next occurrence of the pattern. Replace pattern Repl. all ::: Replace all matches in current document. Replace ::: Replace this match. Match case ::: Find words that match case. RegExp ::: Find using regular expressions. Whole words ::: Find only whole words. Auto hide ::: Hide search/replace when unused for 10 s. shell name ::: The name of this configuration. ipython ::: Use IPython shell if available. Use system default File to run at startup Code to run at startup exe ::: The Python executable. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. startupScript ::: The script to run at startup (not in script mode). startDir ::: The start directory (not in script mode). argv ::: The command line arguments (sys.argv). environ ::: Extra environment variables (os.environ). Delete ::: Delete this shell configuration Shell configurations Add config shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Language changed Getting started with Pyzo Step Welcome to the Interactive Editor for Python! This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' Select language The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components You can execute commands directly in the *shell*, or you can write code in the *editor* and execute that. The editor is where you write your code In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. The shell is where your code gets executed When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Configuring shells Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Running code Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). *Run cell:* a cell is everything between two lines starting with '##'. *Run file:* run all the code in the current file. *Run project main file:* run the code in the current project's main file. Interactive mode vs running as script In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. Tools for your convenience Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Recommended tools We especially recommend the following tools: The *Source structure tool* gives an outline of the source code. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. Get coding! This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_pt_BR.tr.qm0000666000000000000000000000002713156166667021503 0ustar 00000000000000 debug Stack Montão editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter filtro do nome do arquivo Search in files Procurar em ficheiros Projects: Projetos: Click star to bookmark current dir Clique estrela para marcar o seu favorito corrente dir Remove project Remover projeto Change project name Alterar nome do projeto Add path to Python path Adicionar caminho para Python caminho Go to this directory in the current shell Vá para este diretório no shell corrente New project name: Novo nome do projeto: Match case maiúsculas de minúsculas RegExp Expressão regular Search in subdirs Procurar em subdiretórios Unstar this directory Remover estrela este diretório Star this directory Estrela este diretório Open Abrir Reveal in Finder Revelar no Finder Copy path Copiar caminho Import data... Importar dados ... Rename Renomear Delete Apagar Create new file Criar novo arquivo Create new directory Criar novo diretório Give the new name for the file Dê um novo nome para o arquivo Give the name for the new directory Dar um nome para o novo diretório Duplicate duplicar Give the name for the new file Dar um nome para o novo arquivo Are you sure that you want to delete Você tem certeza que deseja deletar Project name Run as script Run Jupyter notebook Open outside Pyzo importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu File Arquivo Edit editar View Vista Settings Configurações Shell Concha Run Corrida Tools Ferramentas Help Ajudar Use tabs Utilize os separadores Use spaces Utilize espaços spaces plural of spacebar character espaços Indentation ::: The indentation used of the current file. Recuo ::: O recuo usado do arquivo atual. Syntax parser ::: The syntax parser of the current file. Sintaxe parser ::: O analisador de sintaxe do arquivo atual. Line endings ::: The line ending character of the current file. Finais de linha ::: A linha de caracteres do arquivo atual de término. Encoding ::: The character encoding of the current file. Codificação ::: A codificação de caracteres do arquivo atual. New ::: Create a new (or temporary) file. Novo ::: Criar um novo (ou temporária) de arquivos. Open... ::: Open an existing file from disk. Abrir ... ::: Abra um arquivo existente do disco. Save ::: Save the current file to disk. Salve ::: Salve o arquivo atual no disco. Save as... ::: Save the current file under another name. Salvar como ... ::: Salve o arquivo atual com outro nome. Save all ::: Save all open files. Salve todo ::: Salve todos os arquivos abertos. Close ::: Close the current file. Fechar ::: Feche o arquivo atual. Close all ::: Close all files. Feche todos ::: Fechar todos os arquivos. Undo ::: Undo the latest editing action. Desfazer ::: Desfazer a última ação de edição. Redo ::: Redo the last undone editong action. Refazer ::: Refazer a última ação editong desfeita. Cut ::: Cut the selected text. Cortar ::: Cortar o texto selecionado. Copy ::: Copy the selected text to the clipboard. Copiar ::: Copie o texto selecionado para a área de transferência. Paste ::: Paste the text that is now on the clipboard. Cole ::: Cole o texto que está agora na área de transferência. Select all ::: Select all text. Selecionar todos ::: Selecione todo o texto. Indent ::: Indent the selected line. Recuo ::: recuo da linha selecionada. Dedent ::: Unindent the selected line. Dedent ::: Unindent da linha selecionada. Comment ::: Comment the selected line. Comentário ::: Comente a linha selecionada. Uncomment ::: Uncomment the selected line. Descomente ::: Remova o comentário da linha selecionada. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Justificar comentário / docstring ::: Remodelar o texto selecionado para que ele está alinhado com cerca de 70 caracteres. Go to line ::: Go to a specific line number. Ir para linha ::: Ir para um número de linha específico. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Localizar ou substituir ::: Mostrar localizar / substituir widget. Inicializar com o texto selecionado. Find selection ::: Find the next occurrence of the selected text. Encontre seleção ::: Localizar a próxima ocorrência do texto selecionado. Find selection backward ::: Find the previous occurrence of the selected text. Encontre seleção backward ::: Localizar a ocorrência anterior do texto selecionado. Find next ::: Find the next occurrence of the search string. Localizar próxima ::: Localizar a próxima ocorrência da seqüência de pesquisa. Find previous ::: Find the previous occurrence of the search string. Encontre anterior ::: Localizar a ocorrência anterior da seqüência de pesquisa. Accept autocompletion with: Zoom in ampliar Zoom out Diminuir o zoom Zoom reset redefinição Zoom Location of long line indicator ::: The location of the long-line-indicator. Localização do indicador de linha longa ::: A localização do indicador de longo-line. Qt theme ::: The styling of the user interface widgets. Qt tema ::: O estilo dos widgets de interface de usuário. Select shell ::: Focus the cursor on the current shell. Selecione shell ::: Foco o cursor sobre o shell atual. Select editor ::: Focus the cursor on the current editor. Selecione editor ::: Foco o cursor sobre o editor atual. Select previous file ::: Select the previously selected file. Selecione o arquivo anterior ::: Selecione o arquivo selecionado anteriormente. Show whitespace ::: Show spaces and tabs. Mostrar espaço em branco ::: Mostrar espaços e tabulações. Show line endings ::: Show the end of each line. Terminações Mostrar linha ::: Mostrar ao final de cada linha. Show indentation guides ::: Show vertical lines to indicate indentation. Mostrar recuo orienta ::: Mostrar linhas verticais para indicar recuo. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Enrole longas filas ::: linhas envoltório que não cabem na tela (ou seja, sem a rolagem horizontal). Highlight current line ::: Highlight the line where the cursor is. Destaque linha atual ::: Selecione a linha onde está o cursor. Font fonte Zooming zunir Clear screen ::: Clear the screen. Limpar ecrã ::: Limpe a tela. Interrupt ::: Interrupt the current running code (does not work for extension code). Interromper ::: Interromper o código de execução atual (não funciona para o código de extensão). Restart ::: Terminate and restart the interpreter. Reinicie ::: Encerrar e reiniciar o intérprete. Terminate ::: Terminate the interpreter, leaving the shell open. Terminar ::: Terminar o intérprete, deixando a casca. Close ::: Terminate the interpreter and close the shell. Fechar ::: Terminar o intérprete e fechar a shell. Debug next: proceed until next line Depurar seguinte: continue até a próxima linha Debug step into: proceed one step Retorno Depurar: Prosseguir Ate a Volta Debug return: proceed until returns Depurar retorno: prosseguir até a volta Debug continue: proceed to next breakpoint Depurar continuar: proceder ao próximo ponto de interrupção Stop debugging Pare de depuração Clear all {} breakpoints Limpe todos os {} pontos de interrupção Postmortem: debug from last traceback Postmortem: debug do último rastreamento Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Editar configurações do shell ... ::: Adicionar novas configs shell e editar as propriedades do intérprete. New shell ... ::: Create new shell to run code in. New shell ... ::: Criar novo shell para executar um código dentro. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Seleção Corrida ::: Corrida linhas do editor atual selecionados, palavras selecionadas na linha atual, ou a linha atual se não houver nenhuma seleção. Close others::: Close all files but this one. Fechar outros ::: Fechar todos os arquivos, mas este. Rename ::: Rename this file. Renomeie ::: Renomeie esse arquivo. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Arquivos Pin / Unpin ::: Fixado ficar fechado menos facilmente. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Activado / desactivado como arquivo principal ::: O arquivo principal pode ser executado enquanto um outro arquivo é selecionado. Run file ::: Run the code in this file. Corrida arquivo ::: Execute o código neste arquivo. Run file as script ::: Run this file as a script (restarts the interpreter). Corrida arquivo como script ::: Corrida o arquivo como um script (reinicia o intérprete). Run file as script ::: Restart and run the current file as a script. Corrida arquivo como script ::: Restart e execute o arquivo atual como um script. Run main file as script ::: Restart and run the main file as a script. Corrida arquivo principal como script ::: Reinicie e execute o arquivo principal como um script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Executar seleção ::: Executar linhas do editor atual selecionados, palavras selecionadas na linha atual, ou a linha atual se não houver nenhuma seleção. Execute cell ::: Execute the current editors's cell in the current shell. Executar celular ::: Executar célula do atual editores no shell atual. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Executar celular e avanço ::: Executar celular e avanço do atual editores para a próxima célula. Execute file ::: Execute the current file in the current shell. Executa o ficheiro ::: Execute o arquivo atual no shell atual. Execute main file ::: Execute the main file in the current shell. Executa o ficheiro principal ::: Execute o arquivo principal no shell atual. Reload tools ::: For people who develop tools. Atualizar ferramentas ::: Para as pessoas que desenvolvem ferramentas. Ask a question ::: Need help? Faça uma pergunta ::: precisar de ajuda? Automatically indent ::: Indent when pressing enter after a colon. Automaticamente travessão ::: recuo ao pressionar enter depois de dois pontos. Enable calltips ::: Show calltips with function signatures. Ativar calltips ::: Mostrar calltips com assinaturas de função. Autocomplete keywords ::: The autocompletion list includes keywords. Palavras preenchimento automático ::: A lista inclui autocompletar palavras. Edit key mappings... ::: Edit the shortcuts for menu items. Edite mapeamentos de teclas ... ::: Edite os atalhos para os itens do menu. Edit syntax styles... ::: Change the coloring of your code. Editar estilos de sintaxe ... ::: Mude a cor do seu código. Export to PDF ::: Export current file to PDF (e.g. for printing). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Goto Definition ::: Go to definition of word under cursor. Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. None Create new Python environment... ::: Install miniconda. Create shell %s: (%s) Execute selection and advance Help on running code ::: Open the pyzo wizard at the page about running code. (as script) No autocompletion Automatic popup Only show popup when pressing Tab Autocompletion Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run Não foi possível executar Could not run script. Não foi possível executar script. Check for the latest version. Verifique se a versão mais recente. Edit syntax styling Editar sintaxe estilo Advanced settings configurações avançadas Language changed Idioma alterado Edit shortcut mapping Mapeamento Editar atalho Shortcut mappings mapeamentos de atalho About Pyzo Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Esconder widget de busca (Escape) Find pattern Encontre padrão Previous ::: Find previous occurrence of the pattern. Anterior ::: Localizar ocorrência anterior do padrão. Next ::: Find next occurrence of the pattern. Próxima ::: Localizar próxima ocorrência do padrão. Replace pattern Substituir padrão Repl. all ::: Replace all matches in current document. Subst. todos ::: Substitua todas as partidas em documento atual. Replace ::: Replace this match. Substitua ::: Substituir este jogo. Match case ::: Find words that match case. Maiúsculas de minúsculas ::: Encontre palavras que correspondem caso. RegExp ::: Find using regular expressions. RegExp ::: Encontre usando expressões regulares. Whole words ::: Find only whole words. Palavras inteiras ::: encontrar somente palavras inteiras. Auto hide ::: Hide search/replace when unused for 10 s. Esconder Auto ::: Esconder busca / substituição quando não for utilizado por 10 s. shell name ::: The name of this configuration. nomear ::: O nome desta configuração. ipython ::: Use IPython shell if available. ipython ::: Use IPython shell if available. Use system default Utilize padrão do sistema File to run at startup Arquivo para executar na inicialização Code to run at startup Código para executar na inicialização exe ::: The Python executable. exe ::: O executável Python. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: O kit de ferramentas GUI para integrar (para plotagem interativo, etc.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. PythonPath ::: A lista de diretórios para procurar por módulos e pacotes. Escreva cada caminho em uma nova linha, ou separar com o separador padrão para este sistema operacional. startupScript ::: The script to run at startup (not in script mode). StartupScript ::: O script para ser executado na inicialização (não no modo de script). startDir ::: The start directory (not in script mode). startDir ::: O diretório de partida (não no modo de script). argv ::: The command line arguments (sys.argv). argv ::: Os argumentos de linha de comando (sys.argv). environ ::: Extra environment variables (os.environ). ambiente ::: variáveis ​​de ambiente extra (os.environ). Delete ::: Delete this shell configuration Excluir ::: Apagar esta configuração shell Shell configurations configurações Concha Add config Adicionar configuração shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Step passo Welcome to the Interactive Editor for Python! Bem-vindo ao editor interativo para Python! Select language Selecione o idioma Language changed Idioma alterado You can execute commands directly in the *shell*, Você pode executar comandos diretamente no shell * * or you can write code in the *editor* and execute that. ou você pode escrever código no * editor * e executar isso. The editor is where you write your code O editor é onde você escreve seu código In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. No * editor *, cada arquivo aberto é representado como um guia. Clicando com o botão direito em uma guia, os arquivos podem ser executados, salvo, fechado, etc. The shell is where your code gets executed A casca é onde o código é executado Configuring shells Configurando conchas Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Via 'Concha configurações> Editar shell', você pode editar e adicionar * configurações do shell *. Isto permite-lhe, por exemplo, selecione o diretório inicial, ou usar um PythonPath personalizado. Running code código em execução *Run cell:* a cell is everything between two lines starting with '##'. * Célula de execução: * a célula é tudo entre duas linhas que começam com '# #'. *Run file:* run all the code in the current file. * Arquivo de execução: * executar todo o código no arquivo atual. *Run project main file:* run the code in the current project's main file. * Arquivo principal projeto Run: * executar o código no arquivo principal do projeto atual. Interactive mode vs running as script O modo interativo vs executado como roteiro In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. No modo interativo, sys.path [0] é uma cadeia vazia (ou seja, o diretório atual) e sys.argv está definido para ['']. Tools for your convenience Ferramentas para a sua conveniência Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Via o * menu Ferramentas *, pode-se selecionar quais ferramentas usar. As ferramentas podem ser posicionados em qualquer maneira que você quiser, e também pode ser un-encaixado. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Note-se que o sistema de ferramentas é projetado de tal forma que é fácil criar suas próprias ferramentas. Olhe para o wiki on-line para obter mais informações, ou utilizar uma das ferramentas existentes como exemplo. Recommended tools ferramentas recomendadas We especially recommend the following tools: Recomendamos especialmente as seguintes ferramentas: The *Source structure tool* gives an outline of the source code. O * estrutura ferramenta Fonte * dá um esboço do código fonte. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. A ferramenta * navegador Arquivo * ajuda a manter uma visão geral de todos os arquivos em um diretório. Para gerenciar seus projetos, clique no ícone de estrela. Get coding! Obter codificação! Getting started with Pyzo This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_pt_PT.tr.qm0000666000000000000000000007507313156166667021540 0ustar 00000000000000  ХhX?N4ET5eHf@'Hp^(IZc>R)!2LIJ͎31g<3@,W0[M?nuRup}.Dq^2} 1uCK~"o߱WB3sHc>6c+r>SJgEdU.  tX8N4{ z .&}L9M,9|I~a>k6.P3!F>$$S8&EnF3 /SnWa@>emh+n4njYH5s " NR>Q'-<>FPI<5L0uY '_pHMJ+&6S*m>X5h/ekVKVe$V~1[#[<)QF-nsyH"EOyѯSzTjj;9NA[A/%kf?I&j^ ,5 B Ix wSFn 5k n ר, @ ._ B` JJ c Y= F17 l] >' 3me e tj nC T  0n7 t*B "= C s@ )Dl_ ˳/ n^ i a i 0 Gn[ MNN VM i ?U S`!r4[>\\b>Vq>E~@nn _JJ:<VKC~ߔHYESގ:>DU^. it MontoStackdebugJAdicionar caminho para Python caminhoAdd path to Python path filebrowserFVoc tem certeza que deseja deletar$Are you sure that you want to delete filebrowser.Alterar nome do projetoChange project name filebrowserlClique estrela para marcar o seu favorito corrente dir"Click star to bookmark current dir filebrowserCopiar caminho Copy path filebrowser(Criar novo diretrioCreate new directory filebrowser$Criar novo arquivoCreate new file filebrowser ApagarDelete filebrowserduplicar Duplicate filebrowser2filtro do nome do arquivoFilename filter filebrowserBDar um nome para o novo diretrio#Give the name for the new directory filebrowser>Dar um nome para o novo arquivoGive the name for the new file filebrowser<D um novo nome para o arquivoGive the new name for the file filebrowserPV para este diretrio no shell corrente)Go to this directory in the current shell filebrowser$Importar dados ...Import data... filebrowser0maisculas de minsculas Match case filebrowser*Novo nome do projeto:New project name: filebrowser AbrirOpen filebrowserProjetos: Projects: filebrowser"Expresso regularRegExp filebrowserRemover projetoRemove project filebrowserRenomearRename filebrowser"Revelar no FinderReveal in Finder filebrowser*Procurar em ficheirosSearch in files filebrowser2Procurar em subdiretriosSearch in subdirs filebrowser,Estrela este diretrioStar this directory filebrowser<Remover estrela este diretrioUnstar this directory filebrowserPFaa uma pergunta ::: precisar de ajuda?Ask a question ::: Need help?menuPalavras preenchimento automtico ::: A lista inclui autocompletar palavras.DAutocomplete keywords ::: The autocompletion list includes keywords.menuAutomaticamente travesso ::: recuo ao pressionar enter depois de dois pontos.BAutomatically indent ::: Indent when pressing enter after a colon.menuNLimpe todos os {} pontos de interrupoClear all {} breakpointsmenu:Limpar ecr ::: Limpe a tela."Clear screen ::: Clear the screen.menuBFechar ::: Feche o arquivo atual.!Close ::: Close the current file.menudFechar ::: Terminar o intrprete e fechar a shell.8Close ::: Terminate the interpreter and close the shell.menuRFeche todos ::: Fechar todos os arquivos.Close all ::: Close all files.menujFechar outros ::: Fechar todos os arquivos, mas este.-Close others::: Close all files but this one.menuVComentrio ::: Comente a linha selecionada.&Comment ::: Comment the selected line.menuCopiar ::: Copie o texto selecionado para a rea de transferncia.1Copy ::: Copy the selected text to the clipboard.menuLCortar ::: Cortar o texto selecionado.Cut ::: Cut the selected text.menuvDepurar continuar: proceder ao prximo ponto de interrupo*Debug continue: proceed to next breakpointmenu\Depurar seguinte: continue at a prxima linha#Debug next: proceed until next linemenuNDepurar retorno: prosseguir at a volta#Debug return: proceed until returnsmenuNRetorno Depurar: Prosseguir Ate a Volta!Debug step into: proceed one stepmenuRDedent ::: Unindent da linha selecionada.&Dedent ::: Unindent the selected line.menu editarEditmenuEdite mapeamentos de teclas ... ::: Edite os atalhos para os itens do menu.;Edit key mappings... ::: Edit the shortcuts for menu items.menuEditar configuraes do shell ... ::: Adicionar novas configs shell e editar as propriedades do intrprete.WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menuvEditar estilos de sintaxe ... ::: Mude a cor do seu cdigo.;Edit syntax styles... ::: Change the coloring of your code.menu~Ativar calltips ::: Mostrar calltips com assinaturas de funo.;Enable calltips ::: Show calltips with function signatures.menuzCodificao ::: A codificao de caracteres do arquivo atual.8Encoding ::: The character encoding of the current file.menuExecutar celular ::: Executar clula do atual editores no shell atual.IExecute cell ::: Execute the current editors's cell in the current shell.menuExecutar celular e avano ::: Executar celular e avano do atual editores para a prxima clula.]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menu|Executa o ficheiro ::: Execute o arquivo atual no shell atual.?Execute file ::: Execute the current file in the current shell.menuExecuta o ficheiro principal ::: Execute o arquivo principal no shell atual.AExecute main file ::: Execute the main file in the current shell.menu0Executar seleo ::: Executar linhas do editor atual selecionados, palavras selecionadas na linha atual, ou a linha atual se no houver nenhuma seleo.Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menuArquivoFilemenuLocalizar prxima ::: Localizar a prxima ocorrncia da seqncia de pesquisa. Editar shell', voc pode editar e adicionar * configuraes do shell *. Isto permite-lhe, por exemplo, selecione o diretrio inicial, ou usar um PythonPath personalizado.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardbVia o * menu Ferramentas *, pode-se selecionar quais ferramentas usar. As ferramentas podem ser posicionados em qualquer maneira que voc quiser, e tambm pode ser un-encaixado.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardhRecomendamos especialmente as seguintes ferramentas:,We especially recommend the following tools:wizardVBem-vindo ao editor interativo para Python!-Welcome to the Interactive Editor for Python!wizardhVoc pode executar comandos diretamente no shell * *1You can execute commands directly in the *shell*,wizardvou voc pode escrever cdigo no * editor * e executar isso.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_ru_RU.tr0000666000000000000000000021605313156166663021121 0ustar 00000000000000 debug Stack Стек editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter Фильтр названия Search in files Искать в файлах Projects: Проекты: Click star to bookmark current dir Добавить каталог в закладки Remove project Удалить проект Change project name Переименовать проект Add path to Python path Добавить путь в Python path Project name Имя проекта New project name: Новое имя проекта: Match case Учитывать регистр RegExp RegExp Search in subdirs Искать в подкаталогах Unstar this directory Удалить из закладок Star this directory В закладки Open Открыть Reveal in Finder Показать в Finder Copy path Копировать путь Rename Переименовать Delete Удалить Create new file Создать новый файл Create new directory Создать новый каталог Give the new name for the file Новое имя файла Give the name for the new directory Новое имя каталога Duplicate Дублировать Give the name for the new file Имя файла Are you sure that you want to delete Вы собираетесь удалить Go to this directory in the current shell Run as script Run Jupyter notebook Import data... Open outside Pyzo importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu File Файл Edit Правка View Вид Settings Настройки Shell Оболочка Run Запуск Tools Инструменты Help Помощь Use tabs Использовать табуляцию Use spaces Использовать пробелы spaces plural of spacebar character пробела(ов) Indentation ::: The indentation used of the current file. Отступы ::: Отступы в текущем файле. Syntax parser ::: The syntax parser of the current file. Синтаксис ::: Синтаксис текущего файла. Line endings ::: The line ending character of the current file. Конец строки ::: Символ конца строки текущего файла. Encoding ::: The character encoding of the current file. Кодировка ::: Кодировка текущего файла. New ::: Create a new (or temporary) file. Новый ::: Создать новый (или временный) файл. Open... ::: Open an existing file from disk. Открыть... ::: Открыть файл с диска. Save ::: Save the current file to disk. Сохранить ::: Сохранить текущий файл на диск. Save as... ::: Save the current file under another name. Сохранить как... ::: Сохранить текущий файл под другим именем. Save all ::: Save all open files. Сохранить все ::: Сохранить все открытые файлы. Close ::: Close the current file. Закрыть ::: Закрыть текущий файл. Close all ::: Close all files. Закрыть все ::: Закрыть все файлы. Undo ::: Undo the latest editing action. Отмена ::: Отменить последнюю правку. Redo ::: Redo the last undone editong action. Повтор ::: Повторить последнюю правку. Cut ::: Cut the selected text. Вырезать ::: Вырезать выделенный текст. Copy ::: Copy the selected text to the clipboard. Копировать ::: Копировать выделенный текст в буфер обмена. Paste ::: Paste the text that is now on the clipboard. Вставить ::: Вставить текст из буфера обмена. Select all ::: Select all text. Выделить все ::: Выделить весь текст. Indent ::: Indent the selected line. Отступ ::: Добавить отступ выбранной строке. Dedent ::: Unindent the selected line. Конец отступа ::: Убрать отступ у выбранной строки. Comment ::: Comment the selected line. Закомментировать ::: Закомментировать выбранную строку. Uncomment ::: Uncomment the selected line. Раскомментировать ::: Раскомментировать выбранную строку. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Выравнивание комментария/строки документации::: Выравнивание выделенного текста до 70 символов на строку. Go to line ::: Go to a specific line number. Перейти к строке ::: Перейти к строке по номеру. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Искать/Заменить ::: Открывает виджет поиска/замены для выделенного текста. Find selection ::: Find the next occurrence of the selected text. Искать выделенное далее ::: Искать далее выделенный текст. Find selection backward ::: Find the previous occurrence of the selected text. Искать выделенное ранее ::: Искать ранее выделенный текст. Find next ::: Find the next occurrence of the search string. Искать далее ::: Искать следующее совпадение. Find previous ::: Find the previous occurrence of the search string. Искать ранее ::: Искать предыдущее совпадание. Accept autocompletion with: Zoom in Увеличить Zoom out Уменьшить Zoom reset Сбросить Location of long line indicator ::: The location of the long-line-indicator. Длина индикатора длинной строки ::: Расположение индикатора длинной строки. Qt theme ::: The styling of the user interface widgets. Qt тема ::: Стиль оформления пользовательского интерфейса. Select shell ::: Focus the cursor on the current shell. Выбрать оболочку ::: Отправляет курсор в текущую оболочку. Select editor ::: Focus the cursor on the current editor. Выбрать редактор ::: Отправляет курсор в текущий редактор. Select previous file ::: Select the previously selected file. Выбрать предыдущий файл ::: Выбрать предыдущий файл. Show whitespace ::: Show spaces and tabs. Показывать пробелы ::: Показывать пробелы и табуляции. Show line endings ::: Show the end of each line. Показывать конец строки ::: Показывать символ конца строки. Show indentation guides ::: Show vertical lines to indicate indentation. Показывать линию отступа ::: Показывать вертикальную линию, индикатор отступа. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Переносить длинные строки ::: Перенос строк, которые не помещаются на экране (т.е. негоризонтальная прокрутка). Highlight current line ::: Highlight the line where the cursor is. Подсвечивать выбранную линию ::: Подсвечивать линию с курсором. Font Шрифт Zooming Масштаб Clear screen ::: Clear the screen. Очистить экран ::: Очистить экран. Interrupt ::: Interrupt the current running code (does not work for extension code). Прервать ::: Прервать выполнение работающего кода (не работает для расширений). Restart ::: Terminate and restart the interpreter. Перезапустить ::: Принудительно завершить и перезапустить интерпретатор. Terminate ::: Terminate the interpreter, leaving the shell open. Завершить ::: Принудительно завершить интерпретатор, но оставить оболочку открытой. Close ::: Terminate the interpreter and close the shell. Закрыть ::: Принудительно завершить интерпретатор и закрыть оболочку. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Настройки оболочки ::: Добавить новые конфигурации оболочки и редактировать свойства интерпретатора. New shell ... ::: Create new shell to run code in. Новая оболочка ... ::: Создать новую оболочку для запуска кода. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Запустить выделенное ::: Запустить выделенные строки, выделенные слова в строке, или выбранную линию, если нет выделения. Close others::: Close all files but this one. Закрыть остальное::: Закрыть все файлы, кроме этого. Rename ::: Rename this file. Переименовать ::: Переименовать этот файл. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Закрепить/Открепить ::: Закрепленный файл не так просто закрыть. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Установить/Сбросить как ГЛАВНЫЙ файл ::: Главный файл может быть запущен, пока выбран другой файл. Run file ::: Run the code in this file. Запустить файл ::: Запустить код в этом файле. Run file as script ::: Run this file as a script (restarts the interpreter). Запустить файл как скрипт ::: Запустить этот файл как сценарий (перезапускает интерпретатор). Reload tools ::: For people who develop tools. Перезапустить инструменты ::: Для разработчиков инструментов. Ask a question ::: Need help? Задать вопрос ::: Нужна помощь? Automatically indent ::: Indent when pressing enter after a colon. Автоматический отступ ::: Автоматический отступ после двоеточия и нажатия Enter. Enable calltips ::: Show calltips with function signatures. Включить подсказки ::: Показывать подсказку сигнатуры функции при ее вводе. Autocomplete keywords ::: The autocompletion list includes keywords. Дополнять ключевые слова ::: Автоматически дополнять ключевые слова. Edit key mappings... ::: Edit the shortcuts for menu items. Горячие клавиши... ::: Редактировать горячие клавиши для элементов меню. Edit syntax styles... ::: Change the coloring of your code. Стиль оформления... ::: Изменить цветовую схему кода. Debug next: proceed until next line Отладка: на следующую строку Debug return: proceed until returns Отладка: шаг назад Debug continue: proceed to next breakpoint Отладка: к следующему брейкпоинту Stop debugging Остановить отладку Clear all {} breakpoints Снять все брейкпоинты {} Postmortem: debug from last traceback Отладка после падения: с последней трассировки Export to PDF ::: Export current file to PDF (e.g. for printing). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Goto Definition ::: Go to definition of word under cursor. Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. None Debug step into: proceed one step Create new Python environment... ::: Install miniconda. Create shell %s: (%s) Run file as script ::: Restart and run the current file as a script. Run main file as script ::: Restart and run the main file as a script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Execute cell ::: Execute the current editors's cell in the current shell. Execute file ::: Execute the current file in the current shell. Execute main file ::: Execute the main file in the current shell. Execute selection and advance Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Help on running code ::: Open the pyzo wizard at the page about running code. (as script) No autocompletion Automatic popup Only show popup when pressing Tab Autocompletion Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run Не удалось запустить Could not run script. Не удалось запустить сценарий. Check for the latest version. Проверка наличия новой версии. Edit syntax styling Редактировать стиль оформления Advanced settings Продвинутые настройки Language changed Язык изменен Edit shortcut mapping Редактировать горячие клавиши Shortcut mappings Горячие клавиши About Pyzo Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Скрыть поисковый виджет (Escape) Find pattern Шаблон поиска Previous ::: Find previous occurrence of the pattern. Ранее ::: Найти предыдущий результат. Next ::: Find next occurrence of the pattern. Далее ::: Найти следующий результат. Replace pattern Шаблон замены Repl. all ::: Replace all matches in current document. Зам. все ::: Заменить все совпадения в текущем документе. Replace ::: Replace this match. Заменить ::: Заменить это совпадение. Match case ::: Find words that match case. Учитывать регистр ::: Найти слова с учетом регистра. RegExp ::: Find using regular expressions. RegExp ::: Найти с использованием регулярного выражения. Whole words ::: Find only whole words. Целые слова ::: Найти только целые слова. Auto hide ::: Hide search/replace when unused for 10 s. Автоматически скрывать ::: Скрывать поиск/замену при простаивании после 10 секунд. shell Use system default Значение в системе name ::: The name of this configuration. name ::: Название этой конфигурации. exe ::: The Python executable. exe ::: Исполняемый файл Python. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: GUI инструментарий для интеграции (для интерактивного построения и т.д.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath ::: Список каталогов для поиска пакетов и модулей. Указывайте каждый путь на новой строке, или с разделителями в этой системе по умолчанию. startupScript ::: The script to run at startup (not in script mode). startupScript ::: Скрипт, выполняемый при запуске (не в режиме сценария). startDir ::: The start directory (not in script mode). startDir ::: Стартовый каталог (не в режиме сценария). Delete ::: Delete this shell configuration Удалить ::: Удалить эту конфигурацию Shell configurations Конфигурации оболочки Add config Добавить ipython ::: Use IPython shell if available. File to run at startup Code to run at startup argv ::: The command line arguments (sys.argv). environ ::: Extra environment variables (os.environ). shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Step Шаг Welcome to the Interactive Editor for Python! Добро пожаловать в интерактивный редактор для Python! Select language Выберите язык Language changed Язык изменен You can execute commands directly in the *shell*, Вы можете выполнять команды непосредственно в *оболочке*, or you can write code in the *editor* and execute that. или ввести их в *редактор* кода и запустить. The editor is where you write your code Редактор, где вы пишете код In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. Каждый открытый файл в *редакторе* выглядит как вкладка. Правым щелчком мыши на вкладке файл может быть запущен, сохранен, закрыт и т.д. The shell is where your code gets executed Оболочка где выполняется ваш код Configuring shells Конфигурация оболочек Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Нажав 'Оболочка > Настройки оболочки', вы можете редактировать *конфигурации оболочки*. Это позволяет выбрать исходный каталог, или указать пользовательский Pythonpath. Running code Запуск кода *Run cell:* a cell is everything between two lines starting with '##'. *Запустить ячейку:* ячейкой является весь код между двумя строками '##'. *Run file:* run all the code in the current file. *Запустить файл:* запускает весь код в текущем файле. *Run project main file:* run the code in the current project's main file. *Запустить главный файл:* запускает код в текущем главном файле проекта. Interactive mode vs running as script Интерактивный режим или запустить как сценарий In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. В интерактивном режиме, sys.path[0] - пустая строка (т.е. текущая директория), и sys.argv имеет значение ['']. Tools for your convenience Инструменты для вашего удобства Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Через меню *Инструменты*, можно выбрать, какие инструменты будут использоваться. Инструменты могут быть закреплены в любом месте, или откреплены вовсе. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Система инструментов разработана так, что можно легко создать свой собственный инструмент. Посетите вики для получения дополнительной информации, или используйте один из существующих инструментов как пример. Recommended tools Рекомендуемые инструменты We especially recommend the following tools: Мы настоятельно рекомендуем следующие инструменты: The *Source structure tool* gives an outline of the source code. Инструмент *Source structure* дает вам схему исходного кода. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. Инструмент *File browser* помогает следить за всеми файлами в каталоге. Для управления своим проектом нажмите на иконку звезды. Get coding! Программируйте! Getting started with Pyzo This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_ru_RU.tr.qm0000666000000000000000000006350113156166667021537 0ustar 00000000000000 | ХVoET)e:f@';kp^<)Zc>D]C2>E͎(SV@,F[?nuDp}.7q^'}HuC>~]K߱W5h3`HcK1mc'r>Eh<ERE. tBX8N)VJ .sL9@,|I~Ock.Ba!F>$S&En93 %SnGa@>Svh+n/njHH5ls  R> N'#FCI<5L%Y _p;J 6E45V|/eV>FVS3V~' [[/%C8na[y;"Ajj.,N3A/%Yf?;j^  ! 4 Ix wS9 5 ר"O @ .M BO J=h H F&w lL- > 3[ eq tX n6R T"  0n+ t "0 5 s2 )DZ ˳$N nM i i 0_ GnJ MNA7 V@ W SN!_4[>K=\P>8:~2 _< (uJ:/V=~ߔ;8YEFގ->DFL. ib!B5:Stackdebug6>1028BL ?CBL 2 Python pathAdd path to Python path filebrowser,K A>18@05B5AL C40;8BL$Are you sure that you want to delete filebrowser(5@58<5=>20BL ?@>5:BChange project name filebrowser6>1028BL :0B0;>3 2 70:;04:8"Click star to bookmark current dir filebrowser>?8@>20BL ?CBL Copy path filebrowser*!>740BL =>2K9 :0B0;>3Create new directory filebrowser$!>740BL =>2K9 D09;Create new file filebrowser#40;8BLDelete filebrowserC1;8@>20BL Duplicate filebrowser$8;LB@ =0720=8OFilename filter filebrowser$>2>5 8<O :0B0;>30#Give the name for the new directory filebrowser<O D09;0Give the name for the new file filebrowser>2>5 8<O D09;0Give the new name for the file filebrowser"#G8BK20BL @538AB@ Match case filebrowser$>2>5 8<O ?@>5:B0:New project name: filebrowserB:@KBLOpen filebrowser<O ?@>5:B0 Project name filebrowser@>5:BK: Projects: filebrowser RegExpRegExp filebrowser#40;8BL ?@>5:BRemove project filebrowser5@58<5=>20BLRename filebrowser">:070BL 2 FinderReveal in Finder filebrowserA:0BL 2 D09;0ESearch in files filebrowser*A:0BL 2 ?>4:0B0;>30ESearch in subdirs filebrowser 70:;04:8Star this directory filebrowser&#40;8BL 87 70:;04>:Unstar this directory filebrowser>040BL 2>?@>A ::: C6=0 ?><>IL?Ask a question ::: Need help?menu>?>;=OBL :;NG52K5 A;>20 ::: 2B><0B8G5A:8 4>?>;=OBL :;NG52K5 A;>20.DAutocomplete keywords ::: The autocompletion list includes keywords.menu2B><0B8G5A:89 >BABC? ::: 2B><0B8G5A:89 >BABC? ?>A;5 42>5B>G8O 8 =060B8O Enter.BAutomatically indent ::: Indent when pressing enter after a colon.menu0!=OBL 2A5 1@59:?>8=BK {}Clear all {} breakpointsmenuDG8AB8BL M:@0= ::: G8AB8BL M:@0=."Clear screen ::: Clear the screen.menuB0:@KBL ::: 0:@KBL B5:CI89 D09;.!Close ::: Close the current file.menu0:@KBL ::: @8=C48B5;L=> 7025@H8BL 8=B5@?@5B0B>@ 8 70:@KBL >1>;>G:C.8Close ::: Terminate the interpreter and close the shell.menuD0:@KBL 2A5 ::: 0:@KBL 2A5 D09;K.Close all ::: Close all files.menuh0:@KBL >AB0;L=>5::: 0:@KBL 2A5 D09;K, :@><5 MB>3>.-Close others::: Close all files but this one.menun0:><<5=B8@>20BL ::: 0:><<5=B8@>20BL 2K1@0==CN AB@>:C.&Comment ::: Comment the selected line.menut>?8@>20BL ::: >?8@>20BL 2K45;5==K9 B5:AB 2 1CD5@ >1<5=0.1Copy ::: Copy the selected text to the clipboard.menuNK@570BL ::: K@570BL 2K45;5==K9 B5:AB.Cut ::: Cut the selected text.menuBB;04:0: : A;54CNI5<C 1@59:?>8=BC*Debug continue: proceed to next breakpointmenu8B;04:0: =0 A;54CNICN AB@>:C#Debug next: proceed until next linemenu$B;04:0: H03 =0704#Debug return: proceed until returnsmenuf>=5F >BABC?0 ::: #1@0BL >BABC? C 2K1@0==>9 AB@>:8.&Dedent ::: Unindent the selected line.menu @02:0Editmenu>@OG85 :;028H8... :::  540:B8@>20BL 3>@OG85 :;028H8 4;O M;5<5=B>2 <5=N.;Edit key mappings... ::: Edit the shortcuts for menu items.menu0AB@>9:8 >1>;>G:8 ::: >1028BL =>2K5 :>=D83C@0F88 >1>;>G:8 8 @540:B8@>20BL A2>9AB20 8=B5@?@5B0B>@0.WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menuj!B8;L >D>@<;5=8O... ::: 7<5=8BL F25B>2CN AE5<C :>40.;Edit syntax styles... ::: Change the coloring of your code.menu:;NG8BL ?>4A:07:8 ::: >:07K20BL ?>4A:07:C A83=0BC@K DC=:F88 ?@8 55 22>45.;Enable calltips ::: Show calltips with function signatures.menuN>48@>2:0 ::: >48@>2:0 B5:CI53> D09;0.8Encoding ::: The character encoding of the current file.menu$09;FilemenuZA:0BL 40;55 ::: A:0BL A;54CNI55 A>2?045=85.8A:0/70<5=K 4;O 2K45;5==>3> B5:AB0.LFind or replace ::: Show find/replace widget. Initialize with selected text.menu\A:0BL @0=55 ::: A:0BL ?@54K4CI55 A>2?040=85.DFind previous ::: Find the previous occurrence of the search string.menutA:0BL 2K45;5==>5 40;55 ::: A:0BL 40;55 2K45;5==K9 B5:AB.AFind selection ::: Find the next occurrence of the selected text.menutA:0BL 2K45;5==>5 @0=55 ::: A:0BL @0=55 2K45;5==K9 B5:AB.NFind selection backward ::: Find the previous occurrence of the selected text.menu (@8DBFontmenu`5@59B8 : AB@>:5 ::: 5@59B8 : AB@>:5 ?> =><5@C.,Go to line ::: Go to a specific line number.menu ><>ILHelpmenu~>4A25G820BL 2K1@0==CN ;8=8N ::: >4A25G820BL ;8=8N A :C@A>@><.BHighlight current line ::: Highlight the line where the cursor is.menuXBABC? ::: >1028BL >BABC? 2K1@0==>9 AB@>:5.$Indent ::: Indent the selected line.menuHBABC?K ::: BABC?K 2 B5:CI5< D09;5.9Indentation ::: The indentation used of the current file.menu@5@20BL ::: @5@20BL 2K?>;=5=85 @01>B0NI53> :>40 (=5 @01>B05B 4;O @0AH8@5=89).TInterrupt ::: Interrupt the current running code (does not work for extension code).menuK@02=820=85 :><<5=B0@8O/AB@>:8 4>:C<5=B0F88::: K@02=820=85 2K45;5==>3> B5:AB0 4> 70 A8<2>;>2 =0 AB@>:C.`Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters.menuh>=5F AB@>:8 ::: !8<2>; :>=F0 AB@>:8 B5:CI53> D09;0.?Line endings ::: The line ending character of the current file.menu;8=0 8=48:0B>@0 4;8==>9 AB@>:8 :::  0A?>;>65=85 8=48:0B>@0 4;8==>9 AB@>:8.LLocation of long line indicator ::: The location of the long-line-indicator.menuZ>2K9 ::: !>740BL =>2K9 (8;8 2@5<5==K9) D09;.)New ::: Create a new (or temporary) file.menu~>20O >1>;>G:0 ... ::: !>740BL =>2CN >1>;>G:C 4;O 70?CA:0 :>40.2New shell ... ::: Create new shell to run code in.menuHB:@KBL... ::: B:@KBL D09; A 48A:0.,Open... ::: Open an existing file from disk.menuZAB028BL ::: AB028BL B5:AB 87 1CD5@0 >1<5=0.6Paste ::: Paste the text that is now on the clipboard.menu0:@5?8BL/B:@5?8BL ::: 0:@5?;5==K9 D09; =5 B0: ?@>AB> 70:@KBL.2Pin/Unpin ::: Pinned files get closed less easily.menu\B;04:0 ?>A;5 ?045=8O: A ?>A;54=59 B@0AA8@>2:8%Postmortem: debug from last tracebackmenutQt B5<0 ::: !B8;L >D>@<;5=8O ?>;L7>20B5;LA:>3> 8=B5@D59A0.7Qt theme ::: The styling of the user interface widgets.menuL>2B>@ ::: >2B>@8BL ?>A;54=NN ?@02:C.-Redo ::: Redo the last undone editong action.menuz5@570?CAB8BL 8=AB@C<5=BK ::: ;O @07@01>BG8:>2 8=AB@C<5=B>2..Reload tools ::: For people who develop tools.menuT5@58<5=>20BL ::: 5@58<5=>20BL MB>B D09;.Rename ::: Rename this file.menu5@570?CAB8BL ::: @8=C48B5;L=> 7025@H8BL 8 ?5@570?CAB8BL 8=B5@?@5B0B>@.2Restart ::: Terminate and restart the interpreter.menu 0?CA:Runmenu\0?CAB8BL D09; ::: 0?CAB8BL :>4 2 MB>< D09;5.'Run file ::: Run the code in this file.menu0?CAB8BL D09; :0: A:@8?B ::: 0?CAB8BL MB>B D09; :0: AF5=0@89 (?5@570?CA:05B 8=B5@?@5B0B>@).LRun file as script ::: Run this file as a script (restarts the interpreter).menu0?CAB8BL 2K45;5==>5 ::: 0?CAB8BL 2K45;5==K5 AB@>:8, 2K45;5==K5 A;>20 2 AB@>:5, 8;8 2K1@0==CN ;8=8N, 5A;8 =5B 2K45;5=8O.Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection.menuZ!>E@0=8BL ::: !>E@0=8BL B5:CI89 D09; =0 48A:.'Save ::: Save the current file to disk.menu^!>E@0=8BL 2A5 ::: !>E@0=8BL 2A5 >B:@KBK5 D09;K.!Save all ::: Save all open files.menu|!>E@0=8BL :0:... ::: !>E@0=8BL B5:CI89 D09; ?>4 4@C38< 8<5=5<.8Save as... ::: Save the current file under another name.menuJK45;8BL 2A5 ::: K45;8BL 25AL B5:AB.Select all ::: Select all text.menutK1@0BL @540:B>@ ::: B?@02;O5B :C@A>@ 2 B5:CI89 @540:B>@.9Select editor ::: Focus the cursor on the current editor.menuhK1@0BL ?@54K4CI89 D09; ::: K1@0BL ?@54K4CI89 D09;.=Select previous file ::: Select the previously selected file.menutK1@0BL >1>;>G:C ::: B?@02;O5B :C@A>@ 2 B5:CICN >1>;>G:C.7Select shell ::: Focus the cursor on the current shell.menu#AB0=>28BL/!1@>A8BL :0: + D09; ::: ;02=K9 D09; <>65B 1KBL 70?CI5=, ?>:0 2K1@0= 4@C3>9 D09;.SSet/Unset as MAIN file ::: The main file can be run while another file is selected.menu0AB@>9:8Settingsmenu1>;>G:0Shellmenu>:07K20BL ;8=8N >BABC?0 ::: >:07K20BL 25@B8:0;L=CN ;8=8N, 8=48:0B>@ >BABC?0.HShow indentation guides ::: Show vertical lines to indicate indentation.menuv>:07K20BL :>=5F AB@>:8 ::: >:07K20BL A8<2>; :>=F0 AB@>:8.0Show line endings ::: Show the end of each line.menul>:07K20BL ?@>15;K ::: >:07K20BL ?@>15;K 8 B01C;OF88.)Show whitespace ::: Show spaces and tabs.menu$AB0=>28BL >B;04:CStop debuggingmenuN!8=B0:A8A ::: !8=B0:A8A B5:CI53> D09;0.8Syntax parser ::: The syntax parser of the current file.menu025@H8BL ::: @8=C48B5;L=> 7025@H8BL 8=B5@?@5B0B>@, => >AB028BL >1>;>G:C >B:@KB>9.@Terminate ::: Terminate the interpreter, leaving the shell open.menu=AB@C<5=BKToolsmenur 0A:><<5=B8@>20BL :::  0A:><<5=B8@>20BL 2K1@0==CN AB@>:C.*Uncomment ::: Uncomment the selected line.menuJB<5=0 ::: B<5=8BL ?>A;54=NN ?@02:C.(Undo ::: Undo the latest editing action.menu(A?>;L7>20BL ?@>15;K Use spacesmenu,A?>;L7>20BL B01C;OF8NUse tabsmenu84Viewmenu5@5=>A8BL 4;8==K5 AB@>:8 ::: 5@5=>A AB@>:, :>B>@K5 =5 ?><5I0NBAO =0 M:@0=5 (B.5. =53>@87>=B0;L=0O ?@>:@CB:0).\Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling).menu#25;8G8BLZoom inmenu#<5=LH8BLZoom outmenu!1@>A8BL Zoom resetmenu0AHB01Zoomingmenu?@>15;0(>2)spacesmenu*@>428=CBK5 =0AB@>9:8Advanced settings menu dialog<@>25@:0 =0;8G8O =>2>9 25@A88.Check for the latest version. menu dialog(5 C40;>AL 70?CAB8BL Could not run menu dialog<5 C40;>AL 70?CAB8BL AF5=0@89.Could not run script. menu dialog: 540:B8@>20BL 3>@OG85 :;028H8Edit shortcut mapping menu dialog< 540:B8@>20BL AB8;L >D>@<;5=8OEdit syntax styling menu dialog/7K: 87<5=5=Language changed menu dialog>@OG85 :;028H8Shortcut mappings menu dialog2B><0B8G5A:8 A:@K20BL ::: !:@K20BL ?>8A:/70<5=C ?@8 ?@>AB0820=88 ?>A;5 10 A5:C=4.7Auto hide ::: Hide search/replace when unused for 10 s.search(01;>= ?>8A:0 Find patternsearch@!:@KBL ?>8A:>2K9 28465B (Escape)Hide search widget (Escape)searchh#G8BK20BL @538AB@ ::: 09B8 A;>20 A CG5B>< @538AB@0.*Match case ::: Find words that match case.searchH0;55 ::: 09B8 A;54CNI89 @57C;LB0B.-Next ::: Find next occurrence of the pattern.searchJ 0=55 ::: 09B8 ?@54K4CI89 @57C;LB0B.5Previous ::: Find previous occurrence of the pattern.searchpRegExp ::: 09B8 A 8A?>;L7>20=85< @53C;O@=>3> 2K@065=8O.*RegExp ::: Find using regular expressions.searchr0<. 2A5 ::: 0<5=8BL 2A5 A>2?045=8O 2 B5:CI5< 4>:C<5=B5.6Repl. all ::: Replace all matches in current document.searchJ0<5=8BL ::: 0<5=8BL MB> A>2?045=85.Replace ::: Replace this match.search(01;>= 70<5=KReplace patternsearchR&5;K5 A;>20 ::: 09B8 B>;L:> F5;K5 A;>20.&Whole words ::: Find only whole words.search>1028BL Add configshellH#40;8BL ::: #40;8BL MBC :>=D83C@0F8N*Delete ::: Delete this shell configurationshell*>=D83C@0F88 >1>;>G:8Shell configurationsshell$=0G5=85 2 A8AB5<5Use system defaultshell@exe ::: A?>;=O5<K9 D09; Python.exe ::: The Python executable.shellgui ::: GUI 8=AB@C<5=B0@89 4;O 8=B53@0F88 (4;O 8=B5@0:B82=>3> ?>AB@>5=8O 8 B.4.).Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shellHname ::: 0720=85 MB>9 :>=D83C@0F88.(name ::: The name of this configuration.shell,pythonPath ::: !?8A>: :0B0;>3>2 4;O ?>8A:0 ?0:5B>2 8 <>4C;59. #:07K209B5 :064K9 ?CBL =0 =>2>9 AB@>:5, 8;8 A @0745;8B5;O<8 2 MB>9 A8AB5<5 ?> C<>;G0=8N.pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shelllstartDir ::: !B0@B>2K9 :0B0;>3 (=5 2 @568<5 AF5=0@8O).6startDir ::: The start directory (not in script mode).shellstartupScript ::: !:@8?B, 2K?>;=O5<K9 ?@8 70?CA:5 (=5 2 @568<5 AF5=0@8O).DstartupScript ::: The script to run at startup (not in script mode).shell*0?CAB8BL OG59:C:* OG59:>9 O2;O5BAO 25AL :>4 <564C 42C<O AB@>:0<8 '##'.F*Run cell:* a cell is everything between two lines starting with '##'.wizardj*0?CAB8BL D09;:* 70?CA:05B 25AL :>4 2 B5:CI5< D09;5.1*Run file:* run all the code in the current file.wizard*0?CAB8BL 3;02=K9 D09;:* 70?CA:05B :>4 2 B5:CI5< 3;02=>< D09;5 ?@>5:B0.I*Run project main file:* run the code in the current project's main file.wizard*>=D83C@0F8O >1>;>G5:Configuring shellswizard@>3@0<<8@C9B5! Get coding!wizard 8=B5@0:B82=>< @568<5, sys.path[0] - ?CAB0O AB@>:0 (B.5. B5:CI0O 48@5:B>@8O), 8 sys.argv 8<55B 7=0G5=85 [''].pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizard 064K9 >B:@KBK9 D09; 2 *@540:B>@5* 2K3;O48B :0: 2:;04:0. @02K< I5;G:>< <KH8 =0 2:;04:5 D09; <>65B 1KBL 70?CI5=, A>E@0=5=, 70:@KB 8 B.4.In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizard\=B5@0:B82=K9 @568< 8;8 70?CAB8BL :0: AF5=0@89%Interactive mode vs running as scriptwizard/7K: 87<5=5=Language changedwizard!8AB5<0 8=AB@C<5=B>2 @07@01>B0=0 B0:, GB> <>6=> ;53:> A>740BL A2>9 A>1AB25==K9 8=AB@C<5=B. >A5B8B5 28:8 4;O ?>;CG5=8O 4>?>;=8B5;L=>9 8=D>@<0F88, 8;8 8A?>;L7C9B5 >48= 87 ACI5AB2CNI8E 8=AB@C<5=B>2 :0: ?@8<5@.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizard2 5:><5=4C5<K5 8=AB@C<5=BKRecommended toolswizard0?CA: :>40 Running codewizardK15@8B5 O7K:Select languagewizard(03Stepwizard=AB@C<5=B *File browser* ?><>305B A;548BL 70 2A5<8 D09;0<8 2 :0B0;>35. ;O C?@02;5=8O A2>8< ?@>5:B>< =06<8B5 =0 8:>=:C 72574K.The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizardx=AB@C<5=B *Source structure* 405B 20< AE5<C 8AE>4=>3> :>40.@The *Source structure tool* gives an outline of the source code.wizard6 540:B>@, 345 2K ?8H5B5 :>4'The editor is where you write your codewizard@1>;>G:0 345 2K?>;=O5BAO 20H :>4*The shell is where your code gets executedwizard>=AB@C<5=BK 4;O 20H53> C4>1AB20Tools for your conveniencewizardp0602 '1>;>G:0 > 0AB@>9:8 >1>;>G:8', 2K <>65B5 @540:B8@>20BL *:>=D83C@0F88 >1>;>G:8*. -B> ?>72>;O5B 2K1@0BL 8AE>4=K9 :0B0;>3, 8;8 C:070BL ?>;L7>20B5;LA:89 Pythonpath.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardN'5@57 <5=N *=AB@C<5=BK*, <>6=> 2K1@0BL, :0:85 8=AB@C<5=BK 1C4CB 8A?>;L7>20BLAO. =AB@C<5=BK <>3CB 1KBL 70:@5?;5=K 2 ;N1>< <5AB5, 8;8 >B:@5?;5=K 2>2A5.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizarddK =0AB>OB5;L=> @5:><5=4C5< A;54CNI85 8=AB@C<5=BK:,We especially recommend the following tools:wizardj>1@> ?>60;>20BL 2 8=B5@0:B82=K9 @540:B>@ 4;O Python!-Welcome to the Interactive Editor for Python!wizardrK <>65B5 2K?>;=OBL :><0=4K =5?>A@54AB25==> 2 *>1>;>G:5*,1You can execute commands directly in the *shell*,wizardX8;8 225AB8 8E 2 *@540:B>@* :>40 8 70?CAB8BL.7or you can write code in the *editor* and execute that.wizard ) , pyzo-4.4.3/pyzo/resources/translations/pyzo_sk_SK.tr0000666000000000000000000017305213156166663021100 0ustar 00000000000000 debug Stack editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter Search in files Projects: Click star to bookmark current dir Remove project Change project name Add path to Python path Go to this directory in the current shell Project name New project name: Match case RegExp Search in subdirs Unstar this directory Star this directory Open Run as script Run Jupyter notebook Import data... Open outside Pyzo Reveal in Finder Copy path Rename Delete Create new file Create new directory Give the new name for the file Give the name for the new directory Duplicate Give the name for the new file Are you sure that you want to delete importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu spaces plural of spacebar character Indentation ::: The indentation used of the current file. Syntax parser ::: The syntax parser of the current file. Line endings ::: The line ending character of the current file. Encoding ::: The character encoding of the current file. Open... ::: Open an existing file from disk. Save ::: Save the current file to disk. Save as... ::: Save the current file under another name. Save all ::: Save all open files. Close ::: Close the current file. Close all ::: Close all files. Undo ::: Undo the latest editing action. Redo ::: Redo the last undone editong action. Cut ::: Cut the selected text. Copy ::: Copy the selected text to the clipboard. Paste ::: Paste the text that is now on the clipboard. Select all ::: Select all text. Indent ::: Indent the selected line. Dedent ::: Unindent the selected line. Comment ::: Comment the selected line. Uncomment ::: Uncomment the selected line. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Go to line ::: Go to a specific line number. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. Find selection ::: Find the next occurrence of the selected text. Find selection backward ::: Find the previous occurrence of the selected text. Find next ::: Find the next occurrence of the search string. Find previous ::: Find the previous occurrence of the search string. Location of long line indicator ::: The location of the long-line-indicator. Qt theme ::: The styling of the user interface widgets. Select shell ::: Focus the cursor on the current shell. Select editor ::: Focus the cursor on the current editor. Select previous file ::: Select the previously selected file. Show whitespace ::: Show spaces and tabs. Show line endings ::: Show the end of each line. Show indentation guides ::: Show vertical lines to indicate indentation. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). Highlight current line ::: Highlight the line where the cursor is. Font Zooming Close others::: Close all files but this one. Rename ::: Rename this file. Copy path ::: Copy the full path of this file. Pin/Unpin ::: Pinned files get closed less easily. Set/Unset as MAIN file ::: The main file can be run while another file is selected. Run file ::: Run the code in this file. Run file as script ::: Run this file as a script (restarts the interpreter). Ask a question ::: Need help? Accept autocompletion with: Export to PDF ::: Export current file to PDF (e.g. for printing). Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. Previous cell ::: Go back to the previous cell. Next cell ::: Advance to the next cell. Previous object ::: Go back to the previous top-level structure. Next object ::: Advance to the next top-level structure. Goto Definition ::: Go to definition of word under cursor. Restart Pyzo ::: Restart the application. Quit Pyzo ::: Close the application. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. Select language ::: The language used by Pyzo. Pyzo website ::: Open the Pyzo website in your browser. Pyzo guide ::: Open the Pyzo guide in your browser. Local documentation ::: Documentation on Python and the Scipy Stack. Highlight brackets ::: Highlight matched and unmatched brackets. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. Pyzo wizard ::: Get started quickly. File Edit View Settings Shell Run Tools Help Use tabs Use spaces New ::: Create a new (or temporary) file. Zoom in Zoom out Zoom reset None Clear screen ::: Clear the screen. Interrupt ::: Interrupt the current running code (does not work for extension code). Restart ::: Terminate and restart the interpreter. Terminate ::: Terminate the interpreter, leaving the shell open. Close ::: Terminate the interpreter and close the shell. Debug next: proceed until next line Debug step into: proceed one step Debug return: proceed until returns Debug continue: proceed to next breakpoint Stop debugging Clear all {} breakpoints Postmortem: debug from last traceback Edit shell configurations... ::: Add new shell configs and edit interpreter properties. Create new Python environment... ::: Install miniconda. Create shell %s: (%s) New shell ... ::: Create new shell to run code in. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. Run file as script ::: Restart and run the current file as a script. Run main file as script ::: Restart and run the main file as a script. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. Execute cell ::: Execute the current editors's cell in the current shell. Execute file ::: Execute the current file in the current shell. Execute main file ::: Execute the main file in the current shell. Execute selection and advance Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. Help on running code ::: Open the pyzo wizard at the page about running code. (as script) Reload tools ::: For people who develop tools. No autocompletion Automatic popup Only show popup when pressing Tab Autocomplete keywords ::: The autocompletion list includes keywords. Automatically indent ::: Indent when pressing enter after a colon. Enable calltips ::: Show calltips with function signatures. Autocompletion Edit key mappings... ::: Edit the shortcuts for menu items. Edit syntax styles... ::: Change the coloring of your code. Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run Could not run script. Check for the latest version. Edit syntax styling Advanced settings Language changed About Pyzo Edit shortcut mapping Shortcut mappings Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) Find pattern Previous ::: Find previous occurrence of the pattern. Next ::: Find next occurrence of the pattern. Replace pattern Repl. all ::: Replace all matches in current document. Replace ::: Replace this match. Match case ::: Find words that match case. RegExp ::: Find using regular expressions. Whole words ::: Find only whole words. Auto hide ::: Hide search/replace when unused for 10 s. shell name ::: The name of this configuration. ipython ::: Use IPython shell if available. Use system default File to run at startup Code to run at startup exe ::: The Python executable. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. startupScript ::: The script to run at startup (not in script mode). startDir ::: The start directory (not in script mode). argv ::: The command line arguments (sys.argv). environ ::: Extra environment variables (os.environ). Delete ::: Delete this shell configuration Shell configurations Add config shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Language changed Getting started with Pyzo Step Welcome to the Interactive Editor for Python! This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' Select language The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components You can execute commands directly in the *shell*, or you can write code in the *editor* and execute that. The editor is where you write your code In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. The shell is where your code gets executed When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Configuring shells Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. Running code Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). *Run cell:* a cell is everything between two lines starting with '##'. *Run file:* run all the code in the current file. *Run project main file:* run the code in the current project's main file. Interactive mode vs running as script In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. Tools for your convenience Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Recommended tools We especially recommend the following tools: The *Source structure tool* gives an outline of the source code. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. Get coding! This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_sk_SK.tr.qm0000666000000000000000000000003313156166667021504 0ustar 00000000000000 debug Stack editor Close, Discard, Cancel, Save Closing Save modified file? Closing pinned Are you sure you want to close this pinned file? editorTabs Select one or more files to open Select the file to save to filebrowser Filename filter 文件名过滤器 Search in files 搜索文件 Projects: 工程: Click star to bookmark current dir 点击星号来把当前目录加到书签中 Remove project 移出项目 Change project name 改变项目名称 Add path to Python path 将路径加入Python路径 Project name 工程名 New project name: 新工程名: Match case 匹配条件 RegExp 正则表达式 Search in subdirs 在子文件夹中搜索 Unstar this directory 取消标注该目录 Star this directory 标注该目录 Open 打开 Reveal in Finder 在探测器中显示 Copy path 复制路径 Rename 重命名 Delete 删除 Create new file 创建新文件 Create new directory 创建新目录 Give the new name for the file 给当前文件重命名 Give the name for the new directory 给当前目录重命名 Duplicate 创建副本 Give the name for the new file 给新文件命名 Are you sure that you want to delete 你确定你要删除 Go to this directory in the current shell 在当前shell中跳转到这个目录 Import data... 引入数据... Run as script 作为脚本运行 Run Jupyter notebook Open outside Pyzo importwizard Select file Browse... Preview: Select columns to import: Close Import data as single array Import data into one variable per column Raise error upon invalid data Import data wizard No current shell active No current file open Import data The import data wizard is already open main unsaved menu File 文件 Edit 编辑 View 视图 Settings 设置 Shell Shell Run 运行 Tools 工具 Help 帮助 Use tabs 使用制表符(tabs) Use spaces 使用空格(spaces) spaces plural of spacebar character 空格 Indentation ::: The indentation used of the current file. 缩进 ::: 作用于当前文件的缩进格式. Syntax parser ::: The syntax parser of the current file. 语言 ::: 作用于当前文件的语法分析器类型. Line endings ::: The line ending character of the current file. 行结束字符 ::: 当前文件的结束字符. Encoding ::: The character encoding of the current file. 编码 ::: 当前文件的编码. New ::: Create a new (or temporary) file. 新建 ::: 创建一个新的文件. Open... ::: Open an existing file from disk. 打开... ::: 从磁盘中打开已存在的文件. Save ::: Save the current file to disk. 保存 ::: 将当前文件保存到磁盘中. Save as... ::: Save the current file under another name. 另存为 ::: 将当前文件用另一个文件名保存. Save all ::: Save all open files. 全部保存 ::: 保存所有打开的文件. Close ::: Close the current file. 关闭 ::: 关闭当前文件. Close all ::: Close all files. 全部关闭 ::: 关闭所有文件. Undo ::: Undo the latest editing action. 撤销 ::: 撤销最近一次编辑操作. Redo ::: Redo the last undone editong action. 恢复 ::: 撤销最近一次撤销操作. Cut ::: Cut the selected text. 剪切 ::: 剪切选中文本. Copy ::: Copy the selected text to the clipboard. 复制 ::: 复制选中文本到剪贴板. Paste ::: Paste the text that is now on the clipboard. 粘贴 ::: 粘贴剪切板中的文本. Select all ::: Select all text. 全选 ::: 选择全部文本. Indent ::: Indent the selected line. 缩进 ::: 当前行插入缩进. Dedent ::: Unindent the selected line. 删除缩进 ::: 删除当前行的缩进. Comment ::: Comment the selected line. 注释 ::: 给选中行加注释. Uncomment ::: Uncomment the selected line. 删除注释 ::: 删除选中行的注释. Go to line ::: Go to a specific line number. 跳转 ::: 跳转到特定行数. Toggle breakpoint ::: Toggle breakpoint on the current line. Find or replace ::: Show find/replace widget. Initialize with selected text. 查找或替换 ::: 以选定文本初始化显示查找/替换窗口. Find selection ::: Find the next occurrence of the selected text. 选定并向下查找 ::: 以选定文本为目标,查找下一个. Find selection backward ::: Find the previous occurrence of the selected text. 选定并向上查找 ::: 以选定文本为目标,查找上一个一个. Find next ::: Find the next occurrence of the search string. 查找下一个 ::: 查找下一个搜索字符串. Find previous ::: Find the previous occurrence of the search string. 查找上一个 ::: 查找上一个搜索字符串. Accept autocompletion with: Zoom in 放大 Zoom out 缩小 Zoom reset 撤销缩放 Location of long line indicator ::: The location of the long-line-indicator. 长行指示位置 ::: 长行指示器的位置. Qt theme ::: The styling of the user interface widgets. Qt主题 ::: UI窗口主题. Select shell ::: Focus the cursor on the current shell. 选中Shell ::: 将光标移到当前Shell中. Select editor ::: Focus the cursor on the current editor. 选中编辑器 ::: 将光标移动到当前编辑器. Select previous file ::: Select the previously selected file. 选中前一文件 ::: 选中上一选中文件. Show whitespace ::: Show spaces and tabs. 显示空白 ::: 显示空格或者制表符. Show line endings ::: Show the end of each line. 显示行末 ::: 显示每行的行末. Show indentation guides ::: Show vertical lines to indicate indentation. 显示缩进指示 ::: 显示垂直线来标示缩进. Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). 折叠长行 ::: 折叠那些无法再屏幕中显示的行(没有水平滚动条). Highlight current line ::: Highlight the line where the cursor is. 高亮当前行 ::: 高亮光标所在行. Highlight brackets ::: Highlight matched and unmatched brackets. 高亮括号 ::: 高亮匹配和不匹配的括号. Font 字体 Zooming 缩放 Close others::: Close all files but this one. 关闭其他文件::: 将该文件以外其他文件关闭. Copy path ::: Copy the full path of this file. Pyzo website ::: Open the Pyzo website in your browser. Pyzo官网::: 在浏览器中打开Pyzo官网. Pyzo guide ::: Open the Pyzo guide in your browser. Pyzo指南::: 在浏览器中打开Pyzo指南. Pyzo wizard ::: Get started quickly. Pyzo向导::: 快速开始. Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? 报告问题:::你找到了bug?或者想要一个新特性? Local documentation ::: Documentation on Python and the Scipy Stack. 本地文档 ::: 关于Python和Scipy Stack. Check for updates ::: Are you using the latest version? About Pyzo ::: More information about Pyzo. 关于Pyzo ::: 关于Pyzo的更多信息. Select language ::: The language used by Pyzo. 选择语言 ::: Pyzo使用的语言. Clear screen ::: Clear the screen. 清理屏幕 ::: 清理屏幕. Interrupt ::: Interrupt the current running code (does not work for extension code). 中断 ::: 中断当前运行代码(对扩展代码无效). Restart ::: Terminate and restart the interpreter. 重启 ::: 结束并重启解释器. Terminate ::: Terminate the interpreter, leaving the shell open. 结束 ::: 结束解释器,仍保持Shell开启. Close ::: Terminate the interpreter and close the shell. 关闭 ::: 结束解释器并关闭Shell. Edit shell configurations... ::: Add new shell configs and edit interpreter properties. 编辑Shell配置 ::: 添加新的Shell配置并编辑解释器属性. New shell ... ::: Create new shell to run code in. 新建Shell ::: 创建新的Shell在里面运行代码. Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. 运行选中代码 ::: 运行选中的代码. Rename ::: Rename this file. 重命名 ::: 重命名此文件. Pin/Unpin ::: Pinned files get closed less easily. 锁定/解锁 ::: 锁定的文件不容易被关闭. Set/Unset as MAIN file ::: The main file can be run while another file is selected. 设定/取消设定为MAIN文件 ::: 当其他文件被选中时,main文件仍能运行. Run file ::: Run the code in this file. 运行文件 ::: 运行此文件中的代码. Run file as script ::: Run this file as a script (restarts the interpreter). 将文件作为脚本运行 ::: 将此文件作为脚本运行(重启解释器). Reload tools ::: For people who develop tools. 重载工具 ::: 面向开发工具的人. Ask a question ::: Need help? 问问题 ::: 需要帮助? Automatically indent ::: Indent when pressing enter after a colon. 自动缩进 ::: 在冒号后回车自动缩进. Enable calltips ::: Show calltips with function signatures. 开启函数提示 ::: 显示函数提示. Autocomplete keywords ::: The autocompletion list includes keywords. 自动完成关键字 ::: 自动完成关键字列表. Edit key mappings... ::: Edit the shortcuts for menu items. 编辑快捷键映射 ::: 修改菜单的快捷键. Edit syntax styles... ::: Change the coloring of your code. 编辑语法风格 ::: 改变代码颜色. Debug next: proceed until next line 调试到下一行:执行到下一行 Debug return: proceed until returns 调试到return:执行到下一个return Debug continue: proceed to next breakpoint 调试到continue:执行到continue Stop debugging 停止调试 Clear all {} breakpoints 清除所用{}断点 Postmortem: debug from last traceback 算后检查:从上一个回溯中调试 Debug step into: proceed one step 调试一步:执行一步 Run file as script ::: Restart and run the current file as a script. 将文件作为脚本运行 ::: 重启并将该文件作为脚本运行. Run main file as script ::: Restart and run the main file as a script. 将main文件作为脚本运行 ::: 重启并将main文件作为脚本运行. Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. 执行选中 ::: 执行选中代码. Execute cell ::: Execute the current editors's cell in the current shell. 执行单元 ::: 在当前shell中执行当前编辑器里的单元. Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. 执行单元并前进 ::: 在当前shell中执行当前编辑器里的单元并前进到下一个单元. Execute file ::: Execute the current file in the current shell. 执行文件 ::: 在当前shell中执行当前的文件. Execute main file ::: Execute the main file in the current shell. 执行main文件 ::: 在当前shell中执行main文件. Export to PDF ::: Export current file to PDF (e.g. for printing). 导出到PDF ::: 导出当前文件到PDF. Restart Pyzo ::: Restart the application. 重启Pyzo ::: 重启整个程序. Quit Pyzo ::: Close the application. 退出Pyzo ::: 关闭程序. Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. 粘贴并选中 ::: 粘贴剪切板中文本,并保持选中,今儿来改变缩进. Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. 调整注释/文档字符串 ::: 调整选中文本以70个字符对齐. Previous cell ::: Go back to the previous cell. 上一单元 ::: 回到上一单元. Next cell ::: Advance to the next cell. 下一单元 ::: 前进到下一单元. Previous object ::: Go back to the previous top-level structure. 前一对象 ::: 回到前一个顶级架构. Next object ::: Advance to the next top-level structure. 后一对象 ::: 前进到后一个顶级架构. Goto Definition ::: Go to definition of word under cursor. 转到定义 ::: 跳转到当前光标所在词的定义. There is no main file selected. Could not save the file. Can only run scripts that are in the file system. None Create new Python environment... ::: Install miniconda. Create shell %s: (%s) Execute selection and advance Help on running code ::: Open the pyzo wizard at the page about running code. (as script) No autocompletion Automatic popup Only show popup when pressing Tab Autocompletion Advanced settings... ::: Configure Pyzo even further. More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. Search History Copy ::: Copy selected lines Run ::: Run selected lines in current shell Remove ::: Remove selected history items(s) Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run 无法运行 Could not run script. 无法运行脚本. Check for the latest version. 检查最新版本. Edit syntax styling 编辑语法风格 Advanced settings 高级设定 Language changed 语言改变 Edit shortcut mapping 修改快捷键映射 Shortcut mappings 快捷键映射 About Pyzo Could not run notebook The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) 隐藏搜索窗口 Find pattern 查找 Previous ::: Find previous occurrence of the pattern. 前一个 ::: 查找前一个出现. Next ::: Find next occurrence of the pattern. 后一个 ::: 查找后一个出现. Replace pattern 替换目标 Repl. all ::: Replace all matches in current document. 替换全部 ::: 替换当前文件中所有匹配的目标. Replace ::: Replace this match. 替换 ::: 替换当前匹配目标. Match case ::: Find words that match case. 匹配条件 ::: 查找匹配条件的语句. RegExp ::: Find using regular expressions. 正则表达式 ::: 使用正则表达式查找. Whole words ::: Find only whole words. 整词 ::: 查找整词. Auto hide ::: Hide search/replace when unused for 10 s. 自动隐藏 ::: 10秒后自动隐藏查找/替换窗口. shell Use system default 使用系统默认 name ::: The name of this configuration. 名称 ::: 当前配置名. exe ::: The Python executable. exe ::: Python执行文件. gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: 整合的GUI工具. pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. python路径 ::: modules和packages路径.每个路径写一行或者使用系统默认分隔符分隔. startupScript ::: The script to run at startup (not in script mode). 启动脚本 ::: 启动时运行的脚本. startDir ::: The start directory (not in script mode). 启动文件夹 ::: 启动文件夹. Delete ::: Delete this shell configuration 删除 ::: 删除当前shell配置 Shell configurations Shell 配置 Add config 添加配置 ipython ::: Use IPython shell if available. ipython ::: 如果可用则使用IPython shell. File to run at startup 启动时执行文件 Code to run at startup 启动时执行代码 argv ::: The command line arguments (sys.argv). 参数 ::: 命令行参数. environ ::: Extra environment variables (os.environ). 环境 ::: 附加的环境变量. shells Click to select shell. splash This is <b>Pyzo</b><br />the Python IDE for scientific computing Version Pyzo is open source software and freely available for everyone. wizard Step Welcome to the Interactive Editor for Python! 欢迎来到Python交互式编译器(IEP)! Select language 选择语言 Language changed 语言改变 You can execute commands directly in the *shell*, 你可以在shell里直接执行命令, or you can write code in the *editor* and execute that. 或者你也可以在编辑器里写代码然后执行. The editor is where you write your code 编辑器是你写代码的地方 In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. 在编辑器中,每个打开的文件以标签的形式显示.通过右键标签, 文件可以运行,保存,关闭,等等. The shell is where your code gets executed shell是你代码运行的地方 Configuring shells 配置shell Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. 通过Shell->编辑shell配置,你可以编辑或添加 shell配置.这允许你,选择 初始文件夹,或者使用特定的Python路径. Running code 运行代码 *Run cell:* a cell is everything between two lines starting with '##'. *运行单元:*单元是两个以##开始的行之间的任何东西. *Run file:* run all the code in the current file. *运行文件:*运行当前文件中的所有代码. *Run project main file:* run the code in the current project's main file. *运行工程main文件:*从当前工程的main文件中执行代码. Interactive mode vs running as script 交互模式vs以脚本模式运行 In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. 在交互模式,sys.path[0]是空的字符串(比如当前文件夹), 并且sys.argv被设定成['']. Tools for your convenience 方便的工具 Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. 通过工具菜单,你可以选择你需要的工具. 工具可以被放在你所希望的地方,并且也可以分离. Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. 贴士 工具系统被设计成 可以容易的建立你自己的工具.查看在线wiki获取更多信息, 或者你也可以用现有工具作为样本. Recommended tools 推荐工具 We especially recommend the following tools: 我们十分推荐以下工具: The *Source structure tool* gives an outline of the source code. 代码结构工具可以提供代码的提纲. The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. 文件浏览器工具可以帮助你纵览在一个文件夹下的所有文件 点击星号图标管理你的工程. Get coding! 码代码吧! Getting started with Pyzo This wizard helps you get familiarized with the workings of Pyzo. Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. This wizard can be opened using 'Help > Pyzo wizard' The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. Pyzo consists of two main components When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). This concludes the Pyzo wizard. Now, get coding and have fun! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_zh_CN.tr.qm0000666000000000000000000005225613156166666021510 0ustar 00000000000000  ХGJ?N(ET(e4f@'4p^D5 Zcc>:=26 ͎&^(G<3v@,=[7Enu:op}.1q^%}uC6~wK߱W03MHcg.=)Kc!r>;25EE;. tf$оX8N'z~#{dS .L97p, |I~N,C k.8!F>$S &En3^3 !Sn=|a@>Eh+nynj>'H5s 7i R> 'a<>@F9bI<5L$(Y +_p49J6:m>=5Gy/e5V6NVErV~%_[[,P92nNy4"c8ѯ:;jj+*N/#n$A/%ICf?4i| Ej^ *k"G0  0> Ix wS3 q 51 n ר @ .A BB ;~'% J5 c w > F$ l@ > 3J; e tH n1T 4, TW s 0n) t "- 0 s. )DI ˳! nA 3#M r& i i 0 Gn@ MN8 V7 G Ͽ^! ?@pZt\D><>2f~/Fn _5T^ (J:-;V6~ߔ4^NyYE>ގ+D<.iN|hStackdebug\_RQePython_Add path to Python path filebrowserO`xn[O`R d$Are you sure that you want to delete filebrowser e9SؘyvT yChange project name filebrowserpQfSgeb_SRMv_URR0Nf{~N-"Click star to bookmark current dir filebrowserY R6_ Copy path filebrowser R^ev_UCreate new directory filebrowser R^eeNCreate new file filebrowserR dDelete filebrowserR^Rog, Duplicate filebrowser eNT nVhFilename filter filebrowser~_SRMv_UT}T #Give the name for the new directory filebrowser ~eeNT}T Give the name for the new file filebrowser~_SRMeNT}T Give the new name for the file filebrowser W(_SRMshellN-lR0N*v_U)Go to this directory in the current shell filebrowser_Qeepcn...Import data... filebrowserS9MgaN Match case filebrowser e]z T :New project name: filebrowserbS_Open filebrowser]z T  Project name filebrowser]z : Projects: filebrowser kcRh_RegExp filebrowseryQyvRemove project filebrowserT}T Rename filebrowserW(cmKVhN-f>y:Reveal in Finder filebrowser O\N:g,ЈL Run as script filebrowserd}"eNSearch in files filebrowserW([PeNY9N-d}"Search in subdirs filebrowser hlv_UStar this directory filebrowserSmhlv_UUnstar this directory filebrowser.QsNPyzo ::: QsNPyzovfYO`o.+About Pyzo ::: More information about Pyzo.menu ::: ^.R?Ask a question ::: Need help?menu,R[bQs.[W ::: R[bQs.[WRh.DAutocomplete keywords ::: The autocompletion list includes keywords.menu(R) ::: W(QSTVޏfR).BAutomatically indent ::: Indent when pressing enter after a colon.menundb@u({}epClear all {} breakpointsmenunt\O^U ::: nt\O^U."Clear screen ::: Clear the screen.menuQs ::: Qs_SRMeN.!Close ::: Close the current file.menu*Qs ::: ~g_Vh^vQsShell.8Close ::: Terminate the interpreter and close the shell.menu QhQs ::: Qsb@g eN.Close all ::: Close all files.menu.QsQvNeN::: \eNNYQvNeNQs.-Close others::: Close all files but this one.menul ::: ~ِ N-LRl.&Comment ::: Comment the selected line.menu$Y R6 ::: Y R6 N-eg,R0Rj4g.1Copy ::: Copy the selected text to the clipboard.menuRjR ::: RjR N-eg,.Cut ::: Cut the selected text.menu.R0continue:bgLR0continue*Debug continue: proceed to next breakpointmenuR0N NL:bgLR0N NL#Debug next: proceed until next linemenu,R0return:bgLR0N NN*return#Debug return: proceed until returnsmenuNke:bgLNke!Debug step into: proceed one stepmenu$R d) ::: R d_SRMLv).&Dedent ::: Unindent the selected line.menuEditmenu*_cw.f \ ::: Oe9SUv_cw..;Edit key mappings... ::: Edit the shortcuts for menu items.menuDShellMn ::: mRevShellMn^vVh\^`'.WEdit shell configurations... ::: Add new shell configs and edit interpreter properties.menu$l՘h< ::: e9SNxr.;Edit syntax styles... ::: Change the coloring of your code.menu$_T/Qepcy: ::: f>y:Qepcy:.;Enable calltips ::: Show calltips with function signatures.menux ::: _SRMeNvx.8Encoding ::: The character encoding of the current file.menubgLmaineN ::: W(_SRMshellN-bgLmaineN.AExecute main file ::: Execute the main file in the current shell.menu bgL N- ::: bgL N-Nx.Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menu,[QR0PDF ::: [Q_SRMeNR0PDF.AExport to PDF ::: Export current file to PDF (e.g. for printing).menueNFilemenu*gb~N NN* ::: gb~N NN*d}"[W{&N2.y:gb~/fcbzS.LFind or replace ::: Show find/replace widget. Initialize with selected text.menu*gb~N NN* ::: gb~N NN*d}"[W{&N2.DFind previous ::: Find the previous occurrence of the search string.menu6 [^vTN gb~ ::: N [eg,N:vh,gb~N NN*.AFind selection ::: Find the next occurrence of the selected text.menu: [^vTN gb~ ::: N [eg,N:vh,gb~N NN*NN*.NFind selection backward ::: Find the previous occurrence of the selected text.menu[WOSFontmenul ::: lR0ry[Lep.,Go to line ::: Go to a specific line number.menu.lR0[NI ::: lR0_SRMQIhb@W(v[NI.:Goto Definition ::: Go to definition of word under cursor.menu^.RHelpmenu*NbS ::: NS9MTN S9MvbS.@Highlight brackets ::: Highlight matched and unmatched brackets.menu$N_SRML ::: NQIhb@W(L.BHighlight current line ::: Highlight the line where the cursor is.menu) ::: _SRMLcQe).$Indent ::: Indent the selected line.menu() ::: O\u(N_SRMeNv)h<_.9Indentation ::: The indentation used of the current file.menu2N-e ::: N-e_SRMЈLNx([bi\UNxeeH).TInterrupt ::: Interrupt the current running code (does not work for extension code).menu<etl/ehc[W{&N2 ::: et N-eg,N70N*[W{&[P.`Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters.menu(L~g_[W{& ::: _SRMeNv~g_[W{&.?Line endings ::: The line ending character of the current file.menuy:)cy: ::: f>y:Wv~gehy:).HShow indentation guides ::: Show vertical lines to indicate indentation.menu"f>y:Lg+ ::: f>y:kψLvLg+.0Show line endings ::: Show the end of each line.menu&f>y:zzv} ::: f>y:zzhy:vL(lg l4^snRga).\Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling).menue>Y'Zoom inmenu)\Zoom outmenud)e> Zoom resetmenu)e>Zoomingmenuzzh<spacesmenu~[Advanced settings menu dialoghggerHg,.Check for the latest version. menu dialogelՏЈL Could not run menu dialogelՏЈLg,.Could not run script. menu dialogOe9_cw.f \Edit shortcut mapping menu dialog l՘h<Edit syntax styling menu dialoge9SLanguage changed menu dialog _cw.f \Shortcut mappings menu dialog2R ::: 10yTRgb~/fcbzS.7Auto hide ::: Hide search/replace when unused for 10 s.searchgb~ Find patternsearch d}"zSHide search widget (Escape)search&S9MgaN ::: gb~S9MgaNvS.*Match case ::: Find words that match case.search TNN* ::: gb~TNN*Qs.-Next ::: Find next occurrence of the pattern.search RMNN* ::: gb~RMNN*Qs.5Previous ::: Find previous occurrence of the pattern.search(kcRh_ ::: Ou(kcRh_gb~.*RegExp ::: Find using regular expressions.search0fcbQh ::: fcb_SRMeNN-b@g S9Mvvh.6Repl. all ::: Replace all matches in current document.search fcb ::: fcb_SRMS9Mvh.Replace ::: Replace this match.searchfcbvhReplace patternsearchet ::: gb~et.&Whole words ::: Find only whole words.searchmRMn Add configshellT/RebgLNxCode to run at startupshell$R d ::: R d_SRMshellMn*Delete ::: Delete this shell configurationshellT/RebgLeNFile to run at startupshellShell MnShell configurationsshell Ou(|~ߞ؋Use system defaultshellSep ::: T}NLSep./argv ::: The command line arguments (sys.argv).shellsX ::: DRvsXSؑ.5environ ::: Extra environment variables (os.environ).shell&exe ::: PythonbgLeN.exe ::: The Python executable.shell"gui ::: etTvGUI]Qw.Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shellBipython ::: YgSu(ROu(IPython shell.+ipython ::: Use IPython shell if available.shellT y ::: _SRMMnT .(name ::: The name of this configuration.shelljpython_ ::: modulesTpackages_.kN*_QNLbOu(|~ߞ؋R{&R.pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shell T/ReNY9 ::: T/ReNY9.6startDir ::: The start directory (not in script mode).shell$T/Rg, ::: T/ReЈLvg,.DstartupScript ::: The script to run at startup (not in script mode).shell6*ЈLSUQC:*SUQCf/N$N*N##_YvLNKvNOUN.F*Run cell:* a cell is everything between two lines starting with '##'.wizard(*ЈLeN:*ЈL_SRMeNN-vb@g Nx.1*Run file:* run all the code in the current file.wizard>*ЈL]z maineN:*N_SRM]z vmaineNN-bgLNx.I*Run project main file:* run the code in the current project's main file.wizardMnshellConfiguring shellswizard xNxT'! Get coding!wizardzW(NNj!_,sys.path[0]f/zzv[W{&N2(kY_SRMeNY9), ^vNsys.argv[b[''].pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizardlW(VhN-,kN*bS_veNNh{~v_b_f>y:.S.h{~ eNSNЈL,O[X,Qs,{I{I.In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizardNNj!_vsNg,j!_ЈL%Interactive mode vs running as scriptwizarde9SLanguage changedwizard4X ]Qw|~߈b SN[fv^zO`]v]Qw.gw W(~wikiSfYO`o, bO`N_SNu(sg ]QwO\N:h7g,.Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizardcP]QwRecommended toolswizardЈLNx Running codewizard bSelect languagewizardkeStepwizard`eNmOVh]QwSN^.RO`~W(NN*eNY9N vb@g eN pQfSVh{tO`v]z .The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizard Nx~g]QwSNcONxvc~.@The *Source structure tool* gives an outline of the source code.wizardVhf/O`QNxvW0e'The editor is where you write your codewizardshellf/O`NxЈLvW0e*The shell is where your code gets executedwizard eOv]QwTools for your conveniencewizardShell->shellMn,O`SNbmR shellMn.QAO`, b RYeNY9,bOu(ry[vPython_.Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardf]QwSU,O`SN bO`v]Qw. ]QwSN别e>W(O`b@^ gvW0e,^vNN_SNRy.Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardbNSARcPNN ]Qw:,We especially recommend the following tools:wizard,k"geR0PythonNN_Vh(IEP)!-Welcome to the Interactive Editor for Python!wizard"O`SNW(shellvcbgLT}N,1You can execute commands directly in the *shell*,wizard&bO`N_SNW(VhQNxq6TbgL.7or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/translations/pyzo_zh_TW.tr0000666000000000000000000020674513156166662021126 0ustar 00000000000000 debug Stack 堆疊 editor Close, Discard, Cancel, Save 關閉, 丟棄, 放棄, 儲存 Closing 關閉 Save modified file? 儲存已修改的檔案? Closing pinned 關閉鎖定的檔案 Are you sure you want to close this pinned file? 您確定要關閉此鎖定檔案? editorTabs Select one or more files to open 選擇開啟一個或多個檔案 Select the file to save to 選擇要儲存的檔案 filebrowser Filename filter 檔名過濾器 Search in files 在檔案中搜尋 Projects: 專案: Click star to bookmark current dir 點擊星號將目前目錄加入書籤 Remove project 刪除專案 Change project name 更改專案名稱 Add path to Python path 將路徑加入 Python 路徑 Go to this directory in the current shell 在目前 Shell 中前往此目錄 Project name 專案名稱 New project name: 新專案名稱: Match case 符合條件 RegExp 常規表示式 Search in subdirs 搜尋子目錄 Unstar this directory 取消標注此目錄 Star this directory 標注此目錄 Open 開啟 Run as script 以腳本模式執行 Import data... 匯入資料... Open outside Pyzo 開啟外部 Pyzo Reveal in Finder 在搜尋器顯示 Copy path 複製路徑 Rename 重新命名 Delete 刪除 Create new file 新增檔案 Create new directory 新增目錄 Give the new name for the file 命名此檔案 Give the name for the new directory 命名此新目錄 Duplicate 建立副本 Give the name for the new file 命名此新檔案 Are you sure that you want to delete 確定要刪除 Run Jupyter notebook 執行 Jupyter notebook importwizard Select file 選擇檔案 Browse... 瀏覽... Preview: 預覽: Select columns to import: 選擇需匯入的欄位: Close 關閉 Import data as single array 以單一陣列方式匯入資料 Import data into one variable per column 以每欄一個變數方式匯入資料 Raise error upon invalid data 資料不正確即發出錯誤訊息 Import data wizard 匯入資料精靈 No current shell active 目前無啟用之 Shell No current file open 目前無開啟的檔案 Import data 匯入資料 The import data wizard is already open 匯入資料精靈已經開啟 main unsaved 未儲存 menu File 檔案 Edit 編輯 View 檢視 Settings 設定 Shell Shell Run 執行 Tools 工具 Help 輔助 Use tabs 使用定位鍵 Use spaces 使用空白 spaces plural of spacebar character 空白 Indentation ::: The indentation used of the current file. 縮排 ::: 目前檔案的縮排。 Syntax parser ::: The syntax parser of the current file. 語法分析器 ::: 目前檔案的語法分析器。 Line endings ::: The line ending character of the current file. 行末 ::: 目前檔案的行末字元。 Encoding ::: The character encoding of the current file. 編碼 ::: 目前檔案的字元編碼。 New ::: Create a new (or temporary) file. 新增 ::: 新增檔案。 Open... ::: Open an existing file from disk. 開啟... ::: 開啟磁碟中的檔案。 Save ::: Save the current file to disk. 儲存 ::: 將目前檔案存到磁碟中。 Save as... ::: Save the current file under another name. 另存新檔... ::: 將目前檔案以其他檔名存檔。 Save all ::: Save all open files. 全部儲存 ::: 儲存所有開啟的檔案。 Close ::: Close the current file. 關閉 ::: 關閉目前檔案。 Close all ::: Close all files. 全部關閉 ::: 關閉所有檔案。 Export to PDF ::: Export current file to PDF (e.g. for printing). 匯出 PDF 格式 ::: 將目前檔案以 PDF 格式匯出 (例如提供列印)。 Restart Pyzo ::: Restart the application. 重啟 Pyzo ::: 重啟 Pyzo 程式。 Quit Pyzo ::: Close the application. 退出 Pyzo ::: 關閉 Pyzo 程式。 Undo ::: Undo the latest editing action. 復原 ::: 復原上一次編輯動作。 Redo ::: Redo the last undone editong action. 重複 ::: 重複上一次復原動作。 Cut ::: Cut the selected text. 剪下 ::: 將選取文字剪下。 Copy ::: Copy the selected text to the clipboard. 複製 ::: 將選取文字複製到剪貼板上。 Paste ::: Paste the text that is now on the clipboard. 貼上 ::: 將剪貼板文字貼上。 Paste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation. 貼上並選取 ::: 將剪貼板文字貼上並且維持選取狀態以便更改縮排。 Select all ::: Select all text. 全部選取 ::: 選取所有文字。 Indent ::: Indent the selected line. 縮排 ::: 縮排選取行。 Dedent ::: Unindent the selected line. 取消縮排 ::: 取消縮排選取行。 Comment ::: Comment the selected line. 註解 ::: 註解選取行。 Uncomment ::: Uncomment the selected line. 取消註解 ::: 取消註解選取行。 Go to line ::: Go to a specific line number. 前往行 ::: 前往指定行。 Toggle breakpoint ::: Toggle breakpoint on the current line. 切換斷點 ::: 在目前行切換斷點。 Find or replace ::: Show find/replace widget. Initialize with selected text. 尋找或置換 ::: 顯示尋找/置換工具。預設為尋找所選取的文字。 Find selection ::: Find the next occurrence of the selected text. 尋找選取文字 ::: 尋找下一個選取文字。 Find selection backward ::: Find the previous occurrence of the selected text. 向上尋找選取文字 ::: 尋找上一個選取文字。 Find next ::: Find the next occurrence of the search string. 尋找下一個 ::: 尋找下一個搜尋字串。 Find previous ::: Find the previous occurrence of the search string. 尋找上一個 :::尋找上一個搜尋字串。 Accept autocompletion with: 接受自動完成: Zoom in 放大 Zoom out 縮小 Zoom reset 重設縮放 Location of long line indicator ::: The location of the long-line-indicator. 長行指示器位置 ::: 長行指示器的位置。 Qt theme ::: The styling of the user interface widgets. QT 主題 ::: 使用者介面工具的樣式。 Select shell ::: Focus the cursor on the current shell. 選擇 Shell ::: 將游標移到目前 Shell。 Select editor ::: Focus the cursor on the current editor. 選擇編輯器 ::: 將游標移到目前編輯器。 Select previous file ::: Select the previously selected file. 選擇先前檔案 ::: 選擇先前選取的檔案。 Show whitespace ::: Show spaces and tabs. 顯示空白 ::: 顯示空白及定位鍵。 Show line endings ::: Show the end of each line. 顯示行末 ::: 顯示每行的行末。 Show indentation guides ::: Show vertical lines to indicate indentation. 顯示縮排指示 ::: 顯示直線來標示縮排。 Wrap long lines ::: Wrap lines that do not fit on the screen (i.e. no horizontal scrolling). 換行 ::: 超出螢幕的文字自動換行 (不出現水平捲軸)。 Highlight current line ::: Highlight the line where the cursor is. 標示目前行 ::: 標示游標所在行。 Previous cell ::: Go back to the previous cell. 上一個單元 ::: 回到上一個單元。 Next cell ::: Advance to the next cell. 下一個單元 ::: 前往下一個單元。 Previous object ::: Go back to the previous top-level structure. 上一個物件 ::: 回到上一個頂層架構。 Next object ::: Advance to the next top-level structure. 下一個物件 ::: 前往下一個頂層架構。 Font 字型 Zooming 縮放 Clear screen ::: Clear the screen. 清除螢幕 ::: 清除螢幕。 Interrupt ::: Interrupt the current running code (does not work for extension code). 中斷 ::: 中斷目前執行的程式(對延伸程式無效)。 Restart ::: Terminate and restart the interpreter. 重啟 ::: 結束並重啟解譯器。 Terminate ::: Terminate the interpreter, leaving the shell open. 結束 ::: 結束解譯器,Shell 保持開啟。 Close ::: Terminate the interpreter and close the shell. 關閉 ::: 結束解譯器並關閉 Shell。 Debug next: proceed until next line 除錯到下一行:前進到下一行 Debug step into: proceed one step 除錯到下一步:前進一步 Debug return: proceed until returns 除錯到 Return:前進到 Return Debug continue: proceed to next breakpoint 除錯到 Continue:前進到下一個斷點 Stop debugging 停止除錯 Clear all {} breakpoints 清除所有 {} 斷點 Postmortem: debug from last traceback 後驗:從上一個追溯點開始除錯 Edit shell configurations... ::: Add new shell configs and edit interpreter properties. 編輯 Shell 組態... ::: 新增 Shell 組態並編輯解譯器特性。 Create new Python environment... ::: Install miniconda. 新增 Python 環境... ::: 安裝 Miniconda。 New shell ... ::: Create new shell to run code in. 新 Shell... ::: 開啟新 Shell 來執行程式。 Goto Definition ::: Go to definition of word under cursor. 前往定義 ::: 前往游標所在文字的定義。 Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection. 執行選取 ::: 執行目前編輯器所選取的行或在目前行所選取的字,如果沒有選取就執行目前行。 Close others::: Close all files but this one. 關閉其他檔案 ::: 除了目前檔案外,關閉所有其他檔案。 Rename ::: Rename this file. 重新命名 ::: 重新命名此檔案。 Copy path ::: Copy the full path of this file. 複製路徑 ::: 複製此檔案的完整路徑。 Pin/Unpin ::: Pinned files get closed less easily. 鎖定/解鎖 ::: 鎖定的檔案較不容易被關閉。 Set/Unset as MAIN file ::: The main file can be run while another file is selected. 設定/取消設定為主檔 ::: 主檔能在其他檔案被選取時執行。 Run file ::: Run the code in this file. 執行檔案 ::: 執行此檔案中的程式。 Run file as script ::: Run this file as a script (restarts the interpreter). 以腳本模式執行檔案 ::: 以腳本模式執行此檔案(重啟解譯器)。 Run file as script ::: Restart and run the current file as a script. 以腳本模式執行檔案 ::: 以腳本模式重啟並執行此檔案。 Run main file as script ::: Restart and run the main file as a script. 以腳本模式執行主檔 ::: 以腳本模式重啟並執行主檔。 Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection. 執行選取 ::: 執行目前編輯器所選取的行或在目前行所選取的字,如果沒有選取就執行目前行。 Execute cell ::: Execute the current editors's cell in the current shell. 執行單元 ::: 執行目前 Shell 中編輯器的單元。 Execute cell and advance ::: Execute the current editors's cell and advance to the next cell. 執行單元並前進 ::: 執行目前編輯器的單元並前往下一個單元。 Execute file ::: Execute the current file in the current shell. 執行檔案 ::: 在目前 Shell 中執行目前檔案。 Execute main file ::: Execute the main file in the current shell. 執行主檔 ::: 在目前 Shell 中執行主檔。 Help on running code ::: Open the pyzo wizard at the page about running code. 執行程式輔助 ::: 開啟 Pyzo 精靈中有關執行程式的頁面。 Reload tools ::: For people who develop tools. 重載工具 ::: 針對開發工具的人員。 Pyzo website ::: Open the Pyzo website in your browser. Pyzo 網站 ::: 在瀏覽器開啟 Pyzo 網站。 Pyzo guide ::: Open the Pyzo guide in your browser. Pyzo 指引 ::: 在瀏覽器開啟 Pyzo 指引。 Ask a question ::: Need help? 詢問問題 ::: 需要協助? Report an issue ::: Did you found a bug in Pyzo, or do you have a feature request? 回報問題 ::: 你發現 Pyzo 有 Bug 或者需要某些新功能? Local documentation ::: Documentation on Python and the Scipy Stack. 本地文件 ::: 有關 Python 及 Scipy Stack 的文件。 Check for updates ::: Are you using the latest version? 檢查更新 ::: 您使用的是最新版本嗎? About Pyzo ::: More information about Pyzo. 有關 Pyzo ::: 更多有關 Pyzo 的資訊。 Select language ::: The language used by Pyzo. 選擇語言 ::: Pyzo 所使用的語言。 Automatically indent ::: Indent when pressing enter after a colon. 自動縮排 ::: 在冒號之後按下 Enter 鍵會自動縮排。 Enable calltips ::: Show calltips with function signatures. 啟用呼叫提示 ::: 顯示函式的呼叫提示。 Autocomplete keywords ::: The autocompletion list includes keywords. 自動完成關鍵字 ::: 自動完成清單包含關鍵字。 Edit key mappings... ::: Edit the shortcuts for menu items. 編輯鍵盤對應... ::: 編輯功能選項的快捷鍵。 Edit syntax styles... ::: Change the coloring of your code. 編輯語法樣式... ::: 更改程式碼的顏色。 Advanced settings... ::: Configure Pyzo even further. 進階設定... ::: 進一步組態 Pyzo。 Highlight brackets ::: Highlight matched and unmatched brackets. 標示方括號 ::: 標示有對應及無對應的方括號。 There is no main file selected. 並未選擇主檔案。 (as script) (以腳本模式) Could not save the file. 無法儲存檔案。 Can only run scripts that are in the file system. 只能執行在檔案系統中的腳本。 Pyzo wizard ::: Get started quickly. Pyzo 精靈 ::: 快速入門。 None Create shell %s: (%s) 新增 Shell %s: (%s) Execute selection and advance 執行所選取然後前進 No autocompletion 沒有自動完成 Automatic popup 自動彈出 Only show popup when pressing Tab 只有在按下 Tab 鍵時才出現彈出視窗 Autocompletion 自動完成 More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. 透過記錄工具有更多的設定可供選擇: - 進階設定儲存在 "pyzo.config.advanced" 結構中。 輸入 "print(pyzo.config.advanced)" 指令來查看所有進階設定。 - 呼叫 "pyzo.resetConfig()" 來重設所有設定。 - 呼叫 "pyzo.resetConfig(True)" 來重設所有設定與狀態。 注意,大多數的設定都需要重新啟動 Pyzo 讓此變更生效。 Search 搜尋 History 歷史 Copy ::: Copy selected lines 複製 ::: 複製選取行 Run ::: Run selected lines in current shell 執行 ::: 在目前 Shell 中執行選取行 Remove ::: Remove selected history items(s) 刪除 ::: 刪除選取的歷史項目 Justify comment/docstring::: Reshape the selected text so it is aligned to around 70 characters. Duplicate line ::: Duplicate the selected line(s). Delete line ::: Delete the selected line(s). default Open current directory in file browser Change current directory to the file browser's path Open directory in file browser No shell to run code in. No editor selected. Change current directory to editor file path Change directory when executing file ::: like Run File As Script does menu dialog Could not run 無法執行 Could not run script. 無法執行腳本。 Check for the latest version. 檢查最新版本。 Edit syntax styling 編輯語法樣式 Advanced settings 進階設定 Language changed 語言已變更 Edit shortcut mapping 編輯快捷鍵對應 Shortcut mappings 快捷鍵對應 Could not run notebook 無法執行 Notebook About Pyzo 有關 Pyzo The language has been changed. Pyzo needs to restart for the change to take effect. Could not change dir pyzoHistoryViewer History viewer pyzoSourceStructure Source structure Parsing search Hide search widget (Escape) 隱藏搜尋工具 (Escape) Find pattern 尋找模式 Previous ::: Find previous occurrence of the pattern. 上一個 ::: 尋找上一個模式。 Next ::: Find next occurrence of the pattern. 下一個 ::: 尋找下一個模式。 Replace pattern 置換模式 Repl. all ::: Replace all matches in current document. 全部置換 ::: 在目前文件中置換所有符合項目。 Replace ::: Replace this match. 置換 ::: 置換這個符合項目。 Match case ::: Find words that match case. 符合條件 ::: 尋找符合條件的字。 RegExp ::: Find using regular expressions. 常規表示式 ::: 利用常規表示式尋找。 Whole words ::: Find only whole words. 整詞 ::: 只尋找整詞。 Auto hide ::: Hide search/replace when unused for 10 s. 自動隱藏 ::: 未使用超過 10 秒後自動隱藏搜尋/置換工具。 shell name ::: The name of this configuration. 名稱 ::: 此組態的名稱。 ipython ::: Use IPython shell if available. ipython ::: 如果可用則使用 Ipython Shell。 Use system default 使用系統預設 File to run at startup 啟動時執行之檔案 Code to run at startup 啟動時執行之程式 exe ::: The Python executable. exe ::: Python 執行檔。 gui ::: The GUI toolkit to integrate (for interactive plotting, etc.). gui ::: 需整合的 GUI 工具 (例如互動式繪圖等)。 pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS. pythonPath ::: 模組與套件的目錄搜尋清單。請將每個路徑寫成一行, 或使用作業系統預設的分隔符號來分隔。 startupScript ::: The script to run at startup (not in script mode). startScript ::: 啟動時執行之程式 (非腳本模式)。 startDir ::: The start directory (not in script mode). starDir ::: 啟動之目錄 (非腳本模式)。 argv ::: The command line arguments (sys.argv). argv ::: 指令行的參數 (sys.argv)。 environ ::: Extra environment variables (os.environ). environ ::: 額外的環境變數 (os.environ)。 Delete ::: Delete this shell configuration 刪除 ::: 刪除此 Shell 組態 Shell configurations Shell 組態 Add config 加入組態 shells Click to select shell. 點選 Shell。 splash This is <b>Pyzo</b><br />the Python IDE for scientific computing 這是 <b>Pyzo</b><br />用於科學運算的 Python IDE Version 版本 Pyzo is open source software and freely available for everyone. Pyzo 是開源軟體,免費提供給每個人使用。 wizard Getting started with Pyzo Pyzo 入門 Step 步驟 Welcome to the Interactive Editor for Python! 歡迎來到 Python 互動編輯器! This wizard helps you get familiarized with the workings of Pyzo. 此精靈協助您熟悉 Pyzo 的功能。 Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*. Pyzo 是一個跨平台的 Python IDE,著重在互動與自我檢查,因此非常適合科學運算。 它的設計目標是 *簡單* 與 *效能*。 This wizard can be opened using 'Help > Pyzo wizard' 此精靈可利用 '協助 > Pyzo精靈' 開啟, Select language 選擇語言 The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. 此精靈的語言已變更,需要重啟 Pyzo 讓此變更生效。 Language changed 語言已變更 Pyzo consists of two main components Pyzo 由兩個主要部份組成 You can execute commands directly in the *shell*, 你可以在 *Shell* 中直接執行指令, or you can write code in the *editor* and execute that. 或者你也可以在 *編輯器* 中撰寫程式然後再執行。 The editor is where you write your code 編輯器就是你撰寫程式的地方 In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. 在 *編輯器* 中,每個開啟的檔案以分頁表示。右鍵點擊分頁可以執行、儲存、或關閉檔案。 The shell is where your code gets executed Shell 就是你執行程式的地方 When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions. Pyzo 啟動後會產生一個預設的 *Shell*。你可以加入多個 Shell 同時並行,各個 Shell 也可以使用不同的 Python 版本。 Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell. Shell 以子程序模式執行,因此當它忙碌時,Pyzo 還是能正常回應,因此你可以繼續撰寫程式或是在另一個 Shell 中執行程式。 Configuring shells 組態 Shell Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib. Pyzo 可以整合 5 個不同 *GUI 工具* 的事件迴圈,因此可透過 Visvis 或 Matplotlib 進行互動繪圖。 Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath. 透過「Shell > 編輯 Shell 組態」可以編輯及加入 *Shell組態*,例如選擇預設目錄或使用客製化 Python 路徑。 Running code 執行程式 Pyzo supports several ways to run source code in the editor. (see the 'Run' menu). Pyzo 支援多種在編輯器裡執行程式的方式 (詳見「執行」選項)。 *Run cell:* a cell is everything between two lines starting with '##'. *執行單元:* 一個單元指的是由「##」符號開始的兩行之間的所有資料。 *Run file:* run all the code in the current file. *執行檔案:* 執行目前檔案裡的所有程式。 *Run project main file:* run the code in the current project's main file. *執行專案主檔:* 執行目前專案主檔裡的所有程式。 Interactive mode vs running as script 互動模式及以腳本模式執行 In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']. 在互動模式中,sys.path[0] 是一個空字串 (也就是目前目錄),sys.argv 則設定為 ['']。 Tools for your convenience 方便的工具 Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. 透過 *工具選單* 可以選擇工具,工具可以放在你喜歡的地方,也可以分離。 Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. 工具系統以方便建立你自己的工具為設計目標,你可以從線上 Wiki 提供更多資訊,或者使用目前已有的工具為範例。 Recommended tools 推薦的工具 We especially recommend the following tools: 我們特別推薦以下工具: The *Source structure tool* gives an outline of the source code. *來源結構工具* 描述來源碼的大綱概要。 The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon. *檔案瀏覽工具* 可以總覽一個目錄裡的所有檔案。若要管理你的專案,請點擊星號。 Get coding! 開始寫程式吧! This concludes the Pyzo wizard. Now, get coding and have fun! Pyzo 精靈結束,現在開心的寫程式吧! The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily. *Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines. You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution. In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script. pyzo-4.4.3/pyzo/resources/translations/pyzo_zh_TW.tr.qm0000666000000000000000000007200613156166666021535 0ustar 000000000000005 Х`?N9ET9eEf@'FY.GZBc>L%*2IS͎7>^k`<3 &@,O[InuLp}.Cq^68}uCH~%g߱WA3jHcRG?s5:Kcr>MGEZN\T.8 tQ$a($о.X8N8z~3{!$'X .)L9I,|I~$$ N'HYk.Kq!F>&$S&EnE63 1lSnP@X>Ca@>[h+nnjQMH5s="M  R>_%C\'/E13]<> FKI<5L4Y *_pF zZ HJ,A!6MFlna0m>PJ 5`^\/e VHV[SV~5g[&1[=L_Q DnkXyF}"Ri 89KѯMrNjj=E;+T4NA#n4A/%df?Fi|0j^k2  0U , M0 I A{ 6  Ix KƎ . wSD q- u" 5 g~i n!h ר-* @  .W BX ij 6 ;~7 ? JH$ OZO6 c Qg F4 q lV >* 3f eG tc nB }  4> T} $ .e ,<. 0n: Y # [=nd aoF t+ "? B s@8 )Dd` v ɠU ˳0 nWP 335 r6 i ( i e ) s 0 7  GnS MNJ O Qun VJ0 b Ͽ^1 ?N SX\D#!i,4[>S:>Ztw\Z>OQ>D\;'~@n _G\ )  :IdJ:>VHg~ߔF.N+>x^TwYE#ގh, Q2[XClose, Discard, Cancel, SaveeditorܕClosingeditorܕ[vjhHClosing pinnededitorQ2[X]Oe9vjhHSave modified file?editorxdǕU_NP bYP jhH Select one or more files to open editorTabsxdljQ2[XvjhHSelect the file to save to editorTabs\_RQe Python _Add path to Python path filebrowser x[R*d$Are you sure that you want to delete filebrowser fe9\hHT z1Change project name filebrowserdf_\vRMvRQef|d"Click star to bookmark current dir filebrowser_ Copy path filebrowsereXvCreate new directory filebrowsereXjhHCreate new file filebrowserR*dDelete filebrowser^zRog, Duplicate filebrowser jT NoVhFilename filter filebrowser T}T kdev#Give the name for the new directory filebrowser T}T kdejhHGive the name for the new file filebrowser T}T kdjhHGive the new name for the file filebrowser W(vRM Shell N-RM_kdv)Go to this directory in the current shell filebrowserS/Qee...Import data... filebrowser{&ThN Match case filebrowser e\hHT z1New project name: filebrowserU_Open filebrowserU_Y PyzoOpen outside Pyzo filebrowser\hHT z1 Project name filebrowser\hH Projects: filebrowser ^8hy:_RegExp filebrowserR*d\hHRemove project filebrowsereT}T Rename filebrowser W(d\ Vhoy:Reveal in Finder filebrowser&WL Jupyter notebookRun Jupyter notebook filebrowserNsg,j!_WL Run as script filebrowser W(jhHN-d\ Search in files filebrowser d\ [PvSearch in subdirs filebrowser jlkdvStar this directory filebrowserSmjlkdvUnstar this directory filebrowser p... Browse... importwizardܕClose importwizardS/Qee Import data importwizardNUNcRe_S/QeeImport data as single array importwizardNkkNP exe_S/Qee(Import data into one variable per column importwizard S/Qee|HImport data wizard importwizardvRMq!U_vjhHNo current file open importwizardvRMq!U_u(NK ShellNo current shell active importwizardPreview: importwizardeN kcxSsv|Q/ `oRaise error upon invalid data importwizardxdǗS/QevkOMSelect columns to import: importwizardxdjhH Select file importwizardS/Qee|H]}U_&The import data wizard is already open importwizardg*Q2[XunsavedmainJ N]Qwg fYv-[SOxd - 2-[Q2[XW( "pyzo.config.advanced" }PiN-0 8Qe "print(pyzo.config.advanced)" cNOgw b@g 2-[0 - T|S "pyzo.resetConfig()" O͊-b@g -[0 - T|S "pyzo.resetConfig(True)" O͊-b@g -[raK0 la Y'Yexv-[eU_R Pyzo kdfueH0  More settings are available via the logger-tool: - Advanced settings are stored in the struct "pyzo.config.advanced". Type "print(pyzo.config.advanced)" to view all advanced settings. - Call "pyzo.resetConfig()" to reset all settings. - Call "pyzo.resetConfig(True)" to reset all settings and state. Note that most settings require a restart for the change to take effect. menu(Nsg,j!_) (as script)menu4g Pyzo ::: fYg Pyzo vNJ 0+About Pyzo ::: More information about Pyzo.menucSׁR[bAccept autocompletion with:menu.2-[... ::: 2Nke}DaK Pyzo05Advanced settings... ::: Configure Pyzo even further.menubUOUOL ::: STRAsk a question ::: Need help?menu0R[bܓu[W ::: R[bnUST+ܓu[W0DAutocomplete keywords ::: The autocompletion list includes keywords.menuR[bAutocompletionmenuR_HQAutomatic popupmenuWLUQCN&RM2 ::: WLvRM}/VhvUQCN&RM_N NP UQC0]Execute cell and advance ::: Execute the current editors's cell and advance to the next cell.menu6WLjhH ::: W(vRM Shell N-WLvRMjhH0?Execute file ::: Execute the current file in the current shell.menu2WLN;j ::: W(vRM Shell N-WLN;j0AExecute main file ::: Execute the main file in the current shell.menuZWLxS ::: WLvRM}/Vhb@xSvLbW(vRMLb@xSv[W Yglg xS\1WLvRML0Execute selection ::: Execute the current editor's selected lines, selected words on the current line, or current line if there is no selection.menuWLb@xSq6_RM2Execute selection and advancemenuNS/Q PDF h<_ ::: \vRMjhHN PDF h<_S/Q (OYcORSp)0AExport to PDF ::: Export current file to PDF (e.g. for printing).menujhHFilemenu(\ b~N NP ::: \ b~N NP d\ [WN20e Shell... ::: U_e Shell OWLz _02New shell ... ::: Create new shell to run code in.menu$N NP UQC ::: RM_N NP UQC0'Next cell ::: Advance to the next cell.menu(N NP riN ::: RM_N NP \dgi08Next object ::: Advance to the next top-level structure.menu lg R[bNo autocompletionmenuq!Nonemenu&Sg W(c N Tab ufBbMQs_HQz!Only show popup when pressing Tabmenu&U_... ::: U_xxN-vjhH0,Open... ::: Open an existing file from disk.menu N ::: \Rjge[WN 06Paste ::: Paste the text that is now on the clipboard.menuBN N&xS ::: \Rjge[WN N&N}cxSraKNOfe9~.c0yPaste and select ::: Paste the text that is now on the clipboard and keep it selected in order to change its indentation.menu.[/㓖 ::: [vjhHN [fܕ02Pin/Unpin ::: Pinned files get closed less easily.menu_W_N NP nޕY˖d/%Postmortem: debug from last tracebackmenu$N NP UQC ::: VR0N NP UQC0/Previous cell ::: Go back to the previous cell.menu(N NP riN ::: VR0N NP \dgi0@Previous object ::: Go back to the previous top-level structure.menu6Pyzo c_ ::: W(pVhU_ Pyzo c_03Pyzo guide ::: Open the Pyzo guide in your browser.menu6Pyzo }z ::: W(pVhU_ Pyzo }z07Pyzo website ::: Open the Pyzo website in your browser.menu"Pyzo |H ::: _Qe0$Pyzo wizard ::: Get started quickly.menu*QT N;L ::: Ou(N˗b]Qwvj#_07Qt theme ::: The styling of the user interface widgets.menu.Q Pyzo ::: ܕ Pyzo z _0$Quit Pyzo ::: Close the application.menu"͉ ::: ͉N Nk!_SRO\0-Redo ::: Redo the last undone editong action.menu&͏ ]Qw ::: \ v|]QwvNT0.Reload tools ::: For people who develop tools.menu R*d ::: R*dxSvkwSv+Remove ::: Remove selected history items(s)menu"eT}T ::: eT}T kdjhH0Rename ::: Rename this file.menuDVX1UOL ::: O`v|s Pyzo g Bug bgNeRRReport an issue ::: Did you found a bug in Pyzo, or do you have a feature request?menu U_ ::: }Pg_N&U_oVh02Restart ::: Terminate and restart the interpreter.menu.U_ Pyzo ::: U_ Pyzo z _0)Restart Pyzo ::: Restart the application.menuWLRunmenu.WL ::: W(vRM Shell N-WLxSֈL+Run ::: Run selected lines in current shellmenu&WLjhH ::: WLkdjhHN-vz _0'Run file ::: Run the code in this file.menu8Nsg,j!_WLjhH ::: Nsg,j!_U_N&WLkdjhH0DRun file as script ::: Restart and run the current file as a script.menu@Nsg,j!_WLjhH ::: Nsg,j!_WLkdjhH(U_oVh)0LRun file as script ::: Run this file as a script (restarts the interpreter).menu6Nsg,j!_WLN;j ::: Nsg,j!_U_N&WLN;j0FRun main file as script ::: Restart and run the main file as a script.menuZWLxS ::: WLvRM}/Vhb@xSvLbW(vRMLb@xSv[W Yglg xS\1WLvRML0Run selection ::: Run the current editor's selected lines, selected words on the current line, or current line if there is no selection.menu$Q2[X ::: \vRMjhH[XR0xxN-0'Save ::: Save the current file to disk.menu&QhQ2[X ::: Q2[Xb@g U_vjhH0!Save all ::: Save all open files.menu2S[Xej... ::: \vRMjhHNQvNjT [Xj08Save as... ::: Save the current file under another name.menud\ Searchmenu QhxS ::: xSb@g e[W0Select all ::: Select all text.menu*xd}/Vh ::: \n8jyR0vRM}/Vh09Select editor ::: Focus the cursor on the current editor.menu*xdNJ ::: Pyzo b@Ou(v0.Select language ::: The language used by Pyzo.menu*xdQHRMjhH ::: xdQHRMxSvjhH0=Select previous file ::: Select the previously selected file.menu6xd Shell ::: \n8jyR0vRM Shell07Select shell ::: Focus the cursor on the current shell.menu<-[/Sm-[pN;j ::: N;jW(QvNjhHxSfBWL0SSet/Unset as MAIN file ::: The main file can be run while another file is selected.menu-[Settingsmenu ShellShellmenu*oy:~.ccy: ::: oy:v}Ojy:~.c0HShow indentation guides ::: Show vertical lines to indicate indentation.menu"oy:Lg+ ::: oy:kψLvLg+00Show line endings ::: Show the end of each line.menu$oy:zzv} ::: oy:zzv}S[OMu0)Show whitespace ::: Show spaces and tabs.menuP\kbd/Stop debuggingmenu*lRgVh ::: vRMjhHvlRgVh08Syntax parser ::: The syntax parser of the current file.menu0}Pg_ ::: }Pg_oVh Shell OcU_0@Terminate ::: Terminate the interpreter, leaving the shell open.menuN&g*xdN;jhH0There is no main file selected.menu$Rce ::: W(vRMLRce0Y'Zoom inmenu~.\Zoom outmenu͊-~.e> Zoom resetmenu~.e>Zoomingmenuzzv}spacesmenug Pyzo About Pyzo menu dialog2-[Advanced settings menu dialogjggerHg,0Check for the latest version. menu dialogq!lWL Could not run menu dialogq!lWL NotebookCould not run notebook menu dialogq!lWLsg,0Could not run script. menu dialog}/_cwu\ aEdit shortcut mapping menu dialog }/lj#_Edit syntax styling menu dialog ]fLanguage changed menu dialog _cwu\ aShortcut mappings menu dialog@RՖ ::: g*Ou(N 10 y_RՖd\ /nc]Qw07Auto hide ::: Hide search/replace when unused for 10 s.search\ b~j!_ Find patternsearchd\ ]Qw (Escape)Hide search widget (Escape)search${&ThN ::: \ b~{&ThNv[W0*Match case ::: Find words that match case.search N NP ::: \ b~N NP j!_0-Next ::: Find next occurrence of the pattern.search N NP ::: \ b~N NP j!_05Previous ::: Find previous occurrence of the pattern.search(^8hy:_ ::: R)u(^8hy:_\ b~0*RegExp ::: Find using regular expressions.search0Qhnc ::: W(vRMeNN-ncb@g {&Tv06Repl. all ::: Replace all matches in current document.search nc ::: ncېP {&Tv0Replace ::: Replace this match.searchncj!_Replace patternsearchet^ ::: S\ b~et^0&Whole words ::: Find only whole words.searchRQe}DaK Add configshellU_RfBWLNKz _Code to run at startupshell&R*d ::: R*dkd Shell }DaK*Delete ::: Delete this shell configurationshellU_RfBWLNKjhHFile to run at startupshellShell }DaKShell configurationsshell Ou(|}q-Use system defaultshell6argv ::: cNLvSex (sys.argv)0/argv ::: The command line arguments (sys.argv).shellBenviron ::: MYvtXex (os.environ)05environ ::: Extra environment variables (os.environ).shell&exe ::: Python WLj0exe ::: The Python executable.shell>gui ::: etTv GUI ]Qw (OYNR_~jW{I)0Fgui ::: The GUI toolkit to integrate (for interactive plotting, etc.).shellDipython ::: YgSu(RGOu( Ipython Shell0+ipython ::: Use IPython shell if available.shellT z1 ::: kd}DaKvT z10(name ::: The name of this configuration.shellpythonPath ::: j!}DYWNvvd\ nU0\kP _[bNL bOu(O\im|}q-vR{&_OR0 pythonPath ::: A list of directories to search for modules and packages. Write each path on a new line, or separate with the default seperator for this OS.shell4starDir ::: U_RNKv (^sg,j!_)06startDir ::: The start directory (not in script mode).shellBstartScript ::: U_RfBWLNKz _ (^sg,j!_)0DstartupScript ::: The script to run at startup (not in script mode).shellސx Shell0Click to select shell.shells,Pyzo f/nߚ QMcO}fkP NOu(0?Pyzo is open source software and freely available for everyone.splashLf/ <b>Pyzo</b><br />u(ey[xK{v Python IDE@This is Pyzo
the Python IDE for scientific computingsplashrHg,Versionsplash6kd|Hv]f U_ Pyzo kdfueH0 The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. wizardF*WLUQC* NP UQCcvf/u10 ##0 {&_YvQiLNKvb@g e0F*Run cell:* a cell is everything between two lines starting with '##'.wizard**WLjhH* WLvRMjhHvb@g z _01*Run file:* run all the code in the current file.wizard2*WL\hHN;j* WLvRM\hHN;jvb@g z _0I*Run project main file:* run the code in the current project's main file.wizard}DaK ShellConfiguring shellswizardY[z _T' Get coding!wizardPyzo QeGetting started with PyzowizardnW(NRj!_N- sys.path[0] f/NP zz[WN2 (N_\1f/vRMv) sys.argv RG-[p ['']0pIn interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to [''].wizardVW( *}/Vh* N- kP U_vjhHNRhy:0SudRSNWL0Q2[X0bܕjhH0In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc.wizardNRj!_SNsg,j!_WL%Interactive mode vs running as scriptwizard ]fLanguage changedwizardn]Qw|}qNeO^zO`]v]Qwp-vj O`SN_}N Wiki cOfYNJ bOu(vRM]g v]Qwp{O0Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example.wizardPyzo SNetT 5 P N T *GUI ]Qw* vNNW VkdSN Visvis b Matplotlib 2LNR~jW0Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib.wizardPyzo u1QiP N;N}Db$Pyzo consists of two main componentswizardPyzo f/NP ^sSv Python IDE WW(NRՂbjg Vkd^^8iTy[xK{0 [v-vjf/ *|!U*  *eH*0Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*.wizardBPyzo e/cYz.W(}/VhWLz _ve_ (s0 WL0 x)0RPyzo supports several ways to run source code in the editor. (see the 'Run' menu).wizard cv]QwRecommended toolswizardWLz _ Running codewizardxdNJSelect languagewizardShell N[Pz ^j!_WL Vkduv[_xfB Pyzo f/kc^8Va VkdO`SN~|~d[z _bf/W(SNP Shell N-WLz _0Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell.wizardke_StepwizardN*jhHp]Qw* SN~=NP vvb@g jhH0剁{tO`v\hH ˞df_0The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon.wizard(*On}Pi]Qw* cϏOnxvY'}i0@The *Source structure tool* gives an outline of the source code.wizard}/Vh\1f/O`d[z _vW0e'The editor is where you write your codewizard Shell \1f/O`WLz _vW0e*The shell is where your code gets executedwizard(Pyzo |H}Pg_ sW(_v[z _T'=This concludes the Pyzo wizard. Now, get coding and have fun!wizard0kd|HSR)u( 'STR > Pyzo|H' U_ 4This wizard can be opened using 'Help > Pyzo wizard'wizard$kd|HSTR`q` Pyzo vR0AThis wizard helps you get familiarized with the workings of Pyzo.wizard eOv]QwTools for your conveniencewizardN0 Shell > }/ Shell }DaK0 SN}/SRQe *Shell}DaK* OYxdǘ-vbOu([S Python _0Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath.wizardHN *]QwxU* SNxd]Qw ]QwSNe>W(O`UkavW0e N_SNR0Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked.wizardbPryR%cNN ]Qw,We especially recommend the following tools:wizard$kaOR0 Python NR}/Vh-Welcome to the Interactive Editor for Python!wizardPyzo U_R_gu"uNP -v *Shell*0O`SNRQeYP Shell T fBN&L TP Shell N_SNOu(N T v Python rHg,0When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions.wizard*O`SNW( *Shell* N-vcWLcN 1You can execute commands directly in the *shell*,wizard2bO`N_SNW( *}/Vh* N-d[z _q6_QWL07or you can write code in the *editor* and execute that.wizardpyzo-4.4.3/pyzo/resources/tutorial.py0000666000000000000000000001506613123037260016112 0ustar 00000000000000## Introduction """ Welcome to the tutorial for Pyzo! This tutorial should get you familiarized with Pyzo in just a few minutes. If you feel this tutorial contains errors or lacks some information, please let us know via pyzo@googlegroups.com. Pyzo is a cross-platform Python IDE focused on interactivity and introspection, which makes it very suitable for scientific computing. Its practical design is aimed at simplicity and efficiency. Pyzo consists of two main components, the editor and the shell, and uses a set of pluggable tools to help the programmer in various ways. """ ## The editor """ The editor (this window) is where your code is located; it is the central component of Pyzo. In the editor, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc. The right mouse button also enables one to make a file the MAIN FILE of a project. This file can be recognized by its star symbol and its blue filename, and it enables running the file more easily (as we will see later in this tutorial). For larger projects, the Project manager tool can be used to manage your files (also described later in this tutorial) """ ## The shells """ The other main component is the window that holds the shells. When Pyzo starts, a default shell is created. You can add more shells that run simultaneously, and which may be of different Python versions. It is good to know that the shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, which allows you to keep coding and even run code in another shell. Another notable feature is that Pyzo can integrate the event loop of five different GUI toolkits, thus enabling interactive plotting with e.g., Visvis or Matplotlib. Via "Shell > Edit shell configurations", you can edit and add shell configurations. This allows you to for example select the initial directory, or use a custom PYTHONPATH. """ ## The tools """ Via the "Tools" menu, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked. Try the "Source Structure" tool to see the outline of this tutorial! Note that the tools system is designed such that it's quite easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example. Also, Pyzo does not need to restart to see new tools, or to update existing tools. """ ## Running code """ Pyzo supports several ways to run source code in the editor. (see also the "Run" menu). * Run selected lines. If a line is partially selected, the whole line is executed. If there is no selection, Pyzo will run the current line. * Run cell. A cell is everything between two commands starting with '##', such as the headings in this tutorial. Try running the code at the bottom of this cell! * Run file. This runs all the code in the current file. * Run project main file. Runs the code in the current project's main file. Additionally, you can run the current file or the current project's main file as a script. This will first restart the shell to provide a clean environment. The shell is also initialized differently: Things done on shell startup in INTERACTIVE MODE: * sys.argv = [''] * sys.path is prepended with an empty string (current working directory) * The working dir is set to the "Initial directory" of the shell config. * The PYTHONSTARTUP script is run. Things done on shell startup in SCRIPT MODE: * __file__ = * sys.argv = [ ] * sys.path is prepended with the directory containing the script. * The working dir is set to the directory containing the script. Depending on the settings of the Project mananger, the current project directory may also be inserted in sys.path. """ a = 3 b = 4 print('The answer is ' + str(a+b)) ## The menu """ Almost all functionality of Pyzo can be accessed via the menu. For more advanced/specific stuff, you can use the logger tool (see also Settings > Advanced settings) All actions in the menu can be accessed via a shortcut. Change the shortcuts using the shortcut editor: Settings > Edit key mappings. """ ## Introspection """ Pyzo has strong introspection capabilities. Pyzo knows about the objects in the shell, and parses (not runs) the source code in order to detect the structure of your code. This enables powerful instospection such as autocompletion, calltips, interactive help and source structure. """ ## Debugging """ Pyzo supports post-mortem debugging, which means that after something went wrong, you can inspect the stack trace to find the error. The easiest way to start debugging is to press the "Debug" button at the upper right corner of the shells. Once in debug mode, the button becomes expandable, allowing you to see the stack trace and go to any frame you like. (Starting debug mode brings you to the bottom frame.) Changing a frame will make all objects in that frame available in the shell. If possible, Pyzo will also show the source file belonging to that frame, and select the line where the error occurred. Debugging can also be controlled via magic commands, enter "?" in the shell for more information. Below follows an example that you can run to test the debugging. """ import random someModuleVariable = True def getNumber(): return random.choice(range(10)) def foo(): spam = 'yum' eggs = 7 value = bar() def bar(): total = 0 for i1 in range(100): i2 = getNumber() total += i1/i2 return total foo() ## The Project manager """ For working on projects, the Project manager tool can help you to keep an overview of all your files. To open up the Project manager tool, select it from the menu (Tools > Project manager). To add, remove, or edit your projects, click the button with the wrench icon. In the dialog, select 'New project' to add a project and select the directory where your project is located. When the project is added, you can change the Project description (name). You can select wether the project path is added to the Python sys.path. This feature allows you to import project modules from the shell, or from scripts which are not in the project root directory. Note that this feature requires a restart of the shell to take effect (Shell > Restart) The Project manager allows you to switch between your projects easily using the selection box at the top. The tree view shows the files and directories in your project. Files can be hidden using the filter that is specified at the bottom of the Project manager, e.g. !*.pyc to hide all files that have the extension pyc. """ pyzo-4.4.3/pyzo/tools/0000777000000000000000000000000013166630337013026 5ustar 00000000000000pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/0000777000000000000000000000000013166630337016173 5ustar 00000000000000pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/browser.py0000666000000000000000000005561513123037260020231 0ustar 00000000000000import sys import os.path as op import pyzo from pyzo import translate from pyzo.util import zon as ssdf from . import QtCore, QtGui, QtWidgets from . import proxies from .tree import Tree from .utils import cleanpath, isdir class Browser(QtWidgets.QWidget): """ A browser consists of an address bar, and tree view, and other widets to help browse the file system. The browser object is responsible for tying the different browser-components together. It is also provides the API for dealing with starred dirs. """ def __init__(self, parent, config, path=None): QtWidgets.QWidget.__init__(self, parent) # Store config self.config = config # Create star button self._projects = Projects(self) # Create path input/display lineEdit self._pathEdit = PathInput(self) # Create file system proxy self._fsProxy = proxies.NativeFSProxy() self.destroyed.connect(self._fsProxy.stop) # Create tree widget self._tree = Tree(self) self._tree.setPath(cleanpath(self.config.path)) # Create name filter self._nameFilter = NameFilter(self) #self._nameFilter.lineEdit().setToolTip('File filter pattern') self._nameFilter.setToolTip(translate('filebrowser', 'Filename filter')) self._nameFilter.setPlaceholderText(self._nameFilter.toolTip()) # Create search filter self._searchFilter = SearchFilter(self) self._searchFilter.setToolTip(translate('filebrowser', 'Search in files')) self._searchFilter.setPlaceholderText(self._searchFilter.toolTip()) # Signals to sync path. # Widgets that can change the path transmit signal to _tree self._pathEdit.dirUp.connect(self._tree.setFocus) self._pathEdit.dirUp.connect(self._tree.setPathUp) self._pathEdit.dirChanged.connect(self._tree.setPath) self._projects.dirChanged.connect(self._tree.setPath) # self._nameFilter.filterChanged.connect(self._tree.onChanged) # == update self._searchFilter.filterChanged.connect(self._tree.onChanged) # The tree transmits signals to widgets that need to know the path self._tree.dirChanged.connect(self._pathEdit.setPath) self._tree.dirChanged.connect(self._projects.setPath) self._layout() # Set and sync path ... if path is not None: self._tree.SetPath(path) self._tree.dirChanged.emit(self._tree.path()) def getImportWizard(self): # Lazy loading try: return self._importWizard except AttributeError: from .importwizard import ImportWizard self._importWizard = ImportWizard() return self._importWizard def _layout(self): layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0,0,0,0) #layout.setSpacing(6) self.setLayout(layout) # layout.addWidget(self._projects) layout.addWidget(self._pathEdit) layout.addWidget(self._tree) # subLayout = QtWidgets.QHBoxLayout() subLayout.setSpacing(2) subLayout.addWidget(self._nameFilter, 5) subLayout.addWidget(self._searchFilter, 5) layout.addLayout(subLayout) def closeEvent(self, event): #print('Closing browser, stopping file system proxy') super().closeEvent(event) self._fsProxy.stop() def nameFilter(self): #return self._nameFilter.lineEdit().text() return self._nameFilter.text() def searchFilter(self): return {'pattern': self._searchFilter.text(), 'matchCase': self.config.searchMatchCase, 'regExp': self.config.searchRegExp, 'subDirs': self.config.searchSubDirs, } @property def expandedDirs(self): """ The list of the expanded directories. """ return self.parent().config.expandedDirs @property def starredDirs(self): """ A list of the starred directories. """ return [d.path for d in self.parent().config.starredDirs] def dictForStarredDir(self, path): """ Return the dict of the starred dir corresponding to the given path, or None if no starred dir was found. """ if not path: return None for d in self.parent().config.starredDirs: if op.normcase(d['path']) == op.normcase(path): return d else: return None def addStarredDir(self, path): """ Add the given path to the starred directories. """ # Create new dict newProject = ssdf.new() newProject.path = op.normcase(path) # Normalize case! newProject.name = op.basename(path) newProject.addToPythonpath = False # Add it to the config self.parent().config.starredDirs.append(newProject) # Update list self._projects.updateProjectList() def removeStarredDir(self, path): """ Remove the given path from the starred directories. The path must exactlty match. """ # Remove starredDirs = self.parent().config.starredDirs pathn = op.normcase(path) for d in starredDirs: if op.normcase(pathn) == op.normcase(d.path): starredDirs.remove(d) # Update list self._projects.updateProjectList() def test(self, sort=False): items = [] for i in range(self._tree.topLevelItemCount()): item = self._tree.topLevelItem(i) items.append(item) #self._tree.removeItemWidget(item, 0) self._tree.clear() #items.sort(key=lambda x: x._path) items = [item for item in reversed(items)] for item in items: self._tree.addTopLevelItem(item) def currentProject(self): """ Return the ssdf dict for the current project, or None. """ return self._projects.currentDict() class LineEditWithToolButtons(QtWidgets.QLineEdit): """ Line edit to which tool buttons (with icons) can be attached. """ def __init__(self, parent): QtWidgets.QLineEdit.__init__(self, parent) self._leftButtons = [] self._rightButtons = [] def addButtonLeft(self, icon, willHaveMenu=False): return self._addButton(icon, willHaveMenu, self._leftButtons) def addButtonRight(self, icon, willHaveMenu=False): return self._addButton(icon, willHaveMenu, self._rightButtons) def _addButton(self, icon, willHaveMenu, L): # Create button button = QtWidgets.QToolButton(self) L.append(button) # Customize appearance button.setIcon(icon) button.setIconSize(QtCore.QSize(16,16)) button.setStyleSheet("QToolButton { border: none; padding: 0px; }") #button.setStyleSheet("QToolButton { border: none; padding: 0px; background-color:red;}"); # Set behavior button.setCursor(QtCore.Qt.ArrowCursor) button.setPopupMode(button.InstantPopup) # Customize alignment if willHaveMenu: button.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) if sys.platform.startswith('win'): button.setText(' ') # Update self self._updateGeometry() return button def setButtonVisible(self, button, visible): for but in self._leftButtons: if but is button: but.setVisible(visible) for but in self._rightButtons: if but is button: but.setVisible(visible) self._updateGeometry() def resizeEvent(self, event): QtWidgets.QLineEdit.resizeEvent(self, event) self._updateGeometry(True) def showEvent(self, event): QtWidgets.QLineEdit.showEvent(self, event) self._updateGeometry() def _updateGeometry(self, light=False): if not self.isVisible(): return # Init rect = self.rect() # Determine padding and height paddingLeft, paddingRight, height = 1, 1, 0 # for but in self._leftButtons: if but.isVisible(): sz = but.sizeHint() height = max(height, sz.height()) but.move( 1+paddingLeft, (rect.bottom() + 1 - sz.height())/2 ) paddingLeft += sz.width() + 1 # for but in self._rightButtons: if but.isVisible(): sz = but.sizeHint() paddingRight += sz.width() + 1 height = max(height, sz.height()) but.move( rect.right()-1-paddingRight, (rect.bottom() + 1 - sz.height())/2 ) # Set padding ss = "QLineEdit { padding-left: %ipx; padding-right: %ipx} " self.setStyleSheet( ss % (paddingLeft, paddingRight) ) # Set minimum size if not light: fw = QtWidgets.qApp.style().pixelMetric(QtWidgets.QStyle.PM_DefaultFrameWidth) msz = self.minimumSizeHint() w = max(msz.width(), paddingLeft + paddingRight + 10) h = max(msz.height(), height + fw*2 + 2) self.setMinimumSize(w,h) class PathInput(LineEditWithToolButtons): """ Line edit for selecting a path. """ dirChanged = QtCore.Signal(str) # Emitted when the user changes the path (and is valid) dirUp = QtCore.Signal() # Emitted when user presses the up button def __init__(self, parent): LineEditWithToolButtons.__init__(self, parent) # Create up button self._upBut = self.addButtonLeft(pyzo.icons.folder_parent) self._upBut.clicked.connect(self.dirUp) # To receive focus events self.setFocusPolicy(QtCore.Qt.StrongFocus) # Set completion mode self.setCompleter(QtWidgets.QCompleter()) c = self.completer() c.setCompletionMode(c.InlineCompletion) # Set dir model to completer dirModel = QtWidgets.QDirModel(c) dirModel.setFilter(QtCore.QDir.Dirs | QtCore.QDir.NoDotAndDotDot) c.setModel(dirModel) # Connect signals #c.activated.connect(self.onActivated) self.textEdited.connect(self.onTextEdited) #self.textChanged.connect(self.onTextEdited) #self.cursorPositionChanged.connect(self.onTextEdited) def setPath(self, path): """ Set the path to display. Does nothing if this widget has focus. """ if not self.hasFocus(): self.setText(path) self.checkValid() # Reset style if it was invalid first def checkValid(self): # todo: This kind of violates the abstraction of the file system # ok for now, but we should find a different approach someday # Check text = self.text() dir = cleanpath(text) isvalid = text and isdir(dir) and op.isabs(dir) # Apply styling ss = self.styleSheet().replace('font-style:italic; ', '') if not isvalid: ss = ss.replace('QLineEdit {', 'QLineEdit {font-style:italic; ') self.setStyleSheet(ss) # Return return isvalid def event(self, event): # Capture key events to explicitly apply the completion and # invoke checking whether the current text is a valid directory. # Test if QtGui is not None (can happen when reloading tools) if QtGui and isinstance(event, QtGui.QKeyEvent): qt = QtCore.Qt if event.key() in [qt.Key_Tab, qt.Key_Enter, qt.Key_Return]: self.setText(self.text()) # Apply completion self.onTextEdited() # Check if this is a valid dir return True return super().event(event) def onTextEdited(self, dummy=None): text = self.text() if self.checkValid(): self.dirChanged.emit(cleanpath(text)) def focusOutEvent(self, event=None): """ focusOutEvent(event) On focusing out, make sure that the set path is correct. """ if event is not None: QtWidgets.QLineEdit.focusOutEvent(self, event) path = self.parent()._tree.path() self.setPath(path) class Projects(QtWidgets.QWidget): dirChanged = QtCore.Signal(str) # Emitted when the user changes the project def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) # Init variables self._path = '' # Create combo button self._combo = QtWidgets.QComboBox(self) self._combo.setEditable(False) self.updateProjectList() # Create star button self._but = QtWidgets.QToolButton(self) self._but.setIcon( pyzo.icons.star3 ) self._but.setStyleSheet("QToolButton { padding: 0px; }") self._but.setIconSize(QtCore.QSize(18,18)) self._but.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) self._but.setPopupMode(self._but.InstantPopup) # self._menu = QtWidgets.QMenu(self._but) self._menu.triggered.connect(self.onMenuTriggered) self.buildMenu() # Make equal height h = max(self._combo.sizeHint().height(), self._but.sizeHint().height()) self._combo.setMinimumHeight(h); self._but.setMinimumHeight(h) # Connect signals self._but.pressed.connect(self.onButtonPressed) self._combo.activated .connect(self.onProjectSelect) # Layout layout = QtWidgets.QHBoxLayout(self) self.setLayout(layout) layout.addWidget(self._but) layout.addWidget(self._combo) layout.setSpacing(2) layout.setContentsMargins(0,0,0,0) def currentDict(self): """ Return the current project-dict, or None. """ path = self._combo.itemData(self._combo.currentIndex()) return self.parent().dictForStarredDir(path) def setPath(self, path): self._path = path # Find project index projectIndex, L = 0, 0 pathn = op.normcase(path) + op.sep for i in range(self._combo.count()): projectPath = self._combo.itemData(i) + op.sep if pathn.startswith(projectPath) and len(projectPath) > L: projectIndex, L = i, len(projectPath) # Select project or not ... self._combo.setCurrentIndex(projectIndex) if projectIndex: self._but.setIcon( pyzo.icons.star2 ) self._but.setMenu(self._menu) else: self._but.setIcon( pyzo.icons.star3 ) self._but.setMenu(None) def updateProjectList(self): # Get sorted version of starredDirs starredDirs = self.parent().starredDirs starredDirs.sort(key=lambda p:self.parent().dictForStarredDir(p).name.lower()) # Refill the combo box self._combo.clear() if starredDirs: self._combo.addItem(translate('filebrowser', 'Projects:'), '') # No-project item for p in starredDirs: name = self.parent().dictForStarredDir(p).name self._combo.addItem(name, p) else: self._combo.addItem( translate('filebrowser', 'Click star to bookmark current dir'), '') def buildMenu(self): menu = self._menu menu.clear() # Add action to remove bookmark action = menu.addAction(translate('filebrowser', 'Remove project')) action._id = 'remove' action.setCheckable(False) # Add action to change name action = menu.addAction(translate('filebrowser', 'Change project name')) action._id = 'name' action.setCheckable(False) menu.addSeparator() # Add check action for adding to Pythonpath action = menu.addAction(translate('filebrowser', 'Add path to Python path')) action._id = 'pythonpath' action.setCheckable(True) d = self.currentDict() if d: checked = bool( d and d['addToPythonpath'] ) action.setChecked(checked) # Add action to cd to the project directory action = menu.addAction(translate('filebrowser', 'Go to this directory in the current shell')) action._id = 'cd' action.setCheckable(False) def onMenuTriggered(self, action): d = self.currentDict() if not d: return if action._id == 'remove': # Remove this project self.parent().removeStarredDir(d.path) elif action._id == 'name': # Open dialog to ask for name name = QtWidgets.QInputDialog.getText(self.parent(), translate('filebrowser', 'Project name'), translate('filebrowser', 'New project name:'), text=d['name'], ) if isinstance(name, tuple): name = name[0] if name[1] else '' if name: d['name'] = name self.updateProjectList() elif action._id == 'pythonpath': # Flip add-to-pythonpath flag d['addToPythonpath'] = not d['addToPythonpath'] elif action._id == 'cd': # cd to the directory shell = pyzo.shells.getCurrentShell() if shell: shell.executeCommand('cd '+d.path+'\n') def onButtonPressed(self): if self._but.menu(): # The directory is starred and has a menu. The user just # used the menu (or not). Update so it is up-to-date next time. self.buildMenu() else: # Not starred right now, create new project! self.parent().addStarredDir(self._path) # Update self.setPath(self._path) def onProjectSelect(self, index): path = self._combo.itemData(index) if path: # Go to dir self.dirChanged.emit(path) else: # Dummy item, reset self.setPath(self._path) class NameFilter(LineEditWithToolButtons): """ Combobox to filter by name. """ filterChanged = QtCore.Signal() def __init__(self, parent): LineEditWithToolButtons.__init__(self, parent) # Create tool button, and attach the menu self._menuBut = self.addButtonRight(pyzo.icons['filter'], True) self._menu = QtWidgets.QMenu(self._menuBut) self._menu.triggered.connect(self.onMenuTriggered) self._menuBut.setMenu(self._menu) # # Add common patterns for pattern in ['*', '!hidden', '!*.pyc !hidden', '*.py *.pyw', '*.py *.pyw *.pyx *.pxd', '*.h *.c *.cpp']: self._menu.addAction(pattern) # Emit signal when value is changed self._lastValue = '' self.returnPressed.connect(self.checkFilterValue) self.editingFinished.connect(self.checkFilterValue) # Ensure the namefilter is in the config and initialize config = self.parent().config if 'nameFilter' not in config: config.nameFilter = '!*.pyc' self.setText(config.nameFilter) def setText(self, value, test=False): """ To initialize the name filter. """ QtWidgets.QLineEdit.setText(self, value) if test: self.checkFilterValue() self._lastValue = value def checkFilterValue(self): value = self.text() if value != self._lastValue: self.parent().config.nameFilter = value self._lastValue = value self.filterChanged.emit() def onMenuTriggered(self, action): self.setText(action.text(), True) class SearchFilter(LineEditWithToolButtons): """ Line edit to do a search in the files. """ filterChanged = QtCore.Signal() def __init__(self, parent): LineEditWithToolButtons.__init__(self, parent) # Create tool button, and attach the menu self._menuBut = self.addButtonRight(pyzo.icons['magnifier'], True) self._menu = QtWidgets.QMenu(self._menuBut) self._menu.triggered.connect(self.onMenuTriggered) self._menuBut.setMenu(self._menu) self.buildMenu() # Create cancel button self._cancelBut = self.addButtonRight(pyzo.icons['cancel']) self._cancelBut.setVisible(False) # Keep track of last value of search (initialized empty) self._lastValue = '' # Connect signals self._cancelBut.pressed.connect(self.onCancelPressed) self.textChanged.connect(self.updateCancelButton) self.editingFinished.connect(self.checkFilterValue) self.returnPressed.connect(self.forceFilterChanged) def onCancelPressed(self): """ Clear text or build menu. """ if self.text(): QtWidgets.QLineEdit.clear(self) self.checkFilterValue() else: self.buildMenu() def checkFilterValue(self): value = self.text() if value != self._lastValue: self._lastValue = value self.filterChanged.emit() def forceFilterChanged(self): self._lastValue = self.text() self.filterChanged.emit() def updateCancelButton(self, text): visible = bool(self.text()) self.setButtonVisible(self._cancelBut, visible) def buildMenu(self): config = self.parent().config menu = self._menu menu.clear() map = [ ('searchMatchCase', False, translate("filebrowser", "Match case")), ('searchRegExp', False, translate("filebrowser", "RegExp")), ('searchSubDirs', True, translate("filebrowser", "Search in subdirs")) ] # Fill menu for option, default, description in map: if option is None: menu.addSeparator() else: # Make sure the option exists if option not in config: config[option] = default # Make action in menu action = menu.addAction(description) action._option = option action.setCheckable(True) action.setChecked( bool(config[option]) ) def onMenuTriggered(self, action): config = self.parent().config option = action._option # Swap this option if option in config: config[option] = not config[option] else: config[option] = True # Update self.filterChanged.emit() pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/importwizard.py0000666000000000000000000004714613123037261021302 0ustar 00000000000000""" The import wizard helps the user importing CSV-like data from a file into a numpy array. The wizard containst three pages: SelectFilePage: - The user selects a file and previews its contents (or, the beginning of it) SetParametersPage: - The user selects delimiters, etc. and selects which columns to import - A preview of the data in tabualar form is shown, with colors indicating how the file is parsed: yellow for header rows, green for the comments column and red for values that could not be parsed ResultPage: - The wizard shows the generated code that is to be used to import the file according to the settings - The user chooses to execute the code in the current shell or paste the code into the editor """ import unicodedata import os.path as op import pyzo.codeeditor from . import QtCore, QtGui, QtWidgets from pyzo import translate # All keywords in Python 2 and 3. Obtained using: import keyword; keyword.kwlist # Merged from Py2 and 3 keywords = ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield'] class CodeView( pyzo.codeeditor.IndentationGuides, pyzo.codeeditor.CodeFolding, pyzo.codeeditor.Indentation, pyzo.codeeditor.HomeKey, pyzo.codeeditor.EndKey, pyzo.codeeditor.NumpadPeriodKey, pyzo.codeeditor.AutoIndent, pyzo.codeeditor.PythonAutoIndent, pyzo.codeeditor.SyntaxHighlighting, pyzo.codeeditor.CodeEditorBase): #CodeEditorBase must be the last one in the list """ Code viewer, stripped down version of the CodeEditor """ pass class SelectFilePage(QtWidgets.QWizardPage): """ First page of the wizard, select file and preview contents """ def __init__(self): QtWidgets.QWizardPage.__init__(self) self.setTitle(translate('importwizard', 'Select file')) self.txtFilename = QtWidgets.QLineEdit() self.btnBrowse = QtWidgets.QPushButton(translate('importwizard', 'Browse...')) self.preview = QtWidgets.QPlainTextEdit() self.preview.setReadOnly(True) vlayout = QtWidgets.QVBoxLayout() hlayout = QtWidgets.QHBoxLayout() hlayout.addWidget(self.txtFilename) hlayout.addWidget(self.btnBrowse) vlayout.addLayout(hlayout) vlayout.addWidget(QtWidgets.QLabel(translate('importwizard', 'Preview:'))) vlayout.addWidget(self.preview) self.setLayout(vlayout) self.registerField('fname', self.txtFilename) self.btnBrowse.clicked.connect(self.onBrowseClicked) self.txtFilename.editingFinished.connect(self.updatePreview) self._isComplete = False def onBrowseClicked(self): # Difference between PyQt4 and PySide: PySide returns filename, filter # while PyQt4 returns only the filename filename = QtWidgets.QFileDialog.getOpenFileName(filter = 'Text files (*.txt *.csv);;All files (*.*)') if isinstance(filename, tuple): filename = filename[0] filename = str(filename).replace('/', op.sep) # Show native file separator self.txtFilename.setText(filename) self.updatePreview() def updatePreview(self): filename = self.txtFilename.text() if not filename: data = '' self._isComplete = False self.wizard().setPreviewData(None) else: try: with open(filename,'rb') as file: maxsize = 5000 data = file.read(maxsize) more = bool(file.read(1)) # See if there is more data available data = data.decode('ascii', 'replace') self.wizard().setPreviewData(data) if more: data += '...' self._isComplete = True # Allow to proceed to the next page except Exception as e: data = str(e) self._isComplete = False self.wizard().setPreviewData(None) self.preview.setPlainText(data) self.completeChanged.emit() def isComplete(self): return self._isComplete class SetParametersPage(QtWidgets.QWizardPage): def __init__(self): QtWidgets.QWizardPage.__init__(self) self.setTitle("Select parameters") self._columnNames = None def genComboBox(choices): cbx = QtWidgets.QComboBox() for choice in choices: cbx.addItem(choice) cbx.setEditable(True) return cbx self.cbxDelimiter = genComboBox(",;") self.cbxComments = genComboBox("#%'") self.sbSkipHeader = QtWidgets.QSpinBox() self.preview = QtWidgets.QTableWidget() self.preview.setSelectionModel(QtCore.QItemSelectionModel(self.preview.model())) # Work-around for reference tracking bug in PySide self.preview.setSelectionBehavior(self.preview.SelectColumns) self.preview.setSelectionMode(self.preview.MultiSelection) # Layout formlayout = QtWidgets.QFormLayout() formlayout.addRow('Delimiter', self.cbxDelimiter) formlayout.addRow('Comments', self.cbxComments) formlayout.addRow('Header rows to skip', self.sbSkipHeader) layout = QtWidgets.QVBoxLayout() layout.addLayout(formlayout) layout.addWidget(QtWidgets.QLabel( translate('importwizard', 'Select columns to import:'))) layout.addWidget(self.preview) self.setLayout(layout) # Wizard fields self.registerField('delimiter', self.cbxDelimiter, "currentText") self.registerField('comments', self.cbxComments, "currentText") self.registerField('skip_header', self.sbSkipHeader) # Signals self.cbxComments.editTextChanged.connect(self.updatePreview) self.cbxDelimiter.editTextChanged.connect(self.updatePreview) self.sbSkipHeader.valueChanged.connect(self.updatePreview) self.preview.verticalHeader().sectionClicked.connect(self.onRowHeaderClicked) def columnNames(self): if self._columnNames is None: return list(['d' + str(i + 1) for i in range(self.preview.columnCount()-1)]) return list(self._columnNames) def updateHorizontalHeaderLabels(self): self.preview.setHorizontalHeaderLabels(self.columnNames() + ['Comments']) def onRowHeaderClicked(self, row): names = self.parseColumnNames(row) self._columnNames = names self.updateHorizontalHeaderLabels() def parseColumnNames(self, row): """ Use the data in the given row to create column names. First, try the data in the data columns. If these are all empty, use the comments column, split by the given delimiter. Names are fixed up to be valid Python 2 / Python 3 identifiers (chars a-z A-Z _ 0-9 , no Python 2 or 3 keywords, not starting with 0-9) returns: list of names, exactly as many as there are data columns """ names = [] columnCount = self.preview.columnCount()-1 for col in range(columnCount): cell = self.preview.item(row, col) if cell is None: names.append('') else: names.append(cell.text().strip()) # If no values found, try the comments: if not any(names): cell = self.preview.item(row, columnCount) if cell is not None: comment = cell.text()[1:].strip() # Remove comment char and whitespace delimiter = self.cbxDelimiter.currentText() names = list(name.strip() for name in comment.split(delimiter)) # Ensure names is exactly columnCount long names += [''] * columnCount names = names[:columnCount] # Fixup names def fixname(name, col): # Remove accents name = ''.join(c for c in unicodedata.normalize('NFD', name) if unicodedata.category(c) != 'Mn') # Replace invalid chars with _ name = ''.join(c if (c.lower()>='a' and c.lower()<='z') or (c>='0' and c<='9') else '_' for c in name) if not name: return 'd' + str(col) if name[0]>='0' and name<='9': name = 'd' + name if name in keywords: name = name + '_' return name names = list(fixname(name, i + 1) for i, name in enumerate(names)) return names def selectedColumns(self): """ Returns a tuple of the columns that are selected, or None if no columns are selected """ selected = [] for selrange in self.preview.selectionModel().selection(): selected += range(selrange.left(), selrange.right() + 1) selected.sort() if not selected: return None else: return tuple(selected) def initializePage(self): self.updatePreview() def updatePreview(self): # Get settings from the currently specified values in the wizard comments = self.cbxComments.currentText() delimiter = self.cbxDelimiter.currentText() skipheader = self.sbSkipHeader.value() if not comments or not delimiter: return # Store current selection, will be restored at the end selectedColumns = self.selectedColumns() # Clear the complete table self.preview.clear() self.preview.setColumnCount(0) self.preview.setRowCount(0) # Iterate through the source file line by line # Process like genfromtxt, with names = False # However, we do keep the header lines and comments; we show them in # distinct colors so that the user can see how the data is selected source = iter(self.wizard().previewData().splitlines()) def split_line(line): """Chop off comments, strip, and split at delimiter.""" line, sep, commentstr = line.partition(comments) line = line.strip(' \r\n') if line: return line.split(delimiter), sep + commentstr else: return [], sep + commentstr # Insert comments column self.preview.insertColumn(0) ncols = 0 # Number of columns, excluding comments column headerrows = 0 # Number of header rows, including empty header rows inheader = True for lineno, line in enumerate(source): fields, commentstr = split_line(line) # Process header like genfromtxt, with names = False if lineno>=skipheader and fields: inheader = False if inheader: headerrows = lineno + 1 # +1 since lineno = 0 is the first line self.preview.insertRow(lineno) # Add columns to fit all fields while len(fields)>ncols: self.preview.insertColumn(ncols) ncols += 1 # Add fields to the table for col, field in enumerate(fields): cell = QtWidgets.QTableWidgetItem(field) if not inheader: try: float(field) except ValueError: cell.setBackground(QtGui.QBrush(QtGui.QColor("pink"))) self.preview.setItem(lineno,col, cell) # Add the comment cell = QtWidgets.QTableWidgetItem(commentstr) cell.setBackground(QtGui.QBrush(QtGui.QColor("lightgreen"))) self.preview.setItem(lineno,ncols, cell) # Colorize the header cells. This is done as the last step, since # meanwhile new columns (and thus new cells) may have been added for row in range(headerrows): for col in range(ncols): cell = self.preview.item(row, col) if not cell: cell = QtWidgets.QTableWidgetItem('') self.preview.setItem(row, col, cell) cell.setBackground(QtGui.QBrush(QtGui.QColor("khaki"))) # Try to restore selection if selectedColumns is not None: for column in selectedColumns: self.preview.selectColumn(column) # Restore column names self.updateHorizontalHeaderLabels() class ResultPage(QtWidgets.QWizardPage): """ The resultpage lets the user select wether to import the data as a single 2D-array, or as one variable (1D-array) per column Then, the code to do the import is generated (Py2 and Py3 compatible). This code can be executed in the current shell, or copied into the current editor """ def __init__(self): QtWidgets.QWizardPage.__init__(self) self.setTitle("Execute import") self.setButtonText(QtWidgets.QWizard.FinishButton, translate('importwizard', 'Close')) self.rbAsArray = QtWidgets.QRadioButton(translate('importwizard', 'Import data as single array')) self.rbPerColumn = QtWidgets.QRadioButton(translate('importwizard', 'Import data into one variable per column')) self.rbAsArray.setChecked(True) self.chkInvalidRaise = QtWidgets.QCheckBox(translate('importwizard', 'Raise error upon invalid data')) self.chkInvalidRaise.setChecked(True) self.codeView = CodeView() self.codeView.setParser('python') self.codeView.setZoom(pyzo.config.view.zoom) self.codeView.setFont(pyzo.config.view.fontname) self.btnExecute = QtWidgets.QPushButton('Execute in current shell') self.btnInsert = QtWidgets.QPushButton('Paste into current file') layout = QtWidgets.QVBoxLayout() layout.addWidget(self.rbAsArray) layout.addWidget(self.rbPerColumn) layout.addWidget(self.chkInvalidRaise) layout.addWidget(QtWidgets.QLabel('Resulting import code:')) layout.addWidget(self.codeView) layout.addWidget(self.btnExecute) layout.addWidget(self.btnInsert) self.setLayout(layout) self.registerField('invalid_raise', self.chkInvalidRaise) self.btnExecute.clicked.connect(self.onBtnExecuteClicked) self.btnInsert.clicked.connect(self.onBtnInsertClicked) self.rbAsArray.clicked.connect(self.updateCode) self.rbPerColumn.clicked.connect(self.updateCode) self.chkInvalidRaise.stateChanged.connect(lambda state: self.updateCode()) def initializePage(self): self.updateCode() def updateCode(self): perColumn = self.rbPerColumn.isChecked() if perColumn: columnNames = self.wizard().field('columnnames') usecols = self.wizard().field('usecols') if usecols is not None: # User selected a subset of all columns # Pick corrsponding column names columnNames = [columnNames[i] for i in usecols] variables = ', '.join(columnNames) else: variables = 'data' code = "import numpy\n" code += variables + " = numpy.genfromtxt(\n" for param, default in ( ('fname', None), ('skip_header', 0), ('comments', '#'), ('delimiter', None), ('usecols', None), ('invalid_raise', True), ): value = self.wizard().field(param) if value != default: code += "\t%s = %r,\n" % (param, value) if perColumn: code += '\tunpack = True,\n' code += '\t)\n' self.codeView.setPlainText(code) def getCode(self): return self.codeView.toPlainText() def onBtnExecuteClicked(self): shell = pyzo.shells.getCurrentShell() if shell is None: QtWidgets.QMessageBox.information(self, translate('importwizard', 'Import data wizard'), translate('importwizard', 'No current shell active')) return shell.executeCode(self.getCode(), 'importwizard') def onBtnInsertClicked(self): editor = pyzo.editors.getCurrentEditor() if editor is None: QtWidgets.QMessageBox.information(self, translate('importwizard', 'Import data wizard'), translate('importwizard', 'No current file open')) return code = self.getCode() # Format tabs/spaces according to editor setting if editor.indentUsingSpaces(): code = code.replace('\t', ' ' * editor.indentWidth()) # insert code at start of line cursor = editor.textCursor() cursor.movePosition(cursor.StartOfBlock) cursor.insertText(code) class ImportWizard(QtWidgets.QWizard): def __init__(self): QtWidgets.QWizard.__init__(self) self.setMinimumSize(500,400) self.resize(700,500) self.setPreviewData(None) self.selectFilePage = SelectFilePage() self.setParametersPage = SetParametersPage() self.resultPage = ResultPage() self.addPage(self.selectFilePage) self.addPage(self.setParametersPage) self.addPage(self.resultPage) self.setWindowTitle(translate('importwizard', 'Import data')) self.currentIdChanged.connect(self.onCurrentIdChanged) def onCurrentIdChanged(self, id): # Hide the 'cancel' button on the last page if self.nextId() == -1: self.button(QtWidgets.QWizard.CancelButton).hide() else: self.button(QtWidgets.QWizard.CancelButton).show() def open(self, filename): if self.isVisible(): QtWidgets.QMessageBox.information(self, translate('importwizard', 'Import data wizard'), translate('importwizard', 'The import data wizard is already open')) return self.restart() self.selectFilePage.txtFilename.setText(filename) self.selectFilePage.updatePreview() self.show() def field(self, name): # Allow access to all data via field, some properties are not avaialble # as actual controls and therefore we have to handle them ourselves if name == 'usecols': return self.setParametersPage.selectedColumns() elif name == 'columnnames': return self.setParametersPage.columnNames() else: return QtWidgets.QWizard.field(self, name) def setPreviewData(self, data): self._previewData = data def previewData(self): if self._previewData is None: raise RuntimeError('Preview data not loaded') return self._previewData if __name__=='__main__': iw = ImportWizard() iw.open('test.txt') pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/proxies.py0000666000000000000000000003104213153506151020226 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013 Almar Klein """ This module defines file system proxies to be used for the file browser. For now, there is only one: the native file system. But in time, we may add proxies for ftp, S3, remote computing, etc. This may seem like an awkward way to use the file system, but (with small modifications) this approach can probably be used also for opening/saving files to any file system that we implement here. This will make Pyzo truly powerful for use in remote computing. """ import time import threading from queue import Queue, Empty import os.path as op from . import QtCore from .utils import isdir class Task: """ Task(**params) A task object. Accepts params as keyword arguments. When overloading, dont forget to set __slots__. Overload and implement the 'process' method to create a task. Then use pushTask on a pathProxy object. Use the 'result' method to obtain the result (or raise an error). """ __slots__ = ['_params', '_result', '_error'] def __init__(self, **params): if not params: params = None self._params = params self._result = None self._error = None def process(self, proxy, **params): """ process(pathProxy, **params): This is the method that represents the task. Overload this to make the task do what is intended. """ pass def _run(self, proxy): """ Run the task. Don't overload or use this. """ try: params = self._params or {} self._result = self.process(proxy, **params) except Exception as err: self._error = 'Task failed: {}:\n{}'.format(self, str(err)) print(self._error) def result(self): """ Get the result. Raises an error if the task failed. """ if self._error: raise Exception(self._error) else: return self._result ## Proxy classes for directories and files class PathProxy(QtCore.QObject): """ Proxy base class for DirProxy and FileProxy. A proxy object is used to get information on a path (folder contents, or file modification time), and keep being updated about changes in that information. One uses an object by connecting to the 'changed' or 'deleted' signal. Use setActive(True) to receive updates on these signals. If the proxy is no longer needed, use close() to unregister it. """ changed = QtCore.Signal() deleted = QtCore.Signal() errored = QtCore.Signal(str) # Or should we pass an error per 'action'? taskFinished = QtCore.Signal(Task) def __init__(self, fsProxy, path): QtCore.QObject.__init__(self) self._lock = threading.RLock() self._fsProxy = fsProxy self._path = path self._cancelled = False # For tasks self._pendingTasks = [] self._finishedTasks = [] def __repr__(self): return '<{} "{}">'.format(self.__class__.__name__, self._path) def path(self): """ Get the path of this proxy. """ return self._path def track(self): """ Start tracking this proxy object in the idle time of the FSProxy thread. """ self._fsProxy._track(self) def push(self): """ Process this proxy object asap; the object is put in the queue of the FSProxy, so it is updated as fast as possible. """ self._cancelled = False self._fsProxy._push(self) def cancel(self): """ Stop tracking this proxy object. Cancel processing if this object was in the queue. """ self._fsProxy._unTrack(self) self._cancelled = True def pushTask(self, task): """ pushTask(task) Give a task to the proxy to be executed in the FSProxy thread. The taskFinished signal will be emitted with the given task when it is done. """ shouldPush = False with self._lock: if not self._pendingTasks: shouldPush = True self._pendingTasks.append(task) if shouldPush: self.push() def _processTasks(self): # Get pending tasks with self._lock: pendingTasks = self._pendingTasks self._pendingTasks = [] # Process pending tasks finishedTasks = [] for task in pendingTasks: task._run(self) finishedTasks.append(task) # Emit signal if there are finished tasks for task in finishedTasks: self.taskFinished.emit(task) class DirProxy(PathProxy): """ Proxy object for a directory. Obtain an instance of this class using filesystemProx.dir() """ def __init__(self, *args): PathProxy.__init__(self, *args) self._dirs = set() self._files = set() def dirs(self): with self._lock: return set(self._dirs) def files(self): with self._lock: return set(self._files) def _process(self, forceUpdate=False): # Get info dirs = self._fsProxy.listDirs(self._path) files = self._fsProxy.listFiles(self._path) # Is it deleted? if dirs is None or files is None: self.deleted.emit() return # All seems ok. Update if necessary dirs, files = set(dirs), set(files) if (dirs != self._dirs) or (files != self._files): with self._lock: self._dirs, self._files = dirs, files self.changed.emit() elif forceUpdate: self.changed.emit() class FileProxy(PathProxy): """ Proxy object for a file. Obtain an instance of this class using filesystemProx.dir() """ def __init__(self, *args): PathProxy.__init__(self, *args) self._modified = 0 def modified(self): with self._lock: return self._modified def _process(self, forceUpdate=False): # Get info modified = self._fsProxy.modified(self._path) # Is it deleted? if modified is None: self.deleted.emit() return # All seems ok. Update if necessary if modified != self._modified: with self._lock: self._modified = modified self.changed.emit() elif forceUpdate: self.changed.emit() def read(self): pass # ? def save(self): pass # ? ## Proxy classes for the file system class BaseFSProxy(threading.Thread): """ Abstract base class for file system proxies. The file system proxy defines an interface that subclasses can implement to "become" a usable file system proxy. This class implements the polling of information for the DirProxy and FileProxy objects, and keeping them up-to-date. For this purpose it keeps a set of PathProxy instances that are polled when idle. There is also a queue for items that need processing asap. This is where objects are put in when they are activated. This class has methods to use the file system (list files and directories, etc.). These can be used directly, but may be slow. Therefor it is recommended to use the FileProxy and DirProxy objects instead. """ # Define how often the registered dirs and files are checked IDLE_TIMEOUT = 1.0 # For testing to induce extra delay. Should normally be close to zero, # but not exactly zero! IDLE_DELAY = 0.01 QUEUE_DELAY = 0.01 # 0.5 def __init__(self): threading.Thread.__init__(self) self.setDaemon(True) # self._interrupt = False self._exit = False # self._lock = threading.RLock() self._q = Queue() self._pathProxies = set() # self.start() def _track(self, pathProxy): # todo: use weak references with self._lock: self._pathProxies.add(pathProxy) def _unTrack(self, pathProxy): with self._lock: self._pathProxies.discard(pathProxy) def _push(self, pathProxy): # todo: use weak ref here too? self._q.put(pathProxy) self._interrupt = True def stop(self, *, timeout=1.0): with self._lock: self._exit = True self._interrupt = True self._pathProxies.clear() self.join(timeout) def dir(self, path): """ Convenience function to create a new DirProxy object. """ return DirProxy(self, path) def file(self, path): """ Convenience function to create a new FileProxy object. """ return FileProxy(self, path) def run(self): try: try: self._run() except Exception as err: if Empty is None or self._lock is None: pass # Shutting down ... else: print('Exception in proxy thread: ' + str(err)) except Exception: pass # Interpreter is shutting down def _run(self): last_sleep = time.time() while True: # Check and reset self._interrupt = False if self._exit: return # Sleep now = time.time() if now - last_sleep > 0.1: last_sleep = now time.sleep(0.05) try: # Process items from the queue item = self._q.get(True, self.IDLE_TIMEOUT) if item is not None and not item._cancelled: self._processItem(item, True) except Empty: # Queue empty, check items periodically self._idle() def _idle(self): # Make a copy of the set if item with self._lock: items = set(self._pathProxies) # Process them for item in items: if self._interrupt: return self._processItem(item) def _processItem(self, pathProxy, forceUpdate=False): # Slow down a bit if forceUpdate: time.sleep(self.QUEUE_DELAY) else: time.sleep(self.IDLE_DELAY) # Process try: pathProxy._process(forceUpdate) except Exception as err: pathProxy.errored.emit(str(err)) # Process tasks pathProxy._processTasks() # To overload ... def listDirs(self, path): raise NotImplemented() # Should rerurn None if it does not exist def listFiles(self, path): raise NotImplemented() # Should rerurn None if it does not exist def modified(self, path): raise NotImplemented() # Should rerurn None if it does not exist def fileSize(self, path): raise NotImplemented() # Should rerurn None if it does not exist def read(self, path): raise NotImplemented() # Should rerurn None if it does not exist def write(self, path, bb): raise NotImplemented() def rename(self, path): raise NotImplemented() def remove(self, path): raise NotImplemented() def createDir(self, path): raise NotImplemented() import os class NativeFSProxy(BaseFSProxy): """ File system proxy for the native file system. """ def listDirs(self, path): if isdir(path): pp = [op.join(path, p) for p in os.listdir(path)] return [str(p) for p in pp if isdir(p)] def listFiles(self, path): if isdir(path): pp = [op.join(path, p) for p in os.listdir(path)] return [str(p) for p in pp if op.isfile(p)] def modified(self, path): if op.isfile(path): return op.getmtime(path) def fileSize(self, path): if op.isfile(path): return op.getsize(path) def read(self, path): if op.isfile(path): return open(path, 'rb').read() def write(self, path, bb): with open(path, 'wb') as f: f.write(bb) def rename(self, path1, path2): os.rename(path1, path2) def remove(self, path): if op.isfile(path): os.remove(path) elif isdir(path): os.rmdir(path) def createDir(self, path): if not isdir(path): os.makedirs(path) pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/tasks.py0000666000000000000000000002315313123037261017664 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013 Almar Klein """ Define tasks that can be executed by the file browser. These inherit from proxies.Task and implement that specific interface. """ import re from . import proxies class SearchTask(proxies.Task): __slots__ = [] def process(self, proxy, pattern=None, matchCase=False, regExp=False, **rest): # Quick test if not pattern: return # Get text text = self._getText(proxy) if not text: return # Get search text. Deal with case sensitivity searchText = text if not matchCase: searchText = searchText.lower() pattern = pattern.lower() # Search indices if regExp: indices = self._getIndicesRegExp(searchText, pattern) else: indices = self._getIndicesNormal1(searchText, pattern) # Return as lines if indices: return self._indicesToLines(text, indices) else: return [] def _getText(self, proxy): # Init path = proxy.path() fsProxy = proxy._fsProxy # Get file size try: size = fsProxy.fileSize(path) except NotImplementedError: pass size = size or 0 # Search all Python files. Other files need be < xx bytes if path.lower().endswith('.py') or size < 100*1024: pass else: return None # Get text bb = fsProxy.read(path) if bb is None: return try: return bb.decode('utf-8') except UnicodeDecodeError: # todo: right now we only do utf-8 return None def _getIndicesRegExp(self, text, pattern): indices = [] for match in re.finditer(pattern, text, re.MULTILINE | re.UNICODE): indices.append( match.start() ) return indices def _getIndicesNormal1(self, text, pattern): indices = [] i = -1 while True: i = text.find(pattern,i+1) if i>=0: indices.append(i) else: break return indices def _getIndicesNormal2(self, text, pattern): indices = [] i = 0 for line in text.splitlines(True): i2 = line.find(pattern) if i2>=0: indices.append(i+i2) i += len(line) return indices def _indicesToLines(self, text, indices): # Determine line endings LE = self._determineLineEnding(text) # Obtain line and line numbers lines = [] for i in indices: # Get linenr and index of the line linenr = text.count(LE, 0, i) + 1 i1 = text.rfind(LE, 0, i) i2 = text.find(LE, i) # Get line and strip if i1<0: i1 = 0 line = text[i1:i2].strip()[:80] # Store lines.append( (linenr, repr(line)) ) # Set result return lines def _determineLineEnding(self, text): """ function to determine quickly whether LF or CR is used as line endings. Windows endings (CRLF) result in LF (you can split lines with either char). """ i = 0 LE = '\n' while i < len(text): i += 128 LF = text.count('\n', 0, i) CR = text.count('\r', 0, i) if LF or CR: if CR > LF: LE = '\r' break return LE class PeekTask(proxies.Task): """ To peek the high level structure of a task. """ __slots__ = [] stringStart = re.compile('("""|\'\'\'|"|\')|#') endProgs = { "'": re.compile(r"(^|[^\\])(\\\\)*'"), '"': re.compile(r'(^|[^\\])(\\\\)*"'), "'''": re.compile(r"(^|[^\\])(\\\\)*'''"), '"""': re.compile(r'(^|[^\\])(\\\\)*"""') } definition = re.compile(r'^(def|class)\s*(\w*)') def process(self, proxy): path = proxy.path() fsProxy = proxy._fsProxy # Search only Python files if not path.lower().endswith('.py'): return None # Get text bb = fsProxy.read(path) if bb is None: return try: text = bb.decode('utf-8') del bb except UnicodeDecodeError: # todo: right now we only do utf-8 return # Parse return list(self._parseLines(text.splitlines())) def _parseLines(self, lines): stringEndProg = None linenr = 0 for line in lines: linenr += 1 # If we are in a triple-quoted multi-line string, find the end if stringEndProg is None: pos = 0 else: endMatch = stringEndProg.search(line) if endMatch is None: continue else: pos = endMatch.end() stringEndProg = None # Now process all tokens while True: match = self.stringStart.search(line, pos) if pos == 0: # If we are at the start of the line, see if we have a top-level class or method definition end = len(line) if match is None else match.start() definitionMatch = self.definition.search(line[:end]) if definitionMatch is not None: if definitionMatch.group(1) == 'def': yield (linenr, 'def ' + definitionMatch.group(2)) else: yield (linenr, 'class ' + definitionMatch.group(2)) if match is None: break # Go to next line if match.group()=="#": # comment # yield 'C:' break # Go to next line else: endMatch = self.endProgs[match.group()].search(line[match.end():]) if endMatch is None: if len(match.group()) == 3 or line.endswith('\\'): # Multi-line string stringEndProg = self.endProgs[match.group()] break else: # incorrect end of single-quoted string break # yield 'S:' + (match.group() + line[match.end():][:endMatch.end()]) pos = match.end() + endMatch.end() class DocstringTask(proxies.Task): __slots__ = [] def process(self, proxy): path = proxy.path() fsProxy = proxy._fsProxy # Search only Python files if not path.lower().endswith('.py'): return None # Get text bb = fsProxy.read(path) if bb is None: return try: text = bb.decode('utf-8') del bb except UnicodeDecodeError: # todo: right now we only do utf-8 return # Find docstring lines = [] delim = None # Not started, in progress, done count = 0 for line in text.splitlines(): count += 1 if count > 200: break # Try to find a start if not delim: if line.startswith('"""'): delim = '"""' line = line.lstrip('"') elif line.startswith("'''"): delim = "'''" line = line.lstrip("'") # Try to find an end (may be on the same line as the start) if delim and delim in line: line = line.split(delim, 1)[0] count = 999999999 # Stop; we found the end # Add this line if delim: lines.append(line) # Limit number of lines if len(lines) > 16: lines = lines[:16] + ['...'] # Make text and strip doc = '\n'.join(lines) doc = doc.strip() return doc class RenameTask(proxies.Task): __slots__ = [] def process(self, proxy, newpath=None, removeold=False): path = proxy.path() fsProxy = proxy._fsProxy if not newpath: return if removeold: # Works for files and dirs fsProxy.rename(path, newpath) # The fsProxy will detect that this file is now deleted else: # Work only for files: duplicate # Read bytes bb = fsProxy.read(path) if bb is None: return # write back with new name fsProxy.write(newpath, bb) class CreateTask(proxies.Task): __slots__ = [] def process(self, proxy, newpath=None, file=True): proxy.path() fsProxy = proxy._fsProxy if not newpath: return if file: fsProxy.write(newpath, b'') else: fsProxy.createDir(newpath) class RemoveTask(proxies.Task): __slots__ = [] def process(self, proxy): path = proxy.path() fsProxy = proxy._fsProxy # Remove fsProxy.remove(path) # The fsProxy will detect that this file is now deleted pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/tree.py0000666000000000000000000010244413153506102017475 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013 Almar Klein """ Defines the tree widget to display the contents of a selected directory. """ import os import sys import subprocess import fnmatch import os.path as op import pyzo from pyzo import translate from . import QtCore, QtGui, QtWidgets from . import tasks from .utils import hasHiddenAttribute, getMounts, cleanpath, isdir, ext # How to name the list of drives/mounts (i.e. 'my computer') MOUNTS = 'drives' # Create icon provider iconprovider = QtWidgets.QFileIconProvider() def addIconOverlays(icon, *overlays, offset=(8,0), overlay_offset=(0,0)): """ Create an overlay for an icon. """ # Create painter and pixmap pm0 = QtGui.QPixmap(16+offset[0],16)#icon.pixmap(16+offset[0],16+offset[1]) pm0.fill(QtGui.QColor(0,0,0,0)) painter = QtGui.QPainter() painter.begin(pm0) # Draw original icon painter.drawPixmap(offset[0], offset[1], icon.pixmap(16,16)) # Draw overlays for overlay in overlays: pm1 = overlay.pixmap(16,16) painter.drawPixmap(overlay_offset[0],overlay_offset[1], pm1) # Finish painter.end() # Done (return resulting icon) return QtGui.QIcon(pm0) def _filterFileByName(basename, filters): # Init default; return True if there are no filters default = True for filter in filters: # Process filters in order if filter.startswith('!'): # If the filename matches a filter starting with !, hide it if fnmatch.fnmatch(basename,filter[1:]): return False default = True else: # If the file name matches a filter not starting with!, show it if fnmatch.fnmatch(basename, filter): return True default = False return default def createMounts(browser, tree): """ Create items for all known mount points (i.e. drives on Windows). """ fsProxy = browser._fsProxy mountPoints = getMounts() mountPoints.sort(key=lambda x: x.lower()) for entry in mountPoints: entry = cleanpath(entry) DriveItem(tree, fsProxy.dir(entry)) def createItemsFun(browser, parent): """ Create the tree widget items for a Tree or DirItem. """ # Get file system proxy and dir proxy for which we shall create items fsProxy = browser._fsProxy dirProxy = parent._proxy # Get meta information from browser searchFilter = browser.searchFilter() searchFilter = searchFilter if searchFilter['pattern'] else None expandedDirs = browser.expandedDirs starredDirs = browser.starredDirs # Prepare name filter info nameFilters = browser.nameFilter().replace(',', ' ').split() hideHidden = '!hidden' in nameFilters nameFilters = [f for f in nameFilters if f not in ('', '!hiddden', 'hidden')] # Filter the contents of this folder try: dirs = [] for entry in dirProxy.dirs(): entry = cleanpath(entry) if hideHidden and op.basename(entry).startswith('.'): continue # Skip hidden files if hideHidden and hasHiddenAttribute(entry): continue # Skip hidden files on Windows if op.basename(entry) == '__pycache__': continue dirs.append(entry) files = [] for entry in dirProxy.files(): entry = cleanpath(entry) if hideHidden and op.basename(entry).startswith('.'): continue # Skip hidden files if hideHidden and hasHiddenAttribute(entry): continue # Skip hidden files on Windows if not _filterFileByName(op.basename(entry), nameFilters): continue files.append(entry) except (OSError, IOError) as err: ErrorItem(parent, str(err)) return # Sort dirs (case insensitive) dirs.sort(key=filename2sortkey) # Sort files (first by type, then by name, logically) files.sort(key=filename2sortkey) if not searchFilter: # Create dirs for path in dirs: starred = op.normcase(path) in starredDirs item = DirItem(parent, fsProxy.dir(path), starred) # Set hidden, we can safely expand programmatically when hidden item.setHidden(True) # Set expanded and visibility if op.normcase(path) in expandedDirs: item.setExpanded(True) item.setHidden(False) # Create files for path in files: item = FileItem(parent, fsProxy.file(path)) else: # If searching, inject everything in the tree # And every item is hidden at first parent = browser._tree if parent.topLevelItemCount(): searchInfoItem = parent.topLevelItem(0) else: searchInfoItem = SearchInfoItem(parent) # Increase number of found files searchInfoItem.increaseTotal(len(files)) # Create temporary file items for path in files: item = TemporaryFileItem(parent, fsProxy.file(path)) item.search(searchFilter) # Create temporary dir items if searchFilter['subDirs']: for path in dirs: item = TemporaryDirItem(parent, fsProxy.dir(path)) # Return number of files added return len(dirs) + len(files) def filename2sortkey(name): """ Convert a file or dir name to a tuple that can be used to logically sort them. Sorting first by extension. """ # Normalize name name = os.path.basename(name).lower() name, e = os.path.splitext(name) # Split the name in logical parts try: numbers = '0123456789' name1 = name.lstrip(numbers) name2 = name1.rstrip(numbers) n_pre = len(name) - len(name1) n_post = len(name1) - len(name2) pre = int(name[:n_pre]) if n_pre else 999999999 post = int(name[-n_post:]) if n_post else -1 return e, pre, name2, post except Exception as err: # I cannot see how this could fail, but lets be safe, as it would break so badly print('Warning: could not filename2sortkey(%r), please report:\n%s' % (name, str(err))) return (e, 999999999, name, -1) class BrowserItem(QtWidgets.QTreeWidgetItem): """ Abstract item in the tree widget. """ def __init__(self, parent, pathProxy, *args): self._proxy = pathProxy QtWidgets.QTreeWidgetItem.__init__(self, parent, [], *args) # Set pathname to show, and icon strippedParentPath = parent.path().rstrip('/\\') if self.path().startswith(strippedParentPath): basename = self.path()[len(strippedParentPath)+1:] else: basename = self.path() # For mount points self.setText(0, basename) self.setFileIcon() # Setup interface with proxy self._proxy.changed.connect(self.onChanged) self._proxy.deleted.connect(self.onDeleted) self._proxy.errored.connect(self.onErrored) self._proxy.taskFinished.connect(self.onTaskFinished) def path(self): return self._proxy.path() def _createDummyItem(self, txt): ErrorItem(self, txt) #QtWidgets.QTreeWidgetItem(self, [txt]) def onDestroyed(self): self._proxy.cancel() def clear(self): """ Clear method that calls onDestroyed on its children. """ for i in reversed(range(self.childCount())): item = self.child(i) if hasattr(item, 'onDestroyed'): item.onDestroyed() self.removeChild(item) # To overload ... def onChanged(self): pass def onDeleted(self): pass def onErrored(self, err): self.clear() self._createDummyItem('Error: ' + err) def onTaskFinished(self, task): # Getting the result raises exception if an error occured. # Which is what we want; so it is visible in the logger shell task.result() class DriveItem(BrowserItem): """ Tree widget item for directories. """ def __init__(self, parent, pathProxy): BrowserItem.__init__(self, parent, pathProxy) # Item is not expandable def setFileIcon(self): # Use folder icon self.setIcon(0, pyzo.icons.drive) def onActivated(self): self.treeWidget().setPath(self.path()) class DirItem(BrowserItem): """ Tree widget item for directories. """ def __init__(self, parent, pathProxy, starred=False): self._starred = starred BrowserItem.__init__(self, parent, pathProxy) # Create dummy item so that the dir is expandable self._createDummyItem('Loading contents ...') def setFileIcon(self): # Use folder icon icon = iconprovider.icon(iconprovider.Folder) overlays = [] if self._starred: overlays.append(pyzo.icons.bullet_yellow) icon = addIconOverlays(icon, *overlays, offset=(8,0), overlay_offset=(-4,0)) self.setIcon(0, icon) def onActivated(self): self.treeWidget().setPath(self.path()) def onExpanded(self): # Update list of expanded dirs expandedDirs = self.treeWidget().parent().expandedDirs p = op.normcase(self.path()) # Normalize case! if p not in expandedDirs: expandedDirs.append(p) # Keep track of changes in our contents self._proxy.track() self._proxy.push() def onCollapsed(self): # Update list of expanded dirs expandedDirs = self.treeWidget().parent().expandedDirs p = op.normcase(self.path()) # Normalize case! while p in expandedDirs: expandedDirs.remove(p) # Stop tracking changes in our contents self._proxy.cancel() # Clear contents and create a single placeholder item self.clear() self._createDummyItem('Loading contents ...') # No need to implement onDeleted: the parent will get a changed event. def onChanged(self): """ Called when a change in the contents has occured, or when we just activated the proxy. Update our items! """ if not self.isExpanded(): return tree = self.treeWidget() tree.createItems(self) class FileItem(BrowserItem): """ Tree widget item for files. """ def __init__(self, parent, pathProxy, mode='normal'): BrowserItem.__init__(self, parent, pathProxy) self._mode = mode self._timeSinceLastDocString = 0 if self._mode=='normal' and self.path().lower().endswith('.py'): self._createDummyItem('Loading high level structure ...') def setFileIcon(self): # Create dummy file in pyzo user dir dummy_filename = op.join(cleanpath(pyzo.appDataDir), 'dummyFiles', 'dummy' + ext(self.path())) # Create file? if not op.isfile(dummy_filename): if not isdir(op.dirname(dummy_filename)): os.makedirs(op.dirname(dummy_filename)) f = open(dummy_filename, 'wb') f.close() # Use that file if sys.platform.startswith('linux') and \ not QtCore.__file__.startswith('/usr/'): icon = iconprovider.icon(iconprovider.File) else: icon = iconprovider.icon(QtCore.QFileInfo(dummy_filename)) icon = addIconOverlays(icon) self.setIcon(0, icon) def searchContents(self, needle, **kwargs): self.setHidden(True) self._proxy.setSearch(needle, **kwargs) def onActivated(self): # todo: someday we should be able to simply pass the proxy object to the editors # so that we can open files on any file system path = self.path() if ext(path) not in ['.pyc','.pyo','.png','.jpg','.ico']: # Load file pyzo.editors.loadFile(path) # Give focus pyzo.editors.getCurrentEditor().setFocus() def onExpanded(self): if self._mode == 'normal': # Create task to retrieve high level structure if self.path().lower().endswith('.py'): self._proxy.pushTask(tasks.DocstringTask()) self._proxy.pushTask(tasks.PeekTask()) def onCollapsed(self): if self._mode == 'normal': self.clear() if self.path().lower().endswith('.py'): self._createDummyItem('Loading high level structure ...') # def onClicked(self): # # Limit sending events to prevent flicker when double clicking # if time.time() - self._timeSinceLastDocString < 0.5: # return # self._timeSinceLastDocString = time.time() # # Create task # if self.path().lower().endswith('.py'): # self._proxy.pushTask(tasks.DocstringTask()) def onChanged(self): pass def onTaskFinished(self, task): if isinstance(task, tasks.DocstringTask): result = task.result() self.clear() # Docstring task is done *before* peek task if result: DocstringItem(self, result) # if isinstance(task, tasks.DocstringTask): # result = task.result() # if result: # #self.setToolTip(0, result) # # Show tooltip *now* if mouse is still over this item # tree = self.treeWidget() # pos = tree.mapFromGlobal(QtGui.QCursor.pos()) # if tree.itemAt(pos) is self: # QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), result) elif isinstance(task, tasks.PeekTask): result = task.result() #self.clear() # Cleared when docstring task result is received if result: for r in result: SubFileItem(self, *r) else: self._createDummyItem('No classes or functions found.') else: BrowserItem.onTaskFinished(self, task) class SubFileItem(QtWidgets.QTreeWidgetItem): """ Tree widget item for search items. """ def __init__(self, parent, linenr, text, showlinenr=False): QtWidgets.QTreeWidgetItem.__init__(self, parent) self._linenr = linenr if showlinenr: self.setText(0, 'Line %i: %s' % (linenr, text)) else: self.setText(0, text) def path(self): return self.parent().path() def onActivated(self): path = self.path() if ext(path) not in ['.pyc','.pyo','.png','.jpg','.ico']: # Load and get editor fileItem = pyzo.editors.loadFile(path) editor = fileItem._editor # Goto line editor.gotoLine(self._linenr) # Give focus pyzo.editors.getCurrentEditor().setFocus() class DocstringItem(QtWidgets.QTreeWidgetItem): """ Tree widget item for docstring placeholder items. """ def __init__(self, parent, docstring): QtWidgets.QTreeWidgetItem.__init__(self, parent) self._docstring = docstring # Get one-line version of docstring shortText = self._docstring.split('\n',1)[0].strip() if len(shortText) < len(self._docstring): shortText += '...' # Set short version now self.setText(0, 'doc: '+shortText) # Long version is the tooltip self.setToolTip(0, docstring) def path(self): return self.parent().path() def onClicked(self): tree = self.treeWidget() pos = tree.mapFromGlobal(QtGui.QCursor.pos()) if tree.itemAt(pos) is self: QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), self._docstring) class ErrorItem(QtWidgets.QTreeWidgetItem): """ Tree widget item for errors and information. """ def __init__(self, parent, info): QtWidgets.QTreeWidgetItem.__init__(self, parent) self.setText(0, info) self.setFlags(QtCore.Qt.NoItemFlags) font = self.font(0) font.setItalic(True) self.setFont(0, font) class SearchInfoItem(ErrorItem): """ Tree widget item that displays info on the search. """ def __init__(self, parent): ErrorItem.__init__(self, parent, 'Searching ...') self._totalCount = 0 self._checkCount = 0 self._hitCount = 0 def increaseTotal(self, c): self._totalCount += c self.updateCounts() def addFile(self, hit): self._checkCount += 1 if hit: self._hitCount += 1 # Update appearance self.updateCounts() def updateCounts(self): counts = self._checkCount, self._totalCount, self._hitCount self.setText(0, 'Searched {}/{} files: {} hits'.format(*counts)) class TemporaryDirItem: """ Created when searching. This object posts a requests for its contents which are then processed, after which this object disbands itself. """ __slots__ = ['_tree', '_proxy', '__weakref__'] def __init__(self, tree, pathProxy): self._tree = tree self._proxy = pathProxy self._proxy.changed.connect(self.onChanged) # Process asap, but do not track self._proxy.push() # Store ourself tree._temporaryItems.add(self) def clear(self): pass # tree.createItems() calls this ... def onChanged(self): # Disband self._tree._temporaryItems.discard(self) # Process contents self._tree.createItems(self) class TemporaryFileItem: """ Created when searching. This object posts a requests to search its contents which are then processed, after which this object disbands itself, passin the proxy object to a real FileItem if the search had results. """ __slots__ = ['_tree', '_proxy', '__weakref__'] def __init__(self, tree, pathProxy): self._tree = tree self._proxy = pathProxy self._proxy.taskFinished.connect(self.onSearchResult) # Store ourself tree._temporaryItems.add(self) def search(self, searchFilter): self._proxy.pushTask(tasks.SearchTask(**searchFilter)) def onSearchResult(self, task): # Disband now self._tree._temporaryItems.discard(self) # Get result. May raise an error result = task.result() # Process contents if result: item = FileItem(self._tree, self._proxy, 'search') # Search mode for r in result: SubFileItem(item, *r, showlinenr=True) # Update counter searchInfoItem = self._tree.topLevelItem(0) if isinstance(searchInfoItem, SearchInfoItem): searchInfoItem.addFile(bool(result)) class Tree(QtWidgets.QTreeWidget): """ Representation of the tree view. Instances of this class are responsible for keeping the contents up-to-date. The Item classes above are dumb objects. """ dirChanged = QtCore.Signal(str) # Emitted when user goes into a subdir def __init__(self, parent): QtWidgets.QTreeWidget.__init__(self, parent) # Initialize self.setMinimumWidth(150) self.setMinimumHeight(150) # self.setColumnCount(1) self.setHeaderHidden(True) self.setIconSize(QtCore.QSize(24,16)) # Connecy signals self.itemExpanded.connect(self.onItemExpanded) self.itemCollapsed.connect(self.onItemCollapsed) self.itemClicked.connect(self.onItemClicked) self.itemActivated.connect(self.onItemActivated) # Variables for restoring the view after updating self._selectedPath = '' # To restore a selection after updating self._selectedScrolling = 0 # Set of temporary items self._temporaryItems = set() # Define context menu self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.contextMenuTriggered) # Initialize proxy (this is where the path is stored) self._proxy = None def path(self): """ Get the current path shown by the treeview. """ return self._proxy.path() def setPath(self, path): """ Set the current path shown by the treeview. """ # Close old proxy if self._proxy is not None: self._proxy.cancel() self._proxy.changed.disconnect(self.onChanged) self._proxy.deleted.disconnect(self.onDeleted) self._proxy.errored.disconnect(self.onErrored) self.destroyed.disconnect(self._proxy.cancel) # Create new proxy if True: self._proxy = self.parent()._fsProxy.dir(path) self._proxy.changed.connect(self.onChanged) self._proxy.deleted.connect(self.onDeleted) self._proxy.errored.connect(self.onErrored) self.destroyed.connect(self._proxy.cancel) # Activate the proxy, we'll get a call at onChanged() asap. if path.lower() == MOUNTS.lower(): self.clear() createMounts(self.parent(), self) else: self._proxy.track() self._proxy.push() # Store dir in config self.parent().config.path = path # Signal that the dir has changed # Note that our contents may not be visible yet. self.dirChanged.emit(self.path()) def setPathUp(self): """ Go one directory up. """ newPath = op.dirname(self.path()) if op.normcase(newPath) == op.normcase(self.path()): self.setPath(cleanpath(MOUNTS)) else: self.setPath(newPath) def clear(self): """ Overload the clear method to remove the items in a nice way, alowing the pathProxy instance to be closed correctly. """ # Clear temporary (invisible) items for item in self._temporaryItems: item._proxy.cancel() self._temporaryItems.clear() # Clear visible items for i in reversed(range(self.topLevelItemCount())): item = self.topLevelItem(i) if hasattr(item, 'clear'): item.clear() if hasattr(item, 'onDestroyed'): item.onDestroyed() QtWidgets.QTreeWidget.clear(self) def mouseDoubleClickEvent(self, event): """ Bypass expanding an item when double-cliking it. Only activate the item. """ item = self.itemAt(event.x(), event.y()) if item is not None: self.onItemActivated(item) def onChanged(self): """ Called when our contents change or when we just changed directories. """ self.createItems(self) def createItems(self, parent): """ High level method to create the items of the tree or a DirItem. This method will handle the restoring of state etc. The actual filtering of entries and creation of tree widget items is done in the createItemsFun() function. """ # Store state and clear self._storeSelectionState() parent.clear() # Create sub items count = createItemsFun(self.parent(), parent) if not count and isinstance(parent, QtWidgets.QTreeWidgetItem): ErrorItem(parent, 'Empty directory') # Restore state self._restoreSelectionState() def onErrored(self, err='...'): self.clear() ErrorItem(self, 'Error: ' + err) def onDeleted(self): self.setPathUp() def onItemExpanded(self, item): if hasattr(item, 'onExpanded'): item.onExpanded() def onItemCollapsed(self, item): if hasattr(item, 'onCollapsed'): item.onCollapsed() def onItemClicked(self, item): if hasattr(item, 'onClicked'): item.onClicked() def onItemActivated(self, item): """ When an item is "activated", make that the new directory, or open that file. """ if hasattr(item, 'onActivated'): item.onActivated() def _storeSelectionState(self): # Store selection items = self.selectedItems() self._selectedPath = items[0].path() if items else '' # Store scrolling self._selectedScrolling = self.verticalScrollBar().value() def _restoreSelectionState(self): # First select the first item # (otherwise the scrolling wont work for some reason) if self.topLevelItemCount(): self.setCurrentItem(self.topLevelItem(0)) # Restore selection if self._selectedPath: items = self.findItems(op.basename(self._selectedPath), QtCore.Qt.MatchExactly, 0) items = [item for item in items if op.normcase(item.path()) == op.normcase(self._selectedPath)] if items: self.setCurrentItem(items[0]) # Restore scrolling self.verticalScrollBar().setValue(self._selectedScrolling) self.verticalScrollBar().setValue(self._selectedScrolling) def contextMenuTriggered(self, p): """ Called when context menu is clicked """ # Get item that was clicked on item = self.itemAt(p) if item is None: item = self # Create and show menu if isinstance(item, (Tree, FileItem, DirItem)): menu = PopupMenu(self, item) menu.popup(self.mapToGlobal(p+QtCore.QPoint(3,3))) class PopupMenu(pyzo.core.menu.Menu): def __init__(self, parent, item): self._item = item pyzo.core.menu.Menu.__init__(self, parent, " ") def build(self): isplat = sys.platform.startswith # The star object if isinstance(self._item, DirItem): if self._item._starred: self.addItem(translate("filebrowser", "Unstar this directory"), None, self._star) else: self.addItem(translate("filebrowser", "Star this directory"), None, self._star) self.addSeparator() # The pyzo related functions if isinstance(self._item, FileItem): self.addItem(translate("filebrowser", "Open"), None, self._item.onActivated) if self._item.path().endswith('.py'): self.addItem(translate("filebrowser", "Run as script"), None, self._runAsScript) elif self._item.path().endswith('.ipynb'): self.addItem(translate("filebrowser", "Run Jupyter notebook"), None, self._runNotebook) else: self.addItem(translate("filebrowser", "Import data..."), None, self._importData) self.addSeparator() # Create items for open and copy path if isinstance(self._item, (FileItem, DirItem)): if isplat('win') or isplat('darwin') or isplat('linux'): self.addItem(translate("filebrowser", "Open outside Pyzo"), None, self._openOutsidePyzo) if isplat('darwin'): self.addItem(translate("filebrowser", "Reveal in Finder"), None, self._showInFinder) if True: self.addItem(translate("filebrowser", "Copy path"), None, self._copyPath) self.addSeparator() # Create items for file management if isinstance(self._item, FileItem): self.addItem(translate("filebrowser", "Rename"), None, self.onRename) self.addItem(translate("filebrowser", "Delete"), None, self.onDelete) #self.addItem(translate("filebrowser", "Duplicate"), None, self.onDuplicate) if isinstance(self._item, (Tree, DirItem)): self.addItem(translate("filebrowser", "Create new file"), None, self.onCreateFile) self.addItem(translate("filebrowser", "Create new directory"), None, self.onCreateDir) if isinstance(self._item, DirItem): self.addSeparator() self.addItem(translate("filebrowser", "Rename"), None, self.onRename) self.addItem(translate("filebrowser", "Delete"), None, self.onDelete) def _star(self): # Prepare browser = self.parent().parent() path = self._item.path() if self._item._starred: browser.removeStarredDir(path) else: browser.addStarredDir(path) # Refresh self.parent().setPath(self.parent().path()) def _openOutsidePyzo(self): path = self._item.path() if sys.platform.startswith('darwin'): subprocess.call(('open', path)) elif sys.platform.startswith('win'): if ' ' in path: # http://stackoverflow.com/a/72796/2271927 subprocess.call(('start', '', path), shell=True) else: subprocess.call(('start', path), shell=True) elif sys.platform.startswith('linux'): # xdg-open is available on all Freedesktop.org compliant distros # http://superuser.com/questions/38984/linux-equivalent-command-for-open-command-on-mac-windows subprocess.call(('xdg-open', path)) def _showInFinder(self): subprocess.call(('open', '-R', self._item.path())) def _copyPath(self): QtWidgets.qApp.clipboard().setText(self._item.path()) def _runAsScript(self): filename = self._item.path() shell = pyzo.shells.getCurrentShell() if shell is not None: shell.restart(filename) else: msg = "No shell to run code in. " m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Could not run")) m.setText("Could not run " + filename + ":\n\n" + msg) m.setIcon(m.Warning) m.exec_() def _runNotebook(self): filename = self._item.path() shell = pyzo.shells.getCurrentShell() if shell is not None: shell.restart(filename) else: msg = "No shell to run notebook in. " m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("menu dialog", "Could not run notebook")) m.setText("Could not run " + filename + ":\n\n" + msg) m.setIcon(m.Warning) m.exec_() def _importData(self): browser = self.parent().parent() wizard = browser.getImportWizard() wizard.open(self._item.path()) def onDuplicate(self): return self._duplicateOrRename(False) def onRename(self): return self._duplicateOrRename(True) def onCreateFile(self): self._createDirOrFile(True) def onCreateDir(self): self._createDirOrFile(False) def _createDirOrFile(self, file=True): # Get title and label if file: title = translate("filebrowser", "Create new file") label = translate("filebrowser", "Give the new name for the file") else: title = translate("filebrowser", "Create new directory") label = translate("filebrowser", "Give the name for the new directory") # Ask for new filename s = QtWidgets.QInputDialog.getText(self.parent(), title, label + ':\n%s' % self._item.path(), QtWidgets.QLineEdit.Normal, 'new name' ) if isinstance(s, tuple): s = s[0] if s[1] else '' # Push rename task if s: newpath = op.join(self._item.path(), s) task = tasks.CreateTask(newpath=newpath, file=file) self._item._proxy.pushTask(task) def _duplicateOrRename(self, rename): # Get dirname and filename dirname, filename = op.split(self._item.path()) # Get title and label if rename: title = translate("filebrowser", "Rename") label = translate("filebrowser", "Give the new name for the file") else: title = translate("filebrowser", "Duplicate") label = translate("filebrowser", "Give the name for the new file") filename = 'Copy of ' + filename # Ask for new filename s = QtWidgets.QInputDialog.getText(self.parent(), title, label + ':\n%s' % self._item.path(), QtWidgets.QLineEdit.Normal, filename ) if isinstance(s, tuple): s = s[0] if s[1] else '' # Push rename task if s: newpath = op.join(dirname, s) task = tasks.RenameTask(newpath=newpath, removeold=rename) self._item._proxy.pushTask(task) def onDelete(self): # Ask for new filename b = QtWidgets.QMessageBox.question(self.parent(), translate("filebrowser", "Delete"), translate("filebrowser", "Are you sure that you want to delete") + ':\n%s' % self._item.path(), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel, ) # Push delete task if b == QtWidgets.QMessageBox.Yes: self._item._proxy.pushTask(tasks.RemoveTask()) pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/utils.py0000666000000000000000000000240413123037261017673 0ustar 00000000000000 import os import ctypes import sys import string import os.path as op def cleanpath(p): return op.normpath(op.expanduser(op.expandvars(p))) def isdir(p): # Add os.sep, because trailing spaces seem to be ignored on Windows return op.isdir(p + op.sep) def ext(p): return os.path.splitext(p)[1] # todo: also include available remote file systems def getMounts(): if sys.platform.startswith('win'): return getDrivesWin() elif sys.platform.startswith('darwin'): return '/' elif sys.platform.startswith('linux'): return ['/'] + [op.join('/media', e) for e in os.listdir('/media')] else: return '/' def getDrivesWin(): drives = [] bitmask = ctypes.windll.kernel32.GetLogicalDrives() for letter in string.ascii_uppercase: if bitmask & 1: drives.append(letter) bitmask >>= 1 return [drive+':\\' for drive in drives] def hasHiddenAttribute(path): """ Test (on Windows) whether a file should be hidden. """ if not sys.platform.startswith('win'): return False try: attrs = ctypes.windll.kernel32.GetFileAttributesW(path) assert attrs != -1 return bool(attrs & 2) except (AttributeError, AssertionError): return False pyzo-4.4.3/pyzo/tools/pyzoFileBrowser/__init__.py0000666000000000000000000001167013153504700020277 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. from pyzo import translate tool_name = translate("pyzoFileBrowser","File Browser") tool_summary = "Browse the files in your projects." """ File browser tool A powerfull tool for managing your projects in a lightweight manner. It has a few file management utilities as well. Config ====== The config consists of three fields: * list expandedDirs, with each element a directory * list starredDirs, with each element a dict with fields: * str path, the directory that is starred * str name, the name of the project (op.basename(path) by default) * bool addToPythonpath * searchMatchCase, searchRegExp, searchSubDirs * nameFilter """ # todo: List! """ * see easily which files are opened (so it can be used as a secondary tab bar) * make visible the "current file" (if applicable) * single click on an file that is open selects it in the editor? * context menu items to run scripts * Support for multiple browsers. """ import os.path as op import pyzo from pyzo.util import zon as ssdf from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa from .browser import Browser from .utils import cleanpath, isdir class PyzoFileBrowser(QtWidgets.QWidget): """ The main tool widget. An instance of this class contains one or more Browser instances. If there are more, they can be selected using a tab bar. """ def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) # Get config toolId = self.__class__.__name__.lower() + '2' # This is v2 of the file browser if toolId not in pyzo.config.tools: pyzo.config.tools[toolId] = ssdf.new() self.config = pyzo.config.tools[toolId] # Ensure three main attributes in config for name in ['expandedDirs', 'starredDirs']: if name not in self.config: self.config[name] = [] # Ensure path in config if 'path' not in self.config or not isdir(self.config.path): self.config.path = op.expanduser('~') # Check expandedDirs and starredDirs. # Make path objects and remove invalid dirs. Also normalize case, # should not be necessary, but maybe the config was manually edited. expandedDirs, starredDirs = [], [] for d in self.config.starredDirs: if 'path' in d and 'name' in d and 'addToPythonpath' in d: if isdir(d.path): d.path = op.normcase(cleanpath(d.path)) starredDirs.append(d) for p in set([str(p) for p in self.config.expandedDirs]): if isdir(p): p = op.normcase(cleanpath(p)) # Add if it is a subdir of a starred dir for d in starredDirs: if p.startswith(d.path): expandedDirs.append(p) break self.config.expandedDirs, self.config.starredDirs = expandedDirs, starredDirs # Create browser(s). self._browsers = [] for i in [0]: self._browsers.append( Browser(self, self.config) ) # Layout layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) layout.addWidget(self._browsers[0]) layout.setSpacing(0) layout.setContentsMargins(4,4,4,4) def path(self): """ Get the current path shown by the file browser. """ browser = self._browsers[0] return browser._tree.path() def setPath(self, path): """ Set the shown path. """ browser = self._browsers[0] browser._tree.setPath(path) def getAddToPythonPath(self): """ Returns the path to be added to the Python path when starting a shell If a project is selected, which has the addToPath checkbox selected, returns the path of the project. Otherwise, returns None """ # Select browser browser = self._browsers[0] # Select active project d = browser.currentProject() if d and d.addToPythonpath: return d.path return None def getDefaultSavePath(self): """ Returns the path to be used as default when saving a new file in pyzo. Or None if the no path could be determined """ # Select current browser browser = self._browsers[0] # Select its path path = browser._tree.path() # Return if op.isabs(path) and isdir(path): return path def closeEvent(self, event): # Close all browsers so they can clean up the file system proxies for browser in self._browsers: browser.close() return QtWidgets.QWidget.closeEvent(self, event) pyzo-4.4.3/pyzo/tools/pyzoHistoryViewer.py0000666000000000000000000001226113153504700017135 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ This file provides the pyzo history viewer. It contains two main components: the History class, which is a Qt model, and the PyzoHistoryViewer, which is a Qt view """ import pyzo from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa from pyzo import translate from pyzo.core.menu import Menu tool_name = translate('pyzoHistoryViewer', 'History viewer') tool_summary = "Shows the last used commands." class PyzoHistoryViewer(QtWidgets.QWidget): """ The history viewer has several ways of using the data stored in the history: - double click a single item to execute in the current shell - drag and drop one or multiple selected lines into the editor or any other widget or application accepting plain text - copy selected items using the copy item in the pyzo edit menu - copy selected items using the context menu - execute selected items in the current shell using the context menu """ def __init__(self, parent = None): super().__init__(parent) # Widgets self._search = QtWidgets.QLineEdit(self) self._list = QtWidgets.QListWidget(self) # Set monospace font = self._list.font() font.setFamily(pyzo.config.view.fontname) self._list.setFont(font) # Layout layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) layout.addWidget(self._search, 0) layout.addWidget(self._list, 1) # Customize line edit self._search.setPlaceholderText(translate('menu', 'Search')) self._search.textChanged.connect(self._on_search) # Drag/drop self._list.setSelectionMode(self._list.ExtendedSelection) self._list.setDragEnabled(True) self._list.doubleClicked.connect(self._onDoubleClicked) # Context menu self._menu = Menu(self, translate("menu", "History")) self._menu.addItem(translate("menu", "Copy ::: Copy selected lines"), pyzo.icons.page_white_copy, self.copy, "copy") self._menu.addItem(translate("menu", "Run ::: Run selected lines in current shell"), pyzo.icons.run_lines, self.runSelection, "run") self._menu.addItem(translate("menu", "Remove ::: Remove selected history items(s)"), pyzo.icons.delete, self.removeSelection, "remove") self._list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._list.customContextMenuRequested.connect(self._onCustomContextMenuRequested) # Populate for command in pyzo.command_history.get_commands(): self._list.addItem(command) # Keep up to date ... pyzo.command_history.command_added.connect(self._on_command_added) pyzo.command_history.command_removed.connect(self._on_command_removed) pyzo.command_history.commands_reset.connect(self._on_commands_reset) def _on_search(self): needle = self._search.text() for i in range(self._list.count()): item = self._list.item(i) item.setHidden(bool(needle and needle not in item.text())) ## Keep track of history def _on_command_added(self, command): item = QtWidgets.QListWidgetItem(command, self._list) self._list.addItem(item) needle = self._search.text() item.setHidden(bool(needle and needle not in command)) def _on_command_removed(self, index): self._list.takeItem(index) def _on_commands_reset(self): self._list.clear() for command in pyzo.command_history.get_commands(): self._list.addItem(command) ## User actions def _onCustomContextMenuRequested(self, pos): self._menu.popup(self._list.viewport().mapToGlobal(pos)) def copy(self, event=None): text = '\n'.join(i.text() for i in self._list.selectedItems()) QtWidgets.qApp.clipboard().setText(text) def removeSelection(self, event=None): indices = [i.row() for i in self._list.selectedIndexes()] for i in reversed(sorted(indices)): pyzo.command_history.pop(i) def runSelection(self, event=None): commands = [i.text() for i in self._list.selectedItems()] shell = pyzo.shells.getCurrentShell() if shell is not None: for command in reversed(commands): pyzo.command_history.append(command) shell.executeCommand('\n'.join(commands) + '\n') if len(commands) > 1 and commands[-1].startswith(' '): shell.executeCommand('\n') # finalize multi-command def _onDoubleClicked(self, index): text = '\n'.join(i.text() for i in self._list.selectedItems()) shell = pyzo.shells.getCurrentShell() if shell is not None: shell.executeCommand(text + '\n') # Do not update history? Was this intended? if __name__ == '__main__': import pyzo.core.main m = pyzo.core.main.MainWindow() view = PyzoHistoryViewer() view.show() pyzo-4.4.3/pyzo/tools/pyzoInteractiveHelp.py0000666000000000000000000003010713153504700017377 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import sys, re from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa import pyzo tool_name = pyzo.translate("pyzoInteractiveHelp","Interactive help") tool_summary = "Shows help on an object when using up/down in autocomplete." # htmlWrap = """ {} """ # Define title text (font-size percentage does not seem to work sadly.) def get_title_text(objectName, h_class='', h_repr=''): title_text = "

" title_text += "Object: {}".format(objectName) if h_class: title_text += ", class: {}".format(h_class) if h_repr: if len(h_repr) > 40: h_repr = h_repr[:37] + '...' title_text += ", repr: {}".format(h_repr) # Finish title_text += '

\n' return title_text initText = """ Help information is queried from the current shell when moving up/down in the autocompletion list and when double clicking on a name. """ class PyzoInteractiveHelp(QtWidgets.QWidget): def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) # Create text field, checkbox, and button self._text = QtWidgets.QLineEdit(self) self._printBut = QtWidgets.QPushButton("Print", self) # Create options button self._options = QtWidgets.QToolButton(self) self._options.setIcon(pyzo.icons.wrench) self._options.setIconSize(QtCore.QSize(16,16)) self._options.setPopupMode(self._options.InstantPopup) self._options.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) # Create options menu self._options._menu = QtWidgets.QMenu() self._options.setMenu(self._options._menu) # Create browser self._browser = QtWidgets.QTextBrowser(self) self._browser_text = initText # Create two sizers self._sizer1 = QtWidgets.QVBoxLayout(self) self._sizer2 = QtWidgets.QHBoxLayout() # Put the elements together self._sizer2.addWidget(self._text, 4) self._sizer2.addWidget(self._printBut, 0) self._sizer2.addWidget(self._options, 2) # self._sizer1.addLayout(self._sizer2, 0) self._sizer1.addWidget(self._browser, 1) # self._sizer1.setSpacing(2) self._sizer1.setContentsMargins(4,4,4,4) self.setLayout(self._sizer1) # Set config toolId = self.__class__.__name__.lower() self._config = config = pyzo.config.tools[toolId] # if not hasattr(config, 'smartNewlines'): config.smartNewlines = True if not hasattr(config, 'fontSize'): if sys.platform == 'darwin': config.fontSize = 12 else: config.fontSize = 10 # Create callbacks self._text.returnPressed.connect(self.queryDoc) self._printBut.clicked.connect(self.printDoc) # self._options.pressed.connect(self.onOptionsPress) self._options._menu.triggered.connect(self.onOptionMenuTiggered) # Start self.setText() # Set default text self.onOptionsPress() # Fill menu def onOptionsPress(self): """ Create the menu for the button, Do each time to make sure the checks are right. """ # Get menu menu = self._options._menu menu.clear() # Add smart format option action = menu.addAction('Smart format') action.setCheckable(True) action.setChecked(bool(self._config.smartNewlines)) # Add delimiter menu.addSeparator() # Add font size options currentSize = self._config.fontSize for i in range(8,15): action = menu.addAction('font-size: %ipx' % i) action.setCheckable(True) action.setChecked(i==currentSize) def onOptionMenuTiggered(self, action): """ The user decides what to show in the structure. """ # Get text text = action.text().lower() if 'smart' in text: # Swap value current = bool(self._config.smartNewlines) self._config.smartNewlines = not current # Update self.queryDoc() elif 'size' in text: # Get font size size = int( text.split(':',1)[1][:-2] ) # Update self._config.fontSize = size # Update self.setText() def setText(self, text=None): # (Re)store text if text is None: text = self._browser_text else: self._browser_text = text # Set text with html header size = self._config.fontSize self._browser.setHtml(htmlWrap.format(size,text)) def setObjectName(self, name): """ Set the object name programatically and query documentation for it. """ self._text.setText(name) self.queryDoc() def printDoc(self): """ Print the doc for the text in the line edit. """ # Get name name = self._text.text() # Tell shell to print doc shell = pyzo.shells.getCurrentShell() if shell and name: shell.processLine('print({}.__doc__)'.format(name)) def queryDoc(self): """ Query the doc for the text in the line edit. """ # Get name name = self._text.text() # Get shell and ask for the documentation shell = pyzo.shells.getCurrentShell() if shell and name: future = shell._request.doc(name) future.add_done_callback(self.queryDoc_response) elif not name: self.setText(initText) def queryDoc_response(self, future): """ Process the response from the shell. """ # Process future if future.cancelled(): #print('Introspect cancelled') # No living kernel return elif future.exception(): print('Introspect-queryDoc-exception: ', future.exception()) return else: response = future.result() if not response: return try: # Get parts parts = response.split('\n') objectName, h_class, h_fun, h_repr = tuple(parts[:4]) h_text = '\n'.join(parts[4:]) # Obtain newlines that we hid for repr h_repr.replace('/r', '/n') # Make all newlines \n in h_text and strip h_text = h_text.replace('\r\n', '\n').replace('\r', '\n') h_text = h_text.lstrip() # Init text text = '' # These signs will fool the html h_repr = h_repr.replace("<","<") h_repr = h_repr.replace(">",">") h_text = h_text.replace("<","<") h_text = h_text.replace(">",">") if self._config.smartNewlines: # Make sure the signature is separated from the rest using at # least two newlines header = '' if True: # Get short version of objectName name = objectName.split('.')[-1] # Is the signature in the docstring? docs = h_text.replace('\n','|') tmp = re.search('[a-zA-z_\.]*?'+name+'\(.*?\)', docs) if tmp and tmp.span(0)[0]<5: header = tmp.group(0) h_text = h_text[len(header):].lstrip(':').lstrip() header = header.replace('|','') #h_text = header + '\n\n' + h_text elif h_text.startswith(objectName) or h_text.startswith(name): header, sep, docs = h_text.partition('\n') #h_text = header + '\n\n' + docs h_text = docs # Parse the text as rest/numpy like docstring h_text = self.smartFormat(h_text) if header: h_text = "

%s

\n%s" % ( header, h_text) #h_text = "%s

\n%s" % (header, h_text) else: # Make newlines html h_text = h_text.replace("\n","
") # Compile rich text text += get_title_text(objectName, h_class, h_repr) text += '{}
'.format(h_text) except Exception as why: try: text += get_title_text(objectName, h_class, h_repr) text += h_text except Exception: text = response # Done # size = self._config.fontSize self.setText(text) def smartFormat(self, text): # Get lines lines = text.splitlines() # Test minimal indentation minIndent = 9999 for line in lines[1:]: line_ = line.lstrip() indent = len(line) - len(line_) if line_: minIndent = min(minIndent, indent) # Remove minimal indentation lines2 = [lines[0]] for line in lines[1:]: lines2.append( line[minIndent:] ) # Prepare prevLine_ = '' prevIndent = 0 prevWasHeader = False inExample = False forceNewline = False # Format line by line lines3 = [] for line in lines2: # Get indentation line_ = line.lstrip() indent = len(line) - len(line_) #indentPart = line[:indent-minIndent] indentPart = line[:indent] if not line_: lines3.append("
") forceNewline = True continue # Indent in html line = " " * len(indentPart) + line # Determine if we should introduce a newline isHeader = False if ("---" in line or "===" in line) and indent == prevIndent: # Header lines3[-1] = '' + lines3[-1] + '' line = ''#'
' + line isHeader = True inExample = False # Special case, examples if prevLine_.lower().startswith('example'): inExample = True else: inExample = False elif ' : ' in line: tmp = line.split(' : ',1) line = '
' + tmp[0] + ' : ' + tmp[1] elif line_.startswith('* '): line = '
   •' + line_[2:] elif prevWasHeader or inExample or forceNewline: line = '
' + line else: if prevLine_: line = " " + line_ else: line = line_ # Force next line to be on a new line if using a colon if ' : ' in line: forceNewline = True else: forceNewline = False # Prepare for next line prevLine_ = line_ prevIndent = indent prevWasHeader = isHeader # Done with line lines3.append(line) # Done formatting return ''.join(lines3) pyzo-4.4.3/pyzo/tools/pyzoLogger.py0000666000000000000000000000663413153504700015540 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import sys, os, code import pyzo from pyzo.core.shell import BaseShell from pyzo.core.pyzoLogging import splitConsole tool_name = pyzo.translate("pyzoLogger","Logger") tool_summary = "Logs messages, warnings and errors within Pyzo." class PyzoLogger(BaseShell): """ Shell that logs all messages produced by pyzo. It also allows to look inside pyzo, which can be handy for debugging and developing. """ def __init__(self, parent): BaseShell.__init__(self, parent) # Set style to Python, or autocompletion does not work self.setParser('python') # Change background color to make the logger look different from shell # Use color as if all lines are highlighted f1 = self.getStyleElementFormat('Editor.text') f2 = self.getStyleElementFormat('Editor.Highlight current line') newStyle = 'back:%s, fore:%s' % (f2.back.name(), f1.fore.name()) self.setStyle(editor_text=newStyle) # Create namespace for logger interpreter locals = {'pyzo':pyzo, 'sys':sys, 'os':os} # Include linguist tools for name in ['linguist', 'lrelease', 'lupdate', 'lhelp']: locals[name] = getattr(pyzo.util._locale, name) # Create interpreter to run code self._interpreter = code.InteractiveConsole(locals, "") # Show welcome text moreBanner = "This is the Pyzo logger shell." self.write("Python %s on %s - %s\n\n" % (sys.version[:5], sys.platform, moreBanner)) self.write(str(sys.ps1), 2) # Split console history = splitConsole(self.write, self.writeErr) self.write(history) def executeCommand(self, command): """ Execute the command here! """ # Use writeErr rather than sys.stdout.write. This prevents # the prompts to be logged by the history. Because if they # are, the text does not look good due to missing newlines # when loading the history. # "Echo" stdin self.write(command, 1) more = self._interpreter.push(command.rstrip('\n')) if more: self.write(str(sys.ps2), 2) else: self.write(str(sys.ps1), 2) def writeErr(self, msg): """ This is what the logger uses to write errors. """ self.write(msg, 0, '#C00') # Note that I did not (yet) implement calltips def processAutoComp(self, aco): """ Processes an autocomp request using an AutoCompObject instance. """ # Try using buffer first if aco.tryUsingBuffer(): return # Include buildins? if not aco.name: command = "__builtins__.keys()" try: names = eval(command, {}, self._interpreter.locals) aco.addNames(names) except Exception: pass # Query list of names command = "dir({})".format(aco.name) try: names = eval(command, {}, self._interpreter.locals) aco.addNames(names) except Exception: pass # Done aco.finish() pyzo-4.4.3/pyzo/tools/pyzoSourceStructure.py0000666000000000000000000002774213156165611017514 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import pyzo from pyzo.util.qt import QtCore, QtGui, QtWidgets from pyzo import translate tool_name = translate('pyzoSourceStructure', 'Source structure') tool_summary = "Shows the structure of your source code." class Navigation: def __init__(self): self.back = [] self.forward = [] class PyzoSourceStructure(QtWidgets.QWidget): def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) # Make sure there is a configuration entry for this tool # The pyzo tool manager makes sure that there is an entry in # config.tools before the tool is instantiated. toolId = self.__class__.__name__.lower() self._config = pyzo.config.tools[toolId] if not hasattr(self._config, 'showTypes'): self._config.showTypes = ['class', 'def', 'cell', 'todo'] if not hasattr(self._config, 'level'): self._config.level = 2 # Keep track of clicks so we can "go back" self._nav = {} # editor-id -> Navigation object # Create buttons for navigation self._navbut_back = QtWidgets.QToolButton(self) self._navbut_back.setIcon(pyzo.icons.arrow_left) self._navbut_back.setIconSize(QtCore.QSize(16,16)) self._navbut_back.setStyleSheet("QToolButton { border: none; padding: 0px; }") self._navbut_back.clicked.connect(self.onNavBack) # self._navbut_forward = QtWidgets.QToolButton(self) self._navbut_forward.setIcon(pyzo.icons.arrow_right) self._navbut_forward.setIconSize(QtCore.QSize(16,16)) self._navbut_forward.setStyleSheet("QToolButton { border: none; padding: 0px; }") self._navbut_forward.clicked.connect(self.onNavForward) # # Create icon for slider # self._sliderIcon = QtWidgets.QToolButton(self) # self._sliderIcon.setIcon(pyzo.icons.text_align_right) # self._sliderIcon.setIconSize(QtCore.QSize(16,16)) # self._sliderIcon.setStyleSheet("QToolButton { border: none; padding: 0px; }") # Create slider self._slider = QtWidgets.QSlider(QtCore.Qt.Horizontal, self) self._slider.setTickPosition(QtWidgets.QSlider.TicksBelow) self._slider.setSingleStep(1) self._slider.setPageStep(1) self._slider.setRange(1,5) self._slider.setValue(self._config.level) self._slider.valueChanged.connect(self.updateStructure) # Create options button #self._options = QtWidgets.QPushButton(self) #self._options.setText('Options')) #self._options.setToolTip("What elements to show.") self._options = QtWidgets.QToolButton(self) self._options.setIcon(pyzo.icons.filter) self._options.setIconSize(QtCore.QSize(16,16)) self._options.setPopupMode(self._options.InstantPopup) self._options.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) # Create options menu self._options._menu = QtWidgets.QMenu() self._options.setMenu(self._options._menu) # Create tree widget self._tree = QtWidgets.QTreeWidget(self) self._tree.setHeaderHidden(True) self._tree.itemCollapsed.connect(self.updateStructure) # keep expanded self._tree.itemClicked.connect(self.onItemClick) # Create two sizers self._sizer1 = QtWidgets.QVBoxLayout(self) self._sizer2 = QtWidgets.QHBoxLayout() self._sizer1.setSpacing(2) self._sizer1.setContentsMargins(4,4,4,4) # Set layout self._sizer1.addLayout(self._sizer2, 0) self._sizer1.addWidget(self._tree, 1) # self._sizer2.addWidget(self._sliderIcon, 0) self._sizer2.addWidget(self._navbut_back, 0) self._sizer2.addWidget(self._navbut_forward, 0) self._sizer2.addStretch(1) self._sizer2.addWidget(self._slider, 6) self._sizer2.addStretch(1) self._sizer2.addWidget(self._options, 0) # self.setLayout(self._sizer1) # Init current-file name self._currentEditorId = 0 # Bind to events pyzo.editors.currentChanged.connect(self.onEditorsCurrentChanged) pyzo.editors.parserDone.connect(self.updateStructure) self._options.pressed.connect(self.onOptionsPress) self._options._menu.triggered.connect(self.onOptionMenuTiggered) # Start # When the tool is loaded, the editorStack is already done loading # all previous files and selected the appropriate file. self.onOptionsPress() # Create menu now self.onEditorsCurrentChanged() def onOptionsPress(self): """ Create the menu for the button, Do each time to make sure the checks are right. """ # Get menu menu = self._options._menu menu.clear() for type in ['class', 'def', 'cell', 'todo', 'import', 'attribute']: checked = type in self._config.showTypes action = menu.addAction('Show %s'%type) action.setCheckable(True) action.setChecked(checked) def onOptionMenuTiggered(self, action): """ The user decides what to show in the structure. """ # What to show type = action.text().split(' ',1)[1] # Swap if type in self._config.showTypes: while type in self._config.showTypes: self._config.showTypes.remove(type) else: self._config.showTypes.append(type) # Update self.updateStructure() def onEditorsCurrentChanged(self): """ Notify that the file is being parsed and make sure that not the structure of a previously selected file is shown. """ # Get editor and clear list editor = pyzo.editors.getCurrentEditor() self._tree.clear() if editor is None: # Set editor id self._currentEditorId = 0 if editor is not None: # Set editor id self._currentEditorId = id(editor) # Notify text = translate('pyzoSourceStructure', 'Parsing ') + editor._name + ' ...' QtWidgets.QTreeWidgetItem(self._tree, [text]) # Try getting the structure right now self.updateStructure() def _getCurrentNav(self): if not self._currentEditorId: return None if self._currentEditorId not in self._nav: self._nav[self._currentEditorId] = Navigation() return self._nav[self._currentEditorId] def onNavBack(self): nav = self._getCurrentNav() if not nav or not nav.back: return linenr = nav.back.pop(-1) old_linenr = self._navigate_to_line(linenr) if old_linenr is not None: nav.forward.append(old_linenr) def onNavForward(self): nav = self._getCurrentNav() if not nav or not nav.forward: return linenr = nav.forward.pop(-1) old_linenr = self._navigate_to_line(linenr) if old_linenr is not None: nav.back.append(old_linenr) def onItemClick(self, item): """ Go to the right line in the editor and give focus. """ # If item is attribute, get parent if not item.linenr: item = item.parent() old_linenr = self._navigate_to_line(item.linenr) if old_linenr is not None: nav = self._getCurrentNav() if nav and (not nav.back or nav.back[-1] != old_linenr): nav.back.append(old_linenr) nav.forward = [] def _navigate_to_line(self, linenr): # Get editor editor = pyzo.editors.getCurrentEditor() if not editor: return None # Keep current line nr old_linenr = editor.textCursor().blockNumber() + 1 # Move to line editor.gotoLine(linenr) # Give focus pyzo.callLater(editor.setFocus) return old_linenr def updateStructure(self): """ Updates the tree. """ # Get editor editor = pyzo.editors.getCurrentEditor() if not editor: return # Something to show result = pyzo.parser._getResult() if result is None: return # Do the ids match? id0, id1, id2 = self._currentEditorId, id(editor), result.editorId if id0 != id1 or id0 != id2: return # Get current line number and the structure ln = editor.textCursor().blockNumber() ln += 1 # is ln as in line number area # Define colours colours = {'cell':'#b58900', 'class':'#cb4b16', 'def':'#073642', 'attribute':'#657b83', 'import':'#268bd2', 'todo':'#d33682', 'nameismain':'#859900'} #colours = {'cell':'#007F00', 'class':'#0000FF', 'def':'#007F7F', # 'attribute':'#444444', 'import':'#8800BB', 'todo':'#FF3333', # 'nameismain':'#007F00'} # Define what to show showTypes = self._config.showTypes # Define to what level to show (now is also a good time to save) showLevel = int( self._slider.value() ) self._config.level = showLevel showLevel = showLevel if showLevel < 5 else 99 # Define function to set items selectedItem = [None] def SetItems(parentItem, fictiveObjects, level): level += 1 for object in fictiveObjects: type = object.type if type not in showTypes and type != 'nameismain': continue # Construct text if type == 'import': text = "→ %s (%s)" % (object.name, object.text) elif type=='todo': text = object.name elif type=='nameismain': text = object.text elif type=='class': text = object.name elif type=='def': text = object.name + '()' elif type=='attribute': text = '- ' + object.name elif type in ('cell', '##', '#%%', '# %%'): type = 'cell' text = '## ' + object.name + ' ' * 120 else: text = "%s %s" % (type, object.name) # Create item thisItem = QtWidgets.QTreeWidgetItem(parentItem, [text]) color = QtGui.QColor(colours[object.type]) thisItem.setForeground(0, QtGui.QBrush(color)) font = thisItem.font(0) font.setBold(True) if type == 'cell': font.setUnderline(True) thisItem.setFont(0, font) thisItem.linenr = object.linenr # Is this the current item? if ln and object.linenr <= ln and object.linenr2 > ln: selectedItem[0] = thisItem # Any children that we should display? if object.children: SetItems(thisItem, object.children, level) # Set visibility thisItem.setExpanded( bool(level < showLevel) ) # Go self._tree.setUpdatesEnabled(False) self._tree.clear() SetItems(self._tree, result.rootItem.children, 0) self._tree.setUpdatesEnabled(True) # Handle selected item selectedItem = selectedItem[0] if selectedItem: selectedItem.setBackground(0, QtGui.QBrush(QtGui.QColor('#CCC'))) self._tree.scrollToItem(selectedItem) # ensure visible pyzo-4.4.3/pyzo/tools/pyzoWebBrowser.py0000666000000000000000000002017213153504700016373 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import urllib.request, urllib.parse from pyzo.util.qt import QtCore, QtWidgets imported_qtwebkit = True try: from pyzo.util.qt import QtWebKit except ImportError: imported_qtwebkit = False import pyzo tool_name = pyzo.translate("pyzoWebBrowser","Web browser") tool_summary = "A very simple web browser." default_bookmarks = [ 'docs.python.org', 'scipy.org', 'doc.qt.nokia.com/4.5/', 'pyzo.org', ] class WebView(QtWidgets.QTextBrowser): """ Inherit the webview class to implement zooming using the mouse wheel. """ loadStarted = QtCore.Signal() loadFinished = QtCore.Signal(bool) def __init__(self, parent): QtWidgets.QTextBrowser.__init__(self, parent) # Current url self._url = '' self._history = [] self._history2 = [] # Connect self.anchorClicked.connect(self.load) def wheelEvent(self, event): # Zooming does not work for this widget if QtCore.Qt.ControlModifier & QtWidgets.qApp.keyboardModifiers(): self.parent().wheelEvent(event) else: QtWidgets.QTextBrowser.wheelEvent(self, event) def url(self): return self._url def _getUrlParts(self): r = urllib.parse.urlparse(self._url) base = r.scheme + '://' + r.netloc return base, r.path, r.fragment # # def loadCss(self, urls=[]): # urls.append('http://docs.python.org/_static/default.css') # urls.append('http://docs.python.org/_static/pygments.css') # text = '' # for url in urls: # tmp = urllib.request.urlopen(url).read().decode('utf-8') # text += '\n' + tmp # self.document().setDefaultStyleSheet(text) def back(self): # Get url and update forward history url = self._history.pop() self._history2.append(self._url) # Go there url = self._load(url) def forward(self): if not self._history2: return # Get url and update forward history url = self._history2.pop() self._history.append(self._url) # Go there url = self._load(url) def load(self, url): # Clear forward history self._history2 = [] # Store current url in history while self._url in self._history: self._history.remove(self._url) self._history.append(self._url) # Load url = self._load(url) def _load(self, url): """ _load(url) Convert url and load page, returns new url. """ # Make url a string if isinstance(url, QtCore.QUrl): url = str(url.toString()) # Compose relative url to absolute if url.startswith('#'): base, path, frag = self._getUrlParts() url = base + path + url elif not '//' in url: base, path, frag = self._getUrlParts() url = base + '/' + url.lstrip('/') # Try loading self.loadStarted.emit() self._url = url try: #print('URL:', url) text = urllib.request.urlopen(url).read().decode('utf-8') self.setHtml(text) self.loadFinished.emit(True) except Exception as err: self.setHtml(str(err)) self.loadFinished.emit(False) # Set return url class PyzoWebBrowser(QtWidgets.QFrame): """ The main window, containing buttons, address bar and browser widget. """ def __init__(self, parent): QtWidgets.QFrame.__init__(self, parent) # Init config toolId = self.__class__.__name__.lower() self._config = pyzo.config.tools[toolId] if not hasattr(self._config, 'zoomFactor'): self._config.zoomFactor = 1.0 if not hasattr(self._config, 'bookMarks'): self._config.bookMarks = [] for item in default_bookmarks: if item not in self._config.bookMarks: self._config.bookMarks.append(item) # Get style object (for icons) style = QtWidgets.QApplication.style() # Create some buttons self._back = QtWidgets.QToolButton(self) self._back.setIcon(style.standardIcon(style.SP_ArrowBack)) self._back.setIconSize(QtCore.QSize(16,16)) # self._forward = QtWidgets.QToolButton(self) self._forward.setIcon(style.standardIcon(style.SP_ArrowForward)) self._forward.setIconSize(QtCore.QSize(16,16)) # Create address bar #self._address = QtWidgets.QLineEdit(self) self._address = QtWidgets.QComboBox(self) self._address.setEditable(True) self._address.setInsertPolicy(self._address.NoInsert) # for a in self._config.bookMarks: self._address.addItem(a) self._address.setEditText('') # Create web view if imported_qtwebkit: self._view = QtWebKit.QWebView(self) else: self._view = WebView(self) # # self._view.setZoomFactor(self._config.zoomFactor) # settings = self._view.settings() # settings.setAttribute(settings.JavascriptEnabled, True) # settings.setAttribute(settings.PluginsEnabled, True) # Layout self._sizer1 = QtWidgets.QVBoxLayout(self) self._sizer2 = QtWidgets.QHBoxLayout() # self._sizer2.addWidget(self._back, 0) self._sizer2.addWidget(self._forward, 0) self._sizer2.addWidget(self._address, 1) # self._sizer1.addLayout(self._sizer2, 0) self._sizer1.addWidget(self._view, 1) # self._sizer1.setSpacing(2) self.setLayout(self._sizer1) # Bind signals self._back.clicked .connect(self.onBack) self._forward.clicked .connect(self.onForward) self._address.lineEdit().returnPressed.connect(self.go) self._address.activated.connect(self.go) self._view.loadFinished.connect(self.onLoadEnd) self._view.loadStarted.connect(self.onLoadStart) # Start self._view.show() self.go('http://docs.python.org') def parseAddress(self, address): if not address.startswith('http'): address = 'http://' + address return address#QtCore.QUrl(address, QtCore.QUrl.TolerantMode) def go(self, address=None): if not isinstance(address, str): address = self._address.currentText() self._view.load( self.parseAddress(address) ) def onLoadStart(self): self._address.setEditText('') def onLoadEnd(self, ok): if ok: #url = self._view.url() #address = str(url.toString()) if imported_qtwebkit: address = self._view.url().toString() else: address = self._view.url() else: address = '' self._address.setEditText(str(address)) def onBack(self): self._view.back() def onForward(self): self._view.forward() def wheelEvent(self, event): if QtCore.Qt.ControlModifier & QtWidgets.qApp.keyboardModifiers(): # Get amount of scrolling degrees = event.delta() / 8.0 steps = degrees / 15.0 # Set factor factor = self._view.zoomFactor() + steps/10.0 if factor < 0.25: factor = 0.25 if factor > 4.0: factor = 4.0 # Store and apply self._config.zoomFactor = factor # self._view.setZoomFactor(factor) else: QtWidgets.QFrame.wheelEvent(self, event) pyzo-4.4.3/pyzo/tools/pyzoWorkspace.py0000666000000000000000000003023313153504700016247 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import pyzo from pyzo.util.qt import QtCore, QtGui, QtWidgets tool_name = pyzo.translate("pyzoWorkspace","Workspace") tool_summary = "Lists the variables in the current shell's namespace." def splitName(name): """ splitName(name) Split an object name in parts, taking dots and indexing into account. """ name = name.replace('[', '.[') parts = name.split('.') return [p for p in parts if p] def joinName(parts): """ joinName(parts) Join the parts of an object name, taking dots and indexing into account. """ name = '.'.join(parts) return name.replace('.[', '[') class WorkspaceProxy(QtCore.QObject): """ WorkspaceProxy A proxy class to handle the asynchonous behaviour of getting information from the shell. The workspace tool asks for a certain name, and this class notifies when new data is available using a qt signal. """ haveNewData = QtCore.Signal() def __init__(self): QtCore.QObject.__init__(self) # Variables self._variables = [] # Element to get more info of self._name = '' # Bind to events pyzo.shells.currentShellChanged.connect(self.onCurrentShellChanged) pyzo.shells.currentShellStateChanged.connect(self.onCurrentShellStateChanged) # Initialize self.onCurrentShellStateChanged() def addNamePart(self, part): """ addNamePart(part) Add a part to the name. """ parts = splitName(self._name) parts.append(part) self.setName(joinName(parts)) def setName(self, name): """ setName(name) Set the name that we want to know more of. """ self._name = name shell = pyzo.shells.getCurrentShell() if shell: future = shell._request.dir2(self._name) future.add_done_callback(self.processResponse) def goUp(self): """ goUp() Cut the last part off the name. """ parts = splitName(self._name) if parts: parts.pop() self.setName(joinName(parts)) def onCurrentShellChanged(self): """ onCurrentShellChanged() When no shell is selected now, update this. In all other cases, the onCurrentShellStateChange will be fired too. """ shell = pyzo.shells.getCurrentShell() if not shell: self._variables = [] self.haveNewData.emit() def onCurrentShellStateChanged(self): """ onCurrentShellStateChanged() Do a request for information! """ shell = pyzo.shells.getCurrentShell() if not shell: # Should never happen I think, but just to be sure self._variables = [] elif shell._state.lower() != 'busy': future = shell._request.dir2(self._name) future.add_done_callback(self.processResponse) def processResponse(self, future): """ processResponse(response) We got a response, update our list and notify the tree. """ response = [] # Process future if future.cancelled(): pass #print('Introspect cancelled') # No living kernel elif future.exception(): print('Introspect-queryDoc-exception: ', future.exception()) else: response = future.result() self._variables = response self.haveNewData.emit() class WorkspaceItem(QtWidgets.QTreeWidgetItem): def __lt__(self, otherItem): column = self.treeWidget().sortColumn() try: return float( self.text(column).strip('[]') ) > float( otherItem.text(column).strip('[]') ) except ValueError: return self.text(column) > otherItem.text(column) class WorkspaceTree(QtWidgets.QTreeWidget): """ WorkspaceTree The tree that displays the items in the current namespace. I first thought about implementing this using the mode/view framework, but it is so much work and I can't seem to fully understand how it works :( The QTreeWidget is so very simple and enables sorting very easily, so I'll stick with that ... """ def __init__(self, parent): QtWidgets.QTreeWidget.__init__(self, parent) self._config = parent._config # Set header stuff self.setHeaderHidden(False) self.setColumnCount(3) self.setHeaderLabels(['Name', 'Type', 'Repr']) #self.setColumnWidth(0, 100) self.setSortingEnabled(True) # Nice rows self.setAlternatingRowColors(True) self.setRootIsDecorated(False) # Create proxy self._proxy = WorkspaceProxy() self._proxy.haveNewData.connect(self.fillWorkspace) # For menu self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) self._menu = QtWidgets.QMenu() self._menu.triggered.connect(self.contextMenuTriggered) # Bind to events self.itemActivated.connect(self.onItemExpand) def contextMenuEvent(self, event): """ contextMenuEvent(event) Show the context menu. """ QtWidgets.QTreeView.contextMenuEvent(self, event) # Get if an item is selected item = self.currentItem() if not item: return # Create menu self._menu.clear() for a in ['Show namespace', 'Show help', 'Delete']: action = self._menu.addAction(a) parts = splitName(self._proxy._name) parts.append(item.text(0)) action._objectName = joinName(parts) action._item = item # Show self._menu.popup(QtGui.QCursor.pos()+QtCore.QPoint(3,3)) def contextMenuTriggered(self, action): """ contextMenuTriggered(action) Process a request from the context menu. """ # Get text req = action.text().lower() if 'namespace' in req: # Go deeper self.onItemExpand(action._item) elif 'help' in req: # Show help in help tool (if loaded) hw = pyzo.toolManager.getTool('pyzointeractivehelp') if hw: hw.setObjectName(action._objectName) elif 'delete' in req: # Delete the variable shell = pyzo.shells.getCurrentShell() if shell: shell.processLine('del ' + action._objectName) def onItemExpand(self, item): """ onItemExpand(item) Inspect the attributes of that item. """ self._proxy.addNamePart(item.text(0)) def fillWorkspace(self): """ fillWorkspace() Update the workspace tree. """ # Clear first self.clear() # Set name line = self.parent()._line line.setText(self._proxy._name) # Add elements for des in self._proxy._variables: # Get parts parts = des.split(',',3) if len(parts) < 4: continue name = parts[0] # Pop the 'kind' element kind = parts.pop(2) # # the typeTranslation dictionary contains "synonyms" for types that will be hidden # Currently only "method"->"function" is used # the try:... is there to have a minimal translation dictionary. try: kind = self._config.typeTranslation[kind] except KeyError: pass # if kind in self._config.hideTypes: continue if name.startswith('_') and 'private' in self._config.hideTypes: continue # Create item item = WorkspaceItem(parts, 0) self.addTopLevelItem(item) # Set tooltip tt = '%s: %s' % (parts[0], parts[-1]) item.setToolTip(0,tt) item.setToolTip(1,tt) item.setToolTip(2,tt) class PyzoWorkspace(QtWidgets.QWidget): """ PyzoWorkspace The main widget for this tool. """ def __init__(self, parent): QtWidgets.QWidget.__init__(self, parent) # Make sure there is a configuration entry for this tool # The pyzo tool manager makes sure that there is an entry in # config.tools before the tool is instantiated. toolId = self.__class__.__name__.lower() self._config = pyzo.config.tools[toolId] if not hasattr(self._config, 'hideTypes'): self._config.hideTypes = [] # # configuring the typeTranslation dictionary if not hasattr(self._config, 'typeTranslation'): # to prevent the exception to be raised, one could init to : # {"method": "function", "function": "function", "type": "type", "private": "private", "module": "module"} self._config.typeTranslation = {} # Defaults self._config.typeTranslation['method'] = 'function' self._config.typeTranslation['builtin_function_or_method'] = 'function' # # Create tool button self._up = QtWidgets.QToolButton(self) style = QtWidgets.qApp.style() self._up.setIcon( style.standardIcon(style.SP_ArrowLeft) ) self._up.setIconSize(QtCore.QSize(16,16)) # Create "path" line edit self._line = QtWidgets.QLineEdit(self) self._line.setReadOnly(True) self._line.setStyleSheet("QLineEdit { background:#ddd; }") self._line.setFocusPolicy(QtCore.Qt.NoFocus) # Create options menu self._options = QtWidgets.QToolButton(self) self._options.setIcon(pyzo.icons.filter) self._options.setIconSize(QtCore.QSize(16,16)) self._options.setPopupMode(self._options.InstantPopup) self._options.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) # self._options._menu = QtWidgets.QMenu() self._options.setMenu(self._options._menu) self.onOptionsPress() # create menu now # Create tree self._tree = WorkspaceTree(self) # Set layout layout = QtWidgets.QHBoxLayout() layout.addWidget(self._up, 0) layout.addWidget(self._line, 1) layout.addWidget(self._options, 0) # mainLayout = QtWidgets.QVBoxLayout(self) mainLayout.addLayout(layout, 0) mainLayout.addWidget(self._tree, 1) mainLayout.setSpacing(2) mainLayout.setContentsMargins(4,4,4,4) self.setLayout(mainLayout) # Bind events self._up.pressed.connect(self._tree._proxy.goUp) self._options.pressed.connect(self.onOptionsPress) self._options._menu.triggered.connect(self.onOptionMenuTiggered) def onOptionsPress(self): """ Create the menu for the button, Do each time to make sure the checks are right. """ # Get menu menu = self._options._menu menu.clear() for type in ['type', 'function', 'module', 'private']: checked = type in self._config.hideTypes action = menu.addAction('Hide %s'%type) action.setCheckable(True) action.setChecked(checked) def onOptionMenuTiggered(self, action): """ The user decides what to hide in the workspace. """ # What to show type = action.text().split(' ',1)[1] # Swap if type in self._config.hideTypes: while type in self._config.hideTypes: self._config.hideTypes.remove(type) else: self._config.hideTypes.append(type) # Update self._tree.fillWorkspace() pyzo-4.4.3/pyzo/tools/__init__.py0000666000000000000000000003135413153507757015152 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Package tools of pyzo A tool consists of a module which contains a class. The id of a tool is its module name made lower case. The module should contain a class corresponding to its id. We advise to follow the common python style and start the class name with a capital letter, case does not matter for the tool to work though. For instance, the tool "pyzologger" is the class "PyzoLogger" found in module "pyzoLogger" The module may contain the following extra variables (which should be placed within the first 50 lines of code): tool_name - A readable name for the tool (may contain spaces, will be shown in the tab) tool_summary - A single line short summary of the tool. To be displayed in the statusbar. """ # tools I'd like: # - find in files # - workspace # - source tree # - snipet manager # - file browser # - pythonpath editor, startupfile editor (or as part of pyzo?) import os, sys, imp import pyzo from pyzo.util.qt import QtCore, QtGui, QtWidgets # noqa from pyzo.util import zon as ssdf from pyzo import translate class ToolDockWidget(QtWidgets.QDockWidget): """ A dock widget that holds a tool. It sets all settings, initializes the tool widget, and notifies the tool manager on closing. """ def __init__(self, parent, toolManager): QtWidgets.QDockWidget.__init__(self, parent) # Store stuff self._toolManager = toolManager # Allow docking anywhere, othwerise restoring state wont work properly # Set other settings self.setFeatures( QtWidgets.QDockWidget.DockWidgetMovable | QtWidgets.QDockWidget.DockWidgetClosable | QtWidgets.QDockWidget.DockWidgetFloatable #QtWidgets.QDockWidget.DockWidgetVerticalTitleBar ) def setTool(self, toolId, toolName, toolClass): """ Set the tool information. Call this right after initialization. """ # Store id and set object name to enable saving/restoring state self._toolId = toolId self.setObjectName(toolId) # Set name self.setWindowTitle(toolName) # Create tool widget self.reload(toolClass) def closeEvent(self, event): if self._toolManager: self._toolManager.onToolClose(self._toolId) self._toolManager = None # Close and delete widget old = self.widget() if old: old.close() old.deleteLater() # Close and delete dock widget self.close() self.deleteLater() # We handled the event event.accept() def reload(self, toolClass): """ Reload the widget with a new widget class. """ old = self.widget() new = toolClass(pyzo.main) self.setWidget(new) if old: old.close() old.deleteLater() class ToolDescription: """ Provides a description of a tool and has a reference to the tool dock instance if it is loaded. """ def __init__(self, modulePath, name='', description=''): # Set names self.modulePath = modulePath self.moduleName = os.path.splitext(os.path.basename(modulePath))[0] self.id = self.moduleName.lower() if name: self.name = name else: self.name = self.id # Set description self.description = description # Init instance to None, will be set when loaded self.instance = None def menuLauncher(self, value): """ Function that is called by the menu when this tool is selected. """ if value is None: return bool(self.instance) #return self.id in pyzo.toolManager._activeTools elif value: pyzo.toolManager.loadTool(self.id) else: self.widget = None pyzo.toolManager.closeTool(self.id) class ToolManager(QtCore.QObject): """ Manages the tools. """ # This signal indicates a change in the loaded tools toolInstanceChange = QtCore.Signal() def __init__(self, parent = None): QtCore.QObject.__init__(self, parent) # list of ToolDescription instances self._toolInfo = None self._activeTools = {} def loadToolInfo(self): """ (re)load the tool information. """ # Get paths to load files from toolDir1 = os.path.join(pyzo.pyzoDir, 'tools') toolDir2 = os.path.join(pyzo.appDataDir, 'tools') # Create list of tool files toolfiles = [] for toolDir in [toolDir1, toolDir2]: tmp = [os.path.join(toolDir, f) for f in os.listdir(toolDir)] toolfiles.extend(tmp) # Note: we do not use the code below anymore, since even the frozen # app makes use of the .py files. # # Get list of files, also when we're in a zip file. # i = tooldir.find('.zip') # if i>0: # # Get list of files from zipfile # tooldir = tooldir[:i+4] # import zipfile # z = zipfile.ZipFile(tooldir) # toolfiles = [os.path.split(i)[1] for i in z.namelist() # if i.startswith('visvis') and i.count('functions')] # else: # # Get list of files from file system # toolfiles = os.listdir(tooldir) # Iterate over tool modules newlist = [] for file in toolfiles: modulePath = file # Check if os.path.isdir(file): file = os.path.join(file, '__init__.py') # A package perhaps if not os.path.isfile(file): continue elif file.endswith('__.py') or not file.endswith('.py'): continue elif file.endswith('pyzoFileBrowser.py'): # Skip old file browser (the file can be there from a previous install) continue # toolName = "" toolSummary = "" # read file to find name or summary linecount = 0 for line in open(file, encoding='utf-8'): linecount += 1 if linecount > 50: break if line.startswith("tool_name"): i = line.find("=") if i<0: continue line = line.rstrip("\n").rstrip("\r") line = line[i+1:].strip(" ") toolName = eval(line) # applies translation elif line.startswith("tool_summary"): i = line.find("=") if i<0: continue line = line.rstrip("\n").rstrip("\r") line = line[i+1:].strip(" ") toolSummary = line.strip("'").strip('"') else: pass # Add stuff tmp = ToolDescription(modulePath, toolName, toolSummary) newlist.append(tmp) # Store and return self._toolInfo = sorted( newlist, key=lambda x:x.id ) self.updateToolInstances() return self._toolInfo def updateToolInstances(self): """ Make tool instances up to date, so that it can be seen what tools are now active. """ for toolDes in self.getToolInfo(): if toolDes.id in self._activeTools: toolDes.instance = self._activeTools[toolDes.id] else: toolDes.instance = None # Emit update signal self.toolInstanceChange.emit() def getToolInfo(self): """ Like loadToolInfo(), but use buffered instance if available. """ if self._toolInfo is None: self.loadToolInfo() return self._toolInfo def getToolClass(self, toolId): """ Get the class of the tool. It will import (and reload) the module and get the class. Some checks are performed, like whether the class inherits from QWidget. Returns the class or None if failed... """ # Make sure we have the info if self._toolInfo is None: self.loadToolInfo() # Get module name and path for toolDes in self._toolInfo: if toolDes.id == toolId: moduleName = toolDes.moduleName modulePath = toolDes.modulePath break else: print("WARNING: could not find module for tool", repr(toolId)) return None # Remove from sys.modules, to force the module to reload for key in [key for key in sys.modules]: if key and key.startswith('pyzo.tools.'+moduleName): del sys.modules[key] # Load module try: m_file, m_fname, m_des = imp.find_module(moduleName, [os.path.dirname(modulePath)]) mod = imp.load_module('pyzo.tools.'+moduleName, m_file, m_fname, m_des) except Exception as why: print("Invalid tool " + toolId +":", why) return None # Is the expected class present? className = "" for member in dir(mod): if member.lower() == toolId: className = member break else: print("Invalid tool, Classname must match module name '%s'!" % toolId) return None # Does it inherit from QWidget? plug = mod.__dict__[className] if not (isinstance(plug,type) and issubclass(plug,QtWidgets.QWidget)): print("Invalid tool, tool class must inherit from QWidget!") return None # Succes! return plug def loadTool(self, toolId, splitWith=None): """ Load a tool by creating a dock widget containing the tool widget. """ # A tool id should always be lower case toolId = toolId.lower() # Close old one if toolId in self._activeTools: old = self._activeTools[toolId].widget() self._activeTools[toolId].setWidget(QtWidgets.QWidget(pyzo.main)) if old: old.close() old.deleteLater() # Get tool class (returns None on failure) toolClass = self.getToolClass(toolId) if toolClass is None: return # Already loaded? reload! if toolId in self._activeTools: self._activeTools[toolId].reload(toolClass) return # Obtain name from buffered list of names for toolDes in self._toolInfo: if toolDes.id == toolId: name = toolDes.name break else: name = toolId # Make sure there is a config entry for this tool if not hasattr(pyzo.config.tools, toolId): pyzo.config.tools[toolId] = ssdf.new() # Create dock widget and add in the main window dock = ToolDockWidget(pyzo.main, self) dock.setTool(toolId, name, toolClass) if splitWith and splitWith in self._activeTools: otherDock = self._activeTools[splitWith] pyzo.main.splitDockWidget(otherDock, dock, QtCore.Qt.Horizontal) else: pyzo.main.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock) # Add to list self._activeTools[toolId] = dock self.updateToolInstances() def reloadTools(self): """ Reload all tools. """ for id in self.getLoadedTools(): self.loadTool(id) def closeTool(self, toolId): """ Close the tool with specified id. """ if toolId in self._activeTools: dock = self._activeTools[toolId] dock.close() def getTool(self, toolId): """ Get the tool widget instance, or None if not available. """ if toolId in self._activeTools: return self._activeTools[toolId].widget() else: return None def onToolClose(self, toolId): # Remove from dict self._activeTools.pop(toolId, None) # Set instance to None self.updateToolInstances() def getLoadedTools(self): """ Get a list with id's of loaded tools. """ tmp = [] for toolDes in self.getToolInfo(): if toolDes.id in self._activeTools: tmp.append(toolDes.id) return tmp pyzo-4.4.3/pyzo/util/0000777000000000000000000000000013166630337012643 5ustar 00000000000000pyzo-4.4.3/pyzo/util/bootstrapconda.py0000666000000000000000000004141313123037261016230 0ustar 00000000000000""" Tools to install miniconda from pyzo and register that env in pyzo's shell config. """ import os import sys import stat import time import struct import shutil import threading import subprocess import urllib.request import pyzo from pyzo.util.qt import QtCore, QtWidgets from pyzo import translate base_url = 'http://repo.continuum.io/miniconda/' links = {'win32': 'Miniconda3-latest-Windows-x86.exe', 'win64': 'Miniconda3-latest-Windows-x86_64.exe', 'osx64': 'Miniconda3-latest-MacOSX-x86_64.sh', 'linux32': 'Miniconda3-latest-Linux-x86.sh', 'linux64': 'Miniconda3-latest-Linux-x86_64.sh', 'arm': 'Miniconda3-latest-Linux-armv7l.sh', # raspberry pi } # Get where we want to put miniconda installer miniconda_path = os.path.join(pyzo.appDataDir, 'miniconda') miniconda_path += '.exe' if sys.platform.startswith('win') else '.sh' # Get default dir where we want the env #default_conda_dir = os.path.join(pyzo.appDataDir, 'conda_root') default_conda_dir = 'C:\\miniconda3' if sys.platform.startswith('win') else os.path.expanduser('~/miniconda3') def check_for_conda_env(parent=None): """ Check if it is reasonable to ask to install a conda env. If users says yes, do it. If user says no, don't, and remember. """ # Interested previously? if getattr(pyzo.config.state, 'did_not_want_conda_env', False): print('User has previously indicated to have no interest in a conda env') return # Needed? if pyzo.config.shellConfigs2: exe = pyzo.config.shellConfigs2[0]['exe'] r = '' try: r = subprocess.check_output([exe, '-m', 'conda', 'info'], shell=sys.platform.startswith('win')) r = r.decode() except Exception: pass # no Python or no conda if r and 'is foreign system : False' in r: print('First shell config looks like a conda env.') return # Ask if interested now? d = AskToInstallConda(parent) d.exec_() if not d.result(): pyzo.config.state.did_not_want_conda_env = True # Mark for next time return # Launch installer d = Installer(parent) d.exec_() class AskToInstallConda(QtWidgets.QDialog): def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle('Install a conda env?') self.setModal(True) text = 'Pyzo is only an editor. To execute code, you need a Python environment.\n\n' text += 'Do you want Pyzo to install a Python environment (miniconda)?\n' text += 'If not, you must arrange for a Python interpreter yourself' if not sys.platform.startswith('win'): text += ' or use the system Python' text += '.' text += '\n(You can always launch the installer from the shell menu.)' self._label = QtWidgets.QLabel(text, self) self._no = QtWidgets.QPushButton("No thanks (dont ask again)") self._yes = QtWidgets.QPushButton("Yes, please install Python!") self._no.clicked.connect(self.reject) self._yes.clicked.connect(self.accept) vbox = QtWidgets.QVBoxLayout(self) hbox = QtWidgets.QHBoxLayout() self.setLayout(vbox) vbox.addWidget(self._label, 1) vbox.addLayout(hbox, 0) hbox.addWidget(self._no, 2) hbox.addWidget(self._yes, 2) self._yes.setDefault(1) class Installer(QtWidgets.QDialog): lineFromStdOut = QtCore.Signal(str) def __init__(self, parent=None): QtWidgets.QDialog.__init__(self, parent) self.setWindowTitle('Install miniconda') self.setModal(True) self.resize(500, 500) text = translate('bootstrapconda', 'This will download and install miniconda on your computer.') self._label = QtWidgets.QLabel(text, self) self._scipystack = QtWidgets.QCheckBox(translate('bootstrapconda', 'Also install scientific packages'), self) self._scipystack.setChecked(True) self._path = QtWidgets.QLineEdit(default_conda_dir, self) self._progress = QtWidgets.QProgressBar(self) self._outputLine = QtWidgets.QLabel(self) self._output = QtWidgets.QPlainTextEdit(self) self._output.setReadOnly(True) self._button = QtWidgets.QPushButton('Install', self) self._outputLine.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed) vbox = QtWidgets.QVBoxLayout(self) self.setLayout(vbox) vbox.addWidget(self._label, 0) vbox.addWidget(self._path, 0) vbox.addWidget(self._scipystack, 0) vbox.addWidget(self._progress, 0) vbox.addWidget(self._outputLine, 0) vbox.addWidget(self._output, 1) vbox.addWidget(self._button, 0) self._button.clicked.connect(self.go) self.addOutput(translate('bootstrapconda', 'Waiting to start installation.\n')) self._progress.setVisible(False) self.lineFromStdOut.connect(self.setStatus) def setStatus(self, line): self._outputLine.setText(line) def addOutput(self, text): #self._output.setPlainText(self._output.toPlainText() + '\n' + text) cursor = self._output.textCursor() cursor.movePosition(cursor.End, cursor.MoveAnchor) cursor.insertText(text) cursor.movePosition(cursor.End, cursor.MoveAnchor) self._output.setTextCursor(cursor) self._output.ensureCursorVisible() def addStatus(self, line): self.addOutput('\n' + line) self.setStatus(line) def go(self): # Check if we can install try: self._conda_dir = self._path.text() if not os.path.isabs(self._conda_dir): raise ValueError('Given installation path must be absolute.') if os.path.exists(self._conda_dir): raise ValueError('The given installation path already exists.') except Exception as err: self.addOutput('\nCould not install:\n' + str(err)) return ok = False try: # Disable user input, get ready for installation self._progress.setVisible(True) self._button.clicked.disconnect() self._button.setEnabled(False) self._scipystack.setEnabled(False) self._path.setEnabled(False) if not os.path.exists(self._conda_dir): self.addStatus('Downloading installer ... ') self._progress.setMaximum(100) self.download() self.addStatus('Done downloading installer.') self.make_done() self.addStatus('Installing (this can take a minute) ... ') self._progress.setMaximum(0) ret = self.install() self.addStatus(('Failed' if ret else 'Done') + ' installing.') self.make_done() self.post_install() if self._scipystack.isChecked(): self.addStatus('Installing scientific packages ... ') self._progress.setMaximum(0) ret = self.install_scipy() self.addStatus('Done installing scientific packages') self.make_done() self.addStatus('Verifying ... ') self._progress.setMaximum(100) ret = self.verify() if ret: self.addOutput('Error\n' + ret) self.addStatus('Verification Failed!') else: self.addOutput('Done verifying') self.addStatus('Ready to go!') self.make_done() ok = True except Exception as err: self.addStatus('Installation failed ...') self.addOutput('\n\nException!\n' + str(err)) if not ok: self.addOutput('\n\nWe recommend installing miniconda or anaconda, ') self.addOutput('and making Pyzo aware if it via the shell configuration.') else: self.addOutput('\n\nYou can install additional packages by running "conda install" in the shell.') # Wrap up, allow user to exit self._progress.hide() self._button.setEnabled(True) self._button.setText('Close') self._button.clicked.connect(self.close) def make_done(self): self._progress.setMaximum(100) self._progress.setValue(100) etime = time.time() + 0.2 while time.time() < etime: time.sleep(0.01) QtWidgets.qApp.processEvents() def download(self): # Installer already downloaded? if os.path.isfile(miniconda_path): self.addOutput('Already downloaded.') return # os.remove(miniconda_path) # Get url key key = '' if sys.platform.startswith('win'): key = 'win' elif sys.platform.startswith('darwin'): key = 'osx' elif sys.platform.startswith('linux'): key = 'linux' key += '64' if is_64bit() else '32' # Get url if key not in links: raise RuntimeError('Cannot download miniconda for this platform.') url = base_url + links[key] _fetch_file(url, miniconda_path, self._progress) def install(self): dest = self._conda_dir # Clear dir assert not os.path.isdir(dest), 'Miniconda dir already exists' assert ' ' not in dest, 'miniconda dest path must not contain spaces' if sys.platform.startswith('win'): return self._run_process([miniconda_path, '/S', '/D=%s' % dest]) else: os.chmod(miniconda_path, os.stat(miniconda_path).st_mode | stat.S_IEXEC) return self._run_process([miniconda_path, '-b', '-p', dest]) def post_install(self): exe = py_exe(self._conda_dir) # Add Pyzo channel cmd = [exe, '-m', 'conda', 'config', '--system', '--add', 'channels', 'pyzo'] subprocess.check_call(cmd, shell=sys.platform.startswith('win')) self.addStatus('Added Pyzo channel to conda env') # Add to pyzo shell config if pyzo.config.shellConfigs2 and pyzo.config.shellConfigs2[0]['exe'] == exe: pass else: s = pyzo.ssdf.new() s.name = 'Py3-conda' s.exe = exe s.gui='PyQt4' pyzo.config.shellConfigs2.insert(0, s) pyzo.saveConfig() self.addStatus('Prepended new env to Pyzo shell configs.') def install_scipy(self): packages = ['numpy', 'scipy', 'pandas', 'matplotlib', 'sympy', #'scikit-image', 'scikit-learn', 'pyopengl', # 'visvis', 'imageio', 'tornado', 'pyqt', #'ipython', 'jupyter', #'requests', 'pygments','pytest', ] exe = py_exe(self._conda_dir) cmd = [exe, '-m', 'conda', 'install', '--yes'] + packages return self._run_process(cmd) def _run_process(self, cmd): """ Run command in a separate process, catch stdout, show lines in the output label. On fail, show all output in output text. """ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=sys.platform.startswith('win')) catcher = StreamCatcher(p.stdout, self.lineFromStdOut) while p.poll() is None: time.sleep(0.01) QtWidgets.qApp.processEvents() catcher.join() if p.poll(): self.addOutput(catcher.output()) return p.poll() def verify(self): self._progress.setValue(1) if not os.path.isdir(self._conda_dir): return 'Conda dir not created.' self._progress.setValue(11) exe = py_exe(self._conda_dir) if not os.path.isfile(exe): return 'Conda dir does not have Python exe' self._progress.setValue(21) try: ver = subprocess.check_output([exe, '-c', 'import sys; print(sys.version)']) except Exception as err: return 'Error getting Python version: ' + str(err) self._progress.setValue(31) if ver.decode() < '3.4': return 'Expected Python version 3.4 or higher' self._progress.setValue(41) try: ver = subprocess.check_output([exe, '-c', 'import conda; print(conda.__version__)']) except Exception as err: return 'Error calling Python exe: ' + str(err) self._progress.setValue(51) if ver.decode() < '3.16': return 'Expected Conda version 3.16 or higher' # Smooth toward 100% for i in range(self._progress.value(), 100, 5): time.sleep(0.05) self._progress.setValue(i) QtWidgets.qApp.processEvents() def is_64bit(): """ Get whether the OS is 64 bit. On WIndows yields what it *really* is, not what the process is. """ if False:#sys.platform.startswith('win'): ARG, causes problems with subprocess if 'PROCESSOR_ARCHITEW6432' in os.environ: return True return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') else: return struct.calcsize('P') == 8 def py_exe(dir): if sys.platform.startswith('win'): return os.path.join(dir, 'python.exe') else: return os.path.join(dir, 'bin', 'python') def _chunk_read(response, local_file, chunk_size=1024, initial_size=0, progress=None): """Download a file chunk by chunk and show advancement """ # Adapted from NISL: # https://github.com/nisl/tutorial/blob/master/nisl/datasets.py bytes_so_far = initial_size # Returns only amount left to download when resuming, not the size of the # entire file total_size = int(response.headers['Content-Length'].strip()) total_size += initial_size if progress: progress.setMaximum(total_size) while True: QtWidgets.qApp.processEvents() chunk = response.read(chunk_size) bytes_so_far += len(chunk) if not chunk: sys.stderr.write('\n') break #_chunk_write(chunk, local_file, progress) progress.setValue(bytes_so_far) local_file.write(chunk) def _fetch_file(url, file_name, progress=None): """Load requested file, downloading it if needed or requested """ # Adapted from NISL: # https://github.com/nisl/tutorial/blob/master/nisl/datasets.py temp_file_name = file_name + ".part" local_file = None initial_size = 0 try: # Checking file size and displaying it alongside the download url response = urllib.request.urlopen(url, timeout=5.) # file_size = int(response.headers['Content-Length'].strip()) # Downloading data (can be extended to resume if need be) local_file = open(temp_file_name, "wb") _chunk_read(response, local_file, initial_size=initial_size, progress=progress) # temp file must be closed prior to the move if not local_file.closed: local_file.close() shutil.move(temp_file_name, file_name) except Exception as e: raise RuntimeError('Error while fetching file %s.\n' 'Dataset fetching aborted (%s)' % (url, e)) finally: if local_file is not None: if not local_file.closed: local_file.close() class StreamCatcher(threading.Thread): def __init__(self, file, signal): self._file = file self._signal = signal self._lines = [] self._line = '' threading.Thread.__init__(self) self.setDaemon(True) # do not let this thread hold up Python shutdown self.start() def run(self): while True: time.sleep(0.0001) try: part = self._file.read(20) except ValueError: # pragma: no cover break if not part: break part = part.decode('utf-8', 'ignore') #print(part, end='') self._line += part.replace('\r', '\n') lines = [line for line in self._line.split('\n') if line] self._lines.extend(lines[:-1]) self._line = lines[-1] if self._lines: self._signal.emit(self._lines[-1]) self._lines.append(self._line) self._signal.emit(self._lines[-1]) def output(self): return '\n'.join(self._lines) if __name__ == '__main__': check_for_conda_env() pyzo-4.4.3/pyzo/util/interpreters/0000777000000000000000000000000013166630337015371 5ustar 00000000000000pyzo-4.4.3/pyzo/util/interpreters/inwinreg.py0000666000000000000000000002033213123037261017553 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2016, Almar Klein """ This module implements functionality to obtain registered Python interpreters and to register a Python interpreter in the Windows registry. """ import sys import os try: import winreg except ImportError: winreg = None PYTHON_KEY = 'SOFTWARE\\Python\\PythonCore' PYTHON_KEY_WOW64 = 'SOFTWARE\\Wow6432Node\\Python\\PythonCore' INSTALL_KEY = "InstallPath" PATH_KEY = "PythonPath" class PythonInReg: """ Class to represent a specific version of the Python interpreter registered (or being registered in the registry). This is a helper class for the functions defined in this module; it should not be instantiated directly. """ USER_ONE = 1 USER_ALL = 2 def __init__(self, user, version, wow64=False): self._user = user self._key = (wow64 and PYTHON_KEY_WOW64 or PYTHON_KEY) + '\\' + version def __repr__(self): userstr = [None, 'USER_ONE', 'USER_ALL'][self._user] installPath = self.installPath() reg = self._reg() if not reg: return '' % (self.version(), userstr) elif installPath: return '' % (self.version(), userstr, installPath) else: return '' % (self.version(), userstr) def _root(self): if self._user == PythonInReg.USER_ONE: return winreg.HKEY_CURRENT_USER else: return winreg.HKEY_LOCAL_MACHINE def _reg(self): # Get key for this version try: return winreg.OpenKey(self._root(), self._key, 0, winreg.KEY_READ) except Exception: return None def create(self): """ Create key. If already exists, does nothing. """ # Get key for this version reg = self._reg() if reg: winreg.CloseKey(reg) #print('Unable to create Python version %s: already exists.' % self.version()) else: # Try to create try: reg = winreg.CreateKey(self._root(), self._key) winreg.CloseKey(reg) except Exception: raise RuntimeError('Unable to create python version %s.' % self.version()) print('Created %s.' % str(self)) def delete(self): # Get key for this version reg = self._reg() if not reg: print('Unable to delete Python version %s: does not exist.') # Delete attributes try: winreg.DeleteKey(reg, INSTALL_KEY) except Exception: pass try: winreg.DeleteKey(reg, PATH_KEY) except Exception: pass # Delete main key for this version, or show warning try: winreg.DeleteKey(self._root(), self._key) except Exception: print('Could not delete %s.' % str(self)) return print('Deleted %s.' % str(self)) def setInstallPath(self, installPath): # Get key for this version reg = self._reg() if not reg: raise RuntimeError('Could not set installPath for version %s: version does not exist.' % self.version()) # Set value or raise error try: winreg.SetValue(reg, INSTALL_KEY, winreg.REG_SZ, installPath) winreg.CloseKey(reg) except Exception: winreg.CloseKey(reg) raise RuntimeError('Could not set installPath for %s.' % str(self)) def installPath(self): # Get key for this version reg = self._reg() if not reg: return None # Get value or return None try: installPath = winreg.QueryValue(reg, INSTALL_KEY) winreg.CloseKey(reg) return installPath except Exception: winreg.CloseKey(reg) return None def setPythonPath(self, pythonPath): # Get key for this version reg = self._reg() if not reg: raise RuntimeError('Could not set pythonPath for version %s: version does not exist.' % self.version()) # Set value or raise error try: winreg.SetValue(reg, PATH_KEY, winreg.REG_SZ, pythonPath) winreg.CloseKey(reg) except Exception: winreg.CloseKey(reg) raise RuntimeError('Could not set pythonPath for %s.' % str(self)) def pythonPath(self): # Get key for this version reg = self._reg() if not reg: return None # Get value or return None try: pythonPath = winreg.QueryValue(reg, PATH_KEY) winreg.CloseKey(reg) return pythonPath except Exception: winreg.CloseKey(reg) return None def version(self): """ Get the Python version. """ return self._key[-3:] def get_interpreters_in_reg(): """ get_interpreters_in_reg() Get a list of PythonInReg instances: one for each interpreter in the registry. This function checks both LOCAL_MACHINE and CURRENT_USER. """ versions = [] for user in [1, 2]: for wow64 in [False, True]: versions.extend( _get_interpreter_in_reg(user, wow64) ) return versions def _get_interpreter_in_reg(user, wow64=False): # Get base key if user == PythonInReg.USER_ONE: HKEY = winreg.HKEY_CURRENT_USER else: HKEY = winreg.HKEY_LOCAL_MACHINE # Get Python key if wow64: PYKEY = PYTHON_KEY_WOW64 else: PYKEY = PYTHON_KEY # Try to open Python key try: reg = winreg.OpenKey(HKEY, PYKEY, 0, winreg.KEY_READ) except Exception: return [] # Get info about subkeys nsub, nval, modified = winreg.QueryInfoKey(reg) # Query all versions = [] for i in range(nsub): # Get name and subkey version = winreg.EnumKey(reg, i) versions.append( PythonInReg(user, version, wow64) ) # Done winreg.CloseKey(reg) return versions def register_interpreter(version=None, installPath=None, user=None, wow64=False): """ register_interpreter(version=None, installPath=None, user=None, wow64=False) Register a certain Python version. If version and installPath are not given, the current Python process is registered. if user is not given, tries LOCAL_MACHINE first but uses CURRENT_USER if that fails. """ if version is None: version = sys.version[:3] if installPath is None: installPath = sys.prefix # Get existing versions existingVersions = get_interpreters_in_reg() # Determine what users to try if user is None: users = [2, 1] else: users = [user] success = False for user in users: # Create new PythonInReg instance v = PythonInReg(user, version, wow64) # Check if already exists ok = True for ev in existingVersions: if ev._key != v._key or ev._user != v._user: continue # Different key; no problem if (not ev.installPath()) or (not os.path.isdir(ev.installPath())): continue # Key the same, but existing entry is invalid if ev.installPath() == installPath: # Exactly the same, no action required, return now! return ev # Ok, there's a problem ok = False print('Warning: version %s is already installed in "%s".' % (version, ev.installPath())) if not ok: continue # Try to create the key try: v.create() v.setInstallPath(installPath) success = True break except RuntimeError: continue if success: return v else: raise RuntimeError('Could not register Python version %s at %s.' % (version, installPath)) if __name__ == '__main__': for v in get_interpreters_in_reg(): print(v) pyzo-4.4.3/pyzo/util/interpreters/pythoninterpreter.py0000666000000000000000000001061213123037261021536 0ustar 00000000000000import os import sys import subprocess from .inwinreg import register_interpreter EXE_DIR = os.path.abspath(os.path.dirname(sys.executable)) if EXE_DIR.endswith('.app/Contents/MacOS'): EXE_DIR = os.path.dirname(EXE_DIR.rsplit('.app')[0]) def make_abs(path): if path.startswith('.'): return os.path.abspath(os.path.join(EXE_DIR, path)) return path class PythonInterpreter: """ Class to represent a Python interpreter. It has properties to get the path and version. Upon creation the version number is acquired by calling the interpreter in a subprocess. If this fails, the version becomes ''. """ def __init__(self, path): if not isinstance(path, str): raise ValueError('Path for PythonInterpreter is not a string: %r' % path) if not os.path.isfile(make_abs(path)): raise ValueError('Path for PythonInterpreter is invalid: %r' % path) self._path = path if path.startswith('.') else os.path.normpath(os.path.abspath(path)) self._normpath = os.path.normcase(self._path) self._problem = '' self._version = None # Set prefix self._prefix = os.path.dirname(self.path) if os.path.basename(self._prefix) == 'bin': self._prefix = os.path.dirname(self._prefix) def __repr__(self): cls_name = self.__class__.__name__ return '<%s version %s at %s>' % (cls_name, self.version, self.path) def __hash__(self): return hash(self._normpath) def __eq__(self, other): return self._normpath == other._normpath @property def path(self): """ The path to the executable of the Python interpreter. If relative (starting with a dot), it is relative to the current sys.executable. """ return self._path @property def prefix(self): """ The prefix of this executable. """ return self._prefix @property def is_conda(self): """ Whether this interpreter is part of a conda environment (either a root or an env). """ return os.path.isdir(os.path.join(make_abs(self._prefix), 'conda-meta')) @property def version(self): """ The version number as a string, usually 3 numbers. """ if self._version is None: self._version = self._getversion() return self._version @property def version_info(self): """ The version number as a tuple of integers. For comparing. """ return versionStringToTuple(self.version) def register(self): """ Register this Python intepreter. On Windows this modifies the CURRENT_USER. On All other OS's this is a no-op. """ if sys.platform.startswith('win'): path = os.path.split(make_abs(self.path))[0] # Remove "python.exe" register_interpreter(self.version[:3], path) def _getversion(self): path = make_abs(self._path) # Check if path is even a file if not os.path.isfile(path): self._problem = '%s is not a valid file.' return '' # Poll Python executable (--version does not work on 2.4) # shell=True prevents loads of command windows popping up on Windows, # but if used on Linux it would enter interpreter mode cmd = [path, '-V'] try: v = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=sys.platform.startswith('win')) except (OSError, IOError, subprocess.CalledProcessError) as e: self._problem = str(e) return '' # Extract the version, apply some defensive programming v = v.decode('ascii','ignore').strip().lower() if v.startswith('python'): v = v.split(' ')[1] v = v.split(' ')[0] # Try turning it into version_info try: versionStringToTuple(v) except ValueError: return '' # Done return v def versionStringToTuple(version): # Truncate version number to first occurance of non-numeric character tversion = '' for c in version: if c in '0123456789.': tversion += c # Split by dots, make each number an integer tversion = tversion.strip('.') return tuple( [int(a) for a in tversion.split('.') if a] ) pyzo-4.4.3/pyzo/util/interpreters/__init__.py0000666000000000000000000001514013123037261017471 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2016, Almar Klein """ This module implements functionality to detect available Python interpreters. This is done by looking at common locations, the Windows registry, and conda's environment list. """ import sys import os from .pythoninterpreter import EXE_DIR, PythonInterpreter, versionStringToTuple from .inwinreg import get_interpreters_in_reg from .. import paths def get_interpreters(minimumVersion=None): """ get_interpreters(minimumVersion=None) Returns a list of PythonInterpreter instances. If minimumVersion is given, return only the interprers with at least that version, and also sort the result by version number. """ # Get Python interpreters if sys.platform.startswith('win'): pythons = _get_interpreters_win() else: pythons = _get_interpreters_posix() pythons = set([PythonInterpreter(p) for p in pythons]) # Get conda paths condas = set([PythonInterpreter(p) for p in _get_interpreters_conda()]) # Get relative interpreters relative = set([PythonInterpreter(p) for p in _get_interpreters_relative()]) # Get Pyzo paths pyzos = set([PythonInterpreter(p) for p in _get_interpreters_pyzo()]) # Almost done interpreters = set.union(pythons, condas, relative, pyzos) minimumVersion = minimumVersion or '0' return _select_interpreters(interpreters, minimumVersion) def _select_interpreters(interpreters, minimumVersion): """ Given a list of PythonInterpreter instances, return a list with the interpreters selected that are valid and have their version equal or larger than the given minimimVersion. The returned list is sorted by version number. """ if not isinstance(minimumVersion, str): raise ValueError('minimumVersion in get_interpreters must be a string.') # Remove invalid interpreters interpreters = [i for i in interpreters if i.version] # Remove the ones below the reference version if minimumVersion is not None: refTuple = versionStringToTuple(minimumVersion) interpreters = [i for i in interpreters if (i.version_info >= refTuple)] # Return, sorted by version return sorted(interpreters, key=lambda x:x.version_info) def _get_interpreters_win(): found = [] # Query from registry for v in get_interpreters_in_reg(): found.append(v.installPath() ) # Check common locations for rootname in ['c:/', 'C:/program files/', 'C:/program files (x86)/', '~']: rootname = os.path.expanduser(rootname) if not os.path.isdir(rootname): continue for dname in os.listdir(rootname): if dname.lower().startswith(('python', 'pypy', 'miniconda', 'anaconda')): found.append(os.path.join(rootname, dname)) # Normalize all paths, and remove trailing backslashes found = [os.path.normcase(os.path.abspath(v)).strip('\\') for v in found] # Append "python.exe" and check if that file exists found2 = [] for dname in found: for fname in ('python.exe', 'pypy.exe'): exename = os.path.join(dname, fname) if os.path.isfile(exename): found2.append(exename) break # Returnas set (remove duplicates) return set(found2) def _get_interpreters_posix(): found=[] # Look for system Python interpreters for searchpath in ['/usr/bin','/usr/local/bin','/opt/local/bin']: searchpath = os.path.expanduser(searchpath) # Get files try: files = os.listdir(searchpath) except Exception: continue # Search for python executables for fname in files: if fname.startswith(('python', 'pypy')) and not fname.count('config'): if len(fname) < 16: # Get filename and resolve symlink filename = os.path.join(searchpath, fname) filename = os.path.realpath(filename) # Seen on OS X that was not a valid file if os.path.isfile(filename): found.append(filename) # Look for user-installed Python interpreters such as pypy and anaconda for rootname in ['~', '/usr/local']: rootname = os.path.expanduser(rootname) if not os.path.isdir(rootname): continue for dname in os.listdir(rootname): if dname.lower().startswith(('python', 'pypy', 'miniconda', 'anaconda')): for fname in ('bin/python', 'bin/pypy'): exename = os.path.join(rootname, dname, fname) if os.path.isfile(exename): found.append(exename) # Remove pythonw, pythonm and the like found = set(found) for path in list(found): if path.endswith(('m', 'w')) and path[:-1] in found: found.discard(path) # Return as set (remove duplicates) return set(found) def _get_interpreters_pyzo(): """ Get a list of known Pyzo interpreters. """ pythonname = 'python' + '.exe' * sys.platform.startswith('win') exes = [] for d in paths.pyzo_dirs(): for fname in [ os.path.join(d, 'bin', pythonname + '3'), os.path.join(d, pythonname), ]: if os.path.isfile(fname): exes.append(fname) break return exes def _get_interpreters_conda(): """ Get known conda environments """ if sys.platform.startswith('win'): pythonname = 'python' + '.exe' else: pythonname = 'bin/python' exes = [] filename = os.path.expanduser('~/.conda/environments.txt') if os.path.isfile(filename): for line in open(filename, 'rt').readlines(): line = line.strip() exe_filename = os.path.join(line, pythonname) if line and os.path.isfile(exe_filename): exes.append(exe_filename) return exes def _get_interpreters_relative(): """ Get interpreters relative to our prefix and the parent directory. This allows Pyzo to be shipped inside a pre-installed conda env. """ pythonname = 'python' + '.exe' * sys.platform.startswith('win') exes = [] for d in ['.', '..']: for fname in [ os.path.join(d, 'bin', pythonname), os.path.join(d, pythonname), ]: filename = os.path.abspath(os.path.join(EXE_DIR, fname)) if os.path.isfile(filename): exes.append(fname) break return exes if __name__ == '__main__': for pi in get_interpreters(): print(pi) pyzo-4.4.3/pyzo/util/paths.py0000666000000000000000000002412513123037261014326 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2016, Almar Klein, Rob Reilink # # This file is distributed under the terms of the (new) BSD License. """ Module paths Get paths to useful directories in a cross platform manner. The functions in this module are designed to be stand-alone, so that they can easily be copied and used in code that does not want pyzo as a dependency. This code was first part of pyzolib, and later moved to pyzo. """ # Notes: # * site.getusersitepackages() returns a dir in roaming userspace on Windows # so better avoid that. # * site.getuserbase() returns appdata_dir('Python', True) # * See docstring: that's why the functions tend to not re-use each-other import sys ISWIN = sys.platform.startswith('win') ISMAC = sys.platform.startswith('darwin') ISLINUX = sys.platform.startswith('linux') PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import sys def is_frozen(): """ is_frozen() Return whether this app is a frozen application (using e.g. cx_freeze). """ return bool( getattr(sys, 'frozen', None) ) # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import os, sys, tempfile def temp_dir(appname=None, nospaces=False): """ temp_dir(appname=None, nospaces=False) Get path to a temporary directory with write access. If appname is given, a subdir is appended (and created if necessary). If nospaces, will ensure that the path has no spaces. """ # Do it the Python way path = tempfile.gettempdir() # Try harder if we have to if nospaces and ' ' in path: if sys.platform.startswith('win'): for path in ['c:\\TEMP', 'c:\\TMP']: if os.path.isdir(path): break if not os.path.isdir(path): os.mkdir(path) else: for path in ['/tmp', '/var/tmp']: # http://www.tuxfiles.org/linuxhelp/linuxdir.html if os.path.isdir(path): break else: raise RuntimeError('Could not locate temporary directory.') # Get path specific for this app if appname: path = os.path.join(path, appname) if not os.path.isdir(path): os.mkdir(path) # Done return path # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import os def user_dir(): """ user_dir() Get the path to the user directory. (e.g. "/home/jack", "c:/Users/jack") """ return os.path.expanduser('~') # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import os, sys def appdata_dir(appname=None, roaming=False, macAsLinux=False): """ appdata_dir(appname=None, roaming=False, macAsLinux=False) Get the path to the application directory, where applications are allowed to write user specific files (e.g. configurations). For non-user specific data, consider using common_appdata_dir(). If appname is given, a subdir is appended (and created if necessary). If roaming is True, will prefer a roaming directory (Windows Vista/7). If macAsLinux is True, will return the Linux-like location on Mac. """ # Define default user directory userDir = os.path.expanduser('~') # Get system app data dir path = None if sys.platform.startswith('win'): path1, path2 = os.getenv('LOCALAPPDATA'), os.getenv('APPDATA') path = (path2 or path1) if roaming else (path1 or path2) elif sys.platform.startswith('darwin') and not macAsLinux: path = os.path.join(userDir, 'Library', 'Application Support') # On Linux and as fallback if not (path and os.path.isdir(path)): path = userDir # Maybe we should store things local to the executable (in case of a # portable distro or a frozen application that wants to be portable) prefix = sys.prefix if getattr(sys, 'frozen', None): # See application_dir() function prefix = os.path.abspath(os.path.dirname(sys.executable)) for reldir in ('settings', '../settings'): localpath = os.path.abspath(os.path.join(prefix, reldir)) if os.path.isdir(localpath): try: open(os.path.join(localpath, 'test.write'), 'wb').close() os.remove(os.path.join(localpath, 'test.write')) except IOError: pass # We cannot write in this directory else: path = localpath break # Get path specific for this app if appname: if path == userDir: appname = '.' + appname.lstrip('.') # Make it a hidden directory path = os.path.join(path, appname) if not os.path.isdir(path): os.mkdir(path) # Done return path # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import os, sys def common_appdata_dir(appname=None): """ common_appdata_dir(appname=None) Get the path to the common application directory. Applications are allowed to write files here. For user specific data, consider using appdata_dir(). If appname is given, a subdir is appended (and created if necessary). """ # Try to get path path = None if sys.platform.startswith('win'): path = os.getenv('ALLUSERSPROFILE', os.getenv('PROGRAMDATA')) elif sys.platform.startswith('darwin'): path = '/Library/Application Support' else: # Not sure what to use. Apps are only allowed to write to the home # dir and tmp dir, right? pass # If no success, use appdata_dir() instead if not (path and os.path.isdir(path)): path = appdata_dir() # Get path specific for this app if appname: path = os.path.join(path, appname) if not os.path.isdir(path): os.mkdir(path) # Done return path # Other approaches that we considered, but which did not work for links, # or are less reliable for other reasons are: # * sys.executable: does not work for links # * sys.prefix: dito # * sys.exec_prefix: dito # * os.__file__: does not work when frozen # * __file__: only accessable from main module namespace, does not work when frozen # todo: get this included in Python sys or os module! # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import os, sys def application_dir(): """ application_dir() Get the directory in which the current application is located. The "application" can be a Python script or a frozen application. This function raises a RuntimeError if in interpreter mode. """ if getattr(sys, 'frozen', False): # When frozen, use sys.executable thepath = os.path.dirname(sys.executable) else: # Test if the current process can be considered an "application" if not sys.path or not sys.path[0]: raise RuntimeError('Cannot determine app path because sys.path[0] is empty!') thepath = sys.path[0] # Return absolute version, or symlinks may not work return os.path.abspath(thepath) ## Pyzo specific # # A Pyzo distribution maintains a file in the appdata dir that lists # the directory where it is intalled. Pyzo can in principle be installed # multiple times. In that case the file contains multiple entries. # This file is checked each time the pyzo executable is run. Therefore # a user can move the Pyzo directory and simply run the Pyzo executable # to update the registration. def pyzo_dirs(newdir=None, makelast=False): """ pyzo_dirs(newdir=None, makelast=False) Compatibility function. Like pyzo_dirs2, but returns a list of directories and does not allow setting the version. """ return [p[0] for p in pyzo_dirs2(newdir, makelast=makelast)] # From pyzolib/paths.py (https://bitbucket.org/pyzo/pyzolib/src/tip/paths.py) import os, sys def pyzo_dirs2(path=None, version='0', **kwargs): """ pyzo_dirs2(dir=None, version='0', makelast=False) Get the locations of installed Pyzo directories. Returns a list of tuples: (dirname, version). In future versions more information may be added to the file, so please take larger tuples into account. If path is a dir containing a python exe, it is added it to the list. If the keyword arg 'makelast' is given and True, will ensure that the given path is the last in the list (i.e. the default). """ defaultPyzo = '', '0' # To fill in values for shorter items newPyzo = (str(path), str(version)) if path else None # Get application dir userDir = os.path.expanduser('~') path = None if sys.platform.startswith('win'): path = os.getenv('LOCALAPPDATA', os.getenv('APPDATA')) elif sys.platform.startswith('darwin'): path = os.path.join(userDir, 'Library', 'Application Support') # Get application dir for Pyzo if path and os.path.isdir(path): path = os.path.join(path, 'pyzo') else: path = os.path.join(userDir, '.pyzo') # On Linux and as fallback if not os.path.isdir(path): os.mkdir(path) # Open file and parse fname = os.path.join(path, 'pyzodirs') pyzos, npyzos = [], 0 if os.path.isfile(fname): lines = open(fname, 'rb').read().decode('utf-8').split('\n') pyzos = [tuple(d.split(':::')) for d in [d.strip() for d in lines] if d] npyzos = len(pyzos) # Add dir if necessary if newPyzo and os.path.isdir(newPyzo[0]): if kwargs.get('makelast', False) or newPyzo not in pyzos: npyzos = 0 # force save pyzos = [p for p in pyzos if p[0] != newPyzo[0]] # rm based on dir pyzos.append(newPyzo) # Check validity of all pyzos, write back if necessary, and return pythonname = 'python' + '.exe' * sys.platform.startswith('win') pyzos = [p for p in pyzos if os.path.isfile(os.path.join(p[0], pythonname))] if len(pyzos) != npyzos: lines = [':::'.join(p) for p in pyzos] open(fname, 'wb').write( ('\n'.join(lines)).encode('utf-8') ) return [p+defaultPyzo[len(p):] for p in pyzos] ## Windows specific # Maybe for directory of programs, pictures etc. pyzo-4.4.3/pyzo/util/pyzowizard.py0000666000000000000000000003413313123037261015431 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ pyzowizard module Implements a wizard to help new users get familiar with pyzo. """ import os import re import pyzo from pyzo.util.qt import QtCore, QtGui, QtWidgets from pyzo import translate from pyzo.util._locale import LANGUAGES, LANGUAGE_SYNONYMS, setLanguage def retranslate(t): """ To allow retranslating after selecting the language. """ if hasattr(t, 'original'): return translate('wizard', t.original) else: return t class PyzoWizard(QtWidgets.QWizard): def __init__(self, parent): QtWidgets.QWizard.__init__(self, parent) # Set some appearance stuff self.setMinimumSize(600, 500) self.setWindowTitle(translate('wizard', 'Getting started with Pyzo')) self.setWizardStyle(self.ModernStyle) self.setButtonText(self.CancelButton, 'Stop') # Set logo pm = QtGui.QPixmap() pm.load(os.path.join(pyzo.pyzoDir, 'resources', 'appicons', 'pyzologo48.png')) self.setPixmap(self.LogoPixmap, pm) # Define pages klasses = [ IntroWizardPage, TwocomponentsWizardPage, EditorWizardPage, ShellWizardPage1, ShellWizardPage2, RuncodeWizardPage1, RuncodeWizardPage2, ToolsWizardPage1, ToolsWizardPage2, FinalPage] # Create pages self._n = len(klasses) for i, klass in enumerate(klasses): self.addPage(klass(self, i)) def show(self, startPage=None): """ Show the wizard. If startPage is given, open the Wizard at that page. startPage can be an integer or a string that matches the classname of a page. """ QtWidgets.QWizard.show(self) # Check startpage if isinstance(startPage, int): pass elif isinstance(startPage, str): for i in range(self._n): page = self.page(i) if page.__class__.__name__.lower() == startPage.lower(): startPage = i break else: print('Pyzo wizard: Could not find start page: %r' % startPage) startPage = None elif startPage is not None: print('Pyzo wizard: invalid start page: %r' % startPage) startPage = None # Go to start page if startPage is not None: for i in range(startPage): self.next() class BasePyzoWizardPage(QtWidgets.QWizardPage): _prefix = translate('wizard', 'Step') _title = 'dummy title' _descriptions = [] _image_filename = '' def __init__(self, parent, i): QtWidgets.QWizardPage.__init__(self, parent) self._i = i # Create label for description self._text_label = QtWidgets.QLabel(self) self._text_label.setTextFormat(QtCore.Qt.RichText) self._text_label.setWordWrap(True) # Create label for image self._comicLabel = QtWidgets.QLabel(self) pm = QtGui.QPixmap() if 'logo' in self._image_filename: pm.load(os.path.join(pyzo.pyzoDir, 'resources', 'appicons', self._image_filename)) elif self._image_filename: pm.load(os.path.join(pyzo.pyzoDir, 'resources', 'images', self._image_filename)) self._comicLabel.setPixmap(pm) self._comicLabel.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) # Layout theLayout = QtWidgets.QVBoxLayout(self) self.setLayout(theLayout) # theLayout.addWidget(self._text_label) theLayout.addStretch() theLayout.addWidget(self._comicLabel) theLayout.addStretch() def initializePage(self): # Get prefix i = self._i n = self.wizard()._n - 2 # Dont count the first and last page prefix = '' if i and i <= n: prefix = retranslate(self._prefix) + ' %i/%i: ' % (i, n) # Set title self.setTitle(prefix + retranslate(self._title)) # Parse description # Two description units are separated with BR tags # Emphasis on words is set to italic tags. lines = [] descriptions = [retranslate(d).strip() for d in self._descriptions] for description in descriptions: for line in description.splitlines(): line = line.strip() line = re.sub(r'\*(.+?)\*', r'\1', line) lines.append(line) lines.append('

') lines = lines[:-1] # Set description self._text_label.setText('\n'.join(lines)) class IntroWizardPage(BasePyzoWizardPage): _title = translate('wizard', 'Welcome to the Interactive Editor for Python!') _image_filename = 'pyzologo128.png' _descriptions = [ translate('wizard', """This wizard helps you get familiarized with the workings of Pyzo."""), translate('wizard', """Pyzo is a cross-platform Python IDE focused on *interactivity* and *introspection*, which makes it very suitable for scientific computing. Its practical design is aimed at *simplicity* and *efficiency*."""), ] def __init__(self, parent, i): BasePyzoWizardPage.__init__(self, parent, i) # Create label and checkbox t1 = translate('wizard', "This wizard can be opened using 'Help > Pyzo wizard'") # t2 = translate('wizard', "Show this wizard on startup") self._label_info = QtWidgets.QLabel(t1, self) #self._check_show = QtWidgets.QCheckBox(t2, self) #self._check_show.stateChanged.connect(self._setNewUser) # Create language switcher self._langLabel = QtWidgets.QLabel(translate('wizard', "Select language"), self) # self._langBox = QtWidgets.QComboBox(self) self._langBox.setEditable(False) # Fill index, theIndex = -1, -1 cur = pyzo.config.settings.language for lang in sorted(LANGUAGES): index += 1 self._langBox.addItem(lang) if lang == LANGUAGE_SYNONYMS.get(cur, cur): theIndex = index # Set current index if theIndex >= 0: self._langBox.setCurrentIndex(theIndex) # Bind signal self._langBox.activated.connect(self.onLanguageChange) # Init check state #if pyzo.config.state.newUser: # self._check_show.setCheckState(QtCore.Qt.Checked) # Create sublayout layout = QtWidgets.QHBoxLayout() layout.addWidget(self._langLabel, 0) layout.addWidget(self._langBox, 0) layout.addStretch(2) self.layout().addLayout(layout) # Add to layout self.layout().addSpacing(10) self.layout().addWidget(self._label_info) #self.layout().addWidget(self._check_show) def _setNewUser(self, newUser): newUser = bool(newUser) self._label_info.setHidden(newUser) pyzo.config.state.newUser = newUser def onLanguageChange(self): languageName = self._langBox.currentText() if pyzo.config.settings.language == languageName: return # Save new language pyzo.config.settings.language = languageName setLanguage(pyzo.config.settings.language) # Notify user text = translate('wizard', """ The language has been changed for this wizard. Pyzo needs to restart for the change to take effect application-wide. """) m = QtWidgets.QMessageBox(self) m.setWindowTitle(translate("wizard", "Language changed")) m.setText(text) m.setIcon(m.Information) m.exec_() # Get props of current wizard geo = self.wizard().geometry() parent = self.wizard().parent() # Close ourself! self.wizard().close() # Start new one w = PyzoWizard(parent) w.setGeometry(geo) w.show() class TwocomponentsWizardPage(BasePyzoWizardPage): _title = translate('wizard', 'Pyzo consists of two main components') _image_filename = 'pyzo_two_components.png' _descriptions = [ translate('wizard', "You can execute commands directly in the *shell*,"), translate('wizard', "or you can write code in the *editor* and execute that."), ] class EditorWizardPage(BasePyzoWizardPage): _title = translate('wizard', 'The editor is where you write your code') _image_filename = 'pyzo_editor.png' _descriptions = [ translate('wizard', """In the *editor*, each open file is represented as a tab. By right-clicking on a tab, files can be run, saved, closed, etc."""), translate('wizard', """The right mouse button also enables one to make a file the *main file* of a project. This file can be recognized by its star symbol, and it enables running the file more easily."""), ] class ShellWizardPage1(BasePyzoWizardPage): _title = translate('wizard', 'The shell is where your code gets executed') _image_filename = 'pyzo_shell1.png' _descriptions = [ translate('wizard', """When Pyzo starts, a default *shell* is created. You can add more shells that run simultaneously, and which may be of different Python versions."""), translate('wizard', """Shells run in a sub-process, such that when it is busy, Pyzo itself stays responsive, allowing you to keep coding and even run code in another shell."""), ] class ShellWizardPage2(BasePyzoWizardPage): _title = translate('wizard', 'Configuring shells') _image_filename = 'pyzo_shell2.png' _descriptions = [ translate('wizard', """Pyzo can integrate the event loop of five different *GUI toolkits*, thus enabling interactive plotting with e.g. Visvis or Matplotlib."""), translate('wizard', """Via 'Shell > Edit shell configurations', you can edit and add *shell configurations*. This allows you to for example select the initial directory, or use a custom Pythonpath."""), ] class RuncodeWizardPage1(BasePyzoWizardPage): _title = translate('wizard', 'Running code') _image_filename = 'pyzo_run1.png' _descriptions = [ translate('wizard', "Pyzo supports several ways to run source code in the editor. (see the 'Run' menu)."), translate('wizard', """*Run selection:* if there is no selected text, the current line is executed; if the selection is on a single line, the selection is evaluated; if the selection spans multiple lines, Pyzo will run the the (complete) selected lines."""), translate('wizard', "*Run cell:* a cell is everything between two lines starting with '##'."), translate('wizard', "*Run file:* run all the code in the current file."), translate('wizard', "*Run project main file:* run the code in the current project's main file."), ] class RuncodeWizardPage2(BasePyzoWizardPage): _title = translate('wizard', 'Interactive mode vs running as script') _image_filename = '' _descriptions = [ translate('wizard', """You can run the current file or the main file normally, or as a script. When run as script, the shell is restared to provide a clean environment. The shell is also initialized differently so that it closely resembles a normal script execution."""), translate('wizard', """In interactive mode, sys.path[0] is an empty string (i.e. the current dir), and sys.argv is set to ['']."""), translate('wizard', """In script mode, __file__ and sys.argv[0] are set to the scripts filename, sys.path[0] and the working dir are set to the directory containing the script."""), ] class ToolsWizardPage1(BasePyzoWizardPage): _title = translate('wizard', 'Tools for your convenience') _image_filename = 'pyzo_tools1.png' _descriptions = [ translate('wizard', """Via the *Tools menu*, one can select which tools to use. The tools can be positioned in any way you want, and can also be un-docked."""), translate('wizard', """Note that the tools system is designed such that it's easy to create your own tools. Look at the online wiki for more information, or use one of the existing tools as an example."""), ] class ToolsWizardPage2(BasePyzoWizardPage): _title = translate('wizard', 'Recommended tools') _image_filename = 'pyzo_tools2.png' _descriptions = [ translate('wizard', """We especially recommend the following tools:"""), translate('wizard', """The *Source structure tool* gives an outline of the source code."""), translate('wizard', """The *File browser tool* helps keep an overview of all files in a directory. To manage your projects, click the star icon."""), ] class FinalPage(BasePyzoWizardPage): _title = translate('wizard', 'Get coding!') _image_filename = 'pyzologo128.png' _descriptions = [ translate('wizard', """This concludes the Pyzo wizard. Now, get coding and have fun!"""), ] # def smooth_images(): # """ This was used to create the images from their raw versions. # """ # # import os # import visvis as vv # import scipy as sp # import scipy.ndimage # for fname in os.listdir('images'): # im = vv.imread(os.path.join('images', fname)) # for i in range(im.shape[2]): # im[:,:,i] = sp.ndimage.gaussian_filter(im[:,:,i], 0.7) # #fname = fname.replace('.png', '.jpg') # print(fname) # vv.imwrite(fname, im[::2,::2,:]) if __name__ == '__main__': w = PyzoWizard(None) w.show() pyzo-4.4.3/pyzo/util/qt/0000777000000000000000000000000013166630337013267 5ustar 00000000000000pyzo-4.4.3/pyzo/util/qt/QtCore.py0000666000000000000000000000332613123037261015030 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Provides QtCore classes and functions. """ from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal from PyQt5.QtCore import pyqtSlot as Slot from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtCore import QT_VERSION_STR as __version__ # Those are imported from `import *` del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYQT4: from PyQt4.QtCore import * # Those are things we inherited from Spyder that fix crazy crashes under # some specific situations. (See #34) from PyQt4.QtCore import QCoreApplication from PyQt4.QtCore import Qt from PyQt4.QtCore import pyqtSignal as Signal from PyQt4.QtCore import pyqtSlot as Slot from PyQt4.QtCore import pyqtProperty as Property from PyQt4.QtGui import (QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel, QStringListModel) from PyQt4.QtCore import QT_VERSION_STR as __version__ # Those are imported from `import *` del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE: from PySide.QtCore import * from PySide.QtGui import (QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel, QStringListModel) import PySide.QtCore __version__ = PySide.QtCore.__version__ else: raise PythonQtError('No Qt bindings could be found') pyzo-4.4.3/pyzo/util/qt/QtGui.py0000666000000000000000000001441413123037261014664 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Provides QtGui classes and functions. .. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtGui are exposed here. Therefore, you need to treat/use this package as if it were the ``PyQt5.QtGui`` module. """ from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtGui import * elif PYQT4: from PyQt4.Qt import QKeySequence, QTextCursor from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, QFontMetricsF, QGlyphRun, QGradient, QHelpEvent, QHideEvent, QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, QImageIOHandler, QImageReader, QImageWriter, QInputEvent, QInputMethodEvent, QKeyEvent, QLinearGradient, QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, QPolygonF, QQuaternion, QRadialGradient, QRawFont, QRegExpValidator, QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, QStandardItem, QStandardItemModel, QStaticText, QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextDocument, QTextDocumentFragment, QTextDocumentWriter, QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qFuzzyCompare, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator) elif PYSIDE: from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, QFontMetricsF, QGradient, QHelpEvent, QHideEvent, QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, QImageIOHandler, QImageReader, QImageWriter, QInputEvent, QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, QPolygonF, QQuaternion, QRadialGradient, QRegExpValidator, QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, QStandardItem, QStandardItemModel, QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, QTextDocument, QTextDocumentFragment, QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator) else: raise PythonQtError('No Qt bindings could be found') pyzo-4.4.3/pyzo/util/qt/QtHelp.py0000666000000000000000000000036413123037261015027 0ustar 00000000000000from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtHelp import * elif PYQT4: from PyQt4.QtHelp import * elif PYSIDE: from PySide.QtHelp import * else: raise PythonQtError('No Qt bindings could be found') pyzo-4.4.3/pyzo/util/qt/QtPrintSupport.py0000666000000000000000000000151613123037261016630 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Provides QtPrintSupport classes and functions. """ from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtPrintSupport import * elif PYQT4: from PyQt4.QtGui import (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) elif PYSIDE: from PySide.QtGui import (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) else: raise PythonQtError('No Qt bindings could be found') pyzo-4.4.3/pyzo/util/qt/QtWidgets.py0000666000000000000000000001375113123037261015551 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Developmet Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) """ Provides widget classes and functions. .. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtWidgets are exposed here. Therefore, you need to treat/use this package as if it were the ``PyQt5.QtWidgets`` module. """ from . import PYQT5, PYQT4, PYSIDE, PythonQtError from ._patch.qcombobox import patch_qcombobox from ._patch.qheaderview import introduce_renamed_methods_qheaderview if PYQT5: from PyQt5.QtWidgets import * elif PYQT4: from PyQt4.QtGui import * QStyleOptionViewItem = QStyleOptionViewItemV4 del QStyleOptionViewItemV4 # These objects belong to QtGui del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, QFontMetricsF, QGlyphRun, QGradient, QHelpEvent, QHideEvent, QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, QImageIOHandler, QImageReader, QImageWriter, QInputEvent, QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, QPolygonF, QQuaternion, QRadialGradient, QRawFont, QRegExpValidator, QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, QStandardItem, QStandardItemModel, QStaticText, QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, QTextDocument, QTextDocumentFragment, QTextDocumentWriter, QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qFuzzyCompare, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator, QStringListModel) # These objects belong to QtPrintSupport del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) # These objects belong to QtCore del (QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel) # Patch QComboBox to allow Python objects to be passed to userData patch_qcombobox(QComboBox) # QHeaderView: renamed methods introduce_renamed_methods_qheaderview(QHeaderView) elif PYSIDE: from PySide.QtGui import * QStyleOptionViewItem = QStyleOptionViewItemV4 del QStyleOptionViewItemV4 # These objects belong to QtGui del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, QFontMetricsF, QGradient, QHelpEvent, QHideEvent, QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, QImageIOHandler, QImageReader, QImageWriter, QInputEvent, QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, QPolygonF, QQuaternion, QRadialGradient, QRegExpValidator, QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, QStandardItem, QStandardItemModel, QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, QTextDocument, QTextDocumentFragment, QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator, QStringListModel) # These objects belong to QtPrintSupport del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) # These objects belong to QtCore del (QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel) # Patch QComboBox to allow Python objects to be passed to userData patch_qcombobox(QComboBox) # QHeaderView: renamed methods introduce_renamed_methods_qheaderview(QHeaderView) else: raise PythonQtError('No Qt bindings could be found') pyzo-4.4.3/pyzo/util/qt/uic.py0000666000000000000000000002214413123037261014412 0ustar 00000000000000import os from . import PYSIDE, PYQT4, PYQT5 from .QtWidgets import QComboBox if PYQT5: from PyQt5.uic import * elif PYQT4: from PyQt4.uic import * elif PYSIDE: __all__ = ['loadUi'] # In PySide, loadUi does not exist, so we define it using QUiLoader, and # then make sure we expose that function. This is adapted from qt-helpers # which was released under a 3-clause BSD license: # qt-helpers - a common front-end to various Qt modules # # Copyright (c) 2015, Chris Beaumont and Thomas Robitaille # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the # distribution. # * Neither the name of the Glue project nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Which itself was based on the solution at # # https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 # # which was released under the MIT license: # # Copyright (c) 2011 Sebastian Wiesner # Modifications by Charl Botha # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. from PySide.QtCore import QMetaObject from PySide.QtUiTools import QUiLoader class UiLoader(QUiLoader): """ Subclass of :class:`~PySide.QtUiTools.QUiLoader` to create the user interface in a base instance. Unlike :class:`~PySide.QtUiTools.QUiLoader` itself this class does not create a new instance of the top-level widget, but creates the user interface in an existing instance of the top-level class if needed. This mimics the behaviour of :func:`PyQt4.uic.loadUi`. """ def __init__(self, baseinstance, customWidgets=None): """ Create a loader for the given ``baseinstance``. The user interface is created in ``baseinstance``, which must be an instance of the top-level class in the user interface to load, or a subclass thereof. ``customWidgets`` is a dictionary mapping from class name to class object for custom widgets. Usually, this should be done by calling registerCustomWidget on the QUiLoader, but with PySide 1.1.2 on Ubuntu 12.04 x86_64 this causes a segfault. ``parent`` is the parent object of this loader. """ QUiLoader.__init__(self, baseinstance) self.baseinstance = baseinstance if customWidgets is None: self.customWidgets = {} else: self.customWidgets = customWidgets def createWidget(self, class_name, parent=None, name=''): """ Function that is called for each widget defined in ui file, overridden here to populate baseinstance instead. """ if parent is None and self.baseinstance: # supposed to create the top-level widget, return the base # instance instead return self.baseinstance else: # For some reason, Line is not in the list of available # widgets, but works fine, so we have to special case it here. if class_name in self.availableWidgets() or class_name == 'Line': # create a new widget for child widgets widget = QUiLoader.createWidget(self, class_name, parent, name) else: # If not in the list of availableWidgets, must be a custom # widget. This will raise KeyError if the user has not # supplied the relevant class_name in the dictionary or if # customWidgets is empty. try: widget = self.customWidgets[class_name](parent) except KeyError: raise Exception('No custom widget ' + class_name + ' ' 'found in customWidgets') if self.baseinstance: # set an attribute for the new child widget on the base # instance, just like PyQt4.uic.loadUi does. setattr(self.baseinstance, name, widget) return widget def _get_custom_widgets(ui_file): """ This function is used to parse a ui file and look for the section, then automatically load all the custom widget classes. """ import sys import importlib from xml.etree.ElementTree import ElementTree # Parse the UI file etree = ElementTree() ui = etree.parse(ui_file) # Get the customwidgets section custom_widgets = ui.find('customwidgets') if custom_widgets is None: return {} custom_widget_classes = {} for custom_widget in custom_widgets.getchildren(): cw_class = custom_widget.find('class').text cw_header = custom_widget.find('header').text module = importlib.import_module(cw_header) custom_widget_classes[cw_class] = getattr(module, cw_class) return custom_widget_classes def loadUi(uifile, baseinstance=None, workingDirectory=None): """ Dynamically load a user interface from the given ``uifile``. ``uifile`` is a string containing a file name of the UI file to load. If ``baseinstance`` is ``None``, the a new instance of the top-level widget will be created. Otherwise, the user interface is created within the given ``baseinstance``. In this case ``baseinstance`` must be an instance of the top-level widget class in the UI file to load, or a subclass thereof. In other words, if you've created a ``QMainWindow`` interface in the designer, ``baseinstance`` must be a ``QMainWindow`` or a subclass thereof, too. You cannot load a ``QMainWindow`` UI file with a plain :class:`~PySide.QtGui.QWidget` as ``baseinstance``. :method:`~PySide.QtCore.QMetaObject.connectSlotsByName()` is called on the created user interface, so you can implemented your slots according to its conventions in your widget class. Return ``baseinstance``, if ``baseinstance`` is not ``None``. Otherwise return the newly created instance of the user interface. """ # We parse the UI file and import any required custom widgets customWidgets = _get_custom_widgets(uifile) loader = UiLoader(baseinstance, customWidgets) if workingDirectory is not None: loader.setWorkingDirectory(workingDirectory) widget = loader.load(uifile) QMetaObject.connectSlotsByName(widget) return widget pyzo-4.4.3/pyzo/util/qt/_patch/0000777000000000000000000000000013166630337014525 5ustar 00000000000000pyzo-4.4.3/pyzo/util/qt/_patch/qcombobox.py0000666000000000000000000001004713123037261017060 0ustar 00000000000000# The code below, as well as the associated test were adapted from # qt-helpers, which was released under a 3-Clause BSD license: # # Copyright (c) 2015, Chris Beaumont and Thomas Robitaille # # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the # distribution. # * Neither the name of the Glue project nor the names of its # contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. def patch_qcombobox(QComboBox): """ In PySide, using Python objects as userData in QComboBox causes Segmentation faults under certain conditions. Even in cases where it doesn't, findData does not work correctly. Likewise, findData also does not work correctly with Python objects when using PyQt4. On the other hand, PyQt5 deals with this case correctly. We therefore patch QComboBox when using PyQt4 and PySide to avoid issues. """ from ..QtGui import QIcon from ..QtCore import Qt, QObject class userDataWrapper(): """ This class is used to wrap any userData object. If we don't do this, then certain types of objects can cause segmentation faults or issues depending on whether/how __getitem__ is defined. """ def __init__(self, data): self.data = data _addItem = QComboBox.addItem def addItem(self, *args, **kwargs): if len(args) == 3 or (not isinstance(args[0], QIcon) and len(args) == 2): args, kwargs['userData'] = args[:-1], args[-1] if 'userData' in kwargs: kwargs['userData'] = userDataWrapper(kwargs['userData']) _addItem(self, *args, **kwargs) _insertItem = QComboBox.insertItem def insertItem(self, *args, **kwargs): if len(args) == 4 or (not isinstance(args[1], QIcon) and len(args) == 3): args, kwargs['userData'] = args[:-1], args[-1] if 'userData' in kwargs: kwargs['userData'] = userDataWrapper(kwargs['userData']) _insertItem(self, *args, **kwargs) _setItemData = QComboBox.setItemData def setItemData(self, index, value, role=Qt.UserRole): value = userDataWrapper(value) _setItemData(self, index, value, role=role) _itemData = QComboBox.itemData def itemData(self, index, role=Qt.UserRole): userData = _itemData(self, index, role=role) if isinstance(userData, userDataWrapper): userData = userData.data return userData def findData(self, value): for i in range(self.count()): if self.itemData(i) == value: return i return -1 QComboBox.addItem = addItem QComboBox.insertItem = insertItem QComboBox.setItemData = setItemData QComboBox.itemData = itemData QComboBox.findData = findData pyzo-4.4.3/pyzo/util/qt/_patch/qheaderview.py0000666000000000000000000000571313123037261017377 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright © The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) def introduce_renamed_methods_qheaderview(QHeaderView): _isClickable = QHeaderView.isClickable def sectionsClickable(self): """ QHeaderView.sectionsClickable() -> bool """ return _isClickable(self) QHeaderView.sectionsClickable = sectionsClickable def isClickable(self): raise Exception('isClickable is only available in Qt4. Use ' 'sectionsClickable instead.') QHeaderView.isClickable = isClickable _isMovable = QHeaderView.isMovable def sectionsMovable(self): """ QHeaderView.sectionsMovable() -> bool """ return _isMovable(self) QHeaderView.sectionsMovable = sectionsMovable def isMovable(self): raise Exception('isMovable is only available in Qt4. Use ' 'sectionsMovable instead.') QHeaderView.isMovable = isMovable _resizeMode = QHeaderView.resizeMode def sectionResizeMode(self, logicalIndex): """ QHeaderView.sectionResizeMode(int) -> QHeaderView.ResizeMode """ return _resizeMode(self, logicalIndex) QHeaderView.sectionResizeMode = sectionResizeMode def resizeMode(self, logicalIndex): raise Exception('resizeMode is only available in Qt4. Use ' 'sectionResizeMode instead.') QHeaderView.resizeMode = resizeMode _setClickable = QHeaderView.setClickable def setSectionsClickable(self, clickable): """ QHeaderView.setSectionsClickable(bool) """ return _setClickable(self, clickable) QHeaderView.setSectionsClickable = setSectionsClickable def setClickable(self, clickable): raise Exception('setClickable is only available in Qt4. Use ' 'setSectionsClickable instead.') QHeaderView.setClickable = setClickable _setMovable = QHeaderView.setMovable def setSectionsMovable(self, movable): """ QHeaderView.setSectionsMovable(bool) """ return _setMovable(self, movable) QHeaderView.setSectionsMovable = setSectionsMovable def setMovable(self, movable): raise Exception('setMovable is only available in Qt4. Use ' 'setSectionsMovable instead.') QHeaderView.setMovable = setMovable _setResizeMode = QHeaderView.setResizeMode def setSectionResizeMode(self, *args): """ QHeaderView.setSectionResizeMode(QHeaderView.ResizeMode) QHeaderView.setSectionResizeMode(int, QHeaderView.ResizeMode) """ _setResizeMode(self, *args) QHeaderView.setSectionResizeMode = setSectionResizeMode def setResizeMode(self, *args): raise Exception('setResizeMode is only available in Qt4. Use ' 'setSectionResizeMode instead.') QHeaderView.setResizeMode = setResizeMode pyzo-4.4.3/pyzo/util/qt/_patch/__init__.py0000666000000000000000000000000113123037261016613 0ustar 00000000000000 pyzo-4.4.3/pyzo/util/qt/_version.py0000666000000000000000000000011013123037261015443 0ustar 00000000000000version_info = (1, 2, 1) __version__ = '.'.join(map(str, version_info)) pyzo-4.4.3/pyzo/util/qt/__init__.py0000666000000000000000000000623313123037261015372 0ustar 00000000000000""" Vendored version of qtpy/__init__.py, but modified to prefer the installed version of qtpy instead. """ try: # Try to use qtpy rather than our vendored version. Throughout Pyzo # we only use from ...qt import QtXxx, i.e. not from ..qt.QtXxx import yy, # so this all we need. # Note that QtHelp and QtPrintSupport are loaded from the vendored version, # but these are only imported when needed. from qtpy import * from qtpy import QtCore, QtGui, QtWidgets except ImportError: import os # Version of QtPy from ._version import __version__ #: Qt API environment variable name QT_API = 'QT_API' #: names of the expected PyQt5 api PYQT5_API = ['pyqt5'] #: names of the expected PyQt4 api PYQT4_API = [ 'pyqt', # name used in IPython.qt 'pyqt4' # pyqode.qt original name ] #: names of the expected PySide api PYSIDE_API = ['pyside'] os.environ.setdefault(QT_API, 'pyqt5') API = os.environ[QT_API].lower() assert API in (PYQT5_API + PYQT4_API + PYSIDE_API) is_old_pyqt = is_pyqt46 = False PYQT5 = True PYQT4 = PYSIDE = False class PythonQtError(Exception): """Error raise if no bindings could be selected""" pass if API in PYQT5_API: try: from PyQt5.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt5.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None except ImportError: API = os.environ['QT_API'] = 'pyqt' if API in PYQT4_API: try: import sip try: sip.setapi('QString', 2) sip.setapi('QVariant', 2) sip.setapi('QDate', 2) sip.setapi('QDateTime', 2) sip.setapi('QTextStream', 2) sip.setapi('QTime', 2) sip.setapi('QUrl', 2) except AttributeError: # PyQt < v4.6 pass from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None PYQT5 = False PYQT4 = True except ImportError: API = os.environ['QT_API'] = 'pyside' else: is_old_pyqt = PYQT_VERSION.startswith(('4.4', '4.5', '4.6', '4.7')) is_pyqt46 = PYQT_VERSION.startswith('4.6') if API in PYSIDE_API: try: from PySide import __version__ as PYSIDE_VERSION # analysis:ignore from PySide.QtCore import __version__ as QT_VERSION # analysis:ignore PYQT_VERSION = None PYQT5 = False PYSIDE = True except ImportError: raise PythonQtError('No Qt bindings could be found') API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', 'pyside': 'PySide'}[API] if PYQT4: import sip try: API_NAME += (" (API v{0})".format(sip.getapi('QString'))) except AttributeError: pass pyzo-4.4.3/pyzo/util/zon.py0000666000000000000000000003556313123037261014025 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2016, Almar Klein """ Reading and saving Zoof Object Notation files. ZON is like JSON, but a more Pythonic format. It's just about 500 lines of code. This format is a spin-off from the SSDF format, it is fully compatible, except that ZON does not support numpy arrays. """ import re import sys import time # From six.py if sys.version_info[0] >= 3: string_types = str, integer_types = int, else: string_types = basestring, # noqa integer_types = (int, long) # noqa float_types = float, ## Dict class try: # pragma: no cover from collections import OrderedDict as _dict except ImportError: _dict = dict def isidentifier(s): # http://stackoverflow.com/questions/2544972/ if not isinstance(s, str): return False return re.match(r'^\w+$', s, re.UNICODE) and re.match(r'^[0-9]', s) is None class Dict(_dict): """ A dict in which the items can be get/set as attributes. """ __reserved_names__ = dir(_dict()) # Also from OrderedDict __pure_names__ = dir(dict()) __slots__ = [] def __repr__(self): identifier_items = [] nonidentifier_items = [] for key, val in self.items(): if isidentifier(key): identifier_items.append('%s=%r' % (key, val)) else: nonidentifier_items.append('(%r, %r)' % (key, val)) if nonidentifier_items: return 'Dict([%s], %s)' % (', '.join(nonidentifier_items), ', '.join(identifier_items)) else: return 'Dict(%s)' % (', '.join(identifier_items)) def __getattribute__(self, key): try: return object.__getattribute__(self, key) except AttributeError: if key in self: return self[key] else: raise def __setattr__(self, key, val): if key in Dict.__reserved_names__: # Either let OrderedDict do its work, or disallow if key not in Dict.__pure_names__: return _dict.__setattr__(self, key, val) else: raise AttributeError('Reserved name, this key can only ' + 'be set via ``d[%r] = X``' % key) else: # if isinstance(val, dict): val = Dict(val) -> no, makes a copy! self[key] = val def __dir__(self): names = [k for k in self.keys() if isidentifier(k)] return Dict.__reserved_names__ + names # SSDF compatibility Struct = Dict Struct.__is_ssdf_struct__ = True ## Public functions def isstruct(ob): # SSDF compatibility """ isstruct(ob) Returns whether the given object is an SSDF struct. """ if hasattr(ob, '__is_ssdf_struct__'): return bool(ob.__is_ssdf_struct__) else: return False def new(): """ new() Create a new Dict object. The same as "Dict()". """ return Dict() def clear(d): # SSDF compatibility """ clear(d) Clear all elements of the given Dict object. """ d.clear() def copy(object): """ copy(objec) Return a deep copy the given object. The object and its children should be dict-compatible data types. Note that dicts are converted to Dict and tuples to lists. """ if isstruct(object) or isinstance(object, dict): newObject = Dict() for key in object: val = object[key] newObject[key] = copy(val) return newObject elif isinstance(object, (tuple, list)): return [copy(ob) for ob in object] else: return object # immutable def count(object, cache=None): """ count(object): Count the number of elements in the given object. An element is defined as one of the 6 datatypes supported by ZON (dict, tuple/list, string, int, float, None). """ cache = cache or [] if isstruct(object) or isinstance(object, (dict, list)): if id(object) in cache: raise RuntimeError('recursion!') cache.append(id(object)) n = 1 if isstruct(object) or isinstance(object, dict): for key in object: val = object[key] n += count(val, cache) elif isinstance(object, (tuple, list)): for val in object: n += count(val, cache) return n def loads(text): """ loads(text) Load a Dict from the given Unicode) string in ZON syntax. """ if not isinstance(text, string_types): raise ValueError('zon.loads() expects a string.') reader = ReaderWriter() return reader.read(text) def load(file): """ load(filename) Load a Dict from the given file or filename. """ if isinstance(file, string_types): file = open(file, 'rb') text = file.read().decode('utf-8') return loads(text) def saves(d): """ saves(d) Serialize the given dict to a (Unicode) string. """ if not (isstruct(d) or isinstance(d, dict)): raise ValueError('ssdf.saves() expects a dict.') writer = ReaderWriter() text = writer.save(d) return text def save(file, d): """ save(file, d) Serialize the given dict to the given file or filename. """ text = saves(d) if isinstance(file, string_types): file = open(file, 'wb') with file: file.write(text.encode('utf-8')) ## The core class ReaderWriter(object): def read(self, text): indent = 0 root = Dict() container_stack = [(0, root)] new_container = None for i, line in enumerate(text.splitlines()): linenr = i + 1 # Strip line line2 = line.lstrip() # Skip comments and empty lines if not line2 or line2[0] == '#': continue # Find the indentation prev_indent = indent indent = len(line) - len(line2) if indent == prev_indent: pass elif indent < prev_indent: while container_stack[-1][0] > indent: container_stack.pop(-1) if container_stack[-1][0] != indent: print('ZON: Ignoring wrong dedentation at %i' % linenr) elif indent > prev_indent and new_container is not None: container_stack.append((indent, new_container)) new_container = None else: print('ZON: Ignoring wrong indentation at %i' % linenr) indent = prev_indent # Split name and data using a regular expression m = re.search("^\w+? *?=", line2) if m: i = m.end(0) name = line2[:i-1].strip() data = line2[i:].lstrip() else: name = None data = line2 # Get value value = self.to_object(data, linenr) # Store the value _indent, current_container = container_stack[-1] if isinstance(current_container, dict): if name: current_container[name] = value else: print('ZON: unnamed item in dict on line %i' % linenr) elif isinstance(current_container, list): if name: print('ZON: named item in list on line %i' % linenr) else: current_container.append(value) else: raise RuntimeError('Invalid container %r' % current_container) # Prepare for next round if isinstance(value, (dict, list)): new_container = value return root def save(self, d): pyver = '%i.%i.%i' % sys.version_info[:3] ct = time.asctime() lines = [] lines.append('# -*- coding: utf-8 -*-') lines.append('# This Zoof Object Notation (ZON) file was') lines.append('# created from Python %s on %s.\n' % (pyver, ct)) lines.append('') lines.extend(self.from_dict(d, -2)[1:]) return '\r\n'.join(lines) # todo: pop toplevel dict def from_object(self, name, value, indent): # Get object's data if value is None: data = 'Null' elif isinstance(value, integer_types): data = self.from_int(value) elif isinstance(value, float_types): data = self.from_float(value) elif isinstance(value, bool): data = self.from_int(int(value)) elif isinstance(value, string_types): data = self.from_unicode(value) elif isinstance(value, dict): data = self.from_dict(value, indent) elif isinstance(value, (list, tuple)): data = self.from_list(value, indent) else: # We do not know data = 'Null' tmp = repr(value) if len(tmp) > 64: tmp = tmp[:64] + '...' if name is not None: print("ZON: %s is unknown object: %s" % (name, tmp)) else: print("ZON: unknown object: %s" % tmp) # Finish line (or first line) if isinstance(data, string_types): data = [data] if name: data[0] = '%s%s = %s' % (' ' * indent, name, data[0]) else: data[0] = '%s%s' % (' ' * indent, data[0]) return data def to_object(self, data, linenr): data = data.lstrip() # Determine what type of object we're dealing with by reading # like a human. if not data: print('ZON: no value specified at line %i.' % linenr) elif data[0] in '-.0123456789': return self.to_int_or_float(data, linenr) elif data[0] == "'": return self.to_unicode(data, linenr) elif data.startswith('dict:'): return self.to_dict(data, linenr) elif data.startswith('list:') or data[0] == '[': return self.to_list(data, linenr) elif data.startswith('Null') or data.startswith('None'): return None else: print("ZON: invalid type on line %i." % linenr) return None def to_int_or_float(self, data, linenr): line = data.partition('#')[0] try: return int(line) except ValueError: try: return float(line) except ValueError: print("ZON: could not parse number on line %i." % linenr) return None def from_int(self, value): return repr(int(value)).rstrip('L') def from_float(self, value): # Use general specifier with a very high precision. # Any spurious zeros are automatically removed. The precision # should be sufficient such that any numbers saved and loaded # back will have the exact same value again. # see e.g. http://bugs.python.org/issue1580 return repr(float(value)) # '%0.17g' % value def from_unicode(self, value): value = value.replace('\\', '\\\\') value = value.replace('\n','\\n') value = value.replace('\r','\\r') value = value.replace('\x0b', '\\x0b').replace('\x0c', '\\x0c') value = value.replace("'", "\\'") return "'" + value + "'" def to_unicode(self, data, linenr): # Encode double slashes line = data.replace('\\\\','0x07') # temp # Find string using a regular expression m = re.search("'.*?[^\\\\]'|''", line) if not m: print("ZON: string not ended correctly on line %i." % linenr) return None # return not-a-string else: line = m.group(0)[1:-1] # Decode stuff line = line.replace('\\n','\n') line = line.replace('\\r','\r') line = line.replace('\\x0b', '\x0b').replace('\\x0c', '\x0c') line = line.replace("\\'","'") line = line.replace('0x07','\\') return line def from_dict(self, value, indent): lines = ["dict:"] # Process children for key, val in value.items(): # Skip all the builtin stuff if key.startswith("__"): continue # Skip methods, or anything else we can call if hasattr(val, '__call__'): continue # Note: py3.x does not have function callable # Add! lines.extend(self.from_object(key, val, indent+2)) return lines def to_dict(self, data, linenr): return Dict() def from_list(self, value, indent): # Collect subdata and check whether this is a "small list" isSmallList = True allowedTypes = integer_types + float_types + string_types subItems = [] for element in value: if not isinstance(element, allowedTypes): isSmallList = False subdata = self.from_object(None, element, 0) # No indent subItems.extend(subdata) isSmallList = isSmallList and len(subItems) < 256 # Return data if isSmallList: return '[%s]' % (', '.join(subItems)) else: data = ["list:"] ind = ' ' * (indent + 2) for item in subItems: data.append(ind + item) return data def to_list(self, data, linenr): value = [] if data[0] == 'l': # list: return list() else: i0 = 1 pieces = [] inString = False escapeThis = False line = data for i in range(1,len(line)): if inString: # Detect how to get out if escapeThis: escapeThis = False continue elif line[i] == "\\": escapeThis = True elif line[i] == "'": inString = False else: # Detect going in a string, break, or end if line[i] == "'": inString = True elif line[i] == ",": pieces.append(line[i0:i]) i0 = i+1 elif line[i] == "]": piece = line[i0:i] if piece.strip(): # Do not add if empty pieces.append(piece) break else: print("ZON: short list not closed right on line %i." % linenr) # Cut in pieces and process each piece value = [] for piece in pieces: v = self.to_object(piece, linenr) value.append(v) return value pyzo-4.4.3/pyzo/util/_locale.py0000666000000000000000000002267613123037261014616 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ pyzo.util._locale Module for locale stuff like language and translations. """ import os, sys, time import pyzo from pyzo.util.qt import QtCore, QtWidgets QLocale = QtCore.QLocale # Define supported languages. The key defines the name as shown to the # user. The value is passed to create a Locale object. From the local # object we obtain the name for the .tr file. # Chinese: LANGUAGES = { 'English (US)': QLocale.C, # == (QLocale.English, QLocale.UnitedStates), #'English (UK)': (QLocale.English, QLocale.UnitedKingdom), 'Dutch': QLocale.Dutch, 'Spanish': QLocale.Spanish, 'Catalan': QLocale.Catalan, 'French': QLocale.French, 'German': QLocale.German, 'Russian': QLocale.Russian, # not updated for 3.4 'Portuguese': QLocale.Portuguese, 'Portuguese (BR)': (QLocale.Portuguese, QLocale.Brazil), 'Simplified Chinese': QLocale.Chinese, 'Traditional Chinese': (QLocale.Chinese, QLocale.Taiwan), # https://bugreports.qt.io/browse/QTBUG-1573 # Languages for which the is a .tr file, but no translations available yet: # 'Slovak': QLocale.Slovak, } LANGUAGE_SYNONYMS = { None: 'English (US)', '': 'English (US)', 'English': 'English (US)'} def getLocale(languageName): """ getLocale(languageName) Get the QLocale object for the given language (as a string). """ # Apply synonyms languageName = LANGUAGE_SYNONYMS.get(languageName, languageName) # Select language in qt terms qtLanguage = LANGUAGES.get(languageName, None) if qtLanguage is None: raise ValueError('Unknown language') # Return locale if isinstance(qtLanguage, tuple): return QLocale(*qtLanguage) else: return QLocale(qtLanguage) def setLanguage(languageName): """ setLanguage(languageName) Set the language for the app. Loads qt and pyzo translations. Returns the QLocale instance to pass to the main widget. """ # Get locale locale = getLocale(languageName) # Get paths were language files are qtTransPath = str(QtCore.QLibraryInfo.location( QtCore.QLibraryInfo.TranslationsPath)) pyzoTransPath = os.path.join(pyzo.pyzoDir, 'resources', 'translations') # Get possible names for language files # (because Qt's .tr files may not have the language component.) localeName1 = locale.name() localeName2 = localeName1.split('_')[0] # Uninstall translators if not hasattr(QtCore, '_translators'): QtCore._translators = [] for trans in QtCore._translators: QtWidgets.QApplication.removeTranslator(trans) # The default language if localeName1 == 'C': return locale # Set Qt translations # Note that the translator instances must be stored # Note that the load() method is very forgiving with the file name for what, where in [('qt', qtTransPath),('pyzo', pyzoTransPath)]: trans = QtCore.QTranslator() # Try loading both names for localeName in [localeName1, localeName2]: success = trans.load(what + '_' + localeName + '.tr', where) if success: QtWidgets.QApplication.installTranslator(trans) QtCore._translators.append(trans) print('loading %s %s: ok' % (what, languageName)) break else: print('loading %s %s: failed' % (what, languageName)) # Done return locale class Translation(str): """ Derives from str class. The translate function returns an instance of this class and assigns extra atrributes: * original: the original text passed to the translation * tt: the tooltip text * key: the original text without tooltip (used by menus as a key) We adopt a simple system to include tooltip text in the same translation as the label text. By including ":::" in the text, the text after that identifier is considered the tooltip. The text returned by the translate function is always the string without tooltip, but the text object has an attribute "tt" that stores the tooltip text. In this way, if you do not use this feature or do not know about this feature, everything keeps working as expected. """ pass def _splitMainAndTt(s): if ':::' in s: parts = s.split(':::', 1) return parts[0].rstrip(), parts[1].lstrip() else: return s, '' def translate(context, text, disambiguation=None): """ translate(context, text, disambiguation=None) The translate function used throughout pyzo. """ # Get translation and split tooltip newtext = QtCore.QCoreApplication.translate(context, text, disambiguation) s, tt = _splitMainAndTt(newtext) # Create translation object (string with extra attributes) translation = Translation(s) translation.original = text translation.tt = tt translation.key = _splitMainAndTt(text)[0].strip() return translation ## Development tools import subprocess LHELP = """ Language help - info for translaters For translating, you will need a set of working Qt language tools: pyside-lupdate, linguist, lrelease. On Windows, these should come with your PySide installation. On (Ubuntu) Linux, you can install these with 'sudo apt-get install pyside-tools qt4-dev-tools'. You also need to run pyzo from source as checked out from the repo (e.g. by running pyzolauncher.py). To create a new language: * the file 'pyzo/util/locale.py' should be edited to add the language to the LANGUAGES dict * run 'linguist(your_lang)', this will raise an erro, but it will show the name of the .tr file * the file 'pyzo/pyzo.pro' should be edited to include the new .tr file * run 'lupdate()' to create the .tr file * run 'linguist(your_lang)' again to initialize the .tr file. To update a language: * run 'lupdate()' * run 'linguist(your_lang)' * make all the translations and save * run lrelease() and restart pyzo to see translations * repeat if necessary """ def lhelp(): """ lhelp() Print help text on using the language tools. """ print(LHELP) def linguist(languageName): """ linguist(languageName) Open linguist with the language file as specified by lang. The languageName can be one of the fields as visible in the language list in the menu. This function is intended for translators. """ # Get locale locale = getLocale(languageName) # Get file to open fname = 'pyzo_{}.tr'.format(locale.name()) filename = os.path.join(pyzo.pyzoDir, 'resources', 'translations', fname) if not os.path.isfile(filename): raise ValueError('Could not find {}'.format(filename)) # Get Command for linguist pysideDir = os.path.abspath(os.path.dirname(pyzo.QtCore.__file__)) ISWIN = sys.platform.startswith('win') exe_ = 'linguist' + '.exe' * ISWIN exe = os.path.join(pysideDir, exe_) if not os.path.isfile(exe): exe = exe_ # Spawn process return subprocess.Popen([exe , filename]) def lupdate(): """ For developers. From pyzo.pro create the .tr files """ # Get file to open fname = 'pyzo.pro' filename = os.path.realpath(os.path.join(pyzo.pyzoDir, '..', fname)) if not os.path.isfile(filename): raise ValueError('Could not find {}. This function must run from the source repo.'.format(fname)) # Get Command for python lupdate pysideDir = os.path.abspath(os.path.dirname(pyzo.QtCore.__file__)) ISWIN = sys.platform.startswith('win') exe_ = 'pylupdate' + pyzo.QtCore.__version__[0] + '.exe' * ISWIN exe = os.path.join(pysideDir, exe_) if not os.path.isfile(exe): exe = exe_ # Spawn process cmd = [exe, '-noobsolete', '-verbose', filename] p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while p.poll() is None: time.sleep(0.1) output = p.stdout.read().decode('utf-8') if p.returncode: raise RuntimeError('lupdate failed (%i): %s' % (p.returncode, output)) else: print(output) def lrelease(): """ For developers. From pyzo.pro and the .tr files, create the .qm files. """ # Get file to open fname = 'pyzo.pro' filename = os.path.realpath(os.path.join(pyzo.pyzoDir, '..', fname)) if not os.path.isfile(filename): raise ValueError('Could not find {}. This function must run from the source repo.'.format(fname)) # Get Command for lrelease pysideDir = os.path.abspath(os.path.dirname(pyzo.QtCore.__file__)) ISWIN = sys.platform.startswith('win') exe_ = 'lrelease' + '.exe' * ISWIN exe = os.path.join(pysideDir, exe_) if not os.path.isfile(exe): exe = exe_ # Spawn process cmd = [exe, filename] p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while p.poll() is None: time.sleep(0.1) output = p.stdout.read().decode('utf-8') if p.returncode: raise RuntimeError('lrelease failed (%i): %s' % (p.returncode, output)) else: print(output) if __name__ == '__main__': # Print names of translator files print('Language data files:') for key in LANGUAGES: s = '{}: {}'.format(key, getLocale(key).name()+'.tr') print(s) pyzo-4.4.3/pyzo/util/__init__.py0000666000000000000000000000000113123037261014731 0ustar 00000000000000 pyzo-4.4.3/pyzo/yoton/0000777000000000000000000000000013166630337013036 5ustar 00000000000000pyzo-4.4.3/pyzo/yoton/channels/0000777000000000000000000000000013166630337014631 5ustar 00000000000000pyzo-4.4.3/pyzo/yoton/channels/channels_base.py0000666000000000000000000002747513131373375020005 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.channels.channels_base Defines the base channel class and the MessageType class. """ import time import threading import yoton from yoton.misc import basestring from yoton.misc import slot_hash, PackageQueue from yoton.core import Package from yoton.context import Context from yoton.channels.message_types import MessageType, TEXT class BaseChannel(object): """ BaseChannel(context, slot_base, message_type=yoton.TEXT) Abstract class for all channels. Parameters ---------- context : yoton.Context instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to enable communicating any type of message they want. Details ------- Messages send via a channel are delivered asynchronically to the corresponding channels. All channels are associated with a context and can be used to send messages to other channels in the network. Each channel is also associated with a slot, which is a string that represents a kind of address. A message send by a channel at slot X can only be received by a channel with slot X. Note that the channel appends an extension to the user-supplied slot name, that represents the message type and messaging pattern of the channel. In this way, it is prevented that for example a PubChannel can communicate with a RepChannel. """ def __init__(self, context, slot_base, message_type=None): # Store context if not isinstance(context, Context): raise ValueError('Context not valid.') self._context = context # Check message type if message_type is None: message_type = TEXT if isinstance(message_type, type) and issubclass(message_type, MessageType): message_type = message_type() if isinstance(message_type, MessageType): message_type = message_type else: raise ValueError('message_type should be a MessageType instance.') # Store message type and conversion methods self._message_type_instance = message_type self.message_from_bytes = message_type.message_from_bytes self.message_to_bytes = message_type.message_to_bytes # Queue for incoming trafic (not used for pure sending channels) self._q_in = PackageQueue(*context._queue_params) # For sending channels: to lock the channel for sending self._send_condition = threading.Condition() self._is_send_locked = 0 # "True" is the timeout time # Signal for receiving data self._received_signal = yoton.events.Signal() self._posted_received_event = False # Channels can be closed self._closed = False # Event driven mode self._run_mode = 0 # Init slots self._init_slots(slot_base) def _init_slots(self, slot_base): """ _init_slots(slot_base) Called from __init__ to initialize the slots and perform all checks. """ # Check if slot is string if not isinstance(slot_base, basestring): raise ValueError('slot_base must be a string.') # Get full slot names, init byte versions slots_t = [] slots_h = [] # Get extension for message type and messaging pattern ext_type = self._message_type_instance.message_type_name() ext_patterns = self._messaging_patterns() # (incoming, outgoing) # Normalize and check slot names for ext_pattern in ext_patterns: if not ext_pattern: slots_t.append(None) slots_h.append(0) continue # Get full name slot = slot_base + '.' + ext_type + '.' + ext_pattern # Store text version slots_t.append(slot) # Strip and make lowercase slot = slot.strip().lower() # Hash slots_h.append(slot_hash(slot)) # Store slots self._slot_out = slots_t[0] self._slot_in = slots_t[1] self._slot_out_h = slots_h[0] self._slot_in_h = slots_h[1] # Register slots (warn if neither slot is valid) if self._slot_out_h: self._context._register_sending_channel(self, self._slot_out_h, self._slot_out) if self._slot_in_h: self._context._register_receiving_channel(self, self._slot_in_h, self._slot_in) if not self._slot_out_h and not self._slot_in_h: raise ValueError('This channel does not have valid slots.') def _messaging_patterns(self): """ _messaging_patterns() Implement to return a string that specifies the pattern for sending and receiving, respecitively. """ raise NotImplementedError() def close(self): """ close() Close the channel, i.e. unregisters this channel at the context. A closed channel cannot be reused. Future attempt to send() messages will result in an IOError being raised. Messages currently in the channel's queue can still be recv()'ed, but no new messages will be delivered at this channel. """ # We keep a reference to the context, otherwise we need locks # The context clears the reference to this channel when unregistering. self._closed = True self._context._unregister_channel(self) def _send(self, message, dest_id=0, dest_seq=0): """ _send(message, dest_id=0, dest_seq=0) Sends a message of raw bytes without checking whether they're bytes. Optionally, dest_id and dest_seq represent the message that this message replies to. These are used for the request/reply pattern. Returns the package that will be send (or None). The context will set _source_id on the package right before sending it away. """ # Check if still open if self._closed: className = self.__class__.__name__ raise IOError("Cannot send from closed %s %i." % (className, id(self))) if message: # If send_locked, wait at most one second if self._is_send_locked: self._send_condition.acquire() try: self._send_condition.wait(1.0) # wait for notify finally: self._send_condition.release() if time.time() > self._is_send_locked: self._is_send_locked = 0 # Push it on the queue as a package slot = self._slot_out_h cid = self._context._id p = Package(message, slot, cid, 0, dest_id, dest_seq, 0) self._context._send_package(p) # Return package return p else: return None def _recv(self, block): """ _recv(block) Receive a package (or None). """ if block is True: # Block for 0.25 seconds so that KeyboardInterrupt works while not self._closed: try: return self._q_in.pop(0.25) except self._q_in.Empty: continue else: # Block normal try: return self._q_in.pop(block) except self._q_in.Empty: return None def _set_send_lock(self, value): """ _set_send_lock(self, value) Set or unset the blocking for the _send() method. """ # Set send lock variable. We adopt a timeout (10s) just in case # the SubChannel that locks the PubChannel gets disconnected and # is unable to unlock it. if value: self._is_send_locked = time.time() + 10.0 else: self._is_send_locked = 0 # Notify any threads that are waiting in _send() if not value: self._send_condition.acquire() try: self._send_condition.notifyAll() finally: self._send_condition.release() ## How packages are inserted in this channel for receiving def _inject_package(self, package): """ _inject_package(package) Same as _recv_package, but by definition do not block. _recv_package is overloaded in SubChannel. _inject_package is not. """ self._q_in.push(package) self._maybe_emit_received() def _recv_package(self, package): """ _recv_package(package) Put package in the queue. """ self._q_in.push(package) self._maybe_emit_received() def _maybe_emit_received(self): """ _maybe_emit_received() We want to emit a signal, but in such a way that multiple arriving packages result in a single emit. This methods only posts an event if it has not been done, or if the previous event has been handled. """ if not self._posted_received_event: self._posted_received_event = True event = yoton.events.Event(self._emit_received) yoton.app.post_event(event) def _emit_received(self): """ _emit_received() Emits the "received" signal. This method is called once new data has been received. However, multiple arrived messages may result in a single call to this method. There is also no guarantee that recv() has not been called in the mean time. Also sets the variabele so that a new event for this may be created. This method is called from the event loop. """ self._posted_received_event = False # Reset self.received.emit_now(self) # Received property sits on the BaseChannel because is is used by almost # all channels. Note that PubChannels never emit this signal as they # catch status messages from the SubChannel by overloading _recv_package(). @property def received(self): """ Signal that is emitted when new data is received. Multiple arrived messages may result in a single call to this method. There is no guarantee that recv() has not been called in the mean time. The signal is emitted with the channel instance as argument. """ return self._received_signal ## Properties @property def pending(self): """ Get the number of pending incoming messages. """ return len(self._q_in) @property def closed(self): """ Get whether the channel is closed. """ return self._closed @property def slot_outgoing(self): """ Get the outgoing slot name. """ return self._slot_out @property def slot_incoming(self): """ Get the incoming slot name. """ return self._slot_in pyzo-4.4.3/pyzo/yoton/channels/channels_file.py0000666000000000000000000001302213131373401017757 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.channels.file Defines a class that can be used to wrap a channel to give it a file like interface. """ import sys import os from yoton.channels import PubChannel, SubChannel PY2 = sys.version_info[0] == 2 class FileWrapper(object): """ FileWrapper(channel, chunksize=0, echo=None) Class that wraps a PubChannel or SubChannel instance to provide a file-like interface by implementing methods such as read() and write(), and other stuff specified in: [[http://docs.python.org/library/stdtypes.html#bltin-file-objects]] The file wrapper also splits messages into smaller messages if they are above the chunksize (only if chunksize > 0). On Python 2, the read methods return str (utf-8 encoded Unicode). """ # Our file-like objects should not implement: # explicitly stated: fileno, isatty # don't seem to make sense: readlines, seek, tell, truncate, errors, # mode, name, def __init__(self, channel, chunksize=0, echo=None, isatty=False): if not isinstance(channel, (PubChannel, SubChannel)): raise ValueError('FileWrapper needs a PubChannel or SubChannel.') if echo is not None: if not isinstance(echo, PubChannel): raise ValueError('FileWrapper echo needs to be a PubChannel.') self._channel = channel self._chunksize = int(chunksize) self._echo = echo self._pid = os.getpid() # To detect whether we are in multi-process self.errors = 'strict' # compat self._isatty = isatty def close(self): """ Close the file object. """ # Deal with multiprocessing if self._pid != os.getpid(): if self is sys.stdin: sys.__stdin__.close() elif self is sys.stdout: sys.__stdout__.close() elif self is sys.stderr: sys.__stderr__.close() return # Normal behavior self._channel.close() @property def encoding(self): """ The encoding used to encode strings to bytes and vice versa. """ return 'UTF-8' @property def closed(self): """ Get whether the file is closed. """ return self._channel._closed def flush(self): """ flush() Wait here until all messages have been send. """ self._channel._context.flush() @property def newlines(self): """ The type of newlines used. Returns None; we never know what the other end could be sending! """ return None # this is for the print statement to keep track spacing stuff def _set_softspace(self, value): self._softspace = bool(value) def _get_softspace(self): return hasattr(self, '_softspace') and self._softspace softspace = property(_get_softspace, _set_softspace, None, '') def read(self, block=None): """ read(block=None) Alias for recv(). """ res = self._channel.recv(block) if res and self._echo is not None: self._echo.send(res) if PY2: return res.encode('utf-8') else: return res def write(self, message): """ write(message) Uses channel.send() to send the message over the Yoton network. The message is partitioned in smaller parts if it is larger than the chunksize. """ # Deal with multiprocessing if self._pid != os.getpid(): realfile = None if self is sys.stdout: realfile = sys.__stdout__ elif self is sys.stderr: realfile = sys.__stderr__ if realfile is not None: sys.__stderr__.write(message) sys.__stderr__.flush() return chunkSize = self._chunksize if chunkSize > 0: for i in range(0, len(message), chunkSize): self._channel.send( message[i:i+chunkSize] ) else: self._channel.send(message) def writelines(self, lines): """ writelines(lines) Write a sequence of messages to the channel. """ for line in lines: self._channel.send(line) def readline(self, size=0): """ readline(size=0) Read one string that was send as one from the other end (always in blocking mode). A newline character is appended if it does not end with one. If size is given, returns only up to that many characters, the rest of the message is thrown away. """ # Get line line = self._channel.recv(True) # Echo if line and self._echo is not None: self._echo.send(line) # Make sure it ends with newline if not line.endswith('\n'): line += '\n' # Decrease size? if size: line = line[:size] # Done if PY2: return line.encode('utf-8') else: return line def isatty(self): """ Get whether this is a terminal. """ return self._isatty pyzo-4.4.3/pyzo/yoton/channels/channels_pubsub.py0000666000000000000000000003144613131373404020355 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.channels.channels_pubsub Defines the channel classes for the pub/sub pattern. """ import time from yoton.misc import bytes, xrange from yoton.channels import BaseChannel QUEUE_NULL = 0 QUEUE_OK = 1 QUEUE_FULL = 2 class PubChannel(BaseChannel): """ PubChannel(context, slot_base, message_type=yoton.TEXT) The publish part of the publish/subscribe messaging pattern. Sent messages are received by all yoton.SubChannel instances with the same slot. There are no limitations for this channel if events are not processed. Parameters ---------- context : yoton.Context instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to let channels any type of message they want. """ def __init__(self, *args, **kwargs): BaseChannel.__init__(self, *args, **kwargs) self._source_set = set() def _messaging_patterns(self): return 'pub-sub', 'sub-pub' def send(self, message): """ send(message) Send a message over the channel. What is send as one message will also be received as one message. The message is queued and delivered to all corresponding SubChannels (i.e. with the same slot) in the network. """ self._send( self.message_to_bytes(message) ) def _recv_package(self, package): """ Overloaded to set blocking mode. Do not call _maybe_emit_received(), a PubChannel never emits the "received" signal. """ message = package._data.decode('utf-8') source_id = package._source_id # Keep track of who's queues are full if message == 'full': self._source_set.add(source_id) else: self._source_set.discard(source_id) # Set lock if there is a channel with a full queue, # Unset if there are none if self._source_set: self._set_send_lock(True) #sys.stderr.write('setting lock\n') else: self._set_send_lock(False) #sys.stderr.write('unsetting lock\n') class SubChannel(BaseChannel): """ SubChannel(context, slot_base, message_type=yoton.TEXT) The subscribe part of the publish/subscribe messaging pattern. Received messages were sent by a yoton.PubChannel instance at the same slot. This channel can be used as an iterator, which yields all pending messages. The function yoton.select_sub_channel can be used to synchronize multiple SubChannel instances. If no events being processed this channel works as normal, except that the received signal will not be emitted, and sync mode will not work. Parameters ---------- context : yoton.Context instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to let channels any type of message they want. """ def __init__(self, *args, **kwargs): BaseChannel.__init__(self, *args, **kwargs) # To detect when to block the sending side self._queue_status = QUEUE_NULL self._queue_status_timeout = 0 self._HWM = 32 self._LWM = 16 # Automatically check queue status when new data # enters the system self.received.bind(self._check_queue_status) def _messaging_patterns(self): return 'sub-pub', 'pub-sub' def __iter__(self): return self def __next__(self): # Python 3.x m = self.recv(False) if m: return m else: raise StopIteration() def next(self): # Python 2.x """ next() Return the next message, or raises StopIteration if non available. """ return self.__next__() ## For sync mode def set_sync_mode(self, value): """ set_sync_mode(value) Set or unset the SubChannel in sync mode. When in sync mode, all channels that send messages to this channel are blocked if the queue for this SubChannel reaches a certain size. This feature can be used to limit the rate of senders if the consumer (i.e. the one that calls recv()) cannot keep up with processing the data. This feature requires the yoton event loop to run at the side of the SubChannel (not necessary for the yoton.PubChannel side). """ value = bool(value) # First reset block status if necessary if self._queue_status == QUEUE_FULL: self._send_block_message_to_senders('ok') # Set new queue status flag if value: self._queue_status = QUEUE_OK else: self._queue_status = QUEUE_NULL def _send_block_message_to_senders(self, what): """ _send_block_message_to_senders(what) Send a message to the PubChannel side to make it block/unblock. """ # Check if not self._context.connection_count: return # Send try: self._send(what.encode('utf-8')) except IOError: # If self._closed self._check_queue_status = QUEUE_NULL def _check_queue_status(self, dummy=None): """ _check_queue_status() Check the queue status. Returns immediately unless this receiving channel runs in sync mode. If the queue is above a certain size, will send out a package that will make the sending side block. If the queue is below a certain size, will send out a package that will make the sending side unblock. """ if self._queue_status == QUEUE_NULL: return elif len(self._q_in) > self._HWM: if self._queue_status == QUEUE_OK: self._queue_status = QUEUE_FULL self._queue_status_timeout = time.time() + 4.0 self._send_block_message_to_senders('full') elif len(self._q_in) < self._LWM: if self._queue_status == QUEUE_FULL: self._queue_status = QUEUE_OK self._queue_status_timeout = time.time() + 4.0 self._send_block_message_to_senders('ok') # Resend every so often. After 10s the PubChannel will unlock itself if self._queue_status_timeout < time.time(): self._queue_status_timeout = time.time() + 4.0 if self._queue_status == QUEUE_OK: self._send_block_message_to_senders('ok') else: self._send_block_message_to_senders('full') ## Receive methods def recv(self, block=True): """ recv(block=True) Receive a message from the channel. What was send as one message is also received as one message. If block is False, returns empty message if no data is available. If block is True, waits forever until data is available. If block is an int or float, waits that many seconds. If the channel is closed, returns empty message. """ # Check queue status, maybe we need to block the sender self._check_queue_status() # Get package package = self._recv(block) # Return message content or None if package is not None: return self.message_from_bytes(package._data) else: return self.message_from_bytes(bytes()) def recv_all(self): """ recv_all() Receive a list of all pending messages. The list can be empty. """ # Check queue status, maybe we need to block the sender self._check_queue_status() # Pop all messages and return as a list pop = self._q_in.pop packages = [pop() for i in xrange(len(self._q_in))] return [self.message_from_bytes(p._data) for p in packages] def recv_selected(self): """ recv_selected() Receive a list of messages. Use only after calling yoton.select_sub_channel with this channel as one of the arguments. The returned messages are all received before the first pending message in the other SUB-channels given to select_sub_channel. The combination of this method and the function select_sub_channel enables users to combine multiple SUB-channels in a way that preserves the original order of the messages. """ # No need to check queue status, we've done that in the # _get_pending_sequence_numbers() method # Prepare q = self._q_in ref_seq = self._ref_seq popped = [] # Pop all messages that have sequence number lower than reference try: for i in xrange(len(q)): part = q.pop() if part._recv_seq > ref_seq: q.insert(part) # put back in queue break else: popped.append(part) except IndexError: pass # Done; return messages return [self.message_from_bytes(p._data) for p in popped] def _get_pending_sequence_numbers(self): """ _get_pending_sequence_numbers() Get the sequence numbers of the first and last pending messages. Returns (-1,-1) if no messages are pending. Used by select_sub_channel() to determine which channel should be read from first and what the reference sequence number is. """ # Check queue status, maybe we need to block the sender self._check_queue_status() # Peek try: q = self._q_in return q.peek(0)._recv_seq, q.peek(-1)._recv_seq + 1 except IndexError: return -1, -1 def select_sub_channel(*args): """ select_sub_channel(channel1, channel2, ...) Returns the channel that has the oldest pending message of all given yoton.SubCannel instances. Returns None if there are no pending messages. This function can be used to read from SubCannels instances in the order that the messages were send. After calling this function, use channel.recv_selected() to obtain all messages that are older than any pending messages in the other given channels. """ # Init smallest_seq1 = 99999999999999999999999999 smallest_seq2 = 99999999999999999999999999 first_channel = None # For each channel ... for channel in args: # Check if channel is of right type if not isinstance(channel, SubChannel): raise ValueError('select_sub_channel() only accepts SUB channels.') # Get and check sequence seq1, seq2 = channel._get_pending_sequence_numbers() if seq1 >= 0: if seq1 < smallest_seq1: # Cannot go beyond number of packages in queue, # or than seq1 of earlier selected channel. smallest_seq2 = min(smallest_seq1, smallest_seq2, seq2) # Store smallest_seq1 = seq1 first_channel = channel else: # The first_channel cannot go beyond the 1st package in THIS queue smallest_seq2 = min(smallest_seq2, seq1) # Set flag at channel and return if first_channel: first_channel._ref_seq = smallest_seq2 return first_channel else: return None pyzo-4.4.3/pyzo/yoton/channels/channels_reqrep.py0000666000000000000000000007414213131373413020353 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.channels.channels_reqprep Defines the channel classes for the req/rep pattern. """ import time import threading import yoton from yoton.misc import basestring, bytes from yoton.misc import getErrorMsg from yoton.channels import BaseChannel, OBJECT # For the req/rep channels to negotiate (simple load balancing) REQREP_SEQ_REF = 2**63 # Define object to recognize errors ERROR_OBJECT = 'yoton_ERROR_HANDLING_REQUEST' # Define exceoptions class TimeoutError(Exception): pass class CancelledError(Exception): pass # # Try loading the exceptions from the concurrency framework # # (or maybe not; it makes yoton less lightweight) # try: # from concurrent.futures import TimeoutError, CancelledError # except ImportError: # pass class Future(object): """ Future(req_channel, req, request_id) The Future object represents the future result of a request done at a yoton.ReqChannel. It enables: * checking whether the request is done. * getting the result or the exception raised during handling the request. * canceling the request (if it is not yet running) * registering callbacks to handle the result when it is available """ def __init__(self, req_channel, req, request_id): # For being a Future object self._result = None self._status = 0 # 0:waiting, 1:running, 2:canceled, 3:error, 4:success self._callbacks = [] # For handling req/rep self._req_channel = req_channel self._req = req self._request_id = request_id self._rep = bytes() self._replier = 0 # For resending self._first_send_time = time.time() self._next_send_time = self._first_send_time + 0.5 self._auto_cancel_timeout = 10.0 def _send(self, msg): """ _send(msg) For sending pre-request messages 'req?', 'req-'. """ msg = msg.encode('utf-8') try: self._req_channel._send(msg, 0, self._request_id+REQREP_SEQ_REF) except IOError: # if self._closed, will call _send again, and catch IOerror, # which will result in one more call to cancel(). self.cancel() def _resend_if_necessary(self): """ _resend_if_necessary() Resends the pre-request message if we have not done so for the last 0.5 second. This will also auto-cancel the message if it is resend over 20 times. """ timetime = time.time() if self._status != 0: pass elif timetime > self._first_send_time + self._auto_cancel_timeout: self.cancel() elif timetime > self._next_send_time: self._send('req?') self._next_send_time = timetime + 0.5 def set_auto_cancel_timeout(self, timeout): """ set_auto_cancel_timeout(timeout): Set the timeout after which the call is automatically cancelled if it is not done yet. By default, this value is 10 seconds. If timeout is None, there is no limit to the wait time. """ if timeout is None: timeout = 999999999999999999.0 if timeout > 0: self._auto_cancel_timeout = float(timeout) else: raise ValueError('A timeout cannot be negative') def cancel(self): """ cancel() Attempt to cancel the call. If the call is currently being executed and cannot be cancelled then the method will return False, otherwise the call will be cancelled and the method will return True. """ if self._status == 1: # Running, cannot cancel return False elif self._status == 0: # Cancel now self._status = 2 self._send('req-') for fn in self._callbacks: yoton.call_later(fn, 0, self) return True else: # Already done or canceled return True def cancelled(self): """ cancelled() Return True if the call was successfully cancelled. """ return self._status == 2 def running(self): """ running() Return True if the call is currently being executed and cannot be cancelled. """ return self._status == 1 def done(self): """ done() Return True if the call was successfully cancelled or finished running. """ return self._status in [2,3,4] def _wait(self, timeout): """ _wait(timeout) Wait for the request to be handled for the specified amount of time. While waiting, the ReqChannel local event loop is called so that pre-request messages can be exchanged. """ # No timout means a veeeery long timeout if timeout is None: timeout = 999999999999999999.0 # Receive packages untill we receive the one we want, # or untill time runs out timestamp = time.time() + timeout while (self._status < 2) and (time.time() < timestamp): self._req_channel._process_events_local() time.sleep(0.01) # 10 ms def result(self, timeout=None): """ result(timeout=None) Return the value returned by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then a TimeoutError will be raised. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time. If the future is cancelled before completing then CancelledError will be raised. If the call raised, this method will raise the same exception. """ # Wait self._wait(timeout) # Return or raise error if self._status < 2 : raise TimeoutError('Result unavailable within the specified time.') elif self._status == 2: raise CancelledError('Result unavailable because request was cancelled.') elif self._status == 3: raise self._result else: return self._result def result_or_cancel(self, timeout=1.0): """ result_or_cancel(timeout=1.0) Return the value returned by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then the call is cancelled and the method will return None. """ # Wait self._wait(timeout) # Return if self._status == 4: return self._result else: self.cancel() return None def exception(self, timeout=None): """ exception(timeout) Return the exception raised by the call. If the call hasn’t yet completed then this method will wait up to timeout seconds. If the call hasn’t completed in timeout seconds, then a TimeoutError will be raised. timeout can be an int or float. If timeout is not specified or None, there is no limit to the wait time. If the future is cancelled before completing then CancelledError will be raised. If the call completed without raising, None is returned. """ # Wait self._wait(timeout) # Return or raise error if self._status < 2 : raise TimeoutError('Exception unavailable within the specified time.') elif self._status == 2: raise CancelledError('Exception unavailable because request was cancelled.') elif self._status == 3: return self._result else: return None # no exception def add_done_callback(self, fn): """ add_done_callback(fn) Attaches the callable fn to the future. fn will be called, with the future as its only argument, when the future is cancelled or finishes running. Added callables are called in the order that they were added. If the callable raises a Exception subclass, it will be logged and ignored. If the callable raises a BaseException subclass, the behavior is undefined. If the future has already completed or been cancelled, fn will be called immediately. """ # Check if not hasattr(fn, '__call__'): raise ValueError('add_done_callback expects a callable.') # Add if self.done(): yoton.call_later(fn, 0, self) else: self._callbacks.append(fn) def set_running_or_notify_cancel(self): """ set_running_or_notify_cancel() This method should only be called by Executor implementations before executing the work associated with the Future and by unit tests. If the method returns False then the Future was cancelled, i.e. Future.cancel() was called and returned True. If the method returns True then the Future was not cancelled and has been put in the running state, i.e. calls to Future.running() will return True. This method can only be called once and cannot be called after Future.set_result() or Future.set_exception() have been called. """ if self._status == 2: return False elif self._status == 0: self._status = 1 return True else: raise RuntimeError('set_running_or_notify_cancel should be called when in a clear state.') def set_result(self, result): """ set_result(result) Sets the result of the work associated with the Future to result. This method should only be used by Executor implementations and unit tests. """ # Set result if indeed in running state if self._status == 1: self._result = result self._status = 4 for fn in self._callbacks: yoton.call_later(fn, 0, self) def set_exception(self, exception): """ set_exception(exception) Sets the result of the work associated with the Future to the Exception exception. This method should only be used by Executor implementations and unit tests. """ # Check if isinstance(exception, basestring): exception = Exception(exception) if not isinstance(exception, Exception): raise ValueError('exception must be an Exception instance.') # Set result if indeed in running state if self._status == 1: self._result = exception self._status = 3 for fn in self._callbacks: yoton.call_later(fn, 0, self) class ReqChannel(BaseChannel): """ ReqChannel(context, slot_base) The request part of the request/reply messaging pattern. A ReqChannel instance sends request and receive the corresponding replies. The requests are replied by a yoton.RepChannel instance. This class adopts req/rep in a remote procedure call (RPC) scheme. The handling of the result is done using a yoton.Future object, which follows the approach specified in PEP 3148. Note that for the use of callbacks, the yoton event loop must run. Basic load balancing is performed by first asking all potential repliers whether they can handle a request. The actual request is then send to the first replier to respond. Parameters ---------- context : yoton.Context instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network Usage ----- One performs a call on a virtual method of this object. The actual method is executed by the yoton.RepChannel instance. The method can be called with normal and keyword arguments, which can be (a combination of): None, bool, int, float, string, list, tuple, dict. Example ------- # Fast, but process is idling when waiting for the response. reply = req.add(3,4).result(2.0) # Wait two seconds # Asynchronous processing, but no waiting. def reply_handler(future): ... # Handle reply future = req.add(3,4) future.add_done_callback(reply_handler) """ # Notes on load balancing: # # Firstly, each request has an id. Which is an integer number # which is increased at each new request. The id is send via # the dest_seq. For pre-request messages an offset is added # to recognize these meta-messages. # # We use an approach I call pre-request. The req channel sends # a pre-request to all repliers (on the same slot) asking whether # they want to handle a request. The content is 'req?' and the # dest_seq is the request-id + offset. # # The repliers collects and queues all pre-requests. It will then # send a reply to acknowledge the first received pre-request. The # content is 'req!' and dest_seq is again request-id + offset. # # The replier is now in a state of waiting for the actual request. # It will not acknowledge pre-requests, but keeps queing them. # # Upon receiving the acknowledge, the requester sends (directed # at only the first replier to acknowledge) the real request. # The content is the real request and dest_seq is the request-id. # Right after this, a pre-request cancel message is sent to all # repliers. The content is 'req-' and dest_seq is request-id + offset. # # When a replier receives a pre-request cancel message, it will # remove the pre-request from the list. If this cancels the # request it was currently waiting for, the replier will go back # to its default state, and acknowledge the first next pre-request # in the queue. # # When the replier answers a request, it will go back to its default # state, and acknowledge the first next pre-request in the queue. # The replier tries to answer as quickly to pre-requests as possible. # # On the request channel, a dictionary of request items is maintained. # Each item has an attribute specifying whether a replier has # acknowledged it (and which one). def __init__(self, context, slot_base): BaseChannel.__init__(self, context, slot_base, OBJECT) # Queue with pending requests self._request_items = {} # Timeout self._next_recheck_time = time.time() + 0.2 # Counter self._request_counter = 0 # The req channel is always in event driven mode self._run_mode = 1 # Bind signals to process the events for this channel # Bind to "received" signal for quick response and a timer # so we can resend requests if we do not receive anything. self.received.bind(self._process_events_local) self._timer = yoton.events.Timer(0.5, False) self._timer.bind(self._process_events_local) self._timer.start() def _messaging_patterns(self): return 'req-rep', 'rep-req' def __getattr__(self, name): if name.startswith('_'): return object.__getattribute__(self, name) try: return object.__getattribute__(self, name) except AttributeError: def proxy_function(*args, **kwargs): return self._handle_request(name, *args, **kwargs) return proxy_function def _handle_request(self, name, *args, **kwargs): """ _handle_request(request, callback, **kwargs) Post a request. This creates a Future instance and stores it. A message is send asking any repliers to respond. The actual request will be send when a reply to our pre-request is received. This all hapens in the yoton event loop. """ # Create request object request = name, args, kwargs # Check and convert request message bb = self.message_to_bytes(request) # Get new request id request_id = self._request_counter = self._request_counter + 1 # Create new item for this request and store under the request id item = Future(self, bb, request_id) self._request_items[request_id] = item # Send pre-request (ask repliers who want to reply to a request) item._send('req?') # Return the Future instance return item def _resend_requests(self): """ _resend_requests() See if we should resend our older requests. Periodically calling this method enables doing a request while the replier is not yet attached to the network. This also allows the Future objects to cancel themselves if it takes too long. """ for request_id in [key for key in self._request_items.keys()]: item = self._request_items[request_id] # Remove items that are really old if item.cancelled(): self._request_items.pop(request_id) else: item._resend_if_necessary() def _recv_item(self): """ _recv_item() Receive item. If a reply is send that is an acknowledgement of a replier that it wants to handle our request, the correpsonding request is send to that replier. This is a kind of mini-event loop thingy that should be called periodically to keep things going. """ # Receive package package = self._recv(False) if not package: return # Get the package reply id and sequence number dest_id = package._dest_id request_id = package._dest_seq # Check dest_id if not dest_id: return # We only want messages that are directed directly at us elif dest_id != self._context._id: return # This should not happen; context should make sure if request_id > REQREP_SEQ_REF: # We received a reply to us asking who can handle the request. # Get item, send actual request. We set the replier to indicate # that this request is being handled, and we can any further # acknowledgements from other repliers. request_id -= REQREP_SEQ_REF item = self._request_items.get(request_id, None) if item and not item._replier: # Status now changes to "running" canceling is not possible ok = item.set_running_or_notify_cancel() if not ok: return # Send actual request to specific replier try: self._send(item._req, package._source_id, request_id) except IOError: pass # Channel closed, will auto-cancel at item._send() item._replier = package._source_id # mark as being processed # Send pre-request-cancel message to everyone item._send('req-') elif request_id > 0: # We received a reply to an actual request # Get item, remove from queue, set reply, return item = self._request_items.pop(request_id, None) if item: item._rep = package._data return item def _process_events_local(self, dummy=None): """ _process_events_local() Process events only for this object. Used by _handle_now(). """ # Check periodically if we should resend (or clean up) old requests if time.time() > self._next_recheck_time: self._resend_requests() self._next_recheck_time = time.time() + 0.1 # Process all received messages while self.pending: item = self._recv_item() if item: reply = self.message_from_bytes(item._rep) if isinstance(reply, tuple) and len(reply)==2 and reply[0]==ERROR_OBJECT: item.set_exception(reply[1]) else: item.set_result(reply) class RepChannel(BaseChannel): """ RepChannel(context, slot_base) The reply part of the request/reply messaging pattern. A RepChannel instance receives request and sends the corresponding replies. The requests are send from a yoton.ReqChannel instance. This class adopts req/rep in a remote procedure call (RPC) scheme. To use a RepChannel, subclass this class and implement the methods that need to be available. The reply should be (a combination of) None, bool, int, float, string, list, tuple, dict. This channel needs to be set to event or thread mode to function (in the first case yoton events need to be processed too). To stop handling events again, use set_mode('off'). Parameters ---------- context : yoton.Context instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network """ def __init__(self, context, slot_base): BaseChannel.__init__(self, context, slot_base, OBJECT) # Pending pre-requests self._pre_requests = [] # Current pre-request and time that it was acknowledged self._pre_request = None self._pre_request_time = 0 # Create thread self._thread = ThreadForReqChannel(self) # Create timer (do not start) self._timer = yoton.events.Timer(2.0, False) self._timer.bind(self._process_events_local) # By default, the replier is off self._run_mode = 0 def _messaging_patterns(self): return 'rep-req', 'req-rep' # Node that setters for normal and event_driven mode are specified in # channels_base.py def set_mode(self, mode): """ set_mode(mode) Set the replier to its operating mode, or turn it off. Modes: * 0 or 'off': do not process requests * 1 or 'event': use the yoton event loop to process requests * 2 or 'thread': process requests in a separate thread """ if isinstance(mode, basestring): mode = mode.lower() if mode in [0, 'off']: self._run_mode = 0 elif mode in [1, 'event', 'event-driven']: self._run_mode = 1 self.received.bind(self._process_events_local) self._timer.start() elif mode in [2, 'thread', 'thread-driven']: self._run_mode = 2 if not self._thread.isAlive(): self._thread.start() else: raise ValueError('Invalid mode for ReqChannel instance.') def _handle_request(self, message): """ _handle_request(message) This method is called for each request, and should return a reply. The message contains the name of the method to call, this function calls that method. """ # Get name and args name, args, kwargs = message # Get function if not hasattr(self, name): raise RuntimeError("Method '%s' not implemented." % name) else: func = getattr(self, name) # Call return func(*args, **kwargs) def _acknowledge_next_pre_request(self): # Cancel current pre-request ourselves if it takes too long. # Failsafe, only for if resetting by requester fails somehow. if time.time() - self._pre_request_time > 10.0: self._pre_request = None # Send any pending pre requests if self._pre_requests and not self._pre_request: # Set current pre-request and its ack time package = self._pre_requests.pop(0) self._pre_request = package self._pre_request_time = time.time() # Send acknowledgement msg = 'req!'.encode('utf-8') try: self._send(msg, package._source_id, package._dest_seq) except IOError: pass # Channel closed, nothing we can do about that # #print 'ack', self._context.id, package._dest_seq-REQREP_SEQ_REF def _replier_iteration(self, package): """ _replier_iteration() Do one iteration: process one request. """ # Get request id request_id = package._dest_seq if request_id > REQREP_SEQ_REF: # Pre-request stuff # Remove offset request_id -= REQREP_SEQ_REF # Get action and pre request id action = package._data.decode('utf-8') # Remove pre-request from pending requests in case of both actions: # Cancel pending pre-request, prevent stacking of the same request. for prereq in [prereq for prereq in self._pre_requests]: if ( package._source_id == prereq._source_id and package._dest_seq == prereq._dest_seq): self._pre_requests.remove(prereq) if action == 'req-': # Cancel current pre-request if ( self._pre_request and package._source_id == self._pre_request._source_id and package._dest_seq == self._pre_request._dest_seq): self._pre_request = None elif action == 'req?': # New pre-request self._pre_requests.append(package) else: # We are asked to handle an actual request # We can reset the state self._pre_request = None # Get request request = self.message_from_bytes(package._data) # Get reply try: reply = self._handle_request(request) except Exception: reply = ERROR_OBJECT, getErrorMsg() print('yoton.RepChannel: error handling request:') print(reply[1]) # Send reply if True: try: bb = self.message_to_bytes(reply) self._send(bb, package._source_id, request_id) except IOError: pass # Channel is closed except Exception: # Probably wrong type of reply returned by handle_request() print('Warning: request could not be send:') print(getErrorMsg()) def _process_events_local(self, dummy=None): """ _process_events_local() Called when a message (or more) has been received. """ # If closed, unregister from signal and stop the timer if self.closed or self._run_mode!=1: self.received.unbind(self._process_events_local) self._timer.stop() # Iterate while we receive data while True: package = self._recv(False) if package: self._replier_iteration(package) self._acknowledge_next_pre_request() else: # We always enter this the last time self._acknowledge_next_pre_request() return def echo(self, arg1, sleep=0.0): """ echo(arg1, sleep=0.0) Default procedure that can be used for testing. It returns a tuple (first_arg, context_id) """ time.sleep(sleep) return arg1, hex(self._context.id) class ThreadForReqChannel(threading.Thread): """ ThreadForReqChannel(channel) Thread to run a RepChannel in threaded mode. """ def __init__(self, channel): threading.Thread.__init__(self) # Check channel if not isinstance(channel, RepChannel): raise ValueError('The given channel must be a REP channel.') # Store channel self._channel = channel # Make deamon self.setDaemon(True) def run(self): """ run() The handler's main loop. """ # Get ref to channel. Remove ref from instance channel = self._channel del self._channel while True: # Stop? if channel.closed or channel._run_mode!=2: break # Wait for data (blocking, look Rob, without spinlocks :) package = channel._recv(2.0) if package: channel._replier_iteration(package) channel._acknowledge_next_pre_request() else: channel._acknowledge_next_pre_request() pyzo-4.4.3/pyzo/yoton/channels/channels_state.py0000666000000000000000000001076113131373416020175 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.channels.channels_state Defines the channel class for state. """ from yoton.misc import bytes from yoton.channels import BaseChannel class StateChannel(BaseChannel): """ StateChannel(context, slot_base, message_type=yoton.TEXT) Channel class for the state messaging pattern. A state is synchronized over all state channels of the same slot. Each channel can send (i.e. set) the state and recv (i.e. get) the current state. Note however, that if two StateChannel instances set the state around the same time, due to the network delay, it is undefined which one sets the state the last. The context will automatically call this channel's send_last() method when a new context enters the network. The recv() call is always non-blocking and always returns the last received message: i.e. the current state. There are no limitations for this channel if events are not processed, except that the received signal is not emitted. Parameters ---------- context : yoton.Context instance The context that this channel uses to send messages in a network. slot_base : string The base slot name. The channel appends an extension to indicate message type and messaging pattern to create the final slot name. The final slot is used to connect channels at different contexts in a network message_type : yoton.MessageType instance (default is yoton.TEXT) Object to convert messages to bytes and bytes to messages. Users can create their own message_type class to let channels any type of message they want. """ def __init__(self, *args, **kwargs): BaseChannel.__init__(self, *args, **kwargs) # Variables to hold the current state. We use only the message # as a reference, so we dont need a lock. # The package is used to make _recv() function more or less, # and to be able to determine if a state was set (because the # message may be set to None) self._current_package = None self._current_message = self.message_from_bytes(bytes()) def _messaging_patterns(self): return 'state', 'state' def send(self, message): """ send(message) Set the state of this channel. The state-message is queued and send over the socket by the IO-thread. Zero-length messages are ignored. """ # Send message only if it is different from the current state # set current_message by unpacking the send binary. This ensures # that if someone does this, things still go well: # a = [1,2,3] # status.send(a) # a.append(4) # status.send(a) if message != self._current_message: self._current_package = self._send( self.message_to_bytes(message) ) self._current_message = self.message_from_bytes(self._current_package._data) def send_last(self): """ send_last() Resend the last message. """ if self._current_package is not None: self._send( self.message_to_bytes(self._current_message) ) def recv(self, block=False): """ recv(block=False) Get the state of the channel. Always non-blocking. Returns the most up to date state. """ return self._current_message def _recv_package(self, package): """ _recv_package(package) Bypass queue and just store it in a variable. """ self._current_message = self.message_from_bytes(package._data) self._current_package = package # self._maybe_emit_received() def _inject_package(self, package): """ Non-blocking version of recv_package. Does the same. """ self._current_message = self.message_from_bytes(package._data) self._current_package = package # self._maybe_emit_received() def _recv(self, block=None): """ _recv(block=None) Returns the last received or send set package. The package may not reflect the current state. """ return self._current_package pyzo-4.4.3/pyzo/yoton/channels/message_types.py0000666000000000000000000002216013131373421020042 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.channels.message_types Defines a few basic message_types for the channels. A MessageType object defines how a message of that type should be converted to bytes and vice versa. The Packer and Unpacker classes for the ObjectMessageType are based on the xdrrpc Python module by Rob Reilink and Windel Bouwman. """ import sys import struct from yoton.misc import bytes, basestring, long # To decode P2k strings that are not unicode if sys.__stdin__ and sys.__stdin__.encoding: STDINENC = sys.__stdin__.encoding elif sys.stdin and sys.stdin.encoding: STDINENC = sys.stdin.encoding else: STDINENC = 'utf-8' class MessageType(object): """ MessageType() Instances of this class are used to convert messages to bytes and bytes to messages. Users can easily inherit from this class to make channels work for user specific message types. Three methods should be overloaded: * message_to_bytes() - given a message, returns bytes * message_from_bytes() - given bytes, returns the message * message_type_name() - a string (for example 'text', 'array') The message_type_name() method is used by the channel to add an extension to the slot name, such that only channels of the same message type (well, with the same message type name) can connect. """ def message_to_bytes(self, message): raise NotImplementedError() def message_from_bytes(self, bb): raise NotImplementedError() def message_type_name(self): raise NotImplementedError() class BinaryMessageType(MessageType): """ BinaryMessageType() To let channels handle binary messages. Available as yoton.BINARY. """ def message_type_name(self): return 'bin' def message_to_bytes(self, message): if not isinstance(message, bytes): raise ValueError("Binary channel requires byte messages.") return message def message_from_bytes(self, bb): return bb class TextMessageType(MessageType): """ BinaryMessageType() To let channels handle Unicode text messages. Available as yoton.TEXT. """ def message_type_name(self): return 'txt' def message_to_bytes(self, message): # Check if not isinstance(message, basestring): raise ValueError("Text channel requires string messages.") # If using py2k and the string is not unicode, make unicode first # by try encoding using UTF-8. When a piece of code stored # in a unicode string is executed, str objects are utf-8 encoded. # Otherwise they are encoded using __stdin__.encoding. In specific # cases, a non utf-8 encoded str might be succesfully encoded # using utf-8, but this is rare. Since I would not # know how to tell the encoding beforehand, we'll take our # chances... Note that in Pyzo (for which this package was created, # all executed code is unicode, so str instrances are always # utf-8 encoded. if isinstance(message, bytes): try: message = message.decode('utf-8') except UnicodeError: try: message = message.decode(STDINENC) except UnicodeError: # Probably not really a string then? message = repr(message) # Encode and send return message.encode('utf-8') def message_from_bytes(self, bb): return bb.decode('utf-8') class ObjectMessageType(MessageType): """ ObjectMessageType() To let channels handle messages consisting of any of the following Python objects: None, bool, int, float, string, list, tuple, dict. Available as yoton.OBJECT. """ def message_type_name(self): return 'obj' def message_to_bytes(self, message): packer = Packer() packer.pack_object(message) return packer.get_buffer() def message_from_bytes(self, bb): if bb: unpacker = Unpacker(bb) return unpacker.unpack_object() else: return None # Formats _FMT_TYPE = ' len(self._buf): raise EOFError else: self._pos = i2 return self._buf[i1:i2] def read_number(self): n, = struct.unpack(' len(self._buf): raise EOFError else: self._pos = i2 data = self._buf[i1:i2] return struct.unpack(fmt, data)[0] def unpack_object(self): object_type = self.unpack(_FMT_TYPE, 1) if object_type == _TYPE_NONE: return None elif object_type == _TYPE_BOOL: return bool( self.unpack(_FMT_BOOL, 1) ) elif object_type == _TYPE_INT: return self.unpack(_FMT_INT, 8) elif object_type == _TYPE_FLOAT: return self.unpack(_FMT_FLOAT, 8) elif object_type == _TYPE_STRING: n = self.read_number() return self.read(n).decode('utf-8') elif object_type == _TYPE_LIST: object = [] for i in range(self.read_number()): object.append( self.unpack_object() ) return object elif object_type == _TYPE_TUPLE: object = [] for i in range(self.read_number()): object.append( self.unpack_object() ) return tuple(object) elif object_type == _TYPE_DICT: object = {} for i in range(self.read_number()): key = self.unpack_object() object[key] = self.unpack_object() return object else: raise ValueError("Unsupported type: %s" % repr(object_type)) # Define constants TEXT = TextMessageType() BINARY = BinaryMessageType() OBJECT = ObjectMessageType() if __name__ == '__main__': # Test s = {} s['foo'] = 3 s['bar'] = 9 s['empty'] = [] s[(2,'aa',3)] = ['pretty', ('nice', 'eh'), 4] bb = OBJECT.message_to_bytes(s) s2 = OBJECT.message_from_bytes(bb) print(s) print(s2) pyzo-4.4.3/pyzo/yoton/channels/__init__.py0000666000000000000000000000152013131373367016737 0ustar 00000000000000# -*- coding: utf-8 -*- # flake8: noqa # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ The channel classes represent the mechanism for the user to send messages into the network and receive messages from it. A channel needs a context to function; the context represents a node in the network. """ from yoton.channels.message_types import MessageType, TEXT, BINARY, OBJECT from yoton.channels.channels_base import BaseChannel from yoton.channels.channels_pubsub import PubChannel, SubChannel, select_sub_channel from yoton.channels.channels_reqrep import ReqChannel, RepChannel, Future, TimeoutError, CancelledError from yoton.channels.channels_state import StateChannel from yoton.channels.channels_file import FileWrapper pyzo-4.4.3/pyzo/yoton/clientserver.py0000666000000000000000000002261713131373302016111 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ yoton.clientserver.py Yoton comes with a small framework to setup a request-reply pattern using a client-server model (over a non-persistent connection), similar to telnet. This allows one process to easily ask small pieces of information from another process. To create a server, create a class that inherits from yoton.RequestServer and implement its handle_request() method. A client process can simply use the yoton.do_request function. Example: ``yoton.do_request('www.google.com:80', 'GET http/1.1\\r\\n')`` The client server model is implemented using one function and one class: yoton.do_request and yoton.RequestServer. Details ------- The server implements a request/reply pattern by listening at a socket. Similar to telnet, each request is handled using a connection and the socket is closed after the response is send. The request server can setup to run in the main thread, or can be started using its own thread. In the latter case, one can easily create multiple servers in a single process, that listen on different ports. """ import time import socket import threading from yoton.misc import basestring, str from yoton.misc import split_address, getErrorMsg from yoton.core import send_all, recv_all class RequestServer(threading.Thread): """ RequestServer(address, async=False, verbose=0) Setup a simple server that handles requests similar to a telnet server, or asyncore. Starting the server using run() will run the server in the calling thread. Starting the server using start() will run the server in a separate thread. To create a server, subclass this class and re-implement the handle_request method. It accepts a request and should return a reply. This server assumes utf-8 encoded messages. Parameters ---------- address : str Should be of the shape hostname:port. async : bool If True, handles each incoming connection in a separate thread. This might be advantageous if a the handle_request() method takes a long time to execute. verbose : bool If True, print a message each time a connection is accepted. Notes on hostname ----------------- The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connections is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also connect to 'localhost'. * 'publichost': the connection is visible by other computers on the same network. """ def __init__(self, address, async=False, verbose=0): threading.Thread.__init__(self) # Store whether to handle requests asynchronously self._async = async # Verbosity self._verbose = verbose # Determine host and port (assume tcp) protocol, host, port = split_address(address) # Create socket. Apply SO_REUSEADDR when binding, so that a # improperly closed socket on the same port will not prevent # us connecting. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind (can raise error is port is not available) s.bind((host,port)) # Store socket instance self._bsd_socket = s # To stop serving self._stop_me = False # Make deamon self.setDaemon(True) def start(self): """ start() Start the server in a separate thread. """ self._stop_me = False threading.Thread.start(self) def stop(self): """ stop() Stop the server. """ self._stop_me = True def run(self): """ run() The server's main loop. """ # Get socket instance s = self._bsd_socket # Notify hostname, port = s.getsockname() if self._verbose: print('Yoton: hosting at %s, port %i' % (hostname, port)) # Tell the socket it is a host, accept multiple s.listen(1) # Set timeout so that we can check _stop_me from time to time self._bsd_socket.settimeout(0.25) # Enter main loop while not self._stop_me: try: s, addr = self._bsd_socket.accept() except socket.timeout: pass except InterruptedError: pass else: # Show handling? if self._verbose: print('handling request from: '+str(addr)) # Handle request if self._async : rh = SocketHandler(self, s) rh.start() else: self._handle_connection(s) # Close down try: self._bsd_socket.close() except socket.error: pass def _handle_connection(self, s): """ _handle_connection(s) Handle an incoming connection. """ try: self._really_handle_connection(s) except Exception: print('Error handling request:') print(getErrorMsg()) def _really_handle_connection(self, s): """ _really_handle_connection(s) Really handle an incoming connection. """ # Get request request = recv_all(s, True) if request is None: return # Get reply reply = self.handle_request(request) # Test if not isinstance(reply, basestring): raise ValueError('handle_request() should return a string.') # Send reply send_all(s, reply, True) # Close the socket try: s.close() except socket.error: pass def handle_request(self, request): """ handle_request(request) Return a reply, given the request. Overload this method to create a server. De standard implementation echos the request, waits one second when receiving 'wait' and stop the server when receiving 'stop'. """ # Special cases if request == 'wait': time.sleep(1.0) elif request == 'stop': self._stop_me = True # Echo return 'Requested: ' + request class SocketHandler(threading.Thread): """ SocketHandler(server, s) Simple thread that handles a connection. """ def __init__(self, server, s): threading.Thread.__init__(self) self._server = server self._bsd_socket = s def run(self): self._server._handle_connection(self._bsd_socket) def do_request(address, request, timeout=-1): """ do_request(address, request, timeout=-1) Do a request at the server at the specified address. The server can be a yoton.RequestServer, or any other server listening on a socket and following a REQ/REP pattern, such as html or telnet. For example: ``html = do_request('www.google.com:80', 'GET http/1.1\\r\\n')`` Parameters ---------- address : str Should be of the shape hostname:port. request : string The request to make. timeout : float If larger than 0, will wait that many seconds for the respons, and return None if timed out. Notes on hostname ----------------- The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connections is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also connect to 'localhost'. * 'publichost': the connection is visible by other computers on the same network. """ # Determine host (assume tcp) protocol, host, port = split_address(address) # Check request if not isinstance(request, basestring): raise ValueError('request should be a string.') # Check timeout if timeout is None: timeout = -1 # Create socket and connect s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.connect((host,port)) except socket.error: raise RuntimeError('No server is listening at the given port.') # Send request send_all(s, request, True) # Receive reply reply = recv_all(s, timeout) # Close socket try: s.close() except socket.error: pass # Done return reply if __name__ == '__main__': class Lala(RequestServer): def handle_request(self, req): print('REQ:',repr(req)) return "The current time is %i" % time.time() s = Lala('localhost:test', 0, 1) s.start() if False: print(do_request('localhost:test', 'wait', 5)) for i in range(10): print(do_request('localhost:test', 'hi'+str(i))) # do_request('localhost:test', 'wait'); do_request('localhost:test', 'hi'); pyzo-4.4.3/pyzo/yoton/connection.py0000666000000000000000000003116713131373323015546 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import os import threading import yoton from yoton.misc import basestring from yoton.misc import Property # Minimum timout TIMEOUT_MIN = 0.5 # For the status STATUS_CLOSED = 0 STATUS_CLOSING = 1 STATUS_WAITING = 2 STATUS_HOSTING = 3 STATUS_CONNECTED = 4 STATUSMAP = ['closed', 'closing', 'waiting', 'hosting', 'connected', ] # Reasons to stop the connection STOP_DEFAULT_REASON = 'Closed on command.' STOP_UNSPECIFIED_PROBLEM = 'Unspecified problem' STOP_INVALID_REASON = 'Invalid stop reason specified (must be string).' STOP_TIMEOUT = "Connection timed out." # Can be used by user STOP_HANDSHAKE_TIMEOUT = "Handshake timed out." STOP_HANDSHAKE_FAILED = "Handshake failed." STOP_HANDSHAKE_SELF = "Handshake failed (context cannot connect to self)." STOP_CLOSED_FROM_THERE = "Closed from other end." class ConnectionCollection(list): """ ContextConnectionCollection() A list class that allows indexing using the name of the required Connection instance. """ def __getitem__(self, key): if isinstance(key, basestring): if not key: raise KeyError('An empty string is not a valid key.') for c in self: if c.name == key: return c else: raise KeyError('No connection know by the name %s' % key) else: return list.__getitem__(self, key) class Connection(object): """ Connection(context, name='') Abstract base class for a connection between two Context objects. This base class defines the full interface; subclasses only need to implement a few private methods. The connection classes are intended as a simple interface for the user, for example to query port number, and be notified of timeouts and closing of the connection. All connection instances are intended for one-time use. To make a new connection, instantiate a new Connection object. After instantiation, either _bind() or _connect() should be called. """ def __init__(self, context, name=''): # Store context and name self._context = context self._name = name # Init hostname and port self._hostname1 = '' self._hostname2 = '' self._port1 = 0 self._port2 = 0 # Init id and pid of target context (set during handshake) # We can easily retrieve our own id and pid; no need to store self._id2 = 0 self._pid2 = 0 # Timeout value (if no data is received for this long, # the timedout signal is fired). Because we do not know the timeout # that the other side uses, we apply a minimum timeout. self._timeout = TIMEOUT_MIN # Create signals self._timedout_signal = yoton.Signal() self._closed_signal = yoton.Signal() # Lock to make setting and getting the status thread safe self._lock = threading.RLock() # Init variables to disconnected state self._set_status(0) ## Properties @property def hostname1(self): """ Get the hostname corresponding to this end of the connection. """ return self._hostname1 @property def hostname2(self): """ Get the hostname for the other end of this connection. Is empty string if not connected. """ return self._hostname2 @property def port1(self): """ Get the port number corresponding to this end of the connection. When binding, use this port to connect the other context. """ return self._port1 @property def port2(self): """ Get the port number for the other end of the connection. Is zero when not connected. """ return self._port2 @property def id1(self): """ The id of the context on this side of the connection. """ return self._context._id @property def id2(self): """ The id of the context on the other side of the connection. """ return self._id2 @property def pid1(self): """ The pid of the context on this side of the connection. (hint: os.getpid()) """ return os.getpid() @property def pid2(self): """ The pid of the context on the other side of the connection. """ return self._pid2 @property def is_alive(self): """ Get whether this connection instance is alive (i.e. either waiting or connected, and not in the process of closing). """ self._lock.acquire() try: return self._status >= 2 finally: self._lock.release() @property def is_connected(self): """ Get whether this connection instance is connected. """ self._lock.acquire() try: return self._status >= 3 finally: self._lock.release() @property def is_waiting(self): """ Get whether this connection instance is waiting for a connection. This is the state after using bind() and before another context connects to it. """ self._lock.acquire() try: return self._status == 2 finally: self._lock.release() @property def closed(self): """ Signal emitted when the connection closes. The first argument is the ContextConnection instance, the second argument is the reason for the disconnection (as a string). """ return self._closed_signal @Property def timeout(): """ Set/get the amount of seconds that no data is received from the other side after which the timedout signal is emitted. """ def fget(self): return self._timeout def fset(self, value): if not isinstance(value, (int,float)): raise ValueError('timeout must be a number.') if value < TIMEOUT_MIN: raise ValueError('timeout must be at least %1.2f.' % TIMEOUT_MIN) self._timeout = value return locals() @property def timedout(self): """ This signal is emitted when no data has been received for over 'timeout' seconds. This can mean that the connection is unstable, or that the other end is running extension code. Handlers are called with two arguments: the ContextConnection instance, and a boolean. The latter is True when the connection times out, and False when data is received again. """ return self._timedout_signal @Property def name(): """ Set/get the name that this connection is known by. This name can be used to obtain the instance using the Context.connections property. The name can be used in networks in which each context has a particular role, to easier distinguish between the different connections. Other than that, the name has no function. """ def fget(self): return self._name def fset(self, value): if not isinstance(value, basestring): raise ValueError('name must be a string.') self._name = value return locals() ## Public methods def flush(self, timeout=3.0): """ flush(timeout=3.0) Wait until all pending packages are send. An error is raised when the timeout passes while doing so. """ return self._flush(timeout) def close(self, reason=None): """ close(reason=None) Close the connection, disconnecting the two contexts and stopping all trafic. If the connection was waiting for a connection, it stops waiting. Optionally, a reason for closing can be specified. A closed connection cannot be reused. """ # No reason, user invoked close if reason is None: reason = STOP_DEFAULT_REASON # If already closed or closing, do nothing if self._status in [STATUS_CLOSED, STATUS_CLOSING]: return # Go! return self._general_close_method(reason, True) def close_on_problem(self, reason=None): """ close_on_problem(reason=None) Disconnect the connection, stopping all trafic. If it was waiting for a connection, we stop waiting. Optionally, a reason for stopping can be specified. This is highly recommended in case the connection is closed due to a problem. In contrast to the normal close() method, this method does not try to notify the other end of the closing. """ # No reason, some unspecified problem if reason is None: reason = STOP_UNSPECIFIED_PROBLEM # If already closed (status==0), do nothing if self._status == STATUS_CLOSED: return # If a connecion problem occurs during closing, we close the connection # so that flush will not block. # The closing that is now in progress will emit the event, so we # do not need to go into the _general_close_method(). if self._status == STATUS_CLOSING: self._set_status(STATUS_CLOSED) return # Go! return self._general_close_method(reason, False) def _general_close_method(self, reason, send_stop_message): """ general close method used by both close() and close_on_problem() """ # Remember current status. Set status to closing, which means that # the connection is still alive but cannot be closed again. old_status = self._status self._set_status(STATUS_CLOSING) # Check reason if not isinstance(reason, basestring): reason = STOP_INVALID_REASON # Tell other end to close? if send_stop_message and self.is_connected: self._notify_other_end_of_closing() # Close socket and set attributes to None self._set_status(STATUS_CLOSED) # Notify user, but only once self.closed.emit(self, reason) # Notify user ++ if self._context._verbose: tmp = STATUSMAP[old_status] print("Yoton: %s connection closed: %s" % (tmp, reason)) # if True: # tmp = STATUSMAP[old_status] # sys.__stdout__.write("Yoton: %s connection closed: %s" % (tmp, reason)) # sys.__stdout__.flush() ## Methods to overload def _bind(self, hostname, port, max_tries): raise NotImplemented() def _connect(self, hostname, port, timeout): raise NotImplemented() def _flush(self, timeout): raise NotImplemented() def _notify_other_end_of_closing(self): raise NotImplemented() def _send_package(self, package): raise NotImplemented() def _inject_package(self, package): raise NotImplemented() def _set_status(self, status): """ Used to change the status. Subclasses can reimplement this to get the desired behavior. """ self._lock.acquire() try: # Store status self._status = status # Notify user ++ if (status > 0) and self._context._verbose: action = STATUSMAP[status] print("Yoton: %s at %s:%s." % (action, self._hostname1, self._port1)) finally: self._lock.release() ## More ideas for connections class InterConnection(Connection): """ InterConnection(context, hostname, port, name='') Not implemented. Communication between two processes on the same machine can also be implemented via a memory mapped file or a pype. Would there be an advantage over the TcpConnection? """ pass class UDPConnection(Connection): """ UDPConnection(context, hostname, port, name='') Not implemented. Communication can also be done over UDP, but need protocol on top of UDP to make the connection robust. Is there a reason to implement this if we have Tcp? """ pass pyzo-4.4.3/pyzo/yoton/connection_itc.py0000666000000000000000000000203313131373327016377 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. # flake8: noqa import sys import time import yoton from yoton.misc import basestring, bytes, str from yoton.misc import Property, getErrorMsg, UID from yoton.misc import PackageQueue from yoton.connection import Connection, TIMEOUT_MIN from yoton.connection import STATUS_CLOSED, STATUS_WAITING, STATUS_HOSTING from yoton.connection import STATUS_CONNECTED, STATUS_CLOSING class ItcConnection(Connection): """ ItcConnection(context, hostname, port, name='') Not implemented . The inter-thread-communication connection class implements a connection between two contexts that are in the same process. Two instances of this class are connected using a weak reference. In case one of the ends is cleaned up by the garbadge collector, the other end will close the connection. """ pass # todo: implement me pyzo-4.4.3/pyzo/yoton/connection_tcp.py0000666000000000000000000006313713131373331016415 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import os import sys import time import socket import threading from yoton.misc import basestring, bytes, str from yoton.misc import getErrorMsg, UID from yoton.misc import TinyPackageQueue from yoton.core import Package, PACKAGE_HEARTBEAT, PACKAGE_CLOSE, EINTR from yoton.core import can_recv, send_all, recv_all, HEADER_SIZE from yoton.connection import Connection, TIMEOUT_MIN # noqa from yoton.connection import STATUS_CLOSED, STATUS_WAITING, STATUS_HOSTING # noqa from yoton.connection import STATUS_CONNECTED, STATUS_CLOSING # noqa # Note that time.sleep(0) yields the current thread's timeslice to any # other >= priority thread in the process, but is otherwise equivalent 0 delay. # Reasons to stop the connection STOP_SOCKET_ERROR = "Socket error." # the error message is appended STOP_EOF = "Other end dropped the connection." STOP_HANDSHAKE_TIMEOUT = "Handshake timed out." STOP_HANDSHAKE_FAILED = "Handshake failed." STOP_HANDSHAKE_SELF = "Handshake failed (context cannot connect to self)." STOP_CLOSED_FROM_THERE = "Closed from other end." STOP_LOST_TRACK = "Lost track of the stream." STOP_THREAD_ERROR = "Error in io thread." # Use a relatively small buffer size, to keep the channels better in sync SOCKET_BUFFERS_SIZE = 10*1024 class TcpConnection(Connection): """ TcpConnection(context, name='') The TcpConnection class implements a connection between two contexts that are in differenr processes or on different machines connected via the internet. This class handles the low-level communication for the context. A ContextConnection instance wraps a single BSD socket for its communication, and uses TCP/IP as the underlying communication protocol. A persisten connection is used (the BSD sockets stay connected). This allows to better distinguish between connection problems and timeouts caused by the other side being busy. """ def __init__(self, context, name=''): # Variables to hold threads self._sendingThread = None self._receivingThread = None # Create queue for outgoing packages. self._qout = TinyPackageQueue(64, *context._queue_params) # Init normally (calls _set_status(0) Connection.__init__(self, context, name) def _set_status(self, status, bsd_socket=None): """ _connected(status, bsd_socket=None) This method is called when a connection is made. Private method to apply the bsd_socket. Sets the socket and updates the status. Also instantiates the IO threads. """ # Lock the connection while we change its status self._lock.acquire() # Update hostname and port number; for hosting connections the port # may be different if max_tries > 0. Each client connection will be # assigned a different ephemeral port number. # http://www.tcpipguide.com/free/t_TCPPortsConnectionsandConnectionIdentification-2.htm # Also get hostname and port for other end if bsd_socket is not None: if True: self._hostname1, self._port1 = bsd_socket.getsockname() if status != STATUS_WAITING: self._hostname2, self._port2 = bsd_socket.getpeername() # Set status as normal Connection._set_status(self, status) try: if status in [STATUS_HOSTING, STATUS_CONNECTED]: # Really connected # Store socket self._bsd_socket = bsd_socket # Set socket to blocking. Needed explicitly on Windows # One of these things it takes you hours to find out ... bsd_socket.setblocking(1) # Create and start io threads self._sendingThread = SendingThread(self) self._receivingThread = ReceivingThread(self) # self._sendingThread.start() self._receivingThread.start() if status == 0: # Close bsd socket try: self._bsd_socket.shutdown() except Exception: pass try: self._bsd_socket.close() except Exception: pass self._bsd_socket = None # Remove references to threads self._sendingThread = None self._receivingThread = None finally: self._lock.release() def _bind(self, hostname, port, max_tries=1): """ Bind the bsd socket. Launches a dedicated thread that waits for incoming connections and to do the handshaking procedure. """ # Create socket. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Set buffer size to be fairly small (less than 10 packages) s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SOCKET_BUFFERS_SIZE) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFFERS_SIZE) # Apply SO_REUSEADDR when binding, so that an improperly closed # socket on the same port will not prevent us from connecting. # It also allows a connection to bind at the same port number, # but only after the previous binding connection has connected # (and has closed the listen-socket). # # SO_REUSEADDR means something different on win32 than it does # for Linux sockets. To get the intended behavior on Windows, # we don't have to do anything. Also see: # * http://msdn.microsoft.com/en-us/library/ms740621%28VS.85%29.aspx # * http://twistedmatrix.com/trac/ticket/1151 # * http://www.tcpipguide.com/free/t_TCPPortsConnectionsandConnectionIdentification-2.htm if not sys.platform.startswith('win'): s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Try all ports in the specified range for port2 in range(port, port+max_tries): try: s.bind((hostname,port2)) break except Exception: # Raise the socket exception if we were asked to try # just one port. Otherwise just try the next if max_tries == 1: raise continue else: # We tried all ports without success tmp = str(max_tries) tmp = "Could not bind to any of the " + tmp + " ports tried." raise IOError(tmp) # Tell the socket it is a host. Backlog of at least 1 to avoid linux # kernel from detecting SYN flood and throttling the connection (#381) s.listen(1) # Set connected (status 1: waiting for connection) # Will be called with status 2 by the hostThread on success self._set_status(STATUS_WAITING, s) # Start thread to wait for a connection # (keep reference so the thread-object stays alive) self._hostThread = HostThread(self, s) self._hostThread.start() def _connect(self, hostname, port, timeout=1.0): """ Connect to a bound socket. """ # Create socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Set buffer size to be fairly small (less than 10 packages) s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, SOCKET_BUFFERS_SIZE) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, SOCKET_BUFFERS_SIZE) # Refuse rediculously low timeouts if timeout<= 0.01: timeout = 0.01 # Try to connect ok = False timestamp = time.time() + timeout while not ok and time.time() timestamp: raise RuntimeError('Sending the packages timed out.') def _send_package(self, package): """ Put package on the queue, where the sending thread will pick it up. """ self._qout.push(package) def _inject_package(self, package): """ Put package in queue, but bypass potential blocking. """ self._qout._q.append(package) class HostThread(threading.Thread): """ HostThread(context_connection, bds_socket) The host thread is used by the ContextConnection when hosting a connection. This thread waits for another context to connect to it, and then performs the handshaking procedure. When a successful connection is made, the context_connection's _connected() method is called and this thread then exits. """ def __init__(self, context_connection, bsd_socket): threading.Thread.__init__(self) # Store connection and socket self._context_connection = context_connection self._bsd_host_socket = bsd_socket # Make deamon self.setDaemon(True) def run(self): """ run() The main loop. Waits for a connection and performs handshaking if successfull. """ # Try making a connection until success or the context is stopped while self._context_connection.is_waiting: # Wait for connection s = self._wait_for_connection() if not s: continue # Check if not closed in the mean time if not self._context_connection.is_waiting: break # Do handshaking hs = HandShaker(s) success, info = hs.shake_hands_as_host(self._context_connection.id1) if success: self._context_connection._id2 = info[0] self._context_connection._pid2 = info[1] else: print('Yoton: Handshake failed: '+info) continue # Success! # Close hosting socket, thereby enabling rebinding at the same port self._bsd_host_socket.close() # Update the status of the connection self._context_connection._set_status(STATUS_HOSTING, s) # Break out of the loop break # Remove ref del self._context_connection del self._bsd_host_socket def _wait_for_connection(self): """ _wait_for_connection() The thread will wait here until someone connects. When a connections is made, the new socket is returned. """ # Set timeout so that we can check _stop_me from time to time self._bsd_host_socket.settimeout(0.25) # Wait while self._context_connection.is_waiting: try: s, addr = self._bsd_host_socket.accept() return s # Return the new socket except socket.timeout: pass except socket.error: # Skip errors caused by interruptions. type, value, tb = sys.exc_info() del tb if value.errno != EINTR: raise class HandShaker: """ HandShaker(bsd_socket) Class that performs the handshaking procedure for Tcp connections. Essentially, the connecting side starts by sending 'YOTON!' followed by its id as a hex string. The hosting side responds with the same message (but with a different id). This process is very similar to a client/server pattern (both messages are also terminated with '\r\n'). This is done such that if for example a web client tries to connect, a sensible error message can be returned. Or when a ContextConnection tries to connect to a web server, it will be able to determine the error gracefully. """ def __init__(self, bsd_socket): # Store bsd socket self._bsd_socket = bsd_socket def shake_hands_as_host(self, id): """ _shake_hands_as_host(id) As the host, we wait for the client to ask stuff, so when for example a http client connects, we can stop the connection. Returns (success, info), where info is the id of the context at the other end, or the error message in case success is False. """ # Make our message with id and pid message = 'YOTON!%s.%i' % (UID(id).get_hex(), os.getpid()) # Get request request = self._recv_during_handshaking() if not request: return False, STOP_HANDSHAKE_TIMEOUT elif request.startswith('YOTON!'): # Get id try: tmp = request[6:].split('.',1) # partition not in Python24 id2_str, pid2_str = tmp[0], tmp[1] id2, pid2 = int(id2_str, 16), int(pid2_str, 10) except Exception: self._send_during_handshaking('ERROR: could not parse id.') return False, STOP_HANDSHAKE_FAILED # Respond and return self._send_during_handshaking(message) # returns error? if id == id2: return False, STOP_HANDSHAKE_SELF else: return True, (id2, pid2) else: # Client is not yoton self._send_during_handshaking('ERROR: this is Yoton.') return False, STOP_HANDSHAKE_FAILED def shake_hands_as_client(self, id): """ _shake_hands_as_client(id) As the client, we ask the host whether it is a Yoton context and whether the channels we want to support are all right. Returns (success, info), where info is the id of the context at the other end, or the error message in case success is False. """ # Make our message with id and pif message = 'YOTON!%s.%i' % (UID(id).get_hex(), os.getpid()) # Do request self._send_during_handshaking(message) # returns error? # Get response response = self._recv_during_handshaking() # Process if not response: return False, STOP_HANDSHAKE_TIMEOUT elif response.startswith('YOTON!'): # Get id try: tmp = response[6:].split('.',1) # Partition not in Python24 id2_str, pid2_str = tmp[0], tmp[1] id2, pid2 = int(id2_str, 16), int(pid2_str, 10) except Exception: return False, STOP_HANDSHAKE_FAILED if id == id2: return False, STOP_HANDSHAKE_SELF else: return True, (id2, pid2) else: return False, STOP_HANDSHAKE_FAILED def _send_during_handshaking(self, text, shutdown=False): return send_all(self._bsd_socket, text+'\r\n', shutdown) def _recv_during_handshaking(self): return recv_all(self._bsd_socket, 2.0, True) class BaseIOThread(threading.Thread): """ The base class for the sending and receiving IO threads. Implements some common functionality. """ def __init__(self, context_connection): threading.Thread.__init__(self) # Thread will "destruct" when the interpreter shuts down self.setDaemon(True) # Store (temporarily) the ref to the context connection # Also of bsd_socket, because it might be removed before the # thread is well up and running. self._context_connection = context_connection self._bsd_socket = context_connection._bsd_socket def run(self): """ Method to prepare to enter main loop. There is a try-except here to catch exceptions caused by interpreter shutdown. """ # Get ref to context connection but make sure the ref # if not stored if the thread stops context_connection = self._context_connection bsd_socket = self._bsd_socket del self._context_connection del self._bsd_socket try: # Run and handle exceptions if someting goes wrong self.run2(context_connection, bsd_socket) except Exception: # An error while handling an exception, most probably #interpreter shutdown pass def run2(self, context_connection, bsd_socket): """ Method to enter main loop. There is a try-except here to catch exceptions in the main loop (such as socket errors and errors due to bugs in the code. """ # Get classname to use in messages className = 'yoton.' + self.__class__.__name__ # Define function to write errors def writeErr(err): sys.__stderr__.write(str(err)+'\n') sys.__stderr__.flush() try: # Enter mainloop stop_reason = self._run(context_connection, bsd_socket) # Was there a specific reason to stop? if stop_reason: context_connection.close_on_problem(stop_reason) else: pass # Stopped because the connection is gone (already stopped) except socket.error: # Socket error. Can happen if the other end closed not so nice # Do get the socket error message and pass it on. msg = STOP_SOCKET_ERROR + getErrorMsg() context_connection.close_on_problem('%s, %s' % (className, msg)) except Exception: # Else: woops, an error! errmsg = getErrorMsg() msg = STOP_THREAD_ERROR + errmsg context_connection.close_on_problem('%s, %s' % (className, msg)) writeErr('Exception in %s.' % className) writeErr(errmsg) class SendingThread(BaseIOThread): """ The thread that reads packages from the queue and sends them over the socket. It uses a timeout while reading from the queue, so it can send heart beat packages if no packages are send. """ def _run(self, context_connection, bsd_socket): """ The main loop. Get package from queue, send package to socket. """ timeout = 0.5*TIMEOUT_MIN queue = context_connection._qout socket_sendall = bsd_socket.sendall while True: time.sleep(0) # Be nice # Get package from the queue. Use heartbeat package # if there have been no packages for a too long time try: package = queue.pop(timeout) except queue.Empty: # Use heartbeat package then package = PACKAGE_HEARTBEAT # Should we stop? if not context_connection.is_connected: return None # No need for a stop message # Process the package in parts to avoid data copying (header+data) for part in package.parts(): socket_sendall(part) class ReceivingThread(BaseIOThread): """ The thread that reads packages from the socket and passes them to the kernel. It uses select() to see if data is available on the socket. This allows using a timeout without putting the socket in timeout mode. If the timeout has expired, the timedout event for the connection is emitted. Upon receiving a package, the _recv_package() method of the context is called, so this thread will eventually dispose the package in one or more queues (of the channel or of another connection). """ def _run(self, context_connection, bsd_socket): """ The main loop. Get package from socket, deposit package in queue(s). """ # Short names in local namespace avoid dictionary lookups socket_recv = bsd_socket.recv recv_package = context_connection._context._recv_package package_from_header = Package.from_header HS = HEADER_SIZE # Variable to keep track if we emitted a timedout signal timedOut = False while True: time.sleep(0) # Be nice # Use select call on a timeout to test if data is available while True: try: ok = can_recv(bsd_socket, context_connection._timeout) except Exception: # select() has it's share of weird errors raise socket.error('select(): ' + getErrorMsg()) if ok: # Set timedout ex? if timedOut: timedOut = False context_connection.timedout.emit(context_connection, False) # Exit from loop break else: # Should we stop? if not context_connection.is_connected: return # No need for a stop message # Should we do a timeout? if not timedOut: timedOut = True context_connection.timedout.emit(context_connection, True) # Continue in loop continue # Get package package = self._getPackage(socket_recv, HS, package_from_header) if package is None: continue elif isinstance(package, basestring): return package # error msg # Let context handle package further (but happens in this thread) try: recv_package(package, context_connection) except Exception: print("Error depositing package in ReceivingThread.") print(getErrorMsg()) def _getPackage(self, socket_recv, HS, package_from_header): """ Get exactly one package from the socket. Blocking. """ # Get header and instantiate package object from it try: header = self._recv_n_bytes(socket_recv, HS) except EOFError: return STOP_EOF package, size = package_from_header(header) # Does it look like a good package? if not package: return STOP_LOST_TRACK if size == 0: # A special package! (or someone sending a # package with no content, which is discarted) if package._source_seq == 0: pass # Heart beat elif package._source_seq == 1: return STOP_CLOSED_FROM_THERE return None else: # Get package data try: package._data = self._recv_n_bytes(socket_recv, size) except EOFError: return STOP_EOF return package def _recv_n_bytes(self, socket_recv, n): """ Receive exactly n bytes from the socket. """ # First round data = socket_recv(n) if len(data) == 0: raise EOFError() # How many more do we need? For small n, we probably only need 1 round n -= len(data) if n==0: return data # We're lucky! # Else, we need more than one round parts = [data] while n: data = socket_recv(n) parts.append(data) n -= len(data) # Return combined data of multiple rounds return bytes().join(parts) pyzo-4.4.3/pyzo/yoton/context.py0000666000000000000000000004652313131373335015100 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.context Defines the context class. """ import threading from yoton import connection from yoton.misc import str, split_address from yoton.misc import UID, PackageQueue from yoton.core import Package, BUF_MAX_LEN from yoton.core import SLOT_CONTEXT from yoton.connection import ConnectionCollection from yoton.connection_tcp import TcpConnection from yoton.connection_itc import ItcConnection class Context(object): """ Context(verbose=0, queue_params=None) A context represents a node in the network. It can connect to multiple other contexts (using a yoton.Connection. These other contexts can be in another process on the same machine, or on another machine connected via a network or the internet. This class represents a context that can be used by channel instances to communicate to other channels in the network. (Thus the name.) The context is the entity that queue routes the packages produced by the channels to the other context in the network, where the packages are distributed to the right channels. A context queues packages while it is not connected to any other context. If messages are send on a channel registered at this context while the context is not connected, the messages are stored by the context and will be send to the first connecting context. Example 1 --------- # Create context and bind to a port on localhost context = yoton.Context() context.bind('localhost:11111') # Create a channel and send a message pub = yoton.PubChannel(context, 'test') pub.send('Hello world!') Example 2 --------- # Create context and connect to the port on localhost context = yoton.Context() context.connect('localhost:11111') # Create a channel and receive a message sub = yoton.SubChannel(context, 'test') print(sub.recv() # Will print 'Hello world!' Queue params ------------ The queue_params parameter allows one to specify the package queues used in the system. It is recommended to use the same parameters for every context in the network. The value of queue_params should be a 2-element tuple specifying queue size and discard mode. The latter can be 'old' (default) or 'new', meaning that if the queue is full, either the oldest or newest messages are discarted. """ def __init__(self, verbose=0, queue_params=None): # Whether or not to write status information self._verbose = verbose # Store queue parameters if queue_params is None: queue_params = BUF_MAX_LEN, 'old' if not (isinstance(queue_params, tuple) and len(queue_params) == 2): raise ValueError('queue_params should be a 2-element tuple.') self._queue_params = queue_params # Create unique key to identify this context self._id = UID().get_int() # Channels currently registered. Maps slots to channel instance. self._sending_channels = {} self._receiving_channels = {} # The list of connections self._connections = [] self._connections_lock = threading.RLock() # Queue used during startup to collect packages # This queue is also subject to the _connections_lock self._startupQueue = PackageQueue(*queue_params) # For numbering and routing the packages self._send_seq = 0 self._recv_seq = 0 self._source_map = {} def close(self): """ close() Close the context in a nice way, by closing all connections and all channels. Closing a connection means disconnecting two contexts. Closing a channel means disasociating a channel from its context. Unlike connections and channels, a Context instance can be reused after closing (although this might not always the best strategy). """ # Close all connections (also the waiting connections!) for c in self.connections_all: c.close('Closed by the context.') # Close all channels self.close_channels() def close_channels(self): """ close_channels() Close all channels associated with this context. This does not close the connections. See also close(). """ # Get all channels channels1 = [c for c in self._sending_channels.values()] channels2 = [c for c in self._receiving_channels.values()] # Close all channels for c in set(channels1+channels2): c.close() ## Properties @property def connections_all(self): """ Get a list of all Connection instances currently associated with this context, including pending connections (connections waiting for another end to connect). In addition to normal list indexing, the connections objects can be queried from this list using their name. """ # Lock self._connections_lock.acquire() try: return [c for c in self._connections if c.is_alive] finally: self._connections_lock.release() @property def connections(self): """ Get a list of the Connection instances currently active for this context. In addition to normal list indexing, the connections objects can be queried from this list using their name. """ # Lock self._connections_lock.acquire() try: # Clean up any dead connections copy = ConnectionCollection() to_remove = [] for c in self._connections: if not c.is_alive: to_remove.append(c) elif c.is_connected: copy.append(c) # Clean for c in to_remove: self._connections.remove(c) # Return copy return copy finally: self._connections_lock.release() @property def connection_count(self): """ Get the number of connected contexts. Can be used as a boolean to check if the context is connected to any other context. """ return len(self.connections) @property def id(self): """ The 8-byte UID of this context. """ return self._id ## Public methods def bind(self, address, max_tries=1, name=''): """ bind(address, max_tries=1, name='') Setup a connection with another Context, by being the host. This method starts a thread that waits for incoming connections. Error messages are printed when an attemped connect fails. the thread keeps trying until a successful connection is made, or until the connection is closed. Returns a Connection instance that represents the connection to the other context. These connection objects can also be obtained via the Context.connections property. Parameters ---------- address : str Should be of the shape hostname:port. The port should be an integer number between 1024 and 2**16. If port does not represent a number, a valid port number is created using a hash function. max_tries : int The number of ports to try; starting from the given port, subsequent ports are tried until a free port is available. The final port can be obtained using the 'port' property of the returned Connection instance. name : string The name for the created Connection instance. It can be used as a key in the connections property. Notes on hostname ----------------- The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connections is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also connect to 'localhost'. * 'publichost': the connection is visible by other computers on the same network. Optionally an integer index can be appended if the machine has multiple IP addresses (see socket.gethostbyname_ex). """ # Trigger cleanup of closed connections self.connections # Split address in protocol, real hostname and port number protocol, hostname, port = split_address(address) # Based on protocol, instantiate connection class (currently only tcp) if False:#protocol == 'itc': connection = ItcConnection(self, name) else: connection = TcpConnection(self, name) # Bind connection connection._bind(hostname, port, max_tries) # Save connection instance self._connections_lock.acquire() try: # Push packages from startup queue while len(self._startupQueue): connection._inject_package(self._startupQueue.pop()) # Add connection object to list of connections self._connections.append(connection) finally: self._connections_lock.release() # Return Connection instance return connection def connect(self, address, timeout=1.0, name=''): """ connect(self, address, timeout=1.0, name='') Setup a connection with another context, by connection to a hosting context. An error is raised when the connection could not be made. Returns a Connection instance that represents the connection to the other context. These connection objects can also be obtained via the Context.connections property. Parameters ---------- address : str Should be of the shape hostname:port. The port should be an integer number between 1024 and 2**16. If port does not represent a number, a valid port number is created using a hash function. max_tries : int The number of ports to try; starting from the given port, subsequent ports are tried until a free port is available. The final port can be obtained using the 'port' property of the returned Connection instance. name : string The name for the created Connection instance. It can be used as a key in the connections property. Notes on hostname ----------------- The hostname can be: * The IP address, or the string hostname of this computer. * 'localhost': the connection is only visible from this computer. Also some low level networking layers are bypassed, which results in a faster connection. The other context should also host as 'localhost'. * 'publichost': the connection is visible by other computers on the same network. Optionally an integer index can be appended if the machine has multiple IP addresses (see socket.gethostbyname_ex). """ # Trigger cleanup of closed connections self.connections # Split address in protocol, real hostname and port number protocol, hostname, port = split_address(address) # Based on protocol, instantiate connection class (currently only tcp) if False:#protocol == 'itc': connection = ItcConnection(self, name) else: connection = TcpConnection(self, name) # Create new connection and connect it connection._connect(hostname, port, timeout) # Save connection instance self._connections_lock.acquire() try: # Push packages from startup queue while self._startupQueue: connection._inject_package(self._startupQueue.pop()) # Add connection object to list of connections self._connections.append(connection) finally: self._connections_lock.release() # Send message in the network to signal a new connection bb = 'NEW_CONNECTION'.encode('utf-8') p = Package(bb, SLOT_CONTEXT, self._id, 0,0,0,0) self._send_package(p) # Return Connection instance return connection def flush(self, timeout=5.0): """ flush(timeout=5.0) Wait until all pending messages are send. This will flush all messages posted from the calling thread. However, it is not guaranteed that no new messages are posted from another thread. Raises an error when the flushing times out. """ # Flush all connections for c in self.connections: c.flush(timeout) # Done (backward compatibility) return True ## Private methods used by the Channel classes def _register_sending_channel(self, channel, slot, slotname=''): """ _register_sending_channel(channel, slot, slotname='') The channel objects use this method to register themselves at a particular slot. """ # Check if this slot is free if slot in self._sending_channels: raise ValueError("Slot not free: " + str(slotname)) # Register self._sending_channels[slot] = channel def _register_receiving_channel(self, channel, slot, slotname=''): """ _register_receiving_channel(channel, slot, slotname='') The channel objects use this method to register themselves at a particular slot. """ # Check if this slot is free if slot in self._receiving_channels: raise ValueError("Slot not free: " + str(slotname)) # Register self._receiving_channels[slot] = channel def _unregister_channel(self, channel): """ _unregister_channel(channel) Unregisters the given channel. That channel can no longer receive messages, and should no longer send messages. """ for D in [self._receiving_channels, self._sending_channels]: for key in [key for key in D.keys()]: if D[key] == channel: D.pop(key) ## Private methods to pass packages between context and io-threads def _send_package(self, package): """ _send_package(package) Used by the channels to send a package into the network. This method routes the package to all currentlt connected connections. If there are none, the packages is queued at the context. """ # Add number self._send_seq += 1 package._source_seq = self._send_seq # Send to all connections, or queue if there are none self._connections_lock.acquire() try: ok = False for c in self._connections: if c.is_alive: # Waiting or connected c._send_package(package) ok = True # Should we queue the package? if not ok: self._startupQueue.push(package) finally: self._connections_lock.release() def _recv_package(self, package, connection): """ _recv_package(package, connection) Used by the connections to receive a package at this context. The package is distributed to all connections except the calling one. The package is also distributed to the right channel (if applicable). """ # Get slot slot = package._slot # Init what to do with the package send_further = False deposit_here = False # Get what to do with the package last_seq = self._source_map.get(package._source_id, 0) if last_seq < package._source_seq: # Update source map self._source_map[package._source_id] = package._source_seq if package._dest_id == 0: # Add to both lists, first attach seq nr self._recv_seq += 1 package._recv_seq = self._recv_seq send_further, deposit_here = True, True elif package._dest_id == self._id: # Add only to process list, first attach seq nr self._recv_seq += 1 package._recv_seq = self._recv_seq deposit_here = True else: # Send package to connected nodes send_further = True # Send package to other context (over all alive connections) if send_further: self._connections_lock.acquire() try: for c in self._connections: if c is connection or not c.is_alive: continue c._send_package(package) finally: self._connections_lock.release() # Process package here or pass to channel if deposit_here: if slot == SLOT_CONTEXT: # Context-to-context messaging; # A slot starting with a space reprsents the context self._recv_context_package(package) else: # Give package to a channel (if applicable) channel = self._receiving_channels.get(slot, None) if channel is not None: channel._recv_package(package) def _recv_context_package(self, package): """ _recv_context_package(package) Process a package addressed at the context itself. This is how the context handles higher-level connection tasks. """ # Get message: context messages are always utf-8 encoded strings message = package._data.decode('utf-8') if message == 'CLOSE_CONNECTION': # Close the connection. Check which one of our connections is # connected with the context that send this message. self._connections_lock.acquire() try: for c in self.connections: if c.is_connected and c.id2 == package._source_id: c.close(connection.STOP_CLOSED_FROM_THERE, False) finally: self._connections_lock.release() elif message == 'NEW_CONNECTION': # Resend all status channels for channel in self._sending_channels.values(): if hasattr(channel, '_current_message') and hasattr(channel, 'send_last'): channel.send_last() else: print('Yoton: Received unknown context message: '+message) pyzo-4.4.3/pyzo/yoton/core.py0000666000000000000000000002223513131373360014334 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. import sys import time import struct import socket # import select # to determine wheter a socket can receive data if sys.platform.startswith('java'): # Jython from select import cpython_compatible_select as select, error as SelectErr else: from select import select, error as SelectErr from yoton.misc import bytes ## Constants # Error code for interruptions try: from errno import EINTR except ImportError: EINTR = None # Queue size BUF_MAX_LEN = 10000 # Define buffer size. For recv 4096 or 8192 chunk size is recommended. # For sending, in principle as large as possible, but prevent too much # message copying. BUFFER_SIZE_IN = 2**13 BUFFER_SIZE_OUT = 1*2**20 # Reserved slots (slot 0 does not exist, so we can test "if slot: ") # The actual slots start counting from 8. # SLOT_TEST = 3 -> Deprecated SLOT_CONTEXT = 2 SLOT_DUMMY = 1 # Struct header packing HEADER_FORMAT = ' n except socket.error: return -1 # Socket closed down badly # Shutdown connection nicely from here if stutdown_after_sending: try: s.shutdown(socket.SHUT_WR) except socket.error: pass def recv_all(s, timeout=-1, end_at_crlf=True): """ recv_all(socket, timeout=-1, end_at_crlf=True) Receive text from the socket (untill socket receiving is shut down). Used during handshaking and in the clientserver module. If end_at_crlf, a message is also ended at a CRLF double-newline code, and a shutdown is not necessary. This takes a tiny bit longer. """ # Init parts (start with one byte, such that len(parts) is always >= 2 parts = [' '.encode('ascii'),] # Determine number of bytes to get per recv nbytesToGet = BUFFER_SIZE_IN if end_at_crlf: nbytesToGet = 1 # Set end bytes end_bytes = '\r\n'.encode('ascii') # Set max time if timeout <= 0: timeout = 2**32 maxtime = time.time() + timeout # Receive data while True: # Receive if we can if can_recv(s): # Get part try: part = s.recv(nbytesToGet) parts.append(part) except socket.error: return None # Socket closed down badly # Detect end by shutdown (EOF) if not part: break # Detect end by \r\n if end_at_crlf and (parts[-2] + parts[-1]).endswith(end_bytes): break else: # Sleep time.sleep(0.01) # Check time if time.time() > maxtime: bb = bytes().join(parts[1:]) return bb.decode('utf-8', 'ignore') # Combine parts (discared first (dummy) part) bb = bytes().join(parts[1:]) # Try returning as Unicode try: return bb.decode('utf-8','ignore') except UnicodeError: return '' ## Package class class Package(object): """ Package(data, slot, source_id, source_seq, dest_id, dest_seq, recv_seq) Represents a package of bytes to be send from one Context instance to another. A package consists of a header and the encoded message. To make this class as fast as reasonably possible, its interface is rather minimalistic and few convenience stuff is implemented. Parameters ---------- data : bytes The message itself. slot : long The slot at which the package is directed. The integer is a hash of a string slot name. source_id : long The id of the context that sent this package. source_seq : long The sequence number of this package, counted at the sending context. Together with source_id, this fully identifies a package. dest_id : long (default 0) The id of the package that this package replies to. dest_seq : long (default 0) The sequence number of the package that this package replies to. recv_seq : long (default 0) The sequence number of this package counted at the receiving context. This is used to synchronize channels. When send, the header is composed of four control bytes, the slot, the source_id, source_seq, dest_id and dest_seq. Notes ----- A package should always have content. Packages without content are only used for low-level communication between two ContextConnection instances. The source_seq is then used as the signal. All other package attributes are ignored. """ # The __slots__ makes instances of this class consume < 20% of memory # Note that this only works for new style classes. # This is important because many packages can exist at the same time # if a receiver cant keep up with a sender. Further, although Python's # garbage collector collects the objects after they're "consumed", # it does not release the memory, because it hopes to reuse it in # an efficient way later. __slots__ = [ '_data', '_slot', '_source_id', '_source_seq', '_dest_id', '_dest_seq', '_recv_seq'] def __init__(self, data, slot, source_id, source_seq, dest_id, dest_seq, recv_seq): self._data = data self._slot = slot # self._source_id = source_id self._source_seq = source_seq self._dest_id = dest_id self._dest_seq = dest_seq self._recv_seq = recv_seq def parts(self): """ parts() Get list of bytes that represents this package. By not concatenating the header and content parts, we prevent unnecesary copying of data. """ # Obtain header L = len(self._data) header = struct.pack(HEADER_FORMAT, CONTROL_BYTES, self._slot, self._source_id, self._source_seq, self._dest_id, self._dest_seq, L) # Return header and message return header, self._data def __str__(self): """ Representation of the package. Mainly for debugging. """ return 'At slot %i: %s' % (self._slot, repr(self._data)) @classmethod def from_header(cls, header): """ from_header(header) Create a package (without data) from the header of a message. Returns (package, data_length). If the header is invalid (checked using the four control bytes) this method returns (None, None). """ # Unpack tmp = struct.unpack(HEADER_FORMAT, header) CTRL, slot, source_id, source_seq, dest_id, dest_seq, L = tmp # Create package p = Package(None, slot, source_id, source_seq, dest_id, dest_seq, 0) # Return if CTRL == CONTROL_BYTES: return p, L else: return None, None # Constant Package instances (source_seq represents the signal) PACKAGE_HEARTBEAT = Package(bytes(), SLOT_DUMMY, 0, 0, 0, 0, 0) PACKAGE_CLOSE = Package(bytes(), SLOT_DUMMY, 0, 1, 0, 0, 0) pyzo-4.4.3/pyzo/yoton/events.py0000666000000000000000000004635513131373352014722 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. # This code is loosely based on the event system of Visvis and on the # signals system of Qt. # Note: Python has a buildin module (sched) that does some of the things # here. Hoever, only since Python3.3 is this buildin functionality # thread safe. And we need thread safety! """ Module yoton.events Yoton comes with a simple event system to enable event-driven applications. All channels are capable of running without the event system, but some channels have limitations. See the documentation of the channels for more information. Note that signals only work if events are processed. """ import time import threading import weakref from yoton.misc import Property, getErrorMsg, PackageQueue class CallableObject(object): """ CallableObject(callable) A class to hold a callable. If it is a plain function, its reference is held (because it might be a closure). If it is a method, we keep the function name and a weak reference to the object. In this way, having for instance a signal bound to a method, the object is not prevented from being cleaned up. """ __slots__ = ['_ob', '_func'] # Use __slots__ to reduce memory footprint def __init__(self, c): # Check if not hasattr(c, '__call__'): raise ValueError('Error: given callback is not callable.') # Store funcion and object if hasattr(c, '__self__'): # Method, store object and method name self._ob = weakref.ref(c.__self__) self._func = c.__func__.__name__ elif hasattr(c, 'im_self'): # Method in older Python self._ob = weakref.ref(c.im_self) self._func = c.im_func.__name__ else: # Plain function self._func = c self._ob = None def isdead(self): """ Get whether the weak ref is dead. """ if self._ob: # Method return self._ob() is None else: return False def compare(self, other): """ compare this instance with another. """ if self._ob and other._ob: return (self._ob() is other._ob()) and (self._func == other._func) elif not (self._ob or other._ob): return self._func == other._func else: return False def __str__(self): return self._func.__str__() def call(self, *args, **kwargs): """ call(*args, **kwargs) Call the callable. Exceptions are caught and printed. """ if self.isdead(): return # Get function try: if self._ob: func = getattr(self._ob(), self._func) else: func = self._func except Exception: return # Call it try: return func(*args, **kwargs) except Exception: print('Exception while handling event:') print(getErrorMsg()) class Event(object): """ Event(callable, *args, **kwargs) An Event instance represents something that is going to be done. It consists of a callable and arguments to call it with. Instances of this class populate the event queue. """ __slots__ = ['_callable', '_args', '_kwargs', '_timeout'] def __init__(self, callable, *args, **kwargs): if isinstance(callable, CallableObject): self._callable = callable else: self._callable = CallableObject(callable) self._args = args self._kwargs = kwargs def dispatch(self): """ dispatch() Call the callable with the arguments and keyword-arguments specified at initialization. """ self._callable.call(*self._args, **self._kwargs) def _on_timeout(self): """ This is what theTimerThread calls. """ app.post_event(self) class Signal: """ Signal() The purpose of a signal is to provide an interface to bind/unbind to events and to fire them. One can bind() or unbind() a callable to the signal. When emitted, an event is created for each bound handler. Therefore, the event loop must run for signals to work. Some signals call the handlers using additional arguments to specify specific information. """ def __init__(self): self._handlers = [] @property def type(self): """ The type (__class__) of this event. """ return self.__class__ def bind(self, func): """ bind(func) Add an eventhandler to this event. The callback/handler (func) must be a callable. It is called with one argument: the event instance, which can contain additional information about the event. """ # make callable object (checks whether func is callable) cnew = CallableObject(func) # check -> warn for c in self._handlers: if cnew.compare(c): print("Warning: handler %s already present for %s" %(func, self)) return # add the handler self._handlers.append(cnew) def unbind(self, func=None): """ unbind(func=None) Unsubscribe a handler, If func is None, remove all handlers. """ if func is None: self._handlers[:] = [] else: cref = CallableObject(func) for c in [c for c in self._handlers]: # remove if callable matches func or object is destroyed if c.compare(cref) or c.isdead(): self._handlers.remove( c ) def emit(self, *args, **kwargs): """ emit(*args, **kwargs) Emit the signal, calling all bound callbacks with *args and **kwargs. An event is queues for each callback registered to this signal. Therefore it is safe to call this method from another thread. """ # Add an event for each callback toremove = [] for func in self._handlers: if func.isdead(): toremove.append(func) else: event = Event(func, *args, **kwargs) app.post_event(event) # Remove dead ones for func in toremove: self._handlers.remove(func) def emit_now(self, *args, **kwargs): """ emit_now(*args, **kwargs) Emit the signal *now*. All handlers are called from the calling thread. Beware, this should only be done from the same thread that runs the event loop. """ # Add an event for each callback toremove = [] for func in self._handlers: if func.isdead(): toremove.append(func) else: func.call(*args, **kwargs) # Remove dead ones for func in toremove: self._handlers.remove(func) class TheTimerThread(threading.Thread): """ TheTimerThread is a singleton thread that is used by all timers and delayed events to wait for a while (in a separate thread) and then post an event to the event-queue. By sharing a single thread timers stay lightweight and there is no time spend on initializing or tearing down threads. The downside is that when there are a lot of timers running at the same time, adding a timer may become a bit inefficient because the registered objects must be sorted each time an object is added. """ def __init__(self): threading.Thread.__init__(self) self.setDaemon(True) self._exit = False self._timers = [] self._somethingChanged = False self._condition = threading.Condition(threading.Lock()) def stop(self, timeout=1.0): self._exit = True self._condition.acquire() try: self._condition.notify() finally: self._condition.release() self.join(timeout) def add(self, timer): """ add(timer) Add item to the list of objects to track. The object should have a _timeout attribute, representing the time.time() at which it runs out, and an _on_timeout() method to call when it does. """ # Check if not (hasattr(timer, '_timeout') and hasattr(timer, '_on_timeout')): raise ValueError('Cannot add this object to theTimerThread.') # Add item self._condition.acquire() try: if timer not in self._timers: self._timers.append(timer) self._sort() self._somethingChanged = True self._condition.notify() finally: self._condition.release() def _sort(self): self._timers = sorted(self._timers, key=lambda x: x._timeout, reverse=True) def discard(self, timer): """Stop the timer if it hasn't finished yet""" self._condition.acquire() try: if timer in self._timers: self._timers.remove(timer) self._somethingChanged = True self._condition.notify() finally: self._condition.release() def run(self): self._condition.acquire() try: self._mainloop() finally: self._condition.release() def _mainloop(self): while not self._exit: # Set flag self._somethingChanged = False # Wait here, in wait() the undelying lock is released if self._timers: timer = self._timers[-1] timeout = timer._timeout - time.time() if timeout > 0: self._condition.wait(timeout) else: timer = None self._condition.wait() # Here the lock has been re-acquired. Take action? if self._exit: break if (timer is not None) and (not self._somethingChanged): if timer._on_timeout(): self._sort() # Keep and resort else: self._timers.pop() # Pop # Instantiate and start the single timer thread # We can do this as long as we do not wait for the threat, and the threat # does not do any imports: # http://docs.python.org/library/threading.html#importing-in-threaded-code theTimerThread = TheTimerThread() theTimerThread.start() class Timer(Signal): """ Timer(interval=1.0, oneshot=True) Timer class. You can bind callbacks to the timer. The timer is fired when it runs out of time. Parameters ---------- interval : number The interval of the timer in seconds. oneshot : bool Whether the timer should do a single shot, or run continuously. """ def __init__(self, interval=1.0, oneshot=True): Signal.__init__(self) # store Timer specific properties self.interval = interval self.oneshot = oneshot # self._timeout = 0 @Property def interval(): """ Set/get the timer's interval in seconds. """ def fget(self): return self._interval def fset(self, value): if not isinstance(value, (int, float)): raise ValueError('interval must be a float or integer.') if value <= 0: raise ValueError('interval must be larger than 0.') self._interval = float(value) return locals() @Property def oneshot(): """ Set/get whether this is a oneshot timer. If not is runs continuously. """ def fget(self): return self._oneshot def fset(self, value): self._oneshot = bool(value) return locals() @property def running(self): """ Get whether the timer is running. """ return self._timeout > 0 def start(self, interval=None, oneshot=None): """ start(interval=None, oneshot=None) Start the timer. If interval or oneshot are not given, their current values are used. """ # set properties? if interval is not None: self.interval = interval if oneshot is not None: self.oneshot = oneshot # put on self._timeout = time.time() + self.interval theTimerThread.add(self) def stop(self): """ stop() Stop the timer from running. """ theTimerThread.discard(self) self._timeout = 0 def _on_timeout(self): """ Method to call when the timer finishes. Called from event-loop-thread. """ # Emit signal self.emit() #print('timer timeout', self.oneshot) # Do we need to stop it now, or restart it if self.oneshot: # This timer instance is removed from the list of Timers # when the timeout is reached. self._timeout = 0 return False else: # keep in the thread self._timeout = time.time() + self.interval return True class YotonApplication(object): """ YotonApplication Represents the yoton application and contains functions for the event system. Multiple instances can be created, they will all operate on the same event queue and share attributes (because these are on the class, not on the instance). One instance of this class is always accesible via yoton.app. For convenience, several of its methods are also accessible directly from the yoton module namespace. """ # Event queues _event_queue = PackageQueue(10000, 'new') # Flag to stop event loop _stop_event_loop = False # Flag to signal whether we are in an event loop # Can be set externally if the event loop is hijacked. _in_event_loop = False # To allow other event loops to embed the yoton event loop _embedding_callback1 = None # The reference _embedding_callback2 = None # Used in post_event def call_later(self, func, timeout=0.0, *args, **kwargs): """ call_later(func, timeout=0.0, *args, **kwargs) Call the given function after the specified timeout. Parameters ---------- func : callable The function to call. timeout : number The time to wait in seconds. If zero, the event is put on the event queue. If negative, the event will be put at the front of the event queue, so that it's processed asap. args : arguments The arguments to call func with. kwargs: keyword arguments. The keyword arguments to call func with. """ # Wrap the object in an event event = Event(func, *args, **kwargs) # Put it in the queue if timeout > 0: self.post_event_later(event, timeout) elif timeout < 0: self.post_event_asap(event) # priority event else: self.post_event(event) def post_event(self, event): """ post_event(events) Post an event to the event queue. """ YotonApplication._event_queue.push(event) # if YotonApplication._embedding_callback2 is not None: YotonApplication._embedding_callback2 = None YotonApplication._embedding_callback1() def post_event_asap(self, event): """ post_event_asap(event) Post an event to the event queue. Handle as soon as possible; putting it in front of the queue. """ YotonApplication._event_queue.insert(event) # if YotonApplication._embedding_callback2 is not None: YotonApplication._embedding_callback2 = None YotonApplication._embedding_callback1() def post_event_later(self, event, delay): """ post_event_later(event, delay) Post an event to the event queue, but with a certain delay. """ event._timeout = time.time() + delay theTimerThread.add(event) # Calls post_event in due time def process_events(self, block=False): """ process_events(block=False) Process all yoton events currently in the queue. This function should be called periodically in order to keep the yoton event system running. block can be False (no blocking), True (block), or a float blocking for maximally 'block' seconds. """ # Reset callback for the embedding event loop YotonApplication._embedding_callback2 = YotonApplication._embedding_callback1 # Process events try: while True: event = YotonApplication._event_queue.pop(block) event.dispatch() block = False # Proceed until there are now more events except PackageQueue.Empty: pass def start_event_loop(self): """ start_event_loop() Enter an event loop that keeps calling yoton.process_events(). The event loop can be stopped using stop_event_loop(). """ # Dont go if we are in an event loop if YotonApplication._in_event_loop: return # Set flags YotonApplication._stop_event_loop = False YotonApplication._in_event_loop = True try: # Keep blocking for 3 seconds so a keyboardinterrupt still works while not YotonApplication._stop_event_loop: self.process_events(3.0) finally: # Unset flag YotonApplication._in_event_loop = False def stop_event_loop(self): """ stop_event_loop() Stops the event loop if it is running. """ if not YotonApplication._stop_event_loop: # Signal stop YotonApplication._stop_event_loop = True # Push an event so that process_events() unblocks def dummy(): pass self.post_event(Event(dummy)) def embed_event_loop(self, callback): """ embed_event_loop(callback) Embed the yoton event loop in another event loop. The given callback is called whenever a new yoton event is created. The callback should create an event in the other event-loop, which should lead to a call to the process_events() method. The given callback should be thread safe. Use None as an argument to disable the embedding. """ YotonApplication._embedding_callback1 = callback YotonApplication._embedding_callback2 = callback # Instantiate an application object app = YotonApplication() pyzo-4.4.3/pyzo/yoton/misc.py0000666000000000000000000004264713131373347014355 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Module yoton.misc Defines a few basic constants, classes and functions. Most importantly, it defines a couple of specific buffer classes that are used for the low-level messaging. """ import sys, time import struct import socket import threading import random from collections import deque # Version dependent defs V2 = sys.version_info[0] == 2 if V2: if sys.platform.startswith('java'): import __builtin__ as D # Jython else: D = __builtins__ if not isinstance(D, dict): D = D.__dict__ bytes = D['str'] str = D['unicode'] xrange = D['xrange'] basestring = basestring # noqa long = long # noqa else: basestring = str # to check if instance is string bytes, str = bytes, str long = int # for the port xrange = range def Property(function): """ Property(function) A property decorator which allows to define fget, fset and fdel inside the function. Note that the class to which this is applied must inherit from object! Code based on an example posted by Walker Hale: http://code.activestate.com/recipes/410698/#c6 """ # Define known keys known_keys = 'fget', 'fset', 'fdel', 'doc' # Get elements for defining the property. This should return a dict func_locals = function() if not isinstance(func_locals, dict): raise RuntimeError('Property function should "return locals()".') # Create dict with kwargs for property(). Init doc with docstring. D = {'doc': function.__doc__} # Copy known keys. Check if there are invalid keys for key in func_locals.keys(): if key in known_keys: D[key] = func_locals[key] else: raise RuntimeError('Invalid Property element: %s' % key) # Done return property(**D) def getErrorMsg(): """ getErrorMsg() Return a string containing the error message. This is usefull, because there is no uniform way to catch exception objects in Python 2.x and Python 3.x. """ # Get traceback info type, value, tb = sys.exc_info() # Store for debugging? if True: sys.last_type = type sys.last_value = value sys.last_traceback = tb # Print err = '' try: if not isinstance(value, (OverflowError, SyntaxError, ValueError)): while tb: err = "line %i of %s." % ( tb.tb_frame.f_lineno, tb.tb_frame.f_code.co_filename) tb = tb.tb_next finally: del tb return str(value) + "\n" + err def slot_hash(name): """ slot_hash(name) Given a string (the slot name) returns a number between 8 and 2**64-1 (just small enough to fit in a 64 bit unsigned integer). The number is used as a slot id. Slots 0-7 are reseved slots. """ fac = 0xd2d84a61 val = 0 offset = 8 for c in name: val += ( val>>3 ) + ( ord(c)*fac ) val += (val>>3) + (len(name)*fac) return offset + (val % (2**64-offset)) def port_hash(name): """ port_hash(name) Given a string, returns a port number between 49152 and 65535. (2**14 (16384) different posibilities) This range is the range for dynamic and/or private ports (ephemeral ports) specified by iana.org. The algorithm is deterministic, thus providing a way to map names to port numbers. """ fac = 0xd2d84a61 val = 0 for c in name: val += ( val>>3 ) + ( ord(c)*fac ) val += (val>>3) + (len(name)*fac) return 49152 + (val % 2**14) def split_address(address): """ split_address(address) -> (protocol, hostname, port) Split address in protocol, hostname and port. The address has the following format: "protocol://hostname:port". If the protocol is omitted, TCP is assumed. The hostname is the name or ip-address of the computer to connect to. One can use "localhost" for a connection that bypasses some network layers (and is not visible from the outside). One can use "publichost" for a connection at the current computers IP address that is visible from the outside. The port can be an integer, or a sting. In the latter case the integer port number is calculated using a hash. One can also use "portname+offset" to specify an integer offset for the port number. """ # Check if not isinstance(address, basestring): raise ValueError("Address should be a string.") if not ":" in address: raise ValueError("Address should be in format 'host:port'.") # Is the protocol explicitly defined (zeromq compatibility) protocol = '' if '://' in address: # Get protocol and stripped address tmp = address.split('://',1) protocol = tmp[0].lower() address = tmp[1] if not protocol: protocol = 'tcp' # Split tmp = address.split(':',1) host, port = tuple(tmp) # Process host if host.lower() == 'localhost': host = '127.0.0.1' if host.lower() == 'publichost': host = 'publichost' + '0' if host.lower().startswith('publichost') and host[10:] in '0123456789': index = int(host[10:]) hostname = socket.gethostname() tmp = socket.gethostbyname_ex(hostname) try: host = tmp[2][index] # This resolves to 127.0.1.1 on some Linuxes except IndexError: raise ValueError('Invalid index (%i) in public host addresses.' % index) # Process port try: port = int(port) except ValueError: # Convert to int, using a hash # Is there an offset? offset = 0 if "+" in port: tmp = port.split('+',1) port, offset = tuple(tmp) try: offset = int(offset) except ValueError: raise ValueError("Invalid offset in address") # Convert port = port_hash(port) + offset # Check port #if port < 1024 or port > 2**16: # raise ValueError("The port must be in the range [1024, 2^16>.") if port > 2**16: raise ValueError("The port must be in the range [0, 2^16>.") # Done return protocol, host, port class UID: """ UID Represents an 8-byte (64 bit) Unique Identifier. """ _last_timestamp = 0 def __init__(self, id=None): # Create nr consisting of two parts if id is None: self._nr = self._get_time_int() << 32 self._nr += self._get_random_int() elif isinstance(id, (int, long)): self._nr = id else: raise ValueError('The id given to UID() should be an int.') def __repr__(self): h = self.get_hex() return "" % (h[:8], h[8:]) def get_hex(self): """ get_hex() Get the hexadecimal representation of this UID. The returned string is 16 characters long. """ h = hex(self._nr) h = h[2:].rstrip('L') h = h.ljust(2*8, '0') return h def get_bytes(self): """ get_bytes() Get the UID as bytes. """ return struct.pack('= self._maxlen def empty(self): """ empty() Returns True if the number of elements is zero right now. Note that in theory, another thread might add an element right after this function returns. """ return len(self) == 0 def push(self, x): """ push(item) Add an item to the queue. If the queue is full, the oldest item in the queue, or the given item is discarted. """ condition = self._condition condition.acquire() try: q = self._q if len(q) < self._maxlen: # Add now and notify any waiting threads in get() q.append(x) condition.notify() # code at wait() procedes else: # Full, either discard or pop (no need to notify) if self._discard_mode == 1: q.popleft() # pop old q.append(x) elif self._discard_mode == 2: pass # Simply do not add finally: condition.release() def insert(self, x): """ insert(x) Insert an item at the front of the queue. A call to pop() will get this item first. This should be used in rare circumstances to give an item priority. This method never causes items to be discarted. """ condition = self._condition condition.acquire() try: self._q.appendleft(x) condition.notify() # code at wait() procedes finally: condition.release() def pop(self, block=True): """ pop(block=True) Pop the oldest item from the queue. If there are no items in the queue: * the calling thread is blocked until an item is available (if block=True, default); * an PackageQueue.Empty exception is raised (if block=False); * the calling thread is blocked for 'block' seconds (if block is a float). """ condition = self._condition condition.acquire() try: q = self._q if not block: # Raise empty if no items in the queue if not len(q): raise self.Empty() elif block is True: # Wait for notify (awakened does not guarantee len(q)>0) while not len(q): condition.wait() elif isinstance(block, float): # Wait if no items, then raise error if still no items if not len(q): condition.wait(block) if not len(q): raise self.Empty() else: raise ValueError('Invalid value for block in PackageQueue.pop().') # Return item return q.popleft() finally: condition.release() def peek(self, index=0): """ peek(index=0) Get an item from the queue without popping it. index=0 gets the oldest item, index=-1 gets the newest item. Note that index access slows to O(n) time in the middle of the queue (due to the undelying deque object). Raises an IndexError if the index is out of range. """ return self._q[index] def __len__(self): return self._q.__len__() def clear(self): """ clear() Remove all items from the queue. """ self._condition.acquire() try: self._q.clear() finally: self._condition.release() class TinyPackageQueue(PackageQueue): """ TinyPackageQueue(N1, N2, discard_mode='old', timeout=1.0) A queue implementation that can be used in blocking and non-blocking mode and allows peeking. The queue has a tiny-size (N1). When this size is reached, a call to push() blocks for up to timeout seconds. The real size (N2) is the same as in the PackageQueue class. The tinysize mechanism can be used to semi-synchronize a consumer and a producer, while still having a small queue and without having the consumer fully block. Uses a deque object for the queue and a threading.Condition for the blocking. """ def __init__(self, N1, N2, discard_mode='old', timeout=1.0): PackageQueue.__init__(self, N2, discard_mode) # Store limit above which the push() method will block self._tinylen = int(N1) # Store timeout self._timeout = timeout def push(self, x): """ push(item) Add an item to the queue. If the queue has >= n1 values, this function will block timeout seconds, or until an item is popped from another thread. """ condition = self._condition condition.acquire() try: q = self._q lq = len(q) if lq < self._tinylen: # We are on safe side. Wake up any waiting threads if queue was empty q.append(x) condition.notify() # pop() at wait() procedes elif lq < self._maxlen: # The queue is above its limit, but not full condition.wait(self._timeout) q.append(x) else: # Full, either discard or pop (no need to notify) if self._discard_mode == 1: q.popleft() # pop old q.append(x) elif self._discard_mode == 2: pass # Simply do not add finally: condition.release() def pop(self, block=True): """ pop(block=True) Pop the oldest item from the queue. If there are no items in the queue: * the calling thread is blocked until an item is available (if block=True, default); * a PackageQueue.Empty exception is raised (if block=False); * the calling thread is blocked for 'block' seconds (if block is a float). """ condition = self._condition condition.acquire() try: q = self._q if not block: # Raise empty if no items in the queue if not len(q): raise self.Empty() elif block is True: # Wait for notify (awakened does not guarantee len(q)>0) while not len(q): condition.wait() elif isinstance(block, float): # Wait if no items, then raise error if still no items if not len(q): condition.wait(block) if not len(q): raise self.Empty() else: raise ValueError('Invalid value for block in PackageQueue.pop().') # Notify if this pop would reduce the length below the threshold if len(q) <= self._tinylen: condition.notifyAll() # wait() procedes # Return item return q.popleft() finally: condition.release() def clear(self): """ clear() Remove all items from the queue. """ self._condition.acquire() try: lq = len(self._q) self._q.clear() if lq >= self._tinylen: self._condition.notify() finally: self._condition.release() pyzo-4.4.3/pyzo/yoton/__init__.py0000666000000000000000000000464513131373313015146 0ustar 00000000000000# -*- coding: utf-8 -*- # flake8: noqa # Copyright (C) 2013, the Pyzo development team # # Yoton is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Yoton is a Python package that provides a simple interface to communicate between two or more processes. Yoton is ... * lightweight * written in pure Python * without dependencies (except Python) * available on Python version >= 2.4, including Python 3 * cross-platform * pretty fast """ # Import stuff from misc and events from yoton.misc import UID, str, bytes from yoton.events import Signal, Timer, app # Inject app function in yoton namespace for convenience call_later = app.call_later process_events = app.process_events start_event_loop = app.start_event_loop stop_event_loop = app.stop_event_loop embed_event_loop = app.embed_event_loop # Import more from yoton.core import Package from yoton.connection import Connection, ConnectionCollection from yoton.connection_tcp import TcpConnection from yoton.context import Context from yoton.clientserver import RequestServer, do_request from yoton.channels import * # Set yoton version __version__ = '2.2' # Define convenience class class SimpleSocket(Context): """ SimpleSocket() A simple socket has an API similar to a BSD socket. This socket sends whole text messages from one end to the other. This class subclasses the Yoton.Context class, which makes setting up this socket very easy. Example ------- # One end s = SimpleSocket() s.bind('localhost:test') s.send("Hi") # Other end s = SimpleSocket() s.connect('localhost:test') print(s.recv()) """ def __init__(self, verbose=False): Context.__init__(self, verbose) # Create channels self._cs = PubChannel(self, 'text') self._cr = SubChannel(self, 'text') def send(self, s): """ send(message) Send a text message. The message is queued and send over the socket by the IO-thread. """ self._cs.send(s) def recv(self, block=None): """ recv(block=None): Read a text from the channel. What was send as one message is always received as one message. If the channel is closed and all messages are read, returns ''. """ return self._cr.recv(block) pyzo-4.4.3/pyzo/yotonloader.py0000666000000000000000000000055113123037260014565 0ustar 00000000000000""" This is a bit awkward, but yoton is a package that is designed to work from Python 2.4 to Python 3.x. As such, it does not have relative imports and must be imported as an absolute package. That is what this module does... """ import os import sys # Import yoton sys.path.insert(0, os.path.dirname(__file__)) import yoton # noqa # Reset sys.path.pop(0) pyzo-4.4.3/pyzo/__init__.py0000666000000000000000000002213213166630157013777 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Pyzo is a cross-platform Python IDE focused on interactivity and introspection, which makes it very suitable for scientific computing. Its practical design is aimed at simplicity and efficiency. Pyzo is written in Python 3 and Qt. Binaries are available for Windows, Linux, and Mac. For questions, there is a discussion group. **Two components + tools** Pyzo consists of two main components, the editor and the shell, and uses a set of pluggable tools to help the programmer in various ways. Some example tools are source structure, project manager, interactive help, and workspace. **Some key features** * Powerful *introspection* (autocompletion, calltips, interactive help) * Allows various ways to *run code interactively* or to run a file as a script. * The shells runs in a *subprocess* and can therefore be interrupted or killed. * *Multiple shells* can be used at the same time, and can be of different Python versions (from v2.4 to 3.x, including pypy) * Support for using several *GUI toolkits* interactively: PySide, PyQt4, wx, fltk, GTK, Tk. * Run IPython shell or native shell. * *Full Unicode support* in both editor and shell. * Various handy *tools*, plus the ability to make your own. * Matlab-style *cell notation* to mark code sections (by starting a line with '##'). """ # Set version number __version__ = '4.4.3' import os import sys import locale import traceback # Check Python version if sys.version < '3': raise RuntimeError('Pyzo requires Python 3.x to run.') # Make each OS find platform plugins etc. if hasattr(sys, 'frozen') and sys.frozen: app_dir = os.path.dirname(sys.executable) if sys.platform.startswith('win'): os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = app_dir if sys.platform.startswith('linux'): os.environ['QT_XKB_CONFIG_ROOT'] = '.' os.environ['FONTCONFIG_FILE'] = os.path.join(app_dir, 'source/pyzo/resources', 'fonts/linux_fonts.conf') # Import yoton as an absolute package from pyzo import yotonloader # noqa from pyzo.util import paths # If there already is an instance of Pyzo, and the user is trying an # Pyzo command, we should send the command to the other process and quit. # We do this here, were we have not yet loaded Qt, so we are very light. from pyzo.core import commandline if commandline.is_our_server_running(): print('Started our command server') else: # Handle command line args now res = commandline.handle_cmd_args() if res: print(res) sys.exit() else: # No args, proceed with starting up print('Our command server is *not* running') from pyzo.util import zon as ssdf # zon is ssdf-light from pyzo.util.qt import QtCore, QtGui, QtWidgets # Import language/translation tools from pyzo.util._locale import translate, setLanguage # noqa # Set environ to let kernel know some stats about us os.environ['PYZO_PREFIX'] = sys.prefix _is_pyqt4 = hasattr(QtCore, 'PYQT_VERSION_STR') os.environ['PYZO_QTLIB'] = 'PyQt4' if _is_pyqt4 else 'PySide' class MyApp(QtWidgets.QApplication): """ So we an open .py files on OSX. OSX is smart enough to call this on the existing process. """ def event(self, event): if isinstance(event, QtGui.QFileOpenEvent): fname = str(event.file()) if fname and fname != 'pyzo': sys.argv[1:] = [] sys.argv.append(fname) res = commandline.handle_cmd_args() if not commandline.is_our_server_running(): print(res) sys.exit() return QtWidgets.QApplication.event(self, event) if not sys.platform.startswith('darwin'): MyApp = QtWidgets.QApplication # noqa ## Install excepthook # In PyQt5 exceptions in Python will cuase an abort # http://pyqt.sourceforge.net/Docs/PyQt5/incompatibilities.html def pyzo_excepthook(type, value, tb): out = 'Uncaught Python exception: ' + str(value) + '\n' out += ''.join(traceback.format_list(traceback.extract_tb(tb))) out += '\n' sys.stderr.write(out) sys.excepthook = pyzo_excepthook ## Define some functions # todo: move some stuff out of this module ... def getResourceDirs(): """ getResourceDirs() Get the directories to the resources: (pyzoDir, appDataDir). Also makes sure that the appDataDir has a "tools" directory and a style file. """ # # Get root of the Pyzo code. If frozen its in a subdir of the app dir # pyzoDir = paths.application_dir() # if paths.is_frozen(): # pyzoDir = os.path.join(pyzoDir, 'source') pyzoDir = os.path.abspath(os.path.dirname(__file__)) if '.zip' in pyzoDir: raise RuntimeError('The Pyzo package cannot be run from a zipfile.') # Get where the application data is stored (use old behavior on Mac) appDataDir = paths.appdata_dir('pyzo', roaming=True, macAsLinux=True) # Create tooldir if necessary toolDir = os.path.join(appDataDir, 'tools') if not os.path.isdir(toolDir): os.mkdir(toolDir) return pyzoDir, appDataDir def resetConfig(preserveState=True): """ resetConfig() Replaces the config file with the default and prevent Pyzo from storing its config on the next shutdown. """ # Get filenames configFileName1 = os.path.join(pyzoDir, 'resources', 'defaultConfig.ssdf') configFileName2 = os.path.join(appDataDir, 'config.ssdf') # Read, edit, write tmp = ssdf.load(configFileName1) if preserveState: tmp.state = config.state ssdf.save(configFileName2, tmp) global _saveConfigFile _saveConfigFile = False print("Replaced config file. Restart Pyzo to revert to the default config.") def loadConfig(defaultsOnly=False): """ loadConfig(defaultsOnly=False) Load default configuration file and that of the user (if it exists). Any missing fields in the user config are set to the defaults. """ # Function to insert names from one config in another def replaceFields(base, new): for key in new: if key in base and isinstance(base[key], ssdf.Struct): replaceFields(base[key], new[key]) else: base[key] = new[key] # Reset our pyzo.config structure ssdf.clear(config) # Load default and inject in the pyzo.config fname = os.path.join(pyzoDir, 'resources', 'defaultConfig.ssdf') defaultConfig = ssdf.load(fname) replaceFields(config, defaultConfig) # Platform specific keybinding: on Mac, Ctrl+Tab (actually Cmd+Tab) is a system shortcut if sys.platform == 'darwin': config.shortcuts2.view__select_previous_file = 'Alt+Tab,' # Load user config and inject in pyzo.config fname = os.path.join(appDataDir, "config.ssdf") if os.path.isfile(fname): try: userConfig = ssdf.load(fname) replaceFields(config, userConfig) except Exception: t = 'Error while reading config file %r, maybe its corrupt?' print(t % fname) raise def saveConfig(): """ saveConfig() Save all configureations to file. """ # Let the editorStack save its state if editors: editors.saveEditorState() # Let the main window save its state if main: main.saveWindowState() # Store config if _saveConfigFile: ssdf.save( os.path.join(appDataDir, "config.ssdf"), config ) def start(): """ Run Pyzo. """ # Do some imports from pyzo.core import pyzoLogging # noqa - to start logging asap from pyzo.core.main import MainWindow # Apply users' preferences w.r.t. date representation etc # this is required for e.g. strftime("%c") # Just using '' does not seem to work on OSX. Thus # this odd loop. #locale.setlocale(locale.LC_ALL, "") for x in ('', 'C', 'en_US', 'en_US.utf8', 'en_US.UTF-8'): try: locale.setlocale(locale.LC_ALL, x) break except locale.Error: pass # Set to be aware of the systems native colors, fonts, etc. QtWidgets.QApplication.setDesktopSettingsAware(True) # Instantiate the application QtWidgets.qApp = MyApp(sys.argv) # QtWidgets.QApplication([]) # Choose language, get locale appLocale = setLanguage(config.settings.language) # Create main window, using the selected locale MainWindow(None, appLocale) # Enter the main loop QtWidgets.qApp.exec_() ## Init # List of names that are later overriden (in main.py) editors = None # The editor stack instance shells = None # The shell stack instance main = None # The mainwindow icon = None # The icon parser = None # The source parser status = None # The statusbar (or None) # Get directories of interest pyzoDir, appDataDir = getResourceDirs() # Whether the config file should be saved _saveConfigFile = True # Create ssdf in module namespace, and fill it config = ssdf.new() loadConfig() # Init default style name (set in main.restorePyzoState()) defaultQtStyleName = '' pyzo-4.4.3/pyzo/__main__.py0000666000000000000000000000303213124664257013760 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ Pyzo __main__ module This module takes enables starting Pyzo via either "python3 -m pyzo" or "python3 path/to/pyzo". In the first case it simply imports pyzo. In the latter case, that import will generally fail, in which case the parent directory is added to sys.path and the import is tried again. Then "pyzo.start()" is called. """ import os import sys # Imports that are maybe not used in Pyzo, but are/can be in the tools. # Import them now, so they are available in the frozen app. import shutil # noqa if hasattr(sys, 'frozen') and sys.frozen: app_dir = os.path.dirname(os.path.abspath(sys.executable)) # Enable loading from source sys.path.insert(0, os.path.join(app_dir, 'source')) sys.path.insert(0, os.path.join(app_dir, 'source/more')) # Import import pyzo else: # Try importing try: import pyzo except ImportError: # Very probably run as a script, either the package or the __main__ # directly. Add parent directory to sys.path and try again. thisDir = os.path.abspath(os.path.dirname(__file__)) sys.path.insert(0, os.path.split(thisDir)[0]) try: import pyzo except ImportError: raise ImportError('Could not import Pyzo in either way.') def main(): pyzo.start() if __name__ == '__main__': main() pyzo-4.4.3/pyzo.appdata.xml0000666000000000000000000000331413164415365014022 0ustar 00000000000000 pyzo.desktop CC0-1.0 BSD-2-Clause Pyzo pyzo A Python IDE aimed at simplicity and interactivity

Pyzo is a cross-platform Python IDE focused on interactivity and introspection, which makes it very suitable for scientific computing. Its practical design is aimed at simplicity and efficiency.

It consists of two main components, the editor and the shell, and uses a set of pluggable tools to help the programmer in various ways. Some example tools are source structure, project manager, interactive help, workspace ...

Pyzo is written in (pure) Python 3 and uses the Qt GUI toolkit. Binaries are provided for all major operating system. After installing Pyzo, it can be used to execute code on any Python version available on your system (Python 2.4 - 3.x, including Pypy).

http://www.pyzo.org/_static/screenshots/screen-default-16-9.png http://www.pyzo.org/_static/screenshots/screen00_win.png http://pyzo.org https://github.com/pyzo/pyzo/wiki/Translations almar.klein@gmail.com
pyzo-4.4.3/pyzo.egg-info/0000777000000000000000000000000013166630335013356 5ustar 00000000000000pyzo-4.4.3/pyzo.egg-info/dependency_links.txt0000666000000000000000000000000113166630334017423 0ustar 00000000000000 pyzo-4.4.3/pyzo.egg-info/entry_points.txt0000666000000000000000000000005513166630334016653 0ustar 00000000000000[console_scripts] pyzo = pyzo.__main__:main pyzo-4.4.3/pyzo.egg-info/not-zip-safe0000666000000000000000000000000212771467276015622 0ustar 00000000000000 pyzo-4.4.3/pyzo.egg-info/pbr.json0000666000000000000000000000005712772713763015047 0ustar 00000000000000{"is_release": false, "git_version": "3335d69"}pyzo-4.4.3/pyzo.egg-info/PKG-INFO0000666000000000000000000000461013166630334014453 0ustar 00000000000000Metadata-Version: 1.1 Name: pyzo Version: 4.4.3 Summary: the Python IDE for scientific computing Home-page: http://www.pyzo.org Author: Almar Klein Author-email: almar.klein@gmail.com License: (new) BSD Description: Pyzo is a cross-platform Python IDE focused on interactivity and introspection, which makes it very suitable for scientific computing. Its practical design is aimed at simplicity and efficiency. Pyzo is written in Python 3 and Qt. Binaries are available for Windows, Linux, and Mac. For questions, there is a discussion group. **Two components + tools** Pyzo consists of two main components, the editor and the shell, and uses a set of pluggable tools to help the programmer in various ways. Some example tools are source structure, project manager, interactive help, and workspace. **Some key features** * Powerful *introspection* (autocompletion, calltips, interactive help) * Allows various ways to *run code interactively* or to run a file as a script. * The shells runs in a *subprocess* and can therefore be interrupted or killed. * *Multiple shells* can be used at the same time, and can be of different Python versions (from v2.4 to 3.x, including pypy) * Support for using several *GUI toolkits* interactively: PySide, PyQt4, wx, fltk, GTK, Tk. * Run IPython shell or native shell. * *Full Unicode support* in both editor and shell. * Various handy *tools*, plus the ability to make your own. * Matlab-style *cell notation* to mark code sections (by starting a line with '##'). Keywords: Python interactive IDE Qt science computing Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Science/Research Classifier: Intended Audience :: Education Classifier: Intended Audience :: Developers Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: Python :: 3 Provides: pyzo pyzo-4.4.3/pyzo.egg-info/SOURCES.txt0000666000000000000000000002352013166630334015243 0ustar 00000000000000Info.plist MANIFEST.in README.md RELEASE_NOTES.md pyzo.appdata.xml pyzolauncher.py setup.cfg setup.py doc/.requirements doc/Makefile doc/conf.py doc/index.rst doc/make.bat doc/codeeditor/index.rst doc/yoton/channels.rst doc/yoton/clientserver.rst doc/yoton/connection.rst doc/yoton/context.rst doc/yoton/events.rst doc/yoton/examples.rst doc/yoton/experiments.rst doc/yoton/index.rst doc/yoton/internals.rst doc/yoton/overview.rst doc/yoton/images/yoton_abstract.png doc/yoton/images/yoton_chat.png doc/yoton/images/yoton_kernel.png doc/yoton/images/yoton_multiple_reqrep.png doc/yoton/images/yoton_simple.png pyzo/__init__.py pyzo/__main__.py pyzo/contributors.txt pyzo/license.txt pyzo/yotonloader.py pyzo.egg-info/PKG-INFO pyzo.egg-info/SOURCES.txt pyzo.egg-info/dependency_links.txt pyzo.egg-info/entry_points.txt pyzo.egg-info/not-zip-safe pyzo.egg-info/pbr.json pyzo.egg-info/top_level.txt pyzo/codeeditor/__init__.py pyzo/codeeditor/_test.py pyzo/codeeditor/base.py pyzo/codeeditor/highlighter.py pyzo/codeeditor/manager.py pyzo/codeeditor/misc.py pyzo/codeeditor/qt.py pyzo/codeeditor/style.py pyzo/codeeditor/textutils.py pyzo/codeeditor/extensions/__init__.py pyzo/codeeditor/extensions/appearance.py pyzo/codeeditor/extensions/autocompletion.py pyzo/codeeditor/extensions/behaviour.py pyzo/codeeditor/extensions/calltip.py pyzo/codeeditor/parsers/__init__.py pyzo/codeeditor/parsers/c_parser.py pyzo/codeeditor/parsers/cython_parser.py pyzo/codeeditor/parsers/python_parser.py pyzo/codeeditor/parsers/tokens.py pyzo/core/__init__.py pyzo/core/about.py pyzo/core/assistant.py pyzo/core/baseTextCtrl.py pyzo/core/codeparser.py pyzo/core/commandline.py pyzo/core/compactTabWidget.py pyzo/core/editor.py pyzo/core/editorTabs.py pyzo/core/history.py pyzo/core/icons.py pyzo/core/kernelbroker.py pyzo/core/main.py pyzo/core/menu.py pyzo/core/pyzoLogging.py pyzo/core/shell.py pyzo/core/shellInfoDialog.py pyzo/core/shellStack.py pyzo/core/splash.py pyzo/pyzokernel/__init__.py pyzo/pyzokernel/_nope.py pyzo/pyzokernel/debug.py pyzo/pyzokernel/guiintegration.py pyzo/pyzokernel/guisupport.py pyzo/pyzokernel/interpreter.py pyzo/pyzokernel/introspection.py pyzo/pyzokernel/magic.py pyzo/pyzokernel/pipper.py pyzo/pyzokernel/start.py pyzo/resources/defaultConfig.ssdf pyzo/resources/pyzo.desktop pyzo/resources/style_scintilla.ssdf pyzo/resources/style_solarizeddark.ssdf pyzo/resources/style_solarizedlight.ssdf pyzo/resources/tutorial.py pyzo/resources/appicons/py.icns pyzo/resources/appicons/py.ico pyzo/resources/appicons/pyzologo.icns pyzo/resources/appicons/pyzologo.ico pyzo/resources/appicons/pyzologo128.bmp pyzo/resources/appicons/pyzologo128.png pyzo/resources/appicons/pyzologo16.png pyzo/resources/appicons/pyzologo256.png pyzo/resources/appicons/pyzologo32.png pyzo/resources/appicons/pyzologo48.bmp pyzo/resources/appicons/pyzologo48.png pyzo/resources/appicons/pyzologo64.png pyzo/resources/fonts/DejaVuSansMono-Bold.ttf pyzo/resources/fonts/DejaVuSansMono-BoldOblique.ttf pyzo/resources/fonts/DejaVuSansMono-Oblique.ttf pyzo/resources/fonts/DejaVuSansMono.ttf pyzo/resources/fonts/SourceCodePro-Bold.otf pyzo/resources/fonts/SourceCodePro-Regular.otf pyzo/resources/fonts/linux_fonts.conf pyzo/resources/icons/README.md pyzo/resources/icons/accept.png pyzo/resources/icons/add.png pyzo/resources/icons/application.png pyzo/resources/icons/application_add.png pyzo/resources/icons/application_cascade.png pyzo/resources/icons/application_delete.png pyzo/resources/icons/application_double.png pyzo/resources/icons/application_edit.png pyzo/resources/icons/application_go.png pyzo/resources/icons/application_lightning.png pyzo/resources/icons/application_link.png pyzo/resources/icons/application_view_tile.png pyzo/resources/icons/arrow_left.png pyzo/resources/icons/arrow_redo.png pyzo/resources/icons/arrow_refresh.png pyzo/resources/icons/arrow_right.png pyzo/resources/icons/arrow_rotate_anticlockwise.png pyzo/resources/icons/arrow_rotate_clockwise.png pyzo/resources/icons/arrow_undo.png pyzo/resources/icons/bug.png pyzo/resources/icons/bug_delete.png pyzo/resources/icons/bug_error.png pyzo/resources/icons/bullet_yellow.png pyzo/resources/icons/cancel.png pyzo/resources/icons/cog.png pyzo/resources/icons/comment_add.png pyzo/resources/icons/comment_delete.png pyzo/resources/icons/comments.png pyzo/resources/icons/cross.png pyzo/resources/icons/cut.png pyzo/resources/icons/delete.png pyzo/resources/icons/disk.png pyzo/resources/icons/disk_multiple.png pyzo/resources/icons/drive.png pyzo/resources/icons/error_add.png pyzo/resources/icons/find.png pyzo/resources/icons/flag_green.png pyzo/resources/icons/folder.png pyzo/resources/icons/folder_page.png pyzo/resources/icons/help.png pyzo/resources/icons/information.png pyzo/resources/icons/keyboard.png pyzo/resources/icons/layout.png pyzo/resources/icons/link.png pyzo/resources/icons/magifier_zoom_out.png pyzo/resources/icons/magnifier.png pyzo/resources/icons/magnifier_zoom_in.png pyzo/resources/icons/monitor.png pyzo/resources/icons/page_add.png pyzo/resources/icons/page_delete.png pyzo/resources/icons/page_save.png pyzo/resources/icons/page_white_copy.png pyzo/resources/icons/page_white_gear.png pyzo/resources/icons/page_white_text.png pyzo/resources/icons/paste_plain.png pyzo/resources/icons/plugin.png pyzo/resources/icons/pyzo_application_eraser.png pyzo/resources/icons/pyzo_application_refresh.png pyzo/resources/icons/pyzo_application_shell.png pyzo/resources/icons/pyzo_application_wrench.png pyzo/resources/icons/pyzo_debug_continue.png pyzo/resources/icons/pyzo_debug_next.png pyzo/resources/icons/pyzo_debug_quit.png pyzo/resources/icons/pyzo_debug_return.png pyzo/resources/icons/pyzo_debug_step.png pyzo/resources/icons/pyzo_disk_as.png pyzo/resources/icons/pyzo_filter.png pyzo/resources/icons/pyzo_folder_hg.png pyzo/resources/icons/pyzo_folder_parent.png pyzo/resources/icons/pyzo_folder_svn.png pyzo/resources/icons/pyzo_overlay_disk.png pyzo/resources/icons/pyzo_overlay_hg.png pyzo/resources/icons/pyzo_overlay_link.png pyzo/resources/icons/pyzo_overlay_star.png pyzo/resources/icons/pyzo_overlay_svn.png pyzo/resources/icons/pyzo_overlay_thumbnail.png pyzo/resources/icons/pyzo_page_delete_all.png pyzo/resources/icons/pyzo_page_white.png pyzo/resources/icons/pyzo_page_white_dirty.png pyzo/resources/icons/pyzo_page_white_py.png pyzo/resources/icons/pyzo_page_white_pyx.png pyzo/resources/icons/pyzo_plugin_refresh.png pyzo/resources/icons/pyzo_run_cell.png pyzo/resources/icons/pyzo_run_file.png pyzo/resources/icons/pyzo_run_file_script.png pyzo/resources/icons/pyzo_run_lines.png pyzo/resources/icons/pyzo_run_mainfile.png pyzo/resources/icons/pyzo_run_mainfile_script.png pyzo/resources/icons/pyzo_star2.png pyzo/resources/icons/pyzo_star3.png pyzo/resources/icons/report.png pyzo/resources/icons/script.png pyzo/resources/icons/star.png pyzo/resources/icons/style.png pyzo/resources/icons/sum.png pyzo/resources/icons/text_align_justify.png pyzo/resources/icons/text_align_right.png pyzo/resources/icons/text_indent.png pyzo/resources/icons/text_indent_remove.png pyzo/resources/icons/text_padding_right.png pyzo/resources/icons/text_replace.png pyzo/resources/icons/tick.png pyzo/resources/icons/wand.png pyzo/resources/icons/wrench.png pyzo/resources/icons/wrench_orange.png pyzo/resources/images/pyzo_editor.png pyzo/resources/images/pyzo_run1.png pyzo/resources/images/pyzo_shell1.png pyzo/resources/images/pyzo_shell2.png pyzo/resources/images/pyzo_tools1.png pyzo/resources/images/pyzo_tools2.png pyzo/resources/images/pyzo_two_components.png pyzo/resources/translations/notes_on_translating.txt pyzo/resources/translations/pyzo_ca_ES.tr pyzo/resources/translations/pyzo_ca_ES.tr.qm pyzo/resources/translations/pyzo_de_DE.tr pyzo/resources/translations/pyzo_de_DE.tr.qm pyzo/resources/translations/pyzo_es_ES.tr pyzo/resources/translations/pyzo_es_ES.tr.qm pyzo/resources/translations/pyzo_fr_FR.tr pyzo/resources/translations/pyzo_fr_FR.tr.qm pyzo/resources/translations/pyzo_nl_NL.tr pyzo/resources/translations/pyzo_nl_NL.tr.qm pyzo/resources/translations/pyzo_pt_BR.tr pyzo/resources/translations/pyzo_pt_BR.tr.qm pyzo/resources/translations/pyzo_pt_PT.tr pyzo/resources/translations/pyzo_pt_PT.tr.qm pyzo/resources/translations/pyzo_ru_RU.tr pyzo/resources/translations/pyzo_ru_RU.tr.qm pyzo/resources/translations/pyzo_sk_SK.tr pyzo/resources/translations/pyzo_sk_SK.tr.qm pyzo/resources/translations/pyzo_zh_CN.tr pyzo/resources/translations/pyzo_zh_CN.tr.qm pyzo/resources/translations/pyzo_zh_TW.tr pyzo/resources/translations/pyzo_zh_TW.tr.qm pyzo/tools/__init__.py pyzo/tools/pyzoHistoryViewer.py pyzo/tools/pyzoInteractiveHelp.py pyzo/tools/pyzoLogger.py pyzo/tools/pyzoSourceStructure.py pyzo/tools/pyzoWebBrowser.py pyzo/tools/pyzoWorkspace.py pyzo/tools/pyzoFileBrowser/__init__.py pyzo/tools/pyzoFileBrowser/browser.py pyzo/tools/pyzoFileBrowser/importwizard.py pyzo/tools/pyzoFileBrowser/proxies.py pyzo/tools/pyzoFileBrowser/tasks.py pyzo/tools/pyzoFileBrowser/tree.py pyzo/tools/pyzoFileBrowser/utils.py pyzo/util/__init__.py pyzo/util/_locale.py pyzo/util/bootstrapconda.py pyzo/util/paths.py pyzo/util/pyzowizard.py pyzo/util/zon.py pyzo/util/interpreters/__init__.py pyzo/util/interpreters/inwinreg.py pyzo/util/interpreters/pythoninterpreter.py pyzo/util/qt/QtCore.py pyzo/util/qt/QtGui.py pyzo/util/qt/QtHelp.py pyzo/util/qt/QtPrintSupport.py pyzo/util/qt/QtWidgets.py pyzo/util/qt/__init__.py pyzo/util/qt/_version.py pyzo/util/qt/uic.py pyzo/util/qt/_patch/__init__.py pyzo/util/qt/_patch/qcombobox.py pyzo/util/qt/_patch/qheaderview.py pyzo/yoton/__init__.py pyzo/yoton/clientserver.py pyzo/yoton/connection.py pyzo/yoton/connection_itc.py pyzo/yoton/connection_tcp.py pyzo/yoton/context.py pyzo/yoton/core.py pyzo/yoton/events.py pyzo/yoton/misc.py pyzo/yoton/channels/__init__.py pyzo/yoton/channels/channels_base.py pyzo/yoton/channels/channels_file.py pyzo/yoton/channels/channels_pubsub.py pyzo/yoton/channels/channels_reqrep.py pyzo/yoton/channels/channels_state.py pyzo/yoton/channels/message_types.pypyzo-4.4.3/pyzo.egg-info/top_level.txt0000666000000000000000000000000513166630334016102 0ustar 00000000000000pyzo pyzo-4.4.3/pyzolauncher.py0000666000000000000000000000164412665666437014004 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (C) 2016, the Pyzo development team # # Pyzo is distributed under the terms of the (new) BSD License. # The full license can be found in 'license.txt'. """ pyzolauncher.py script This is a script used to startup Pyzo. Added for convenience. Pyzo can be installed as a package, but it does not have to. You can start Pyzo in a few different ways: * execute this script (pyzolauncher.py) * execute the pyzo directory (Python will seek out pyzo/__main__.py) * execute the pyzo package ("python -m pyzo") Only in the latter must Pyzo be installed. """ import sys # faulthandler helps debugging hard crashes, it is included in py3.3 try: if sys.executable.lower().endswith('pythonw.exe'): raise ImportError('Dont use faulthandler in pythonw.exe') import faulthandler faulthandler.enable() except ImportError: pass import pyzo pyzo.start() pyzo-4.4.3/README.md0000666000000000000000000000300712763275614012151 0ustar 00000000000000# Pyzo - The Interactive editor for scientific Python Main website: [pyzo.org](http://pyzo.org) ### Description Pyzo is a cross-platform Python IDE focused on interactivity and introspection, which makes it very suitable for scientific computing. Its practical design is aimed at simplicity and efficiency. It consists of two main components, the editor and the shell, and uses a set of pluggable tools to help the programmer in various ways. Some example tools are source structure, project manager, interactive help, workspace ... Pyzo is written in (pure) Python 3 and uses the Qt GUI toolkit. Binaries are provided for all major operating system. After installing Pyzo, it can be used to execute code on any Python version available on your system (Python 2.4 - 3.x, including Pypy). ### Installation Pyzo runs on Python3 and needs Pyside or PyQt4. On a modern OS these should be easy to obtain. One can then install Pyzo with `sudo python3 -m pip install pyzo` We also provide binaries for Windows, Linux and OS X. ### License Pyzo is free and open source. BSD licensed. ### About us The development of Pyzo is coordinated by Almar Klein. See contributors.txt for a complete list of people who helped develop Pyzo. ### Contributions If you want to help out, create a pull request or start a discussion in our mailing list. ### More information * main website: http://pyzo.org * mailing list: http://groups.google.com/group/pyzo * code repository: http://github.com/pyzo/pyzo * wiki: http://github.com/pyzo/pyzo/wiki pyzo-4.4.3/RELEASE_NOTES.md0000666000000000000000000005401213156167460013242 0ustar 00000000000000# Release notes ### Version 4.4.2 (13-09-2017) * Fix that running file as script would not work if the IPython flag was never turned on (#498) * Fix delete action in filer browser (#505) * Nicer appearance and coloring of source structure content (thanks to WangYi) * more translations (e.g. for tool titles) (thanks to WangYi) ### Version 4.4.1 (14-07-2017) Fixes: * Fix that code editor fonts are oblique on some OS X machines. * Restore broken introspection (for interactive help and calltips). * Calltips now also work for builtins. * Restore ability to run Python kernels as low as version 2.4. * More updates to translations. Small improvements: * Added config option to change directory when executing file (not as script). * Use `%run -i` to run script when ipython console is used (Diti24). * Added another shell context menu entry to change directory. * Added a config option to make the shell widget floatable. ### Version 4.4 (29-06-2017) This release represents a large amount of new features and improvements: Editor and shell: * Tab titles also show part of the filename path if multiple files with the same name are present. * The command history is now shared between all shells, and saved between sessions. * Running selection will add to command history, also more options for command history tool. * The Autocompletion accept keys can now be configured via the menu. * The Autocompletion has more modes: automatic popup and popup only when pressing tab. * Ctrl+up/down now "scrolls" the editor. * Menu action (and shortcut Ctr+Shift+up/down) to move (selected) lines up/down (with help from Yehuda Davis). * Menu action (and shortcut) to duplicate lines. * Breakpoints can now be toggled with a menu action (and shortcut). * Breakpoints now move along when lines are inserted above it. * Fixed that Shift+Enter inserted a newline. * Menu action in editor context menu and editor tab menu to open the directory in the file browser. * Menu action in shell context menu to open current directory in file browser. * Menu action in shell context menu to change the current directory to match the file browser. * Fixes to shell help system (#422). * Add new `install xx`, `update xx` and `remove xx` commands as shortcuts for conda commands. Tools: * Fix file browser's data import wizard. * The file browser now logically sorts files and directories. * The file browser can now show hidden files (can be turned off by filtering with `!hidden`). * The `if __name__ == '__main__':` thingy is now shown in the source structure. * The source structure tool has back/forward buttons (i.e. you can click on a method and then jump back). * Methods and builtin functions are now also hidden in workspace when "hide function" filter is used. Other: * Added GUI integration support for PySide2 (by Rob Reilink). * Improved support for high resolution (retina) displays (by Seb Jachec). * Fixed the sometimes blurry icons. * Fix PDF printing and include line numbers. * Fix that keyboard shortcuts were not working on Linux with Qt5 (#470). * More text is now translatable (and more translations have been made, though not yet in all languages). * Fix that conda' errors messages were not shown. * And many more small tweaks and fixes ... ### Version 4.3.1 (28-09-2016) * Fix in workspace variable introspection * Fix severe regression in PyQt4 GUI integration * Add PyQt5 GUI integration ### Version 4.3 (27-09-2016) * Pyzo can now run on PyQt5 (in addition to PyQt4 and PySide) * Remove dependency on pyzolib * Reduced scrolling while stepping through the debugger (#417) * Updated translations * Fixed regression in call tip (#421) * The binaries for Linux have improved (fonts, load time, #423, #414) * Numeric items in workspace are correctly sorted (#428) * Improved Tornado integration * And more ... ### Version 4.2 (27-06-2016) No big refactoring, but a few improvements and fixes: * Much improved brace matching (skips strings, shows missing/wrong) (thanks to Yann Salmon) * Brace matching can be configured * `conda install` looks in `conda-forge` by default * Add `isatty()` to standard file streams to fix interop with pip (#400) * Fixed a breakpoint bug * Fixed TypeError on Fedora #393 * Added freedesktop file (thanks to Ghis Vaillant) * Passed sys.argv to the QApplication, which helps Linux associate the program (thanks to Mark Harfouche) * Tornado event loop is now integrated by running Tornado's event loop for real. The downside is that events cannot be processed during debugging, but at least there are no weird delays now (Tornado was not meant to run the way we ran it). * Fix "open outside Pyzo" on Windows when a path has spaces in it. * Workspace can hide private variables ### Version 4.1 (04-04-2016) First announced release after merging the Pyzo and IEP projects. * Improved dialog that detects an interpreter and guides the user. * Detect interpreters relative to the Pyzo executable. This way Pyzo can be shipped along with a prebuild anaconda (#382). * Fixed bug that made Pyzo slow on new Linux kernel (#381). * New translations for Traditional Chinese. * Ability to start Jupyter notebook from shell and from file browser tool. * Clicking on a syntax error for code run as cell brings you to correct line. * Breakpoints work in cells and code an as selected lines. * Conda command no longer spams the shell with loads of messages. * Shell can deal with clearline using `\r`, e.g. used in conda commands. ### Version 4.0 We smashed the Pyzo and IEP projects together into a single project: * The name is Pyzo * The logo is the one from IEP * It is not longer a distribution, but a lightweight IDE (like IEP was) that helps the user install a (miniconda) environment. Other improvements: * Cells can be written as in Spyder: `# %% this is a cell`. * Autodetection of a GUI toolkit. * Minor tweaks here and there. ### Version 3.6 (18-02-2015) (Simultaneously released version 3.6.1: a hotfix for OSX) * Support for Jython! (issue #323) * New tool: command history * Smarter copying and pasting * Experimental support for integrating the Tornado event loop * IEP is available in Debian! (issue #337) Thanks Ghis! * issue #311: Zoom level of code text is inconsistent between architectures * issue #314: Unable to launch after installing from source. * issue #330: Comment line puts # on correctly indented position * issue #172: Filter Workspace view * issue #317: magic open command should go to appropriate line * issue #315: sys.stdout has no attribute 'errors' * issue #332: Cannot save file when File browser shows drives list on Windows * issue #325: multiprocessing.pool doesn't work * issue #322: Cannot run IEP form IEP * issue #318: Line ending removed from last line upon save Editor * issue #306: Easier to modify syntax style Menu / settings * issue #305: cursor jumping to file beginning upon save * pr #16: Fix the import wizard on PyQt4 (Scott Logan) * pr #13: Allowing shell window to float is useful in dual screen mode (Laurent Signac) * pr #14: Fix previous tab selection without history (Scott Logan) * pr #11: cell navigation (Jan Müller) ### Version 3.5 (01-07-2014) (This release ended up in Pyzo 2014a, but binaries for IEP itself were not build) * issue 301: IEP can now be associated with .py files (on Windows and OSX) * Running `iep_exe foo.py` will open the file in IEP (using already running process if possible) (see issue #301) * issue #297: Enable keeping processing GUI events while debugging (via IEP_PROCESS_EVENTS_WHILE_DEBUGGING environment arg) * issue #298: Add config option to remove trailing whitespace on save * issue #295: Add goto definition (Thanks to Jason Sexauer) * issue #292: Add config option to set text justification width * issue #302: Fix in Spanish translation * issue #300: Created AppData file for package managers * issue #296: Easier use of timeit in non-IPython mode * issue #294: On startup focus on editor * issue #288: Installing IEP via pip now also installs script to launch IEP * issue #290: allow executing startup code *after* importing the GUI * issue #287: Starup script got cleared in shell config * Added functionality for easily creating screenshots (`iep.screenshot()` in logger shell) * The Windows installer does not need admin priveleges if installing in the right place. ### Version 3.4 (02-04-2014) This version is marked by many improvements to the shell. Most notably, we now have *IPython integration*! Further, there are more ways to customize the shell (e.g. define command line and environment args), there is support for coloured text, you can click on filenames in tracebacks to open them, and it just behaves better overall. Also, many improvements have been made to our debug system. Improvements/fixes related to the shell and kernel: * issue #136: Embed IPython shell * Shell can now deal with ANSI control chars to display color text * issue #261: Allow custom code to execute on startup * Code/script to run at startup runs before GUI is integrated (fixes issue #264) * issue #268: Allow specifying extra environment arguments in shell config * issue #239: Enable passing command line arguments (sys.argv) * issue #262: Clicking in shell to focus to it causes scrolling to last line to stop * issue #250: Prevent early exit for programs entering PySide event loop * issue #240: On Windows allow kernel to start also if 'cmd' is not recognized. * issue #275: Paths in shell output (e.g. tracebacks) can be clicked on to open the file at the corresponding file number. * issue #278: Fixed that on OSX Maverick, App Nap made the IEP kernel slow Other notable improvements: * issue #249: Unclear where to place breakpoints * issue #241: Debug-stepping in a new module change current file to there * issue #266: Editor should auto-scroll to breakpoint when it becomes active * issue #252: Fix that cursor is gone after dragging in a file (Linux) * issue #267: Improved terminology for different RUN actions * issue #285: Print/export code to pdf * Webbrowser tool used QWebkit if available (Thanks to David Salter) Other fixes issues: #161, #188, #201, #209, #245, #255, #251, #265, #270, #271, #276, #242, #229, #260, #259, #227, #254, #253, #243, #244 During the beta period we also fixed: #286, #283, #277, #281 ### Version 3.3.2 (12-11-2013) * Fixed issue #243: Running IEP from source did not work with PyQt4 * Fixed issue #240: IEP now runs also if 'cmd' is unknown on Windows * Fixed problem with registering Pyzo on Windows * Fixed problem with IEP binaries (MSVCR runtime) on Windows 32. * Fixed problem with IEP binaries (qt.conf) on OS X. ### Version 3.3 (29-10-2013) Since last release we have a new website, a new logo, and this release introduces our experimental libre license model. In terms of functionality, the biggest change is that IEP now supports debugging with breakpoints! The binaries for Linux are now build without -gtkstyle, making them look better or worse, depending on your OS. There is an experimental feature that tries to load PySide from the system libraries. The Python version on your system must be 3.3. E.g. on Ubuntu 13.10 you can do `sudo apt-get install python3-pyside`. To enable this feature, check the `qt.conf` file. Further, there have been several bug fixes and improvements: * Several small improvements to file browser tool * New splash screen (with new logo) * Added `conda` command to shell * Added `pip` command to shell * Qt backend runs in the real Qt event loop, which allows Qt applications to be much more responsive. * Do not auto-indent in a comment (Gijs van Oort) * New translations for Russian (George Volkov) * IEP binaries should now not clash with system Qt libraries * Project manager tool is now removed. * "cd to project dir" option added to file browser (by Laurent Signac) * issue #1: debugging with breakpoints * issue #225: replace-all scrolls to start * issue #226: QTextBlockUserData object has no attribute 'indentation' * issues: #182, #197, #207, #192, #194, #160, #110, #205, #211, #215 ### version 3.2 (13-03-2013) This is the first release for which all binaries are build with Python 3.3 and PySide. The most notable change is the new file browser, which replaces the old file browser and project manager tools. It combines the power of both in one simple interface. It also has functionality for peeking inside python modules. Since its design uses a generalization of the file system, implementing alternative file systems (like zip files or remote machines) should be quite easy in the future. IEP now also comes with two fonts: 'DejaVu Sans Mono' (the default font) and Adobes new 'Source code pro'. IEP now supports multiple languages. Translations for Dutch, French, Spanish and Catalan are available. Hoping for more in future versions. List of issues that are fixed in this release: * issue #82: most texts are now translatable * issue #86: redesign of file browser tool * issue #149 and issue #150: better context menu in the editor * issue #156: Popup window for autocompletion can now be resized (via the config) * issue #157: Ctrl+Shift+Enter execute cell and go to next cell (as in Matlab) * issue #159: Exit code of Python process was incorrect * issue #163, issue #164, issue #165: Fixed problems running on Python 3.3 and Pyside. * issue #166: smart handling of indentation when deleting text. * issue #178: The font can now be chosen in the menu, and IEP ships with a good default font. * Further: issue #14, issue #138, issue #139, issue #144, issue #147, issue #158, issue #186 Other changes: * Tools can now be packages, allowing their code to be better structured. * Fault handler module is used on Python 3.3 to debug hard crashes. * When IEP closes, it takes better care of stopping daemon threads. * Some cosmetic enhancements of the tools. * The keys to accept autocompletion can now be configured (e.g. use Enter instead of Tab), see issue #134. During the beta period, a few more issues were fixed: * issue #189 (cannot mix incompatible Qt libraries) * and issue #186, issue #189, issue #200 ### version 3.1.1 (21-12-2012) Fix for issue #137: crucial issue on Ubuntu 12.10. ### version 3.1 (19-12-2012) Most notable changes: * IEP is now a package, making it to easier to integrate in other software. * IEP will be the IDE for [Pyzo](http://www.pyzo.org), and now has some functionality to integrate nicely with the Pyzo distro. * Multiple lines can now be pasted and executed in the shell (issue #120). * Run selection (F9) runs the selected statement if on a single line (issue #42). * IEP is now build with PySide (issue #85). * Goto-line functionality (double-click on the line number area) (issue #76) * Comments and docstrings can now easily be reshaped (CTR+J) (issue #105) * The shell widget now uses a powerful menu instead of tabs. * IEP now has a Wizard to help new users on their way. * Cells are separated more clearly. Complete list of fixed issues: issue #42, issue #76, issue #85, issue #90, issue #95, issue #99, issue #101, issue #105, issue #106, issue #107, issue #111, issue #112, issue #113, issue #115, issue #116, issue #120, issue #122, issue #123, issue #124, issue #128, issue #129, issue #130, issue #132, issue #133. ### version 3.0 (14-05-2012) We fixed the issues that we collected for 3.0.beta: * We fixed issue #89, issue #91, issue #93, issue #94, issue #95, issue #96, issue #98, issue #100. (See [overview](http://code.google.com/p/iep/issues/list?can=1&q=Milestone%3DRelease3.0|overview)) * The syntax style now uses a pure-white background; the solarized version becomes pinkish on some systems. * The shortcuts are reset. For 3.0.beta we carefully selected the key bindings so that they feel native for Windows, Linux and Mac. By resetting shortcuts, users that already used IEP 2.3 get the new bindings as well. ### version 3.0.beta (18-04-2012) About 14 months after releasing 2.3, we finally got version 3.0 out. The main reason for this delay that we re-implemented some core components, which took a lot of time to get right. Funny fact: the amount of changesets in the repository has more than doubled since the last release. The core things that were changed: * We have rewritten the code that does the communication between the kernel and the IEP. This code is organized separately in a package we've called [Yoton(http://code.google.com/p/yoton/). We've designed it in such a way that it will allow us to run a kernel on a different machine (remote computing) and to connect multiple users to the same kernel (collaborative computing). You might expect these features in one of the coming releases. Further, it paves the way for parallel computing (but that's for the further future). * Another big change is the editing component. We've gotten rid of Scintilla (which is an old library with an inconsistent API, with bad support for Unicode, and was very buggy on Mac). We have designed our own editing component using pure Qt components. We've designed that part too to be independent of IEP, so it might be reused in other projects. * The code for the menu has been completely redone, allowing for easier incorporation of icons, and using (contex) menus in other places. * The editorstack has been gotten rid of. In its place is the project manager and a more classic tab bar, custom-made to make the tabs more compact and provide information about the open documents in a subtle and non-intrusive manner. There've been tons of other things we've improved and fixed along the way. Because we changed so much to the code, it's hard to list (and remember) all of them. Because we designed a new editing component, some features are now also removed, e.g. brace matching. We plan on implementing most of these features in the coming releases. ### version 2.3 (23-11-2010) For this release we implemented many improvements and bug-fixes. Further, we implemented a few new tools and made IEP work for the Mac. * From this release, IEP uses the BSD license. * Binaries are now also available for 64bit Linux and Mac. * Improved the interactive help; it looks better and can show numpy docstrings well. * Source structure tool can now also show class attributes (in addition to methods). * New Workspace tool. * New File browser tool which has the ability to search inside files. * New webbrowser tool (very simple though, I did not want to use the QT webkit to keep the binaries small). * IEP uses the guisupport.py module to integrate the event loops for GUI toolkits. * The GTK event loop can now also be integrated. * Many bug/issue fixes: * Fixed bug in shell config dialog when there is no Python installed on Windows. * Fixed bug: sys.argv = [''] in inreractive mode (and not []). * Prevent restoring window position if it's not on screen. * Many more that I failed to document properly :) * Files with Windows line ending are now correctly executed when running as script. * Fixed issue #9 that IEP sometimes hangs when doing 'open xxx'. * Better signatures for extension code. * newlines are now correctly displayed when showing whitespace. * Can now also interrupt files run as script. * Loads of other small improvements ... ### version 2.2 (24-08-2010) A few beta tester played around with the first version and gave me list of things to improve. Special thanks to Stef for his suggestions. * Better distinction between running code interactively or as a script. Also significantly improved the shell configuration dialog. * Append '' to sys.path in interactive mode. In Script mode, add the directory of the script. * Allow enter and other chars to complete the autocompletion. * Better detection of classes and defs in the code parser (also for cython code). * Let user change the PythonPath in shell configs. * Shell always fits 80 colums set to default False. * Shells can also be set not to wrap to 80 columns. * The code parser handles multiline strings in the code also if they dedent. * Fixed autocompletion lag. * In keymapping dialog let dubbleclick on name open up shortcut 1. * Remove statusbar (for now) as it does contain no extra information. * If no project selected, give message when trying to run code in the project. * Made a small tutorial file that is loaded on first startup. * When showing line endings, show \r and \r\n correctly. * Source structure can also show class attributes. * Fixed autocompletion list update bug. * Handle Unicode correctly when typing in the shell. * Fonts and style now work/look better on KDE and older Linuxes. * Fixed some issues with check-for-updates. * EditorStack can be scrolled with the scroll wheel. * IEP can now also integrate the GTK event loop. * Many other changes and fixes ... ### version 2.1 (21-07-2010) The first official version of Iep. ### version 2.0.1 I intended to release alpha release while I was developing Iep, but only released the one right after I finished the editor stack. ### version 1.0 A bit of history ... When I started working with Python, I used IPython and Pype, which I both really liked, but I felt that the two should be combined in one application. Since I could not find a free IDE that did this (Spyder did not exist yet) I set out to make my own. I wrote a first version of IEP in Python 2.5 using the wx GUI toolkit. When I thought it was at a stage that it was suitable for a public release, I tested in on Linux, and it looked like `cr*p`. I could (and should) have expected this, because I used some widgets on a rather low-level, and some widgets behave rather differently on different OS's (since wx wraps the native widgets to the os). This made me look for other GUI toolkits. I took a (brief) look at fltk, but ended up with Qt4, right around the time that it went LGPL. Although a bit big memory-wise, the consistent library and powerful widgets of PyQt4 gave me hope that I was on the right track. I started from scratch, reusing as much code as I could, but also redesigning large parts to fix all the little things I was not quite happy about. Since I was designing the same application for the second time, I had a pretty good idea what I wanted and how it should be done. Nevertheless, it took me another year or so to get it to a level I found suitable for release: version 2.1. pyzo-4.4.3/setup.cfg0000666000000000000000000000077113166630337012513 0ustar 00000000000000[flake8] exclude = test_*.py,docs/*,build/*,dist/*,_feedstock/*,pyzo/util/qt/, pyzo/util/paths.py,pyzo/resources/tutorial.py ignore = W291,W293,E123,E124,E126,E127,E203,E225,E226,E265,E301,E302,E303,E402, E266,E731,E128,E306,E305,I,D,T,CG, E201,E202,E303,W293,E231,E228,E125,E261,E701,E702,E227,E241,E221,E222,E713, E251,E116,E114,E262,W503, W293,E231,E401,E211,W391,W292,E121,E122,N,I max-line-length = 160 [tool:pytest] [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 pyzo-4.4.3/setup.py0000666000000000000000000000670012772461440012401 0ustar 00000000000000# -*- coding: utf-8 -*- """ Setup script for the Pyzo package. """ import os import sys try: from setuptools import setup except ImportError: from distutils.core import setup def get_version_and_doc(filename): NS = dict(__version__='', __doc__='') docStatus = 0 # Not started, in progress, done for line in open(filename, 'rb').read().decode().splitlines(): if line.startswith('__version__'): exec(line.strip(), NS, NS) elif line.startswith('"""'): if docStatus == 0: docStatus = 1 line = line.lstrip('"') elif docStatus == 1: docStatus = 2 if docStatus == 1: NS['__doc__'] += line + '\n' if not NS['__version__']: raise RuntimeError('Could not find __version__') return NS['__version__'], NS['__doc__'] def package_tree(pkgroot): subdirs = [os.path.relpath(i[0], THIS_DIR).replace(os.path.sep, '.') for i in os.walk(os.path.join(THIS_DIR, pkgroot)) if '__init__.py' in i[2]] return subdirs ## Define info of this package THIS_DIR = os.path.dirname(__file__) name = 'pyzo' description = 'the Python IDE for scientific computing' version, doc = get_version_and_doc(os.path.join(THIS_DIR, name, '__init__.py')) ## Setup setup( name = name, version = version, author = 'Almar Klein', author_email = 'almar.klein@gmail.com', license = '(new) BSD', url = 'http://www.pyzo.org', keywords = "Python interactive IDE Qt science computing", description = description, long_description = doc, platforms = 'any', provides = ['pyzo'], install_requires = [], # and 'PySide' or 'PyQt4' packages = package_tree(name), package_dir = {'pyzo': 'pyzo'}, package_data = {'pyzo': ['license.txt', 'contributors.txt', 'resources/*.*', 'resources/icons/*.*', 'resources/appicons/*.*', 'resources/images/*.*', 'resources/fonts/*.*', 'resources/translations/*.*']}, zip_safe = False, classifiers = [ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', 'Intended Audience :: Education', 'Intended Audience :: Developers', 'Topic :: Scientific/Engineering', 'Topic :: Software Development', 'License :: OSI Approved :: BSD License', 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', 'Programming Language :: Python :: 3', ], entry_points = {'console_scripts': ['pyzo = pyzo.__main__:main',], }, ) ## Post processing # Install appdata.xml on Linux if we are installing in the system Python if sys.platform.startswith('linux') and sys.prefix.startswith('/usr'): if len(sys.argv) >= 2 and sys.argv[1] == 'install': fname = 'pyzo.appdata.xml' filename1 = os.path.join(os.path.dirname(__file__), fname) filename2 = os.path.join('/usr/share/appdata', fname) try: bb = open(filename1, 'rb').read() open(filename2, 'wb').write(bb) except PermissionError: pass # No sudo, no need to warn except Exception as err: print('Could not install %s: %s' % (fname, str(err))) else: print('Installed %s' % fname)