--- /dev/null
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
--- /dev/null
+PROJECT_NAME=ejpi
+SOURCE_PATH=src
+SOURCE=$(shell find $(SOURCE_PATH) -iname *.py)
+OBJ=$(SOURCE:.py=.pyc)
+TAG_FILE=~/.ctags/$(PROJECT_NAME).tags
+
+DEBUGGER=winpdb
+UNIT_TEST=nosetests -w $(TEST_PATH)
+STYLE_TEST=../../Python/tools/pep8.py --ignore=W191
+LINT_RC=./support/pylint.rc
+LINT=pylint --rcfile=$(LINT_RC)
+COVERAGE_TEST=figleaf
+PROFILER=pyprofiler
+CTAGS=ctags-exuberant
+
+.PHONY: all run debug test lint tags package clean
+
+all: test package
+
+run: $(SOURCE)
+ $(SOURCE_PATH)/ejpi_glade.py
+
+debug: $(SOURCE)
+ $(DEBUGGER) $(SOURCE_PATH)/ejpi_glade.py
+
+test: $(SOURCE)
+ $(SOURCE_PATH)/ejpi_glade.py -t
+
+package:
+ ./builddeb.py
+
+lint:
+ $(foreach file, $(SOURCE), $(LINT) $(file) ; )
+
+tags: $(TAG_FILE)
+
+clean:
+ rm -Rf $(OBJ)
+
+$(TAG_FILE): $(SOURCE)
+ mkdir -p $(dir $(TAG_FILE))
+ $(CTAGS) -o $(TAG_FILE) $(SOURCE)
+
+#Makefile Debugging
+#Target to print any variable, can be added to the dependencies of any other target
+#Userfule flags for make, -d, -p, -n
+print-%: ; @$(error $* is $($*) ($(value $*)) (from $(origin $*)))
--- /dev/null
+#!/usr/bin/env python
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.4.5 on Tue Jan 27 20:26:23 2009 -->
+<glade-interface>
+ <widget class="GtkWindow" id="mainWindow">
+ <property name="title" translatable="yes">Cluttered Calc</property>
+ <property name="default_width">800</property>
+ <property name="default_height">480</property>
+ <child>
+ <widget class="GtkVBox" id="mainLayout">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuBar" id="mainMenubar">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkMenuItem" id="fileMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="fileMenu">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImageMenuItem" id="quitMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">gtk-quit</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_calculator_quit"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="editMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="editMenuIte">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImageMenuItem" id="pasteMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">gtk-paste</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_paste"/>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkImageMenuItem" id="deleteMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">gtk-delete</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_clear_entry"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkMenuItem" id="helpMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child>
+ <widget class="GtkMenu" id="helpMenu">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImageMenuItem" id="aboutMenuItem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">gtk-about</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <signal name="activate" handler="on_about"/>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkHBox" id="calculatorLayout">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkVBox" id="historyLayout">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkEntry" id="entryView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">False</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkEventBox" id="errorEventBox">
+ <property name="visible">True</property>
+ <property name="events">GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+ <child>
+ <widget class="GtkHBox" id="errorBox">
+ <property name="visible">True</property>
+ <child>
+ <widget class="GtkImage" id="errorImage">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-error</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="errorDescription">
+ <property name="visible">True</property>
+ <property name="use_markup">True</property>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkImage" id="errorClose">
+ <property name="visible">True</property>
+ <property name="tooltip_markup">Close Error Message</property>
+ <property name="stock">gtk-close</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkScrolledWindow" id="scrollingHistory">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <child>
+ <widget class="GtkTreeView" id="historyView">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ <property name="reorderable">True</property>
+ <property name="rules_hint">True</property>
+ <property name="enable_search">False</property>
+ <property name="hover_selection">True</property>
+ <property name="hover_expand">True</property>
+ <property name="rubber_banding">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ <child>
+ <widget class="GtkVBox" id="functionLayout">
+ <property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkNotebook" id="pluginKeyboards">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tab_pos">GTK_POS_BOTTOM</property>
+ <property name="scrollable">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <widget class="GtkLabel" id="NullFunctionTab">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">None</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
--- /dev/null
+#!/usr/bin/env python
+
+import os
+
+import plugin_utils
+import history
+
+
+PLUGIN_SEARCH_PATHS = [
+ os.path.join(os.path.dirname(__file__), "plugins/"),
+]
+
+
+OPERATIONS = {}
+
+CONSTANTS = {}
+
+
+class CliEntry(object):
+
+ def __init__(self):
+ self.value = ""
+
+ def set_value(self, value):
+ self.value = value
+
+ def get_value(self):
+ return self.value
+
+ def clear(self):
+ self.value = ""
+
+
+def parse_command(userInput):
+ return OPERATIONS[userInput.strip()]
+
+
+def ambiguous_parse(calc, userInput):
+ try:
+ Node = parse_command(userInput)
+ calc.apply_operation(Node)
+ return True
+ except KeyError:
+ return False
+
+
+def repl():
+ entry = CliEntry()
+ stack = history.CalcHistory()
+ rpnCalc = history.RpnCalcHistory(
+ stack,
+ entry, history.ErrorWarning(),
+ CONSTANTS, OPERATIONS
+ )
+ while True:
+ userInput = raw_input(">")
+ isUsed = ambiguous_parse(rpnCalc, userInput)
+ if not isUsed:
+ entry.set_value(userInput)
+ rpnCalc.push_entry()
+
+ if 0 < len(stack):
+ node = stack.peek()
+ print "\t= %s" % str(node)
+ print "\t~= %s" % str(node.simplify(**CONSTANTS))
+
+
+def main():
+ constantPlugins = plugin_utils.ConstantPluginManager()
+ constantPlugins.add_path(*PLUGIN_SEARCH_PATHS)
+ constantPlugins.enable_plugin(constantPlugins.lookup_plugin("Builtin"))
+ CONSTANTS.update(constantPlugins.constants)
+
+ operatorPlugins = plugin_utils.OperatorPluginManager()
+ operatorPlugins.add_path(*PLUGIN_SEARCH_PATHS)
+ operatorPlugins.enable_plugin(operatorPlugins.lookup_plugin("Builtin"))
+ OPERATIONS.update(operatorPlugins.operators)
+
+ repl()
+
+if __name__ == "__main__":
+ main()
--- /dev/null
+#!/usr/bin/python
+
+"""
+@todo Add preference file
+ @li enable/disable plugins
+ @li plugin search path
+ @li Number format
+ @li Current tab
+@todo Expand operations to support
+ @li mathml then to cairo?
+ @li cairo directly?
+@todo Expanded copy/paste (Unusure how far to go)
+ @li Copy formula, value, serialized, mathml, latex?
+ @li Paste serialized, value?
+
+Some useful things on Maemo
+@li http://maemo.org/api_refs/4.1/libosso-2.16-1/group__Statesave.html
+@li http://maemo.org/api_refs/4.1/libosso-2.16-1/group__Autosave.html
+"""
+
+
+from __future__ import with_statement
+
+
+import sys
+import gc
+import os
+import string
+import warnings
+
+import gtk
+import gtk.glade
+
+try:
+ import hildon
+except ImportError:
+ hildon = None
+
+from libraries import gtkpie
+from libraries import gtkpieboard
+import plugin_utils
+import history
+import gtkhistory
+
+
+PLUGIN_SEARCH_PATHS = [
+ os.path.join(os.path.dirname(__file__), "plugins/"),
+]
+
+
+class ValueEntry(object):
+
+ def __init__(self, widget):
+ self.__widget = widget
+ self.__actualEntryDisplay = ""
+
+ def get_value(self):
+ value = self.__actualEntryDisplay.strip()
+ if any(
+ 0 < value.find(whitespace)
+ for whitespace in string.whitespace
+ ):
+ self.clear()
+ raise ValueError('Invalid input "%s"' % value)
+ return value
+
+ def set_value(self, value):
+ value = value.strip()
+ if any(
+ 0 < value.find(whitespace)
+ for whitespace in string.whitespace
+ ):
+ raise ValueError('Invalid input "%s"' % value)
+ self.__actualEntryDisplay = value
+ self.__widget.set_text(value)
+
+ def append(self, value):
+ value = value.strip()
+ if any(
+ 0 < value.find(whitespace)
+ for whitespace in string.whitespace
+ ):
+ raise ValueError('Invalid input "%s"' % value)
+ self.set_value(self.get_value() + value)
+
+ def pop(self):
+ value = self.get_value()[0:-1]
+ self.set_value(value)
+
+ def clear(self):
+ self.set_value("")
+
+ value = property(get_value, set_value, clear)
+
+
+class ErrorDisplay(history.ErrorReporting):
+
+ def __init__(self, widgetTree):
+ super(ErrorDisplay, self).__init__()
+ self.__errorBox = widgetTree.get_widget("errorEventBox")
+ self.__errorDescription = widgetTree.get_widget("errorDescription")
+ self.__errorClose = widgetTree.get_widget("errorClose")
+ self.__parentBox = self.__errorBox.get_parent()
+
+ self.__errorBox.connect("button_release_event", self._on_close)
+
+ self.__messages = []
+ self.__parentBox.remove(self.__errorBox)
+
+ def push_message(self, message):
+ if 0 < len(self.__messages):
+ self.__messages.append(message)
+ else:
+ self.__show_message(message)
+
+ def pop_message(self):
+ if 0 < len(self.__messages):
+ self.__show_message(self.__messages[0])
+ del self.__messages[0]
+ else:
+ self.__hide_message()
+
+ def _on_close(self, *args):
+ self.pop_message()
+
+ def __show_message(self, message):
+ self.__errorDescription.set_text(message)
+ self.__parentBox.pack_start(self.__errorBox, False, False)
+ self.__parentBox.reorder_child(self.__errorBox, 1)
+
+ def __hide_message(self):
+ self.__errorDescription.set_text("")
+ self.__parentBox.remove(self.__errorBox)
+
+
+class Calculator(object):
+
+ __pretty_app_name__ = "e^(j pi) + 1 = 0"
+ __app_name__ = "ejpi"
+ __version__ = "0.1.0"
+ __app_magic__ = 0xdeadbeef
+
+ _glade_files = [
+ '/usr/lib/ejpi/calc.glade',
+ os.path.join(os.path.dirname(__file__), "calc.glade"),
+ os.path.join(os.path.dirname(__file__), "../lib/calc.glade"),
+ ]
+
+ _plugin_search_paths = [
+ os.path.join(os.path.dirname(__file__), "plugins/")
+ ]
+
+ _user_data = os.path.expanduser("~/.%s/" % __app_name__)
+ _user_settings = "%s/settings.ini" % _user_data
+ _user_history = "%s/history.stack" % _user_data
+
+ def __init__(self):
+ self.__constantPlugins = plugin_utils.ConstantPluginManager()
+ self.__constantPlugins.add_path(*self._plugin_search_paths)
+ self.__constantPlugins.enable_plugin(self.__constantPlugins.lookup_plugin("Builtin"))
+ self.__constantPlugins.enable_plugin(self.__constantPlugins.lookup_plugin("Trigonometry"))
+ self.__constantPlugins.enable_plugin(self.__constantPlugins.lookup_plugin("Computer"))
+ self.__constantPlugins.enable_plugin(self.__constantPlugins.lookup_plugin("Alphabet"))
+
+ self.__operatorPlugins = plugin_utils.OperatorPluginManager()
+ self.__operatorPlugins.add_path(*self._plugin_search_paths)
+ self.__operatorPlugins.enable_plugin(self.__operatorPlugins.lookup_plugin("Builtin"))
+ self.__operatorPlugins.enable_plugin(self.__operatorPlugins.lookup_plugin("Trigonometry"))
+ self.__operatorPlugins.enable_plugin(self.__operatorPlugins.lookup_plugin("Computer"))
+ self.__operatorPlugins.enable_plugin(self.__operatorPlugins.lookup_plugin("Alphabet"))
+
+ self.__keyboardPlugins = plugin_utils.KeyboardPluginManager()
+ self.__keyboardPlugins.add_path(*self._plugin_search_paths)
+ self.__activeKeyboards = {}
+
+ for path in self._glade_files:
+ if os.path.isfile(path):
+ self._widgetTree = gtk.glade.XML(path)
+ break
+ else:
+ self.display_error_message("Cannot find calc.glade")
+ gtk.main_quit()
+ try:
+ os.makedirs(self._user_data)
+ except OSError, e:
+ if e.errno != 17:
+ raise
+
+ self._clipboard = gtk.clipboard_get()
+ self.__window = self._widgetTree.get_widget("mainWindow")
+
+ global hildon
+ self._app = None
+ self._isFullScreen = False
+ if hildon is not None and self.__window is gtk.Window:
+ warnings.warn("Hildon installed but glade file not updated to work with hildon", UserWarning, 2)
+ hildon = None
+ elif hildon is not None:
+ self._app = hildon.Program()
+ self.__window = hildon.Window()
+ self._widgetTree.get_widget("mainLayout").reparent(self.__window)
+ self._app.add_window(self.__window)
+ hildon.hildon_helper_set_thumb_scrollbar(self._widgetTree.get_widget('scrollingHistory'), True)
+
+ gtkMenu = self._widgetTree.get_widget("mainMenubar")
+ menu = gtk.Menu()
+ for child in gtkMenu.get_children():
+ child.reparent(menu)
+ self.__window.set_menu(menu)
+ gtkMenu.destroy()
+
+ self.__window.connect("key-press-event", self._on_key_press)
+ self.__window.connect("window-state-event", self._on_window_state_change)
+ else:
+ warnings.warn("No Hildon", UserWarning, 2)
+
+ self.__errorDisplay = ErrorDisplay(self._widgetTree)
+ self.__userEntry = ValueEntry(self._widgetTree.get_widget("entryView"))
+ self.__stackView = self._widgetTree.get_widget("historyView")
+
+ self.__historyStore = gtkhistory.GtkCalcHistory(self.__stackView)
+ self.__history = history.RpnCalcHistory(
+ self.__historyStore,
+ self.__userEntry, self.__errorDisplay,
+ self.__constantPlugins.constants, self.__operatorPlugins.operators
+ )
+ self.__load_history()
+
+ self.__sliceStyle = gtkpie.generate_pie_style(self.__window)
+ self.__handler = gtkpieboard.KeyboardHandler(self._on_entry_direct)
+ self.__handler.register_command_handler("push", self._on_push)
+ self.__handler.register_command_handler("unpush", self._on_unpush)
+ self.__handler.register_command_handler("backspace", self._on_entry_backspace)
+ self.__handler.register_command_handler("clear", self._on_entry_clear)
+
+ builtinKeyboardId = self.__keyboardPlugins.lookup_plugin("Builtin")
+ self.__keyboardPlugins.enable_plugin(builtinKeyboardId)
+ self.__builtinPlugin = self.__keyboardPlugins.keyboards["Builtin"].construct_keyboard()
+ self.__builtinKeyboard = self.__builtinPlugin.setup(self.__history, self.__sliceStyle, self.__handler)
+ self._widgetTree.get_widget("functionLayout").pack_start(self.__builtinKeyboard)
+ self._widgetTree.get_widget("functionLayout").reorder_child(self.__builtinKeyboard, 0)
+ self.enable_plugin(self.__keyboardPlugins.lookup_plugin("Trigonometry"))
+ self.enable_plugin(self.__keyboardPlugins.lookup_plugin("Computer"))
+ self.enable_plugin(self.__keyboardPlugins.lookup_plugin("Alphabet"))
+
+ callbackMapping = {
+ "on_calculator_quit": self._on_close,
+ "on_paste": self._on_paste,
+ "on_clear_entry": self._on_clear_all,
+ "on_about": self._on_about_activate,
+ }
+ self._widgetTree.signal_autoconnect(callbackMapping)
+
+ if self.__window:
+ if hildon is None:
+ self.__window.set_title("%s" % self.__pretty_app_name__)
+ self.__window.connect("destroy", self._on_close)
+ self.__window.show_all()
+
+ try:
+ import osso
+ except ImportError:
+ osso = None
+
+ self._osso = None
+ if osso is not None:
+ self._osso = osso.Context(Calculator.__app_name__, Calculator.__version__, False)
+ device = osso.DeviceState(self._osso)
+ device.set_device_state_callback(self._on_device_state_change, 0)
+ else:
+ warnings.warn("No OSSO", UserWarning, 2)
+
+ def display_error_message(self, msg):
+ error_dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg)
+
+ def close(dialog, response, editor):
+ editor.about_dialog = None
+ dialog.destroy()
+ error_dialog.connect("response", close, self)
+ error_dialog.run()
+
+ def enable_plugin(self, pluginId):
+ self.__keyboardPlugins.enable_plugin(pluginId)
+ pluginData = self.__keyboardPlugins.plugin_info(pluginId)
+ pluginName = pluginData[0]
+ plugin = self.__keyboardPlugins.keyboards[pluginName].construct_keyboard()
+ pluginKeyboard = plugin.setup(self.__history, self.__sliceStyle, self.__handler)
+
+ keyboardTabs = self._widgetTree.get_widget("pluginKeyboards")
+ keyboardTabs.append_page(pluginKeyboard, gtk.Label(pluginName))
+ keyboardPageNum = keyboardTabs.page_num(pluginKeyboard)
+ assert keyboardPageNum not in self.__activeKeyboards
+ self.__activeKeyboards[keyboardPageNum] = {
+ "pluginName": pluginName,
+ "plugin": plugin,
+ "pluginKeyboard": pluginKeyboard,
+ }
+
+ def __load_history(self):
+ serialized = []
+ try:
+ with open(self._user_history, "rU") as f:
+ serialized = (
+ (part.strip() for part in line.split(" "))
+ for line in f.readlines()
+ )
+ except IOError, e:
+ if e.errno != 2:
+ raise
+ self.__history.deserialize_stack(serialized)
+
+ def __save_history(self):
+ serialized = self.__history.serialize_stack()
+ with open(self._user_history, "w") as f:
+ for lineData in serialized:
+ line = " ".join(data for data in lineData)
+ f.write("%s\n" % line)
+
+ def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
+ """
+ For system_inactivity, we have no background tasks to pause
+
+ @note Hildon specific
+ """
+ if memory_low:
+ gc.collect()
+
+ if save_unsaved_data or shutdown:
+ self.__save_history()
+
+ def _on_window_state_change(self, widget, event, *args):
+ """
+ @note Hildon specific
+ """
+ if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
+ self._isFullScreen = True
+ else:
+ self._isFullScreen = False
+
+ def _on_close(self, *args, **kwds):
+ try:
+ self.__save_history()
+ finally:
+ gtk.main_quit()
+
+ def _on_paste(self, *args):
+ contents = self._clipboard.wait_for_text()
+ self.__userEntry.append(contents)
+
+ def _on_key_press(self, widget, event, *args):
+ """
+ @note Hildon specific
+ """
+ if event.keyval == gtk.keysyms.F6:
+ if self._isFullScreen:
+ self.__window.unfullscreen()
+ else:
+ self.__window.fullscreen()
+
+ def _on_push(self, *args):
+ self.__history.push_entry()
+
+ def _on_unpush(self, *args):
+ self.__historyStore.unpush()
+
+ def _on_entry_direct(self, keys, modifiers):
+ if "shift" in modifiers:
+ keys = keys.upper()
+ self.__userEntry.append(keys)
+
+ def _on_entry_backspace(self, *args):
+ self.__userEntry.pop()
+
+ def _on_entry_clear(self, *args):
+ self.__userEntry.clear()
+
+ def _on_clear_all(self, *args):
+ self.__history.clear()
+
+ def _on_about_activate(self, *args):
+ dlg = gtk.AboutDialog()
+ dlg.set_name(self.__pretty_app_name__)
+ dlg.set_version(self.__version__)
+ dlg.set_copyright("Copyright 2008 - LGPL")
+ dlg.set_comments("")
+ dlg.set_website("")
+ dlg.set_authors([""])
+ dlg.run()
+ dlg.destroy()
+
+
+def run_doctest():
+ import doctest
+
+ failureCount, testCount = doctest.testmod()
+ if not failureCount:
+ print "Tests Successful"
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
+
+def run_calculator():
+ gtk.gdk.threads_init()
+
+ if hildon is not None:
+ gtk.set_application_name(Calculator.__pretty_app_name__)
+ handle = Calculator()
+ gtk.main()
+
+
+class DummyOptions(object):
+
+ def __init__(self):
+ self.test = False
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ try:
+ import optparse
+ except ImportError:
+ optparse = None
+
+ if optparse is not None:
+ parser = optparse.OptionParser()
+ parser.add_option("-t", "--test", action="store_true", dest="test", help="Run tests")
+ (commandOptions, commandArgs) = parser.parse_args()
+ else:
+ commandOptions = DummyOptions()
+ commandArgs = []
+
+ if commandOptions.test:
+ run_doctest()
+ else:
+ gtkpie.IMAGES.add_path(os.path.join(os.path.dirname(__file__), "libraries/images"), )
+ run_calculator()
--- /dev/null
+#!/usr/bin/env python
+
+"""
+http://www.grigoriev.ru/svgmath/ (MathML->SVG in Python)
+http://helm.cs.unibo.it/mml-widget/ (MathML widget in C++)
+"""
+
+
+import gobject
+import gtk
+
+
+import history
+import operation
+
+
+class GtkCalcHistory(history.AbstractHistory):
+
+ BUTTON_IDX = 0
+ VIEW_DATA_IDX = 1
+ VIEW_RESULT_IDX = 2
+ DATA_IDX = 3
+ RESULT_IDX = 4
+
+ def __init__(self, view):
+ super(GtkCalcHistory, self).__init__()
+ self.__prettyRenderer = operation.render_number()
+ self._historyView = view
+
+ # stock-id, display, value
+ self.__historyStore = gtk.ListStore(
+ gobject.TYPE_STRING, # stock id for pixbuf
+ gobject.TYPE_STRING, # view of data
+ gobject.TYPE_STRING, # view of result
+ object, # data
+ object, # result
+ )
+ self._historyView.set_model(self.__historyStore)
+
+ # create the TreeViewColumns to display the data
+ self.__closeColumn = gtk.TreeViewColumn('')
+ self._historyView.append_column(self.__closeColumn)
+
+ self.__historyColumn = gtk.TreeViewColumn('History')
+ self.__historyColumn.set_sort_column_id(0)
+ self.__historyColumn.set_expand(True)
+ self._historyView.append_column(self.__historyColumn)
+
+ self.__resultColumn = gtk.TreeViewColumn('')
+ self.__resultColumn.set_sort_column_id(0)
+ self._historyView.append_column(self.__resultColumn)
+
+ # create a CellRenderers to render the data
+ self.__closeCell = gtk.CellRendererPixbuf()
+ self.__closeColumn.pack_start(self.__closeCell, False)
+ self.__closeColumn.set_attributes(self.__closeCell, stock_id=0)
+
+ self.__expressionCell = gtk.CellRendererText()
+ self.__historyColumn.pack_start(self.__expressionCell, True)
+ self.__historyColumn.set_attributes(self.__expressionCell, text=1)
+
+ self.__valueCell = gtk.CellRendererText()
+ self.__resultColumn.pack_end(self.__valueCell, False)
+ self.__resultColumn.set_attributes(self.__valueCell, text=2)
+
+ self._historyView.set_reorderable(True)
+ self._historyView.connect("row-activated", self._on_close_activated)
+
+ def push(self, node):
+ simpleNode = node.simplify()
+ self.__historyStore.prepend([
+ gtk.STOCK_CLOSE,
+ operation.render_operation(self.__prettyRenderer, node),
+ operation.render_operation(self.__prettyRenderer, simpleNode),
+ node,
+ simpleNode
+ ])
+
+ def pop(self):
+ if len(self.__historyStore) == 0:
+ raise IndexError("Not enough items in the history for the operation")
+
+ row = self.__historyStore[0]
+ data = row[self.DATA_IDX]
+ del self.__historyStore[0]
+
+ return data
+
+ def peek(self):
+ if len(self.__historyStore) == 0:
+ raise IndexError("Not enough items in the history for the operation")
+ row = self.__historyStore[0]
+ data = row[self.DATA_IDX]
+ return data
+
+ def clear(self):
+ self.__historyStore.clear()
+
+ def __len__(self):
+ return len(self.__historyStore)
+
+ def __iter__(self):
+ for row in iter(self.__historyStore):
+ data = row[self.DATA_IDX]
+ yield data
+
+ def _on_close_activated(self, treeView, path, viewColumn):
+ if viewColumn is self.__closeColumn:
+ del self.__historyStore[path[0]]
+ elif viewColumn is self.__resultColumn:
+ row = self.__historyStore[path[0]]
+ data = row[self.RESULT_IDX]
+ self.push(data)
+ elif viewColumn is self.__historyColumn:
+ row = self.__historyStore[path[0]]
+ data = row[self.DATA_IDX]
+ self.push(data)
+ else:
+ assert False
--- /dev/null
+#!/usr/bin/env python
+
+
+import weakref
+import warnings
+
+from libraries.recipes import algorithms
+import operation
+
+
+__BASE_MAPPINGS = {
+ "0x": 16,
+ "0o": 8,
+ "0b": 2,
+}
+
+
+def parse_number(userInput):
+ try:
+ base = __BASE_MAPPINGS.get(userInput[0:2], 10)
+ if base != 10:
+ userInput = userInput[2:] # Remove prefix
+ value = int(userInput, base)
+ return value, base
+ except ValueError:
+ pass
+
+ try:
+ value = float(userInput)
+ return value, 10
+ except ValueError:
+ pass
+
+ try:
+ value = complex(userInput)
+ return value, 10
+ except ValueError:
+ pass
+
+ raise ValueError('Cannot parse "%s" as a number' % userInput)
+
+
+class ErrorReporting(object):
+
+ def push_message(self, message):
+ raise NotImplementedError
+
+ def push_exception(self, exception):
+ self.push_message(exception.message)
+ warnings.warn(exception, stacklevel=3)
+
+ def pop_message(self):
+ raise NotImplementedError
+
+
+class ErrorIgnore(ErrorReporting):
+
+ def push_message(self, message):
+ pass
+
+ def pop_message(self):
+ pass
+
+
+class ErrorWarning(ErrorReporting):
+
+ def push_message(self, message):
+ warnings.warn(message, stacklevel=2)
+
+ def pop_message(self):
+ pass
+
+
+class AbstractHistory(object):
+ """
+ Is it just me or is this class name begging for some jokes?
+ """
+
+ def push(self, node):
+ raise NotImplementedError
+
+ def pop(self):
+ raise NotImplementedError
+
+ def unpush(self):
+ node = self.pop()
+ for child in node.get_children():
+ self.push(child)
+
+ def peek(self):
+ raise NotImplementedError
+
+ def clear(self):
+ raise NotImplementedError
+
+ def __len__(self):
+ raise NotImplementedError
+
+ def __iter__(self):
+ raise NotImplementedError
+
+
+class CalcHistory(AbstractHistory):
+
+ def __init__(self):
+ super(CalcHistory, self).__init__()
+ self.__nodeStack = []
+
+ def push(self, node):
+ assert node is not None
+ self.__nodeStack.append(node)
+ return node
+
+ def pop(self):
+ popped = self.__nodeStack[-1]
+ del self.__nodeStack[-1]
+ return popped
+
+ def peek(self):
+ return self.__nodeStack[-1]
+
+ def clear(self):
+ self.__nodeStack = []
+
+ def __len__(self):
+ return len(self.__nodeStack)
+
+ def __iter__(self):
+ return self.__nodeStack[::-1]
+
+
+class RpnCalcHistory(object):
+
+ def __init__(self, history, entry, errorReporting, constants, operations):
+ self.history = history
+ self.__entry = weakref.ref(entry)
+
+ self.__errorReporter = errorReporting
+ self.__constants = constants
+ self.__operations = operations
+
+ self.__serialRenderer = operation.render_number()
+
+ @property
+ def errorReporter(self):
+ return self.__errorReporter
+
+ @property
+ def OPERATIONS(self):
+ return self.__operations
+
+ @property
+ def CONSTANTS(self):
+ return self.__constants
+
+ def clear(self):
+ self.history.clear()
+ self.__entry().clear()
+
+ def push_entry(self):
+ """
+ @todo Add operation duplication. If value is empty, peek at the top
+ item. If it has children, grab the last one, push it and reapply the
+ operation. If there are no children then just duplicate the item
+ """
+
+ value = self.__entry().get_value()
+
+ valueNode = None
+ if 0 < len(value):
+ valueNode = self._parse_value(value)
+ self.history.push(valueNode)
+
+ self.__entry().clear()
+ return valueNode
+
+ def apply_operation(self, Node):
+ try:
+ self.push_entry()
+
+ node = self._apply_operation(Node)
+ return node
+ except StandardError, e:
+ self.errorReporter.push_exception(e)
+ return None
+
+ def serialize_stack(self):
+ serialized = (
+ stackNode.serialize(self.__serialRenderer)
+ for stackNode in self.history
+ )
+ serialized = list(serialized)
+ serialized.reverse()
+ return serialized
+
+ def deserialize_stack(self, data):
+ for possibleNode in data:
+ for nodeValue in possibleNode:
+ if nodeValue in self.OPERATIONS:
+ Node = self.OPERATIONS[nodeValue]
+ self._apply_operation(Node)
+ else:
+ node = self._parse_value(nodeValue)
+ self.history.push(node)
+
+ def _parse_value(self, userInput):
+ try:
+ value, base = parse_number(userInput)
+ return operation.Value(value, base)
+ except ValueError:
+ pass
+
+ try:
+ return self.CONSTANTS[userInput]
+ except KeyError:
+ pass
+
+ return operation.Variable(userInput)
+
+ def _apply_operation(self, Node):
+ numArgs = Node.argumentCount
+
+ if len(self.history) < numArgs:
+ raise ValueError(
+ "Not enough arguments. The stack has %d but %s needs %d" % (
+ len(self.history), Node.symbol, numArgs
+ )
+ )
+
+ args = [arg for arg in algorithms.func_repeat(numArgs, self.history.pop)]
+ args.reverse()
+
+ try:
+ node = Node(*args)
+ except StandardError:
+ for arg in args:
+ self.history.push(arg)
+ raise
+ self.history.push(node)
+ return node
--- /dev/null
+#!/usr/bin/env python
+
+from pkgutil import extend_path
+
+
+#__path__ = extend_path(__path__, __name__)
--- /dev/null
+#!/usr/bin/env python
+
+"""
+@todo Handle sizing in a better manner http://www.gtkmm.org/docs/gtkmm-2.4/docs/tutorial/html/sec-custom-widgets.html
+"""
+
+
+from __future__ import division
+
+import os
+import weakref
+import math
+import copy
+import warnings
+
+import gobject
+import gtk
+import cairo
+import pango
+
+try:
+ import rsvg
+except ImportError:
+ rsvg = None
+
+
+def deg_to_rad(deg):
+ return (2 * math.pi * deg) / 360.0
+
+
+def rad_to_deg(rad):
+ return (360.0 * rad) / (2 * math.pi)
+
+
+def normalize_radian_angle(radAng):
+ """
+ Restricts @param radAng to the range [0..2pi)
+ """
+ twoPi = 2 * math.pi
+
+ while radAng < 0:
+ radAng += twoPi
+ while twoPi <= radAng:
+ radAng -= twoPi
+
+ return radAng
+
+
+def delta_to_rtheta(dx, dy):
+ distance = math.sqrt(dx**2 + dy**2)
+
+ angleInRads = math.atan2(-dy, dx)
+ if angleInRads < 0:
+ angleInRads = 2*math.pi + angleInRads
+ return distance, angleInRads
+
+
+class FontCache(object):
+
+ def __init__(self):
+ self.__fontCache = {}
+
+ def get_font(self, s):
+ if s in self.__fontCache:
+ return self.__fontCache[s]
+
+ descr = pango.FontDescription(s)
+ self.__fontCache[s] = descr
+
+ return descr
+
+
+FONTS = FontCache()
+
+
+class ImageCache(object):
+
+ def __init__(self):
+ self.__imageCache = {}
+ self.__imagePaths = [
+ os.path.join(os.path.dirname(__file__), "images"),
+ ]
+
+ def add_path(self, path):
+ self.__imagePaths.append(path)
+
+ def get_image(self, s):
+ if s in self.__imageCache:
+ return self.__imageCache[s]
+
+ image = None
+
+ if s.lower().endswith(".png"):
+ for path in self.__imagePaths:
+ imagePath = os.path.join(path, s)
+ try:
+ image = cairo.ImageSurface.create_from_png(imagePath)
+ break
+ except:
+ warnings.warn("Unable to load image %s" % imagePath)
+ elif s.lower().endswith(".svg") and rsvg is not None:
+ for path in self.__imagePaths:
+ imagePath = os.path.join(path, s)
+ try:
+ image = rsvg.Handle(file=imagePath)
+ except:
+ warnings.warn("Unable to load image %s" % imagePath)
+ else:
+ print "Don't know how to load image file type:", s
+
+ if image is not None:
+ self.__imageCache[s] = image
+
+ return image
+
+
+IMAGES = ImageCache()
+
+
+def convert_color(gtkColor):
+ r = gtkColor.red / 65535
+ g = gtkColor.green / 65535
+ b = gtkColor.blue / 65535
+ return r, g, b
+
+
+def generate_pie_style(widget):
+ """
+ @bug This seems to always pick the same colors irregardless of the theme
+ """
+ # GTK states:
+ # * gtk.STATE_NORMAL - The state of a sensitive widget that is not active and does not have the focus
+ # * gtk.STATE_ACTIVE - The state of a sensitive widget when it is active e.g. a button that is pressed but not yet released
+ # * gtk.STATE_PRELIGHT - The state of a sensitive widget that has the focus e.g. a button that has the mouse pointer over it.
+ # * gtk.STATE_SELECTED - The state of a widget that is selected e.g. selected text in a gtk.Entry widget
+ # * gtk.STATE_INSENSITIVE - The state of a widget that is insensitive and will not respond to any events e.g. cannot be activated, selected or prelit.
+
+ gtkStyle = widget.get_style()
+ sliceStyle = dict(
+ (gtkStyleState, {
+ "text": convert_color(gtkStyle.text[gtkStyleState]),
+ "fill": convert_color(gtkStyle.bg[gtkStyleState]),
+ "stroke": convert_color(gtkStyle.bg[gtkStyleState]),
+ })
+ for gtkStyleState in (
+ gtk.STATE_NORMAL, gtk.STATE_ACTIVE, gtk.STATE_PRELIGHT, gtk.STATE_SELECTED, gtk.STATE_INSENSITIVE
+ )
+ )
+
+ return sliceStyle
+
+
+class PieSlice(object):
+
+ SLICE_CENTER = 0
+ SLICE_EAST = 1
+ SLICE_SOUTH_EAST = 2
+ SLICE_SOUTH = 3
+ SLICE_SOUTH_WEST = 4
+ SLICE_WEST = 5
+ SLICE_NORTH_WEST = 6
+ SLICE_NORTH = 7
+ SLICE_NORTH_EAST = 8
+
+ MAX_ANGULAR_SLICES = 8
+
+ SLICE_DIRECTIONS = [
+ SLICE_CENTER,
+ SLICE_EAST,
+ SLICE_SOUTH_EAST,
+ SLICE_SOUTH,
+ SLICE_SOUTH_WEST,
+ SLICE_WEST,
+ SLICE_NORTH_WEST,
+ SLICE_NORTH,
+ SLICE_NORTH_EAST,
+ ]
+
+ SLICE_DIRECTION_NAMES = [
+ "CENTER",
+ "EAST",
+ "SOUTH_EAST",
+ "SOUTH",
+ "SOUTH_WEST",
+ "WEST",
+ "NORTH_WEST",
+ "NORTH",
+ "NORTH_EAST",
+ ]
+
+ def __init__(self, handler = (lambda p, s, d: None)):
+ self._direction = self.SLICE_CENTER
+ self._pie = None
+ self._style = None
+ self._handler = handler
+
+ def menu_init(self, pie, direction):
+ self._direction = direction
+ self._pie = weakref.ref(pie)
+ self._style = pie.sliceStyle
+
+ def calculate_minimum_radius(self, context, textLayout):
+ return 0
+
+ def draw_fg(self, styleState, isSelected, context, textLayout):
+ if isSelected:
+ styleState = gtk.STATE_ACTIVE
+ self._draw_fg(styleState, context, textLayout)
+
+ def draw_bg(self, styleState, isSelected, context, textLayout):
+ if isSelected:
+ styleState = gtk.STATE_ACTIVE
+ self._draw_bg(styleState, context, textLayout)
+
+ def _draw_fg(self, styleState, context, textLayout):
+ pass
+
+ def _draw_bg(self, styleState, context, textLayout):
+ centerPosition = self._pie().centerPosition
+ radius = max(self._pie().radius, self.calculate_minimum_radius(context, textLayout))
+ outerRadius = self._pie().outerRadius
+
+ fillColor = self._style[styleState]["fill"]
+ strokeColor = self._style[styleState]["stroke"]
+ if self._direction == self.SLICE_CENTER:
+ if fillColor:
+ context.arc(
+ centerPosition[0],
+ centerPosition[1],
+ radius,
+ 0,
+ 2 * math.pi
+ )
+
+ context.set_source_rgb(*fillColor)
+ if strokeColor:
+ context.fill_preserve()
+ else:
+ context.fill()
+
+ if strokeColor:
+ context.arc(
+ centerPosition[0],
+ centerPosition[1],
+ radius,
+ 0,
+ 2 * math.pi
+ )
+ context.set_source_rgb(*strokeColor)
+ context.stroke()
+ else:
+ sliceCenterAngle = self.quadrant_to_theta(self._direction)
+ sliceArcWidth = 2*math.pi / self.MAX_ANGULAR_SLICES
+ sliceStartAngle = sliceCenterAngle - sliceArcWidth/2
+ sliceEndAngle = sliceCenterAngle + sliceArcWidth/2
+
+ context.arc(
+ centerPosition[0],
+ centerPosition[1],
+ radius,
+ sliceStartAngle,
+ sliceEndAngle,
+ )
+ context.arc_negative(
+ centerPosition[0],
+ centerPosition[1],
+ outerRadius,
+ sliceEndAngle,
+ sliceStartAngle,
+ )
+ context.close_path()
+
+ if fillColor:
+ context.set_source_rgb(*fillColor)
+ if strokeColor:
+ context.fill_preserve()
+ else:
+ context.fill()
+
+ if strokeColor:
+ context.set_source_rgb(*strokeColor)
+ context.stroke()
+
+ def activate(self):
+ self._handler(self._pie(), self, self._direction)
+
+ @classmethod
+ def rtheta_to_quadrant(cls, distance, angleInRads, innerRadius):
+ if distance < innerRadius:
+ quadrant = 0
+ else:
+ gradians = angleInRads / (2*math.pi)
+ preciseQuadrant = gradians * cls.MAX_ANGULAR_SLICES + cls.MAX_ANGULAR_SLICES / (2 * 2*math.pi)
+ quadrantWithWrap = int(preciseQuadrant)
+ quadrant = quadrantWithWrap % cls.MAX_ANGULAR_SLICES
+ quadrant += 1
+
+ return quadrant
+
+ @classmethod
+ def quadrant_to_theta(cls, quadrant):
+ assert quadrant != 0
+ quadrant -= 1
+
+ gradians = quadrant / cls.MAX_ANGULAR_SLICES
+ radians = gradians * 2*math.pi
+
+ return radians
+
+
+class NullPieSlice(PieSlice):
+
+ def draw_bg(self, styleState, isSelected, context, textLayout):
+ super(NullPieSlice, self).draw_bg(styleState, False, context, textLayout)
+
+
+class LabelPieSlice(PieSlice):
+
+ def _align_label(self, labelWidth, labelHeight):
+ centerPosition = self._pie().centerPosition
+ if self._direction == PieSlice.SLICE_CENTER:
+ labelX = centerPosition[0] - labelWidth/2
+ labelY = centerPosition[1] - labelHeight/2
+ else:
+ if self._direction in (PieSlice.SLICE_NORTH_WEST, PieSlice.SLICE_WEST, PieSlice.SLICE_SOUTH_WEST):
+ outerX = 0
+ labelX = outerX
+ elif self._direction in (PieSlice.SLICE_SOUTH, PieSlice.SLICE_NORTH):
+ outerX = centerPosition[0]
+ labelX = outerX - labelWidth/2
+ elif self._direction in (PieSlice.SLICE_NORTH_EAST, PieSlice.SLICE_EAST, PieSlice.SLICE_SOUTH_EAST):
+ outerX = centerPosition[0] * 2
+ labelX = outerX - labelWidth
+ else:
+ assert False, "Direction %d is incorrect" % self._direction
+
+ if self._direction in (PieSlice.SLICE_NORTH_EAST, PieSlice.SLICE_NORTH, PieSlice.SLICE_NORTH_WEST):
+ outerY = 0
+ labelY = outerY
+ elif self._direction in (PieSlice.SLICE_EAST, PieSlice.SLICE_WEST):
+ outerY = centerPosition[1]
+ labelY = outerY - labelHeight/2
+ elif self._direction in (PieSlice.SLICE_SOUTH_EAST, PieSlice.SLICE_SOUTH, PieSlice.SLICE_SOUTH_WEST):
+ outerY = centerPosition[1] * 2
+ labelY = outerY - labelHeight
+ else:
+ assert False, "Direction %d is incorrect" % self._direction
+
+ return int(labelX), int(labelY)
+
+
+class TextLabelPieSlice(LabelPieSlice):
+
+ def __init__(self, text, fontName = 'Helvetica 12', handler = (lambda p, s, d: None)):
+ super(TextLabelPieSlice, self).__init__(handler = handler)
+ self.__text = text
+ self.__fontName = fontName
+
+ def calculate_minimum_radius(self, context, textLayout):
+ font = FONTS.get_font(self.__fontName)
+ textLayout.set_font_description(font)
+ textLayout.set_markup(self.__text)
+
+ labelWidth, labelHeight = textLayout.get_pixel_size()
+ return min(labelWidth, labelHeight) / 2
+
+ def _draw_fg(self, styleState, context, textLayout):
+ super(TextLabelPieSlice, self)._draw_fg(styleState, context, textLayout)
+
+ textColor = self._style[styleState]["text"]
+ font = FONTS.get_font(self.__fontName)
+
+ context.set_source_rgb(*textColor)
+ textLayout.set_font_description(font)
+ textLayout.set_markup(self.__text)
+ labelWidth, labelHeight = textLayout.get_pixel_size()
+ labelX, labelY = self._align_label(labelWidth, labelHeight)
+
+ context.move_to(
+ labelX,
+ labelY,
+ )
+
+ context.show_layout(textLayout)
+
+
+class ImageLabelPieSlice(LabelPieSlice):
+
+ def __init__(self, imagePath, handler = (lambda p, s, d: None)):
+ super(ImageLabelPieSlice, self).__init__(handler = handler)
+ self.__imagePath = imagePath
+
+ def calculate_minimum_radius(self, context, textLayout):
+ image = IMAGES.get_image(self.__imagePath)
+ if image is None:
+ return
+ labelWidth, labelHeight = image.get_width(), image.get_height()
+ return min(labelWidth, labelHeight) / 2
+
+ def _draw_fg(self, styleState, context, textLayout):
+ super(ImageLabelPieSlice, self)._draw_fg(styleState, context, textLayout)
+
+ image = IMAGES.get_image(self.__imagePath)
+ if image is None:
+ return
+
+ labelWidth, labelHeight = image.get_width(), image.get_height()
+ labelX, labelY = self._align_label(labelWidth, labelHeight)
+
+ context.set_source_surface(
+ image,
+ labelX,
+ labelY,
+ )
+
+ context.paint()
+
+
+class PieMenu(gtk.DrawingArea):
+
+ def __init__(self, style = None, **kwds):
+ super(PieMenu, self).__init__()
+
+ self.sliceStyle = style
+ self.centerPosition = 0, 0
+ self.radius = 20
+ self.outerRadius = self.radius * 2
+
+ self.connect("expose_event", self._on_expose)
+ self.connect("motion_notify_event", self._on_motion_notify)
+ self.connect("leave_notify_event", self._on_leave_notify)
+ self.connect("proximity_in_event", self._on_motion_notify)
+ self.connect("proximity_out_event", self._on_leave_notify)
+ self.connect("button_press_event", self._on_button_press)
+ self.connect("button_release_event", self._on_button_release)
+
+ self.set_events(
+ gtk.gdk.EXPOSURE_MASK |
+ gtk.gdk.POINTER_MOTION_MASK |
+ gtk.gdk.POINTER_MOTION_HINT_MASK |
+ gtk.gdk.BUTTON_MOTION_MASK |
+ gtk.gdk.BUTTON_PRESS_MASK |
+ gtk.gdk.BUTTON_RELEASE_MASK |
+ gtk.gdk.PROXIMITY_IN_MASK |
+ gtk.gdk.PROXIMITY_OUT_MASK |
+ gtk.gdk.LEAVE_NOTIFY_MASK
+ )
+
+ self.__activeSlice = None
+ self.__slices = {}
+ for direction in PieSlice.SLICE_DIRECTIONS:
+ self.add_slice(NullPieSlice(), direction)
+
+ self.__clickPosition = 0, 0
+ self.__styleState = gtk.STATE_NORMAL
+
+ def add_slice(self, slice, direction):
+ assert direction in PieSlice.SLICE_DIRECTIONS
+
+ slice.menu_init(self, direction)
+ self.__slices[direction] = slice
+
+ if direction == PieSlice.SLICE_CENTER:
+ self.__activeSlice = self.__slices[PieSlice.SLICE_CENTER]
+
+ def __update_state(self, mousePosition):
+ rect = self.get_allocation()
+ newStyleState = self.__styleState
+
+ if (
+ 0 <= mousePosition[0] and mousePosition[1] < rect.width and
+ 0 <= mousePosition[1] and mousePosition[1] < rect.height
+ ):
+ if self.__clickPosition == (0, 0):
+ newStyleState = gtk.STATE_PRELIGHT
+ else:
+ if self.__clickPosition != (0, 0):
+ newStyleState = gtk.STATE_PRELIGHT
+
+ if newStyleState != self.__styleState:
+ self.__generate_draw_event()
+ self.__styleState = newStyleState
+
+ def __process_mouse_position(self, mousePosition):
+ self.__update_state(mousePosition)
+ if self.__clickPosition == (0, 0):
+ return
+
+ delta = (
+ mousePosition[0] - self.centerPosition[0],
+ - (mousePosition[1] - self.centerPosition[1])
+ )
+ distance, angleInRads = delta_to_rtheta(delta[0], delta[1])
+ quadrant = PieSlice.rtheta_to_quadrant(distance, angleInRads, self.radius)
+ self.__select_slice(self.__slices[quadrant])
+
+ def __select_slice(self, newSlice):
+ if newSlice is self.__activeSlice:
+ return
+
+ oldSlice = self.__activeSlice
+ self.__activeSlice = newSlice
+ self.__generate_draw_event()
+
+ def __generate_draw_event(self):
+ if self.window is None:
+ return
+ rect = self.get_allocation()
+ self.window.invalidate_rect(rect, True)
+
+ def _on_expose(self, widget, event):
+ # @bug Not getting all of the invalidation events needed on Hildon
+ # @bug Not highlighting proper slice besides center on Hildon
+ cairoContext = self.window.cairo_create()
+ pangoContext = self.create_pango_context()
+ textLayout = pango.Layout(pangoContext)
+
+ rect = self.get_allocation()
+
+ self.centerPosition = event.area.x + event.area.width / 2, event.area.y + event.area.height / 2
+ self.radius = max(rect.width, rect.width) / 3 / 2
+ self.outerRadius = max(rect.width, rect.height)
+
+ for slice in self.__slices.itervalues():
+ isSelected = (slice is self.__activeSlice)
+ if not isSelected:
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ slice.draw_bg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ isSelected = self.__clickPosition != (0, 0)
+ self.__activeSlice.draw_bg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ for slice in self.__slices.itervalues():
+ isSelected = (slice is self.__activeSlice)
+ if not isSelected:
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ slice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ isSelected = self.__clickPosition != (0, 0)
+ self.__activeSlice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ def _on_leave_notify(self, widget, event):
+ newStyleState = gtk.STATE_NORMAL
+ if newStyleState != self.__styleState:
+ self.__generate_draw_event()
+ self.__styleState = newStyleState
+
+ mousePosition = event.get_coords()
+ self.__process_mouse_position(mousePosition)
+
+ def _on_motion_notify(self, widget, event):
+ mousePosition = event.get_coords()
+ self.__process_mouse_position(mousePosition)
+
+ def _on_button_press(self, widget, event):
+ self.__clickPosition = event.get_coords()
+
+ self._on_motion_notify(widget, event)
+ self.__generate_draw_event()
+
+ def _on_button_release(self, widget, event):
+ self._on_motion_notify(widget, event)
+
+ self.__activeSlice.activate()
+ self.__activeSlice = self.__slices[PieSlice.SLICE_CENTER]
+ self.__clickPosition = 0, 0
+
+ self.__generate_draw_event()
+
+
+gobject.type_register(PieMenu)
+
+
+class FakeEvent(object):
+
+ def __init__(self, x, y, isHint):
+ self.x = x
+ self.y = y
+ self.is_hint = isHint
+
+ def get_coords(self):
+ return self.x, self.y
+
+
+class PiePopup(gtk.DrawingArea):
+
+ def __init__(self, style = None, **kwds):
+ super(PiePopup, self).__init__()
+
+ self.showAllSlices = True
+ self.sliceStyle = style
+ self.centerPosition = 0, 0
+ self.radius = 20
+ self.outerRadius = self.radius * 2
+
+ self.connect("expose_event", self._on_expose)
+ self.connect("motion_notify_event", self._on_motion_notify)
+ self.connect("proximity_in_event", self._on_motion_notify)
+ self.connect("proximity_out_event", self._on_leave_notify)
+ self.connect("leave_notify_event", self._on_leave_notify)
+ self.connect("button_press_event", self._on_button_press)
+ self.connect("button_release_event", self._on_button_release)
+
+ self.set_events(
+ gtk.gdk.EXPOSURE_MASK |
+ gtk.gdk.POINTER_MOTION_MASK |
+ gtk.gdk.POINTER_MOTION_HINT_MASK |
+ gtk.gdk.BUTTON_MOTION_MASK |
+ gtk.gdk.BUTTON_PRESS_MASK |
+ gtk.gdk.BUTTON_RELEASE_MASK |
+ gtk.gdk.PROXIMITY_IN_MASK |
+ gtk.gdk.PROXIMITY_OUT_MASK |
+ gtk.gdk.LEAVE_NOTIFY_MASK
+ )
+
+ self.__activeSlice = None
+ self.__slices = {}
+ self.__localSlices = {}
+ for direction in PieSlice.SLICE_DIRECTIONS:
+ self.add_slice(NullPieSlice(), direction)
+ #self.__activeSlice = NullPieSlice()
+ #self.__activeSlice.menu_init(self, PieSlice.SLICE_CENTER)
+
+ self.__clickPosition = 0, 0
+ self.__popupWindow = None
+ self.__pie = None
+ self.__popupTimeDelay = None
+ self.__styleState = gtk.STATE_NORMAL
+
+ def add_slice(self, slice, direction):
+ assert direction in PieSlice.SLICE_DIRECTIONS
+
+ self.__slices[direction] = slice
+ if self.showAllSlices or direction == PieSlice.SLICE_CENTER:
+ self.__localSlices[direction] = copy.copy(slice)
+ self.__localSlices[direction].menu_init(self, direction)
+
+ if direction == PieSlice.SLICE_CENTER:
+ self.__activeSlice = self.__localSlices[PieSlice.SLICE_CENTER]
+
+ def __update_state(self, mousePosition):
+ rect = self.get_allocation()
+ newStyleState = self.__styleState
+
+ if (
+ 0 <= mousePosition[0] and mousePosition[0] < rect.width and
+ 0 <= mousePosition[1] and mousePosition[1] < rect.height
+ ):
+ if self.__clickPosition == (0, 0):
+ newStyleState = gtk.STATE_PRELIGHT
+ else:
+ if self.__clickPosition != (0, 0):
+ newStyleState = gtk.STATE_PRELIGHT
+
+ if newStyleState != self.__styleState:
+ self.__styleState = newStyleState
+ self.__generate_draw_event()
+
+ def __generate_draw_event(self):
+ rect = self.get_allocation()
+ rect.x = 0
+ rect.y = 0
+ self.window.invalidate_rect(rect, True)
+
+ def _on_expose(self, widget, event):
+ cairoContext = self.window.cairo_create()
+ pangoContext = self.create_pango_context()
+ textLayout = pango.Layout(pangoContext)
+
+ rect = self.get_allocation()
+
+ self.centerPosition = event.area.x + event.area.width / 2, event.area.y + event.area.height / 2
+ self.radius = max(rect.width, rect.width) / 3 / 2
+ self.outerRadius = max(rect.width, rect.height)
+
+ for slice in self.__localSlices.itervalues():
+ isSelected = (slice is self.__activeSlice)
+ if not isSelected:
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ slice.draw_bg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ isSelected = self.__clickPosition != (0, 0)
+ self.__activeSlice.draw_bg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ for slice in self.__localSlices.itervalues():
+ isSelected = (slice is self.__activeSlice)
+ if not isSelected:
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ slice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ cairoContext.rectangle(
+ event.area.x,
+ event.area.y,
+ event.area.width,
+ event.area.height,
+ )
+ cairoContext.clip()
+ isSelected = self.__clickPosition != (0, 0)
+ self.__activeSlice.draw_fg(self.__styleState, isSelected, cairoContext, textLayout)
+
+ def _on_leave_notify(self, widget, event):
+ newStyleState = gtk.STATE_NORMAL
+ if newStyleState != self.__styleState:
+ self.__styleState = newStyleState
+ self.__generate_draw_event()
+
+ self._on_motion_notify(widget, event)
+
+ def _on_motion_notify(self, widget, event):
+ self.__update_state(event.get_coords())
+ if self.__popupWindow is None or self.__pie is None:
+ return
+
+ mousePosition = event.get_root_coords()
+ piePosition = self.__popupWindow.get_position()
+ event.x = mousePosition[0] - piePosition[0]
+ event.y = mousePosition[1] - piePosition[1]
+ self.__pie._on_motion_notify(self.__pie, event)
+
+ def _on_button_press(self, widget, event):
+ if len(self.__slices) == 0:
+ return
+
+ self.__clickPosition = event.get_root_coords()
+ self.__generate_draw_event()
+ self.__popupTimeDelay = gobject.timeout_add(100, self._on_delayed_popup)
+
+ def _on_delayed_popup(self):
+ self.__popup(self.__clickPosition)
+ gobject.source_remove(self.__popupTimeDelay)
+ self.__popupTimeDelay = None
+ return False
+
+ def _on_button_release(self, widget, event):
+ if len(self.__slices) == 0:
+ return
+
+ if self.__popupTimeDelay is None:
+ mousePosition = event.get_root_coords()
+ piePosition = self.__popupWindow.get_position()
+ eventX = mousePosition[0] - piePosition[0]
+ eventY = mousePosition[1] - piePosition[1]
+ pieRelease = FakeEvent(eventX, eventY, False)
+ self.__pie._on_button_release(self.__pie, pieRelease)
+
+ self.__unpop()
+ else:
+ gobject.source_remove(self.__popupTimeDelay)
+ self.__popupTimeDelay = None
+ self.__activeSlice.activate()
+
+ self.__clickPosition = 0, 0
+ self.__generate_draw_event()
+
+ def __popup(self, position):
+ # @bug Figure out what to do with this assert
+ assert self.__popupWindow is None and self.__pie is None
+ self.__popupWindow = gtk.Window(type = gtk.WINDOW_POPUP)
+ self.__popupWindow.set_title("")
+
+ width, height = 256, 256
+ popupX, popupY = position[0] - width/2, position[1] - height/2
+ self.__popupWindow.move(int(popupX), int(popupY))
+ self.__popupWindow.resize(width, height)
+
+ self.__pie = PieMenu(self.sliceStyle)
+ self.__popupWindow.add(self.__pie)
+ for direction, slice in self.__slices.iteritems():
+ self.__pie.add_slice(copy.copy(slice), direction)
+ pieClick = FakeEvent(width/2, height/2, False)
+ self.__pie._on_button_press(self.__pie, pieClick)
+ self.__pie.connect("button_release_event", self._on_button_release)
+
+ #self.__pie.grab_add()
+ self.__pie.grab_focus()
+
+ #gtk.gdk.pointer_grab(
+ # self.__pie.window,
+ # True,
+ # gtk.gdk.BUTTON_PRESS_MASK |
+ # gtk.gdk.BUTTON_RELEASE_MASK |
+ # gtk.gdk.ENTER_NOTIFY_MASK |
+ # gtk.gdk.LEAVE_NOTIFY_MASK |
+ # gtk.gdk.POINTER_MOTION_MASK
+ #)
+
+ #gtk.gdk.keyboard_grab(
+ # self.__pie.window,
+ # owner_events=True
+ #)
+
+ self.__popupWindow.show_all()
+
+ def __unpop(self):
+ piePosition = self.__popupWindow.get_position()
+
+ #self.__pie.grab_remove()
+ self.grab_focus()
+ #gtk.gdk.pointer_ungrab()
+
+ self.__popupWindow.destroy()
+ self.__popupWindow = None
+ self.__pie = None
+
+
+gobject.type_register(PiePopup)
+
+
+def pie_main(isPop):
+ win = gtk.Window()
+ win.set_title("Pie Menu Test")
+
+ sliceStyle = generate_pie_style(win)
+ if isPop:
+ target = PiePopup(sliceStyle)
+ else:
+ target = PieMenu(sliceStyle)
+
+ def handler(pie, slice, direction):
+ print pie, slice, direction
+ target.add_slice(TextLabelPieSlice("C", handler=handler), PieSlice.SLICE_CENTER)
+ target.add_slice(TextLabelPieSlice("N", handler=handler), PieSlice.SLICE_NORTH)
+ target.add_slice(TextLabelPieSlice("S", handler=handler), PieSlice.SLICE_SOUTH)
+ target.add_slice(TextLabelPieSlice("E", handler=handler), PieSlice.SLICE_EAST)
+ target.add_slice(TextLabelPieSlice("W", handler=handler), PieSlice.SLICE_WEST)
+
+ win.add(target)
+ win.resize(300, 300)
+ win.connect("destroy", lambda w: gtk.main_quit())
+ win.show_all()
+
+
+if __name__ == "__main__":
+ pie_main(False)
+ pie_main(True)
+ gtk.main()
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import division
+
+import copy
+import warnings
+
+import gobject
+import gtk
+
+import gtkpie
+
+
+class PieKeyboard(gtk.Table):
+
+ def __init__(self, style, rows, columns, alternateStyles=True):
+ super(PieKeyboard, self).__init__(rows, columns, homogeneous=True)
+
+ self.__cells = {}
+ for row in xrange(rows):
+ for column in xrange(columns):
+ popup = gtkpie.PiePopup(
+ self._alternate_style(row, column, style) if alternateStyles else style
+ )
+ self.attach(popup, column, column+1, row, row+1)
+ self.__cells[(row, column)] = popup
+
+ def add_slice(self, row, column, slice, direction):
+ pie = self.__cells[(row, column)]
+ pie.add_slice(slice, direction)
+
+ def add_slices(self, row, column, slices):
+ pie = self.__cells[(row, column)]
+ for direction, slice in slices.iteritems():
+ pie.add_slice(slice, direction)
+
+ def get_pie(self, row, column):
+ return self.__cells[(row, column)]
+
+ @classmethod
+ def _alternate_style(cls, row, column, style):
+ i = row + column
+ isEven = (i % 2) == 0
+
+ if not isEven:
+ return style
+
+ altStyle = copy.copy(style)
+ selected = altStyle[True]
+ notSelected = altStyle[False]
+ altStyle[False] = selected
+ altStyle[True] = notSelected
+ return altStyle
+
+
+class KeyboardModifier(object):
+
+ def __init__(self, name):
+ self.name = name
+ self.lock = False
+ self.once = False
+
+ @property
+ def isActive(self):
+ return self.lock or self.once
+
+ def on_toggle_lock(self, *args, **kwds):
+ self.lock = not self.lock
+
+ def on_toggle_once(self, *args, **kwds):
+ self.once = not self.once
+
+ def reset_once(self):
+ self.once = False
+
+
+gobject.type_register(PieKeyboard)
+
+
+def parse_keyboard_data(text):
+ return eval(text)
+
+
+def load_keyboard(keyboardName, dataTree, keyboard, keyboardHandler):
+ for (row, column), pieData in dataTree.iteritems():
+ showAllSlices = pieData["showAllSlices"]
+ keyboard.get_pie(row, column).showAllSlices = showAllSlices
+ for direction, directionName in enumerate(gtkpie.PieSlice.SLICE_DIRECTION_NAMES):
+ if directionName not in pieData:
+ continue
+ sliceName = "%s-(%d, %d)-%s" % (keyboardName, row, column, directionName)
+
+ sliceData = pieData[directionName]
+ sliceAction = sliceData["action"]
+ sliceType = sliceData["type"]
+ if sliceType == "text":
+ text = sliceData["text"]
+ # font = sliceData["font"] # @TODO
+ slice = gtkpie.TextLabelPieSlice(text, handler=keyboardHandler)
+ elif sliceType == "image":
+ path = sliceData["path"]
+ slice = gtkpie.ImageLabelPieSlice(path, handler=keyboardHandler)
+
+ slice.name = sliceName
+ keyboard.add_slice(row, column, slice, direction)
+ keyboardHandler.map_slice_action(slice, sliceAction)
+
+
+class KeyboardHandler(object):
+
+ def __init__(self, keyhandler):
+ self.__keyhandler = keyhandler
+ self.__commandHandlers = {}
+ self.__modifiers = {}
+ self.__sliceActions = {}
+
+ self.register_modifier("Shift")
+ self.register_modifier("Super")
+ self.register_modifier("Control")
+ self.register_modifier("Alt")
+
+ def register_command_handler(self, command, handler):
+ #@todo Make this handle multiple handlers or switch to gobject events
+ self.__commandHandlers["[%s]" % command] = handler
+
+ def unregister_command_handler(self, command):
+ #@todo Make this handle multiple handlers or switch to gobject events
+ del self.__commandHandlers["[%s]" % command]
+
+ def register_modifier(self, modifierName):
+ mod = KeyboardModifier(modifierName)
+ self.register_command_handler(modifierName, mod.on_toggle_lock)
+ self.__modifiers["<%s>" % modifierName] = mod
+
+ def unregister_modifier(self, modifierName):
+ self.unregister_command_handler(modifierName)
+ del self.__modifiers["<%s>" % modifierName]
+
+ def map_slice_action(self, slice, action):
+ self.__sliceActions[slice.name] = action
+
+ def __call__(self, pie, slice, direction):
+ try:
+ action = self.__sliceActions[slice.name]
+ except KeyError:
+ return
+
+ activeModifiers = [
+ mod.name
+ for mod in self.__modifiers.itervalues()
+ if mod.isActive
+ ]
+
+ needResetOnce = False
+ if action.startswith("[") and action.endswith("]"):
+ commandName = action[1:-1]
+ if action in self.__commandHandlers:
+ self.__commandHandlers[action](commandName, activeModifiers)
+ needResetOnce = True
+ else:
+ warnings.warn("Unknown command: [%s]" % commandName)
+ elif action.startswith("<") and action.endswith(">"):
+ modName = action[1:-1]
+ for mod in self.__modifiers.itervalues():
+ if mod.name == modName:
+ mod.on_toggle_once()
+ break
+ else:
+ warnings.warn("Unknown modifier: <%s>" % modName)
+ else:
+ self.__keyhandler(action, activeModifiers)
+ needResetOnce = True
+
+ if needResetOnce:
+ for mod in self.__modifiers.itervalues():
+ mod.reset_once()
--- /dev/null
+#!/usr/bin/env python
--- /dev/null
+#!/usr/bin/env python
+
+"""
+@note Source http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66448
+"""
+
+import itertools
+import functools
+import datetime
+import types
+
+
+def ordered_itr(collection):
+ """
+ >>> [v for v in ordered_itr({"a": 1, "b": 2})]
+ [('a', 1), ('b', 2)]
+ >>> [v for v in ordered_itr([3, 1, 10, -20])]
+ [-20, 1, 3, 10]
+ """
+ if isinstance(collection, types.DictType):
+ keys = list(collection.iterkeys())
+ keys.sort()
+ for key in keys:
+ yield key, collection[key]
+ else:
+ values = list(collection)
+ values.sort()
+ for value in values:
+ yield value
+
+
+def itercat(*iterators):
+ """
+ Concatenate several iterators into one.
+
+ >>> [v for v in itercat([1, 2, 3], [4, 1, 3])]
+ [1, 2, 3, 4, 1, 3]
+ """
+ for i in iterators:
+ for x in i:
+ yield x
+
+
+def iterwhile(func, iterator):
+ """
+ Iterate for as long as func(value) returns true.
+ >>> through = lambda b: b
+ >>> [v for v in iterwhile(through, [True, True, False])]
+ [True, True]
+ """
+ iterator = iter(iterator)
+ while 1:
+ next = iterator.next()
+ if not func(next):
+ raise StopIteration
+ yield next
+
+
+def iterfirst(iterator, count=1):
+ """
+ Iterate through 'count' first values.
+
+ >>> [v for v in iterfirst([1, 2, 3, 4, 5], 3)]
+ [1, 2, 3]
+ """
+ iterator = iter(iterator)
+ for i in xrange(count):
+ yield iterator.next()
+
+
+def iterstep(iterator, n):
+ """
+ Iterate every nth value.
+
+ >>> [v for v in iterstep([1, 2, 3, 4, 5], 1)]
+ [1, 2, 3, 4, 5]
+ >>> [v for v in iterstep([1, 2, 3, 4, 5], 2)]
+ [1, 3, 5]
+ >>> [v for v in iterstep([1, 2, 3, 4, 5], 3)]
+ [1, 4]
+ """
+ iterator = iter(iterator)
+ while True:
+ yield iterator.next()
+ # skip n-1 values
+ for dummy in xrange(n-1):
+ iterator.next()
+
+
+def itergroup(iterator, count, to_container=tuple):
+ """
+ Iterate in groups of 'count' values. If there
+ aren't enough values, the last result is padded with
+ None.
+
+ >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3):
+ ... print val
+ (1, 2, 3)
+ (4, 5, 6)
+ >>> for val in itergroup([1, 2, 3, 4, 5, 6], 3, list):
+ ... print val
+ [1, 2, 3]
+ [4, 5, 6]
+ >>> for val in itergroup([1, 2, 3, 4, 5, 6, 7], 3):
+ ... print val
+ (1, 2, 3)
+ (4, 5, 6)
+ (7, None, None)
+ >>> for val in itergroup("123456", 3):
+ ... print val
+ ('1', '2', '3')
+ ('4', '5', '6')
+ >>> for val in itergroup("123456", 3, lambda i: "".join(s for s in i if s is not None)):
+ ... print repr(val)
+ '123'
+ '456'
+ """
+
+ iterator = iter(iterator)
+ values_left = [True]
+
+ def values():
+ values_left[0] = False
+ for x in range(count):
+ try:
+ yield iterator.next()
+ values_left[0] = True
+ except StopIteration:
+ yield None
+ while True:
+ value = to_container(values())
+ if not values_left[0]:
+ raise StopIteration
+ yield value
+
+
+def xzip(*iterators):
+ """Iterative version of builtin 'zip'."""
+ iterators = itertools.imap(iter, iterators)
+ while 1:
+ yield tuple([x.next() for x in iterators])
+
+
+def xmap(func, *iterators):
+ """Iterative version of builtin 'map'."""
+ iterators = itertools.imap(iter, iterators)
+ values_left = [1]
+
+ def values():
+ # Emulate map behaviour, i.e. shorter
+ # sequences are padded with None when
+ # they run out of values.
+ values_left[0] = 0
+ for i in range(len(iterators)):
+ iterator = iterators[i]
+ if iterator is None:
+ yield None
+ else:
+ try:
+ yield iterator.next()
+ values_left[0] = 1
+ except StopIteration:
+ iterators[i] = None
+ yield None
+ while 1:
+ args = tuple(values())
+ if not values_left[0]:
+ raise StopIteration
+ yield func(*args)
+
+
+def xfilter(func, iterator):
+ """Iterative version of builtin 'filter'."""
+ iterator = iter(iterator)
+ while 1:
+ next = iterator.next()
+ if func(next):
+ yield next
+
+
+def xreduce(func, iterator, default=None):
+ """Iterative version of builtin 'reduce'."""
+ iterator = iter(iterator)
+ try:
+ prev = iterator.next()
+ except StopIteration:
+ return default
+ single = 1
+ for next in iterator:
+ single = 0
+ prev = func(prev, next)
+ if single:
+ return func(prev, default)
+ return prev
+
+
+def daterange(begin, end, delta = datetime.timedelta(1)):
+ """
+ Form a range of dates and iterate over them.
+
+ Arguments:
+ begin -- a date (or datetime) object; the beginning of the range.
+ end -- a date (or datetime) object; the end of the range.
+ delta -- (optional) a datetime.timedelta object; how much to step each iteration.
+ Default step is 1 day.
+
+ Usage:
+ """
+ if not isinstance(delta, datetime.timedelta):
+ delta = datetime.timedelta(delta)
+
+ ZERO = datetime.timedelta(0)
+
+ if begin < end:
+ if delta <= ZERO:
+ raise StopIteration
+ test = end.__gt__
+ else:
+ if delta >= ZERO:
+ raise StopIteration
+ test = end.__lt__
+
+ while test(begin):
+ yield begin
+ begin += delta
+
+
+class LazyList(object):
+ """
+ A Sequence whose values are computed lazily by an iterator.
+
+ Module for the creation and use of iterator-based lazy lists.
+ this module defines a class LazyList which can be used to represent sequences
+ of values generated lazily. One can also create recursively defined lazy lists
+ that generate their values based on ones previously generated.
+
+ Backport to python 2.5 by Michael Pust
+ """
+
+ __author__ = 'Dan Spitz'
+
+ def __init__(self, iterable):
+ self._exhausted = False
+ self._iterator = iter(iterable)
+ self._data = []
+
+ def __len__(self):
+ """Get the length of a LazyList's computed data."""
+ return len(self._data)
+
+ def __getitem__(self, i):
+ """Get an item from a LazyList.
+ i should be a positive integer or a slice object."""
+ if isinstance(i, int):
+ #index has not yet been yielded by iterator (or iterator exhausted
+ #before reaching that index)
+ if i >= len(self):
+ self.exhaust(i)
+ elif i < 0:
+ raise ValueError('cannot index LazyList with negative number')
+ return self._data[i]
+
+ #LazyList slices are iterators over a portion of the list.
+ elif isinstance(i, slice):
+ start, stop, step = i.start, i.stop, i.step
+ if any(x is not None and x < 0 for x in (start, stop, step)):
+ raise ValueError('cannot index or step through a LazyList with'
+ 'a negative number')
+ #set start and step to their integer defaults if they are None.
+ if start is None:
+ start = 0
+ if step is None:
+ step = 1
+
+ def LazyListIterator():
+ count = start
+ predicate = (
+ (lambda: True)
+ if stop is None
+ else (lambda: count < stop)
+ )
+ while predicate():
+ try:
+ yield self[count]
+ #slices can go out of actual index range without raising an
+ #error
+ except IndexError:
+ break
+ count += step
+ return LazyListIterator()
+
+ raise TypeError('i must be an integer or slice')
+
+ def __iter__(self):
+ """return an iterator over each value in the sequence,
+ whether it has been computed yet or not."""
+ return self[:]
+
+ def computed(self):
+ """Return an iterator over the values in a LazyList that have
+ already been computed."""
+ return self[:len(self)]
+
+ def exhaust(self, index = None):
+ """Exhaust the iterator generating this LazyList's values.
+ if index is None, this will exhaust the iterator completely.
+ Otherwise, it will iterate over the iterator until either the list
+ has a value for index or the iterator is exhausted.
+ """
+ if self._exhausted:
+ return
+ if index is None:
+ ind_range = itertools.count(len(self))
+ else:
+ ind_range = range(len(self), index + 1)
+
+ for ind in ind_range:
+ try:
+ self._data.append(self._iterator.next())
+ except StopIteration: #iterator is fully exhausted
+ self._exhausted = True
+ break
+
+
+class RecursiveLazyList(LazyList):
+
+ def __init__(self, prod, *args, **kwds):
+ super(RecursiveLazyList, self).__init__(prod(self, *args, **kwds))
+
+
+class RecursiveLazyListFactory:
+
+ def __init__(self, producer):
+ self._gen = producer
+
+ def __call__(self, *a, **kw):
+ return RecursiveLazyList(self._gen, *a, **kw)
+
+
+def lazylist(gen):
+ """
+ Decorator for creating a RecursiveLazyList subclass.
+ This should decorate a generator function taking the LazyList object as its
+ first argument which yields the contents of the list in order.
+
+ >>> #fibonnacci sequence in a lazy list.
+ >>> @lazylist
+ ... def fibgen(lst):
+ ... yield 0
+ ... yield 1
+ ... for a, b in itertools.izip(lst, lst[1:]):
+ ... yield a + b
+ ...
+ >>> #now fibs can be indexed or iterated over as if it were an infinitely long list containing the fibonnaci sequence
+ >>> fibs = fibgen()
+ >>>
+ >>> #prime numbers in a lazy list.
+ >>> @lazylist
+ ... def primegen(lst):
+ ... yield 2
+ ... for candidate in itertools.count(3): #start at next number after 2
+ ... #if candidate is not divisible by any smaller prime numbers,
+ ... #it is a prime.
+ ... if all(candidate % p for p in lst.computed()):
+ ... yield candidate
+ ...
+ >>> #same for primes- treat it like an infinitely long list containing all prime numbers.
+ >>> primes = primegen()
+ >>> print fibs[0], fibs[1], fibs[2], primes[0], primes[1], primes[2]
+ 0 1 1 2 3 5
+ >>> print list(fibs[:10]), list(primes[:10])
+ [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
+ """
+ return RecursiveLazyListFactory(gen)
+
+
+def map_func(f):
+ """
+ >>> import misc
+ >>> misc.validate_decorator(map_func)
+ """
+
+ @functools.wraps(f)
+ def wrapper(*args):
+ result = itertools.imap(f, args)
+ return result
+ return wrapper
+
+
+def reduce_func(function):
+ """
+ >>> import misc
+ >>> misc.validate_decorator(reduce_func(lambda x: x))
+ """
+
+ def decorator(f):
+
+ @functools.wraps(f)
+ def wrapper(*args):
+ result = reduce(function, f(args))
+ return result
+ return wrapper
+ return decorator
+
+
+def any_(iterable):
+ """
+ @note Python Version <2.5
+
+ >>> any_([True, True])
+ True
+ >>> any_([True, False])
+ True
+ >>> any_([False, False])
+ False
+ """
+
+ for element in iterable:
+ if element:
+ return True
+ return False
+
+
+def all_(iterable):
+ """
+ @note Python Version <2.5
+
+ >>> all_([True, True])
+ True
+ >>> all_([True, False])
+ False
+ >>> all_([False, False])
+ False
+ """
+
+ for element in iterable:
+ if not element:
+ return False
+ return True
+
+
+def for_every(pred, seq):
+ """
+ for_every takes a one argument predicate function and a sequence.
+ @param pred The predicate function should return true or false.
+ @returns true if every element in seq returns true for predicate, else returns false.
+
+ >>> for_every (lambda c: c > 5,(6,7,8,9))
+ True
+
+ @author Source:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52907
+ """
+
+ for i in seq:
+ if not pred(i):
+ return False
+ return True
+
+
+def there_exists(pred, seq):
+ """
+ there_exists takes a one argument predicate function and a sequence.
+ @param pred The predicate function should return true or false.
+ @returns true if any element in seq returns true for predicate, else returns false.
+
+ >>> there_exists (lambda c: c > 5,(6,7,8,9))
+ True
+
+ @author Source:http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52907
+ """
+
+ for i in seq:
+ if pred(i):
+ return True
+ return False
+
+
+def func_repeat(quantity, func, *args, **kwd):
+ """
+ Meant to be in connection with "reduce"
+ """
+ for i in xrange(quantity):
+ yield func(*args, **kwd)
+
+
+def function_map(preds, item):
+ """
+ Meant to be in connection with "reduce"
+ """
+ results = (pred(item) for pred in preds)
+
+ return results
+
+
+def functional_if(combiner, preds, item):
+ """
+ Combines the result of a list of predicates applied to item according to combiner
+
+ @see any, every for example combiners
+ """
+ pass_bool = lambda b: b
+
+ bool_results = function_map(preds, item)
+ return combiner(pass_bool, bool_results)
--- /dev/null
+#!/usr/bin/env python
+
+from __future__ import with_statement
+
+import os
+import sys
+import cPickle
+import weakref
+import threading
+import functools
+import contextlib
+
+
+def synchronized(lock):
+ """
+ Synchronization decorator.
+
+ >>> import misc
+ >>> misc.validate_decorator(synchronized(object()))
+ """
+
+ def wrap(f):
+
+ @functools.wraps(f)
+ def newFunction(*args, **kw):
+ lock.acquire()
+ try:
+ return f(*args, **kw)
+ finally:
+ lock.release()
+ return newFunction
+ return wrap
+
+
+def threaded(f):
+ """
+ This decorator calls the method in a new thread, so execution returns straight away
+
+ >>> import misc
+ >>> misc.validate_decorator(threaded)
+ """
+
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ t = threading.Thread(target=f, args=args, kwargs=kwargs)
+ t.setDaemon(True)
+ t.start()
+ return wrapper
+
+
+def fork(f):
+ """
+ Fork a function into a seperate process and block on it, for forcing reclaiming of resources for highly intensive functions
+ @return The original value through pickling. If it is unable to be pickled, then the pickling exception is passed through
+ @throws Through pickling, exceptions are passed back and re-raised
+ @note source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/511474
+
+ >>> import misc
+ >>> misc.validate_decorator(fork)
+ """
+
+ @functools.wraps(f)
+ def wrapper(*args, **kwds):
+ pread, pwrite = os.pipe()
+ pid = os.fork()
+ if pid > 0:
+ os.close(pwrite)
+ with os.fdopen(pread, 'rb') as f:
+ status, result = cPickle.load(f)
+ os.waitpid(pid, 0)
+ if status == 0:
+ return result
+ else:
+ raise result
+ else:
+ os.close(pread)
+ try:
+ result = f(*args, **kwds)
+ status = 0
+ except Exception, exc:
+ result = exc
+ status = 1
+ with os.fdopen(pwrite, 'wb') as f:
+ try:
+ cPickle.dump((status, result), f, cPickle.HIGHEST_PROTOCOL)
+ except cPickle.PicklingError, exc:
+ cPickle.dump((2, exc), f, cPickle.HIGHEST_PROTOCOL)
+ f.close()
+ sys.exit(0)
+ return wrapper
+
+
+@contextlib.contextmanager
+def qlock(queue, gblock = True, gtimeout = None, pblock = True, ptimeout = None):
+ """
+ Locking with a queue, good for when you want to lock an item passed around
+
+ >>> item = 5
+ >>> lock = Queue.Queue()
+ >>> lock.put(item)
+ >>> with qlock(lock) as i:
+ ... print i
+ 5
+ """
+ item = queue.get(gblock, gtimeout)
+ yield item
+ queue.put(item, pblock, ptimeout)
+
+
+class EventSource(object):
+ """
+ Asynchronous implementation of the observer pattern
+
+ >>> sourceRoot = EventSource()
+ >>> sourceChild1 = EventSource()
+ >>> sourceChild1.register_provided_events("1-event-0", "1-event-1")
+ >>> sourceChild2 = EventSource()
+ >>> sourceChild2.register_provided_events("1-event-0", "1-event-1")
+ >>> sourceRoot.add_children(sourceChild1, sourceChild2)
+ """
+
+ def __init__(self):
+ """
+ @warning Not thread safe
+ """
+
+ self.__callbackQueues = {}
+ self.__children = []
+
+ def add_children(self, *childrenSources):
+ """
+ @warning Not thread safe
+ """
+
+ self.__children.extend(childrenSources)
+
+ def remove_children(self, *childrenSources):
+ """
+ @warning Not thread safe
+ """
+
+ for child in childrenSources:
+ self.__children.remove(child)
+
+ def register_provided_events(self, *events):
+ """
+ @warning Not thread safe
+ """
+
+ self.__callbackQueues.update(dict((event, []) for event in events))
+
+ def notify_observers(self, event, message):
+ """
+ @warning As threadsafe as the queue used. qlock is recommended for the message if it needs locking
+ """
+
+ for queue in self.__callbackQueues[event]:
+ queue.put(message)
+
+ def _register_queue(self, event, queue):
+ """
+ @warning Not thread safe
+ """
+
+ if event in self.__callbackQueues:
+ self.__callbackQueues[event].append(queue)
+ return self
+ else:
+ for child in self.__children:
+ source = child._register_queue(event, queue)
+ if source is not None:
+ return source
+ else:
+ return None
+
+ def _unregister_queue(self, event, queue):
+ """
+ @warning Not thread safe
+ """
+
+ if event in self.__callbackQueues:
+ self.__callbackQueues[event].remove(queue)
+ return self
+ else:
+ for child in self.__children:
+ source = child._unregister_queue(event, queue)
+ if source is not None:
+ return source
+ else:
+ return None
+
+
+class StrongEventSourceProxy(object):
+
+ def __init__(self, source):
+ """
+ @warning Not thread safe
+ """
+
+ self.source = source
+
+ def register(self, event, queue):
+ """
+ @warning Not thread safe
+ """
+
+ actualSource = self.source._register_queue(event, queue)
+ ActualType = type(self)
+ return ActualType(actualSource)
+
+ def unregister(self, event, queue):
+ """
+ @warning Not thread safe
+ """
+
+ actualSource = self.source._unregister_queue(event, queue)
+ ActualType = type(self)
+ return ActualType(actualSource)
+
+
+class WeakEventSourceProxy(object):
+
+ def __init__(self, source):
+ """
+ @warning Not thread safe
+ """
+
+ self.source = weakref.ref(source)
+
+ def register(self, event, queue):
+ """
+ @warning Not thread safe
+ """
+
+ actualSource = self.source()._register_queue(event, queue)
+ ActualType = type(self)
+ return ActualType(actualSource)
+
+ def unregister(self, event, queue):
+ """
+ @warning Not thread safe
+ """
+
+ actualSource = self.source()._unregister_queue(event, queue)
+ ActualType = type(self)
+ return ActualType(actualSource)
+
+
+class EventObserver(object):
+ """
+
+ >>> import Queue
+ >>> class Observer(EventObserver):
+ ... def connect_to_source(self, eventSourceRoot):
+ ... self.queue = Queue.Queue()
+ ... self.source = eventSourceRoot.register("1-event-0", self.queue)
+ >>>
+ >>> sourceRoot = EventSource()
+ >>> sourceChild1 = EventSource()
+ >>> sourceChild1.register_provided_events("1-event-0", "1-event-1")
+ >>> sourceChild2 = EventSource()
+ >>> sourceChild2.register_provided_events("1-event-0", "1-event-1")
+ >>> sourceRoot.add_children(sourceChild1, sourceChild2)
+ >>>
+ >>> o1 = Observer()
+ >>> o1.connect_to_source(StrongEventSourceProxy(sourceRoot))
+ >>> o2 = Observer()
+ >>> o2.connect_to_source(WeakEventSourceProxy(sourceRoot))
+ >>>
+ >>> sourceChild1.notify_observers("1-event-0", "Hello World")
+ >>> o1.queue.get(False)
+ 'Hello World'
+ >>> o2.queue.get(False)
+ 'Hello World'
+ """
+
+ def connect_to_source(self, eventSourceRoot):
+ raise NotImplementedError
--- /dev/null
+#!/usr/bin/env python
+
+
+"""
+This module provides three types of queues, with these constructors:
+ Stack([items]) -- Create a Last In First Out queue, implemented as a list
+ Queue([items]) -- Create a First In First Out queue
+ PriorityQueue([items]) -- Create a queue where minimum item (by <) is first
+Here [items] is an optional list of initial items; if omitted, queue is empty.
+Each type supports the following methods and functions:
+ len(q) -- number of items in q (also q.__len__())
+ q.append(item)-- add an item to the queue
+ q.extend(items) -- add each of the items to the queue
+ q.pop() -- remove and return the "first" item from the queue
+"""
+
+
+import types
+import operator
+
+
+def Stack(items=None):
+ "A stack, or last-in-first-out queue, is implemented as a list."
+ return items or []
+
+
+class Queue(object):
+ "A first-in-first-out queue."
+
+ def __init__(self, initialItems=None):
+ self.start = 0
+ self.items = initialItems or []
+
+ def __len__(self):
+ return len(self.items) - self.start
+
+ def append(self, item):
+ self.items.append(item)
+
+ def extend(self, items):
+ self.items.extend(items)
+
+ def pop(self):
+ items = self.items
+ item = items[self.start]
+ self.start += 1
+ if self.start > 100 and self.start > len(items)/2:
+ del items[:self.start]
+ self.start = 0
+ return item
+
+
+class PriorityQueue(object):
+ "A queue in which the minimum element (as determined by cmp) is first."
+
+ def __init__(self, initialItems=None, comparator=operator.lt):
+ self.items = []
+ self.cmp = comparator
+ if initialItems is not None:
+ self.extend(initialItems)
+
+ def __len__(self):
+ return len(self.items)
+
+ def append(self, item):
+ items, cmp_func = self.items, self.cmp
+ items.append(item)
+ i = len(items) - 1
+ while i > 0 and cmp_func(item, items[i//2]):
+ items[i], i = items[i//2], i//2
+ items[i] = item
+
+ def extend(self, items):
+ for item in items:
+ self.append(item)
+
+ def pop(self):
+ items = self.items
+ if len(items) == 1:
+ return items.pop()
+ e = items[0]
+ items[0] = items.pop()
+ self.heapify(0)
+ return e
+
+ def heapify(self, i):
+ """
+ itemsssumes items is an array whose left and right children are heaps,
+ move items[i] into the correct position.See CLR&S p. 130
+ """
+ items, cmp_func = self.items, self.cmp
+ left, right, N = 2*i + 1, 2*i + 2, len(items)-1
+ if left <= N and cmp_func(items[left], items[i]):
+ smallest = left
+ else:
+ smallest = i
+
+ if right <= N and cmp_func(items[right], items[smallest]):
+ smallest = right
+ if smallest != i:
+ items[i], items[smallest] = items[smallest], items[i]
+ self.heapify(smallest)
+
+
+class AttrDict(object):
+ """
+ Can act as a mixin to add dictionary access to members to ease dynamic attribute access
+ or as a wrapper around a class
+
+ >>> class Mixin (AttrDict):
+ ... def __init__ (self):
+ ... AttrDict.__init__ (self)
+ ... self.x = 5
+ ...
+ >>> mixinExample = Mixin ()
+ >>> mixinExample.x
+ 5
+ >>> mixinExample["x"]
+ 5
+ >>> mixinExample["x"] = 10; mixinExample.x
+ 10
+ >>> "x" in mixinExample
+ True
+ >>> class Wrapper (object):
+ ... def __init__ (self):
+ ... self.y = 10
+ ...
+ >>> wrapper = Wrapper()
+ >>> wrapper.y
+ 10
+ >>> wrapperExample = AttrDict (wrapper)
+ >>> wrapperExample["y"]
+ 10
+ >>> wrapperExample["y"] = 20; wrapper.y
+ 20
+ >>> "y" in wrapperExample
+ True
+ """
+
+ def __init__(self, obj = None):
+ self.__obj = obj if obj is not None else self
+
+ def __getitem__(self, name):
+ return getattr(self.__obj, name)
+
+ def __setitem__(self, name, value):
+ setattr(self.__obj, name, value)
+
+ def __delitem__(self, name):
+ delattr(self.__obj, name)
+
+ def __contains__(self, name):
+ return hasattr(self.__obj, name)
+
+
+class Uncertain(object):
+ """
+ Represents a numeric value with a known small uncertainty
+ (error, standard deviation...).
+ Numeric operators are overloaded to work with other Uncertain or
+ numeric objects.
+ The uncertainty (error) must be small. Otherwise the linearization
+ employed here becomes wrong.
+
+ >>> pie = Uncertain(3.14, 0.01)
+ >>> ee = Uncertain(2.718, 0.001)
+ >>> pie, repr(pie)
+ (Uncertain(3.14, 0.01), 'Uncertain(3.14, 0.01)')
+ >>> ee, repr(ee)
+ (Uncertain(2.718, 0.001), 'Uncertain(2.718, 0.001)')
+ >>> pie + ee
+ Uncertain(5.858, 0.0100498756211)
+ >>> pie * ee
+ Uncertain(8.53452, 0.0273607748428)
+ """
+
+ def __init__(self, value=0., error=0., *a, **t):
+ self.value = value
+ self.error = abs(error)
+ super(Uncertain, self).__init__(*a, **t)
+
+ # Conversions
+
+ def __str__(self):
+ return "%g+-%g" % (self.value, self.error)
+
+ def __repr__(self):
+ return "Uncertain(%s, %s)" % (self.value, self.error)
+
+ def __complex__(self):
+ return complex(self.value)
+
+ def __int__(self):
+ return int(self.value)
+
+ def __long__(self):
+ return long(self.value)
+
+ def __float__(self):
+ return self.value
+
+ # Comparison
+
+ def __eq__(self, other):
+ epsilon = max(self.error, other.error)
+ return abs(other.value - self.value) < epsilon
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __hash__(self):
+ return hash(self.value) ^ hash(self.error)
+
+ def __le__(self, other):
+ return self.value < other.value or self == other
+
+ def __lt__(self, other):
+ return self.value < other.value and self != other
+
+ def __gt__(self, other):
+ return not (self <= other)
+
+ def __ge__(self, other):
+ return not (self < other)
+
+ def __nonzero__(self):
+ return self.error < abs(self.value)
+
+ # Math
+
+ def assign(self, other):
+ if isinstance(other, Uncertain):
+ self.value = other.value
+ self.error = other.error
+ else:
+ self.value = other
+ self.error = 0.
+
+ def __add__(self, other):
+ if isinstance(other, Uncertain):
+ v = self.value + other.value
+ e = (self.error**2 + other.error**2) ** .5
+ return Uncertain(v, e)
+ else:
+ return Uncertain(self.value+other, self.error)
+
+ def __sub__(self, other):
+ return self + (-other)
+
+ def __mul__(self, other):
+ if isinstance(other, Uncertain):
+ v = self.value * other.value
+ e = ((self.error * other.value)**2 + (other.error * self.value)**2) ** .5
+ return Uncertain(v, e)
+ else:
+ return Uncertain(self.value*other,
+ self.error*other)
+
+ def __div__(self, other):
+ return self*(1./other)
+
+ def __truediv__(self, other):
+ return self*(1./other)
+
+ def __radd__(self, other):
+ return self + other
+
+ def __rsub__(self, other):
+ return -self + other
+
+ def __rmul__(self, other):
+ return self * other
+
+ def __rdiv__(self, other):
+ return (self/other)**-1.
+
+ def __rtruediv__(self, other):
+ return (self/other)**-1.
+
+ def __neg__(self):
+ return self*-1
+
+ def __pos__(self):
+ return self
+
+ def __abs__(self):
+ return Uncertain(abs(self.value), self.error)
+
+
+class Enumeration(object):
+ """
+ C-Style enumeration mapping attributes to numbers
+
+ >>> Color = Enumeration("Color", ["Red", "Green", "Blue"])
+ >>> Color.Red, Color.Green, Color.Blue
+ (0, 1, 2)
+ >>>
+ >>> Color["Red"], Color.whatis(0)
+ (0, 'Red')
+ >>> Color.names(), Color.values()
+ (['Blue', 'Green', 'Red'], [2, 1, 0])
+ >>>
+ >>> str(Color)
+ "Color: {'Blue': 2, 'Green': 1, 'Red': 0}"
+ >>>
+ >>> 0 in Color, 10 in Color
+ (True, False)
+ >>> "Red" in Color, "Black" in Color
+ (True, False)
+ """
+
+ def __init__(self, name, enumList):
+ self.__name__ = name
+ self.__doc__ = name
+ lookup = { }
+ reverseLookup = { }
+
+ i = 0
+ uniqueNames = [ ]
+ uniqueValues = [ ]
+ for x in enumList:
+ if type(x) == types.TupleType:
+ x, i = x
+ if type(x) != types.StringType:
+ raise TypeError("enum name is not a string: " + x)
+ if type(i) != types.IntType:
+ raise TypeError("enum value is not an integer: " + str(i))
+ if x in uniqueNames:
+ raise ValueError("enum name is not unique: " + x)
+ if i in uniqueValues:
+ raise ValueError("enum value is not unique for " + x)
+ uniqueNames.append(x)
+ uniqueValues.append(i)
+ lookup[x] = i
+ reverseLookup[i] = x
+ i = i + 1
+
+ self.__lookup = lookup
+ self.__reverseLookup = reverseLookup
+
+ def whatis(self, value):
+ return self.__reverseLookup[value]
+
+ def names(self):
+ return self.__lookup.keys()
+
+ def values(self):
+ return self.__lookup.values()
+
+ def __getattr__(self, attr):
+ if attr not in self.__lookup:
+ raise (AttributeError)
+ return self.__lookup[attr]
+
+ def __str__(self):
+ return str(self.__doc__)+": "+str(self.__lookup)
+
+ def __len__(self):
+ return len(self.__lookup)
+
+ def __contains__(self, x):
+ return (x in self.__lookup) or (x in self.__reverseLookup)
+
+ def __getitem__(self, attr):
+ return self.__lookup[attr]
+
+ def __iter__(self):
+ return self.__lookup.itervalues()
+
+ def iterkeys(self):
+ return self.__lookup.iterkeys()
+
+ def itervalues(self):
+ return self.__lookup.itervalues()
+
+ def iteritems(self):
+ return self.__lookup.iteritems()
+
+
+def make_enum(cls):
+ """
+ @todo Make more object orientated (inheritance?)
+ """
+ name = cls.__name__
+ values = cls.__values__
+ return Enumeration(name, values)
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+import contextlib
+import functools
+import math
+
+import gobject
+import gtk
+import gtk.glade
+
+
+def make_idler(func):
+ """
+ Decorator that makes a generator-function into a function that will continue execution on next call
+
+ >>> import misc
+ >>> misc.validate_decorator(make_idler)
+
+ """
+ a = []
+
+ @functools.wraps(func)
+ def decorated_func(*args, **kwds):
+ if not a:
+ a.append(func(*args, **kwds))
+ try:
+ a[0].next()
+ return True
+ except StopIteration:
+ del a[:]
+ return False
+
+ return decorated_func
+
+
+@contextlib.contextmanager
+def gtk_critical_section():
+ #The API changed and I hope these are the right calls
+ gtk.gdk.threads_enter()
+ yield
+ gtk.gdk.threads_leave()
+
+
+if __name__ == "__main__":
+ #gtk.gdk.threads_init()
+ pass
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+import os
+import pickle
+import contextlib
+import itertools
+import functools
+
+
+@contextlib.contextmanager
+def change_directory(directory):
+ previousDirectory = os.getcwd()
+ os.chdir(directory)
+ currentDirectory = os.getcwd()
+
+ try:
+ yield previousDirectory, currentDirectory
+ finally:
+ os.chdir(previousDirectory)
+
+
+@contextlib.contextmanager
+def pickled(filename):
+ """
+ Here is an example usage:
+ with pickled("foo.db") as p:
+ p("users", list).append(["srid", "passwd", 23])
+ """
+
+ if os.path.isfile(filename):
+ data = pickle.load(open(filename))
+ else:
+ data = {}
+
+ def getter(item, factory):
+ if item in data:
+ return data[item]
+ else:
+ data[item] = factory()
+ return data[item]
+
+ yield getter
+
+ pickle.dump(data, open(filename, "w"))
+
+
+@contextlib.contextmanager
+def redirect(object_, attr, value):
+ """
+ >>> import sys
+ ... with redirect(sys, 'stdout', open('stdout', 'w')):
+ ... print "hello"
+ ...
+ >>> print "we're back"
+ """
+ orig = getattr(object_, attr)
+ setattr(object_, attr, value)
+ try:
+ yield
+ finally:
+ setattr(object_, attr, orig)
+
+
+def pathsplit(path):
+ """
+ >>> pathsplit("/a/b/c")
+ ['', 'a', 'b', 'c']
+ >>> pathsplit("./plugins/builtins.ini")
+ ['.', 'plugins', 'builtins.ini']
+ """
+ pathParts = path.split(os.path.sep)
+ return pathParts
+
+
+def commonpath(l1, l2, common=None):
+ """
+ >>> commonpath(pathsplit('/a/b/c/d'), pathsplit('/a/b/c1/d1'))
+ (['', 'a', 'b'], ['c', 'd'], ['c1', 'd1'])
+ >>> commonpath(pathsplit("./plugins/"), pathsplit("./plugins/builtins.ini"))
+ (['.', 'plugins'], [''], ['builtins.ini'])
+ >>> commonpath(pathsplit("./plugins/builtins"), pathsplit("./plugins"))
+ (['.', 'plugins'], ['builtins'], [])
+ """
+ if common is None:
+ common = []
+
+ if l1 == l2:
+ return l1, [], []
+
+ for i, (leftDir, rightDir) in enumerate(zip(l1, l2)):
+ if leftDir != rightDir:
+ return l1[0:i], l1[i:], l2[i:]
+ else:
+ if leftDir == rightDir:
+ i += 1
+ return l1[0:i], l1[i:], l2[i:]
+
+
+def relpath(p1, p2):
+ """
+ >>> relpath('/', '/')
+ './'
+ >>> relpath('/a/b/c/d', '/')
+ '../../../../'
+ >>> relpath('/a/b/c/d', '/a/b/c1/d1')
+ '../../c1/d1'
+ >>> relpath('/a/b/c/d', '/a/b/c1/d1/')
+ '../../c1/d1'
+ >>> relpath("./plugins/builtins", "./plugins")
+ '../'
+ >>> relpath("./plugins/", "./plugins/builtins.ini")
+ 'builtins.ini'
+ """
+ sourcePath = os.path.normpath(p1)
+ destPath = os.path.normpath(p2)
+
+ (common, sourceOnly, destOnly) = commonpath(pathsplit(sourcePath), pathsplit(destPath))
+ if len(sourceOnly) or len(destOnly):
+ relParts = itertools.chain(
+ (('..' + os.sep) * len(sourceOnly), ),
+ destOnly,
+ )
+ return os.path.join(*relParts)
+ else:
+ return "."+os.sep
--- /dev/null
+#!/usr/bin/env python
+
+from __future__ import with_statement
+
+import sys
+import cPickle
+
+import functools
+import itertools
+import contextlib
+import inspect
+
+import optparse
+import traceback
+import warnings
+import string
+
+
+def printfmt(template):
+ """
+ This hides having to create the Template object and call substitute/safe_substitute on it. For example:
+
+ >>> num = 10
+ >>> word = "spam"
+ >>> printfmt("I would like to order $num units of $word, please") #doctest: +SKIP
+ I would like to order 10 units of spam, please
+ """
+ frame = inspect.stack()[-1][0]
+ try:
+ print string.Template(template).safe_substitute(frame.f_locals)
+ finally:
+ del frame
+
+
+def is_special(name):
+ return name.startswith("__") and name.endswith("__")
+
+
+def is_private(name):
+ return name.startswith("_") and not is_special(name)
+
+
+def privatize(clsName, attributeName):
+ """
+ At runtime, make an attributeName private
+
+ Example:
+ >>> class Test(object):
+ ... pass
+ ...
+ >>> dir(Test)
+ ['__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__']
+ >>> setattr(Test, privatize(Test.__name__, "me"), "Hello World")
+ >>> dir(Test)
+ ['_Test__me', '__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__']
+ >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
+ Hello World
+ >>>
+ >>> is_private(privatize(Test.__name__, "me"))
+ True
+ >>> is_special(privatize(Test.__name__, "me"))
+ False
+ """
+ return "".join(["_", clsName, "__", attributeName])
+
+
+def obfuscate(clsName, attributeName):
+ """
+ At runtime, turn a private name into the obfuscated form
+
+ Example:
+ >>> class Test(object):
+ ... __me = "Hello World"
+ ...
+ >>> dir(Test)
+ ['_Test__me', '__class__', '__delattr__', '__dict__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__']
+ >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
+ Hello World
+ >>> is_private(obfuscate(Test.__name__, "__me"))
+ True
+ >>> is_special(obfuscate(Test.__name__, "__me"))
+ False
+ """
+ return "".join(["_", clsName, attributeName])
+
+
+class PAOptionParser(optparse.OptionParser, object):
+ """
+ >>> if __name__ == '__main__':
+ ... #parser = PAOptionParser("My usage str")
+ ... parser = PAOptionParser()
+ ... parser.add_posarg("Foo", help="Foo usage")
+ ... parser.add_posarg("Bar", dest="bar_dest")
+ ... parser.add_posarg("Language", dest='tr_type', type="choice", choices=("Python", "Other"))
+ ... parser.add_option('--stocksym', dest='symbol')
+ ... values, args = parser.parse_args()
+ ... print values, args
+ ...
+
+ python mycp.py -h
+ python mycp.py
+ python mycp.py foo
+ python mycp.py foo bar
+
+ python mycp.py foo bar lava
+ Usage: pa.py <Foo> <Bar> <Language> [options]
+
+ Positional Arguments:
+ Foo: Foo usage
+ Bar:
+ Language:
+
+ pa.py: error: option --Language: invalid choice: 'lava' (choose from 'Python', 'Other'
+ """
+
+ def __init__(self, *args, **kw):
+ self.posargs = []
+ super(PAOptionParser, self).__init__(*args, **kw)
+
+ def add_posarg(self, *args, **kw):
+ pa_help = kw.get("help", "")
+ kw["help"] = optparse.SUPPRESS_HELP
+ o = self.add_option("--%s" % args[0], *args[1:], **kw)
+ self.posargs.append((args[0], pa_help))
+
+ def get_usage(self, *args, **kwargs):
+ params = (' '.join(["<%s>" % arg[0] for arg in self.posargs]), '\n '.join(["%s: %s" % (arg) for arg in self.posargs]))
+ self.usage = "%%prog %s [options]\n\nPositional Arguments:\n %s" % params
+ return super(PAOptionParser, self).get_usage(*args, **kwargs)
+
+ def parse_args(self, *args, **kwargs):
+ args = sys.argv[1:]
+ args0 = []
+ for p, v in zip(self.posargs, args):
+ args0.append("--%s" % p[0])
+ args0.append(v)
+ args = args0 + args
+ options, args = super(PAOptionParser, self).parse_args(args, **kwargs)
+ if len(args) < len(self.posargs):
+ msg = 'Missing value(s) for "%s"\n' % ", ".join([arg[0] for arg in self.posargs][len(args):])
+ self.error(msg)
+ return options, args
+
+
+def explicitly(name, stackadd=0):
+ """
+ This is an alias for adding to '__all__'. Less error-prone than using
+ __all__ itself, since setting __all__ directly is prone to stomping on
+ things implicitly exported via L{alias}.
+
+ @note Taken from PyExport (which could turn out pretty cool):
+ @li @a http://codebrowse.launchpad.net/~glyph/
+ @li @a http://glyf.livejournal.com/74356.html
+ """
+ packageVars = sys._getframe(1+stackadd).f_locals
+ globalAll = packageVars.setdefault('__all__', [])
+ globalAll.append(name)
+
+
+def public(thunk):
+ """
+ This is a decorator, for convenience. Rather than typing the name of your
+ function twice, you can decorate a function with this.
+
+ To be real, @public would need to work on methods as well, which gets into
+ supporting types...
+
+ @note Taken from PyExport (which could turn out pretty cool):
+ @li @a http://codebrowse.launchpad.net/~glyph/
+ @li @a http://glyf.livejournal.com/74356.html
+ """
+ explicitly(thunk.__name__, 1)
+ return thunk
+
+
+def _append_docstring(obj, message):
+ if obj.__doc__ is None:
+ obj.__doc__ = message
+ else:
+ obj.__doc__ += message
+
+
+def validate_decorator(decorator):
+
+ def simple(x):
+ return x
+
+ f = simple
+ f.__name__ = "name"
+ f.__doc__ = "doc"
+ f.__dict__["member"] = True
+
+ g = decorator(f)
+
+ if f.__name__ != g.__name__:
+ print f.__name__, "!=", g.__name__
+
+ if g.__doc__ is None:
+ print decorator.__name__, "has no doc string"
+ elif not g.__doc__.startswith(f.__doc__):
+ print g.__doc__, "didn't start with", f.__doc__
+
+ if not ("member" in g.__dict__ and g.__dict__["member"]):
+ print "'member' not in ", g.__dict__
+
+
+def deprecated(func):
+ """
+ This is a decorator which can be used to mark functions
+ as deprecated. It will result in a warning being emitted
+ when the function is used.
+
+ >>> validate_decorator(deprecated)
+ """
+
+ @functools.wraps(func)
+ def newFunc(*args, **kwargs):
+ warnings.warn("Call to deprecated function %s." % func.__name__, category=DeprecationWarning)
+ return func(*args, **kwargs)
+ _append_docstring(newFunc, "\n@deprecated")
+ return newFunc
+
+
+def enabled(func):
+ """
+ This decorator doesn't add any behavior
+
+ >>> validate_decorator(enabled)
+ """
+ return func
+
+
+def disabled(func):
+ """
+ This decorator disables the provided function, and does nothing
+
+ >>> validate_decorator(disabled)
+ """
+
+ @functools.wraps(func)
+ def emptyFunc(*args, **kargs):
+ pass
+ _append_docstring(emptyFunc, "\n@note Temporarily Disabled")
+ return emptyFunc
+
+
+def metadata(document=True, **kwds):
+ """
+ >>> validate_decorator(metadata(author="Ed"))
+ """
+
+ def decorate(func):
+ for k, v in kwds.iteritems():
+ setattr(func, k, v)
+ if document:
+ _append_docstring(func, "\n@"+k+" "+v)
+ return func
+ return decorate
+
+
+def prop(func):
+ """Function decorator for defining property attributes
+
+ The decorated function is expected to return a dictionary
+ containing one or more of the following pairs:
+ fget - function for getting attribute value
+ fset - function for setting attribute value
+ fdel - function for deleting attribute
+ This can be conveniently constructed by the locals() builtin
+ function; see:
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
+ @author http://kbyanc.blogspot.com/2007/06/python-property-attribute-tricks.html
+
+ Example:
+ >>> #Due to transformation from function to property, does not need to be validated
+ >>> #validate_decorator(prop)
+ >>> class MyExampleClass(object):
+ ... @prop
+ ... def foo():
+ ... "The foo property attribute's doc-string"
+ ... def fget(self):
+ ... print "GET"
+ ... return self._foo
+ ... def fset(self, value):
+ ... print "SET"
+ ... self._foo = value
+ ... return locals()
+ ...
+ >>> me = MyExampleClass()
+ >>> me.foo = 10
+ SET
+ >>> print me.foo
+ GET
+ 10
+ """
+ return property(doc=func.__doc__, **func())
+
+
+def print_handler(e):
+ """
+ @see ExpHandler
+ """
+ print "%s: %s" % (type(e).__name__, e)
+
+
+def print_ignore(e):
+ """
+ @see ExpHandler
+ """
+ print 'Ignoring %s exception: %s' % (type(e).__name__, e)
+
+
+def print_traceback(e):
+ """
+ @see ExpHandler
+ """
+ #print sys.exc_info()
+ traceback.print_exc(file=sys.stdout)
+
+
+def ExpHandler(handler = print_handler, *exceptions):
+ """
+ An exception handling idiom using decorators
+ Examples
+ Specify exceptions in order, first one is handled first
+ last one last.
+
+ >>> validate_decorator(ExpHandler())
+ >>> @ExpHandler(print_ignore, ZeroDivisionError)
+ ... @ExpHandler(None, AttributeError, ValueError)
+ ... def f1():
+ ... 1/0
+ >>> @ExpHandler(print_traceback, ZeroDivisionError)
+ ... def f2():
+ ... 1/0
+ >>> @ExpHandler()
+ ... def f3(*pargs):
+ ... l = pargs
+ ... return l[10]
+ >>> @ExpHandler(print_traceback, ZeroDivisionError)
+ ... def f4():
+ ... return 1
+ >>>
+ >>>
+ >>> f1()
+ Ignoring ZeroDivisionError exception: integer division or modulo by zero
+ >>> f2() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ZeroDivisionError: integer division or modulo by zero
+ >>> f3()
+ IndexError: tuple index out of range
+ >>> f4()
+ 1
+ """
+
+ def wrapper(f):
+ localExceptions = exceptions
+ if not localExceptions:
+ localExceptions = [Exception]
+ t = [(ex, handler) for ex in localExceptions]
+ t.reverse()
+
+ def newfunc(t, *args, **kwargs):
+ ex, handler = t[0]
+ try:
+ if len(t) == 1:
+ return f(*args, **kwargs)
+ else:
+ #Recurse for embedded try/excepts
+ dec_func = functools.partial(newfunc, t[1:])
+ dec_func = functools.update_wrapper(dec_func, f)
+ return dec_func(*args, **kwargs)
+ except ex, e:
+ return handler(e)
+
+ dec_func = functools.partial(newfunc, t)
+ dec_func = functools.update_wrapper(dec_func, f)
+ return dec_func
+ return wrapper
+
+
+class bindclass(object):
+ """
+ >>> validate_decorator(bindclass)
+ >>> class Foo(BoundObject):
+ ... @bindclass
+ ... def foo(this_class, self):
+ ... return this_class, self
+ ...
+ >>> class Bar(Foo):
+ ... @bindclass
+ ... def bar(this_class, self):
+ ... return this_class, self
+ ...
+ >>> f = Foo()
+ >>> b = Bar()
+ >>>
+ >>> f.foo() # doctest: +ELLIPSIS
+ (<class '...Foo'>, <...Foo object at ...>)
+ >>> b.foo() # doctest: +ELLIPSIS
+ (<class '...Foo'>, <...Bar object at ...>)
+ >>> b.bar() # doctest: +ELLIPSIS
+ (<class '...Bar'>, <...Bar object at ...>)
+ """
+
+ def __init__(self, f):
+ self.f = f
+ self.__name__ = f.__name__
+ self.__doc__ = f.__doc__
+ self.__dict__.update(f.__dict__)
+ self.m = None
+
+ def bind(self, cls, attr):
+
+ def bound_m(*args, **kwargs):
+ return self.f(cls, *args, **kwargs)
+ bound_m.__name__ = attr
+ self.m = bound_m
+
+ def __get__(self, obj, objtype=None):
+ return self.m.__get__(obj, objtype)
+
+
+class ClassBindingSupport(type):
+ "@see bindclass"
+
+ def __init__(mcs, name, bases, attrs):
+ type.__init__(mcs, name, bases, attrs)
+ for attr, val in attrs.iteritems():
+ if isinstance(val, bindclass):
+ val.bind(mcs, attr)
+
+
+class BoundObject(object):
+ "@see bindclass"
+ __metaclass__ = ClassBindingSupport
+
+
+def bindfunction(f):
+ """
+ >>> validate_decorator(bindfunction)
+ >>> @bindfunction
+ ... def factorial(thisfunction, n):
+ ... # Within this function the name 'thisfunction' refers to the factorial
+ ... # function(with only one argument), even after 'factorial' is bound
+ ... # to another object
+ ... if n > 0:
+ ... return n * thisfunction(n - 1)
+ ... else:
+ ... return 1
+ ...
+ >>> factorial(3)
+ 6
+ """
+
+ @functools.wraps(f)
+ def bound_f(*args, **kwargs):
+ return f(bound_f, *args, **kwargs)
+ return bound_f
+
+
+class Memoize(object):
+ """
+ Memoize(fn) - an instance which acts like fn but memoizes its arguments
+ Will only work on functions with non-mutable arguments
+ @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
+
+ >>> validate_decorator(Memoize)
+ """
+
+ def __init__(self, fn):
+ self.fn = fn
+ self.__name__ = fn.__name__
+ self.__doc__ = fn.__doc__
+ self.__dict__.update(fn.__dict__)
+ self.memo = {}
+
+ def __call__(self, *args):
+ if args not in self.memo:
+ self.memo[args] = self.fn(*args)
+ return self.memo[args]
+
+
+class MemoizeMutable(object):
+ """Memoize(fn) - an instance which acts like fn but memoizes its arguments
+ Will work on functions with mutable arguments(slower than Memoize)
+ @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
+
+ >>> validate_decorator(MemoizeMutable)
+ """
+
+ def __init__(self, fn):
+ self.fn = fn
+ self.__name__ = fn.__name__
+ self.__doc__ = fn.__doc__
+ self.__dict__.update(fn.__dict__)
+ self.memo = {}
+
+ def __call__(self, *args, **kw):
+ text = cPickle.dumps((args, kw))
+ if text not in self.memo:
+ self.memo[text] = self.fn(*args, **kw)
+ return self.memo[text]
+
+
+callTraceIndentationLevel = 0
+
+
+def call_trace(f):
+ """
+ Synchronization decorator.
+
+ >>> validate_decorator(call_trace)
+ >>> @call_trace
+ ... def a(a, b, c):
+ ... pass
+ >>> a(1, 2, c=3)
+ Entering a((1, 2), {'c': 3})
+ Exiting a((1, 2), {'c': 3})
+ """
+
+ @functools.wraps(f)
+ def verboseTrace(*args, **kw):
+ global callTraceIndentationLevel
+
+ print "%sEntering %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
+ callTraceIndentationLevel += 1
+ try:
+ result = f(*args, **kw)
+ except:
+ callTraceIndentationLevel -= 1
+ print "%sException %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
+ raise
+ callTraceIndentationLevel -= 1
+ print "%sExiting %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
+ return result
+
+ @functools.wraps(f)
+ def smallTrace(*args, **kw):
+ global callTraceIndentationLevel
+
+ print "%sEntering %s" % ("\t"*callTraceIndentationLevel, f.__name__)
+ callTraceIndentationLevel += 1
+ try:
+ result = f(*args, **kw)
+ except:
+ callTraceIndentationLevel -= 1
+ print "%sException %s" % ("\t"*callTraceIndentationLevel, f.__name__)
+ raise
+ callTraceIndentationLevel -= 1
+ print "%sExiting %s" % ("\t"*callTraceIndentationLevel, f.__name__)
+ return result
+
+ #return smallTrace
+ return verboseTrace
+
+
+@contextlib.contextmanager
+def lexical_scope(*args):
+ """
+ @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/520586
+ Example:
+ >>> b = 0
+ >>> with lexical_scope(1) as (a):
+ ... print a
+ ...
+ >>> with lexical_scope(1,2,3) as (a,b,c):
+ ... print a,b,c
+ ...
+ >>> with lexical_scope():
+ ... d = 10
+ ... def foo():
+ ... pass
+ ...
+ >>> print dir() # check those temporary variables are deleted.
+ >>> print b
+ """
+
+ frame = inspect.currentframe().f_back.f_back
+ saved = frame.f_locals.keys()
+ try:
+ if not args:
+ yield
+ elif len(args) == 1:
+ yield args[0]
+ else:
+ yield args
+ finally:
+ f_locals = frame.f_locals
+ for key in (x for x in f_locals.keys() if x not in saved):
+ del f_locals[key]
+ del frame
--- /dev/null
+#!/usr/bin/env python
+
+"""
+Example Operators for comparison
+>>> class C(object):
+... def __init__(self, x):
+... self.x = x
+...
+>>> x, y, z = C(1), C(1), C(2)
+>>> x == y, hash(x) == hash(y)
+(False, False)
+"""
+
+
+import operator
+import itertools
+
+
+class KeyedEqualityOperators(object):
+ """
+ Mixin for auto-implementing comparison operators
+ @note Requires inheriting class to implement a '__key__' function
+ Example:
+ >>> class C(KeyedEqualityOperators):
+ ... def __init__(self, x):
+ ... self.x = x
+ ... def __key__(self):
+ ... return self.x
+ ...
+ >>> x, y, z = C(1), C(1), C(2)
+ >>> x == y, hash(x) == hash(y)
+ (True, False)
+ """
+
+ def __init__(self):
+ self.__key__ = None
+
+ def __eq__(self, other):
+ return self.__key__() == other.__key__()
+
+ def __ne__(self, other):
+ return self.__key__() != other.__key__()
+
+
+class KeyedComparisonOperators(KeyedEqualityOperators):
+ """
+ Mixin for auto-implementing comparison operators
+ @note Requires inheriting class to implement a '__key__' function
+ Example:
+ >>> class C(KeyedComparisonOperators):
+ ... def __init__(self, x):
+ ... self.x = x
+ ... def __key__(self):
+ ... return self.x
+ ...
+ >>> x, y, z = C(1), C(1), C(2)
+ >>> x == y, y < z, hash(x) == hash(y)
+ (True, True, False)
+ """
+
+ def __init__(self):
+ self.__key__ = None
+
+ def __cmp__(self, other):
+ return cmp(self.__key__(), other.__key__())
+
+ def __lt__(self, other):
+ return self.__key__() < other.__key__()
+
+ def __le__(self, other):
+ return self.__key__() <= other.__key__()
+
+ def __gt__(self, other):
+ return self.__key__() > other.__key__()
+
+ def __ge__(self, other):
+ return self.__key__() >= other.__key__()
+
+
+class KeyedHashing(object):
+ """
+ Mixin for auto-implementing comparison operators
+ @note Requires inheriting class to implement a '__key__' function
+ Example:
+ >>> class C(KeyedHashing):
+ ... def __init__(self, x):
+ ... self.x = x
+ ... def __key__(self):
+ ... return self.x
+ ...
+ >>> x, y, z = C(1), C(1), C(2)
+ >>> x == y, hash(x) == hash(y)
+ (False, True)
+ """
+
+ def __init__(self):
+ self.__key__ = None
+
+ def __hash__(self):
+ return hash(self.__key__())
+
+
+class NotEqualOperator(object):
+ """
+ Mixin for auto-implementing comparison operators
+ @note Requires inheriting class to implement '__eq__' function
+ """
+
+ def __ne__(self, other):
+ return not (self == other)
+
+
+class ComparisonOperators(NotEqualOperator):
+ """
+ Mixin for auto-implementing comparison operators
+ @note Requires inheriting class to implement '__lt__' function
+ """
+
+ def __le__(self, other):
+ return(self < other) or(self == other)
+
+ def __gt__(self, other):
+ return not(self <= other)
+
+ def __ge__(self, other):
+ return not(self < other)
+
+
+class infix(object):
+ """
+ Recipe #384122
+ http://code.activestate.com/recipes/384122/
+
+ >>> import operator
+ >>> x = infix(operator.mul)
+ >>> 1 |x| 2 |x| 10
+ 20
+ """
+
+ def __init__(self, func):
+ self.__name__ = func.__name__
+ self.__doc__ = func.__doc__
+ try:
+ self.__dict__.update(func.__dict__)
+ except AttributeError:
+ pass
+ self.function = func
+
+ def __ror__(self, other):
+ return infix(lambda x: self.function(other, x))
+
+ def __or__(self, other):
+ return self.function(other)
+
+ def __call__(self, lhs, rhs):
+ return self.function(lhs, rhs)
+
+
+class Just(object):
+ """
+ @see mreturn
+ """
+
+ def __init__(self, value):
+ self.value = value
+
+
+@infix
+def mbind(maybe, func):
+ """
+ @see mreturn
+ """
+ if maybe is None:
+ return None
+ else:
+ return func(maybe.value)
+
+
+def mreturn(value):
+ """
+ >>> class Sheep(object):
+ ... def __init__(self, name):
+ ... self.name = name
+ ... self.mother = None
+ ... self.father = None
+ ...
+ >>> def father(sheep):
+ ... if sheep.father is None:
+ ... return None
+ ... else:
+ ... return Just(sheep.father)
+ ...
+ >>> def mother(sheep):
+ ... if sheep.mother is None:
+ ... return None
+ ... else:
+ ... return Just(sheep.mother)
+ ...
+ >>> def mothersFather(sheep):
+ ... return mreturn(sheep) |mbind| mother |mbind| father
+ ...
+ >>> def mothersPaternalGrandfather(sheep):
+ ... return mreturn(sheep) |mbind| mother |mbind| father |mbind| father
+ ...
+ >>> shawn = Sheep("Shawn")
+ >>> gertrude = Sheep("Gertrude")
+ >>> ernie = Sheep("Ernie")
+ >>> frank = Sheep("Frank")
+ >>>
+ >>> shawn.mother = gertrude
+ >>> gertrude.father = ernie
+ >>> ernie.father = frank
+ >>>
+ >>> print mothersFather(shawn).value.name
+ Ernie
+ >>> print mothersPaternalGrandfather(shawn).value.name
+ Frank
+ >>> print mothersPaternalGrandfather(ernie)
+ None
+ """
+ return Just(value)
+
+
+def xor(*args):
+ truth = itertools.imap(operator.truth, args)
+ return reduce(operator.xor, truth)
+
+
+def equiv(*args):
+ truth = itertools.imap(operator.truth, args)
+ return reduce(lambda a, b: not operator.xor(a, b), truth)
--- /dev/null
+#!/usr/bin/env python
+import new
+
+# Make the environment more like Python 3.0
+__metaclass__ = type
+from itertools import izip as zip
+import textwrap
+import inspect
+
+
+__all__ = [
+ "AnyType",
+ "overloaded"
+]
+
+
+AnyType = object
+
+
+class overloaded:
+ """
+ Dynamically overloaded functions.
+
+ This is an implementation of (dynamically, or run-time) overloaded
+ functions; also known as generic functions or multi-methods.
+
+ The dispatch algorithm uses the types of all argument for dispatch,
+ similar to (compile-time) overloaded functions or methods in C++ and
+ Java.
+
+ Most of the complexity in the algorithm comes from the need to support
+ subclasses in call signatures. For example, if an function is
+ registered for a signature (T1, T2), then a call with a signature (S1,
+ S2) is acceptable, assuming that S1 is a subclass of T1, S2 a subclass
+ of T2, and there are no other more specific matches (see below).
+
+ If there are multiple matches and one of those doesn't *dominate* all
+ others, the match is deemed ambiguous and an exception is raised. A
+ subtlety here: if, after removing the dominated matches, there are
+ still multiple matches left, but they all map to the same function,
+ then the match is not deemed ambiguous and that function is used.
+ Read the method find_func() below for details.
+
+ @note Python 2.5 is required due to the use of predicates any() and all().
+ @note only supports positional arguments
+
+ @author http://www.artima.com/weblogs/viewpost.jsp?thread=155514
+
+ >>> import misc
+ >>> misc.validate_decorator (overloaded)
+ >>>
+ >>>
+ >>>
+ >>>
+ >>> #################
+ >>> #Basics, with reusing names and without
+ >>> @overloaded
+ ... def foo(x):
+ ... "prints x"
+ ... print x
+ ...
+ >>> @foo.register(int)
+ ... def foo(x):
+ ... "prints the hex representation of x"
+ ... print hex(x)
+ ...
+ >>> from types import DictType
+ >>> @foo.register(DictType)
+ ... def foo_dict(x):
+ ... "prints the keys of x"
+ ... print [k for k in x.iterkeys()]
+ ...
+ >>> #combines all of the doc strings to help keep track of the specializations
+ >>> foo.__doc__ # doctest: +ELLIPSIS
+ "prints x\\n\\n...overloading.foo (<type 'int'>):\\n\\tprints the hex representation of x\\n\\n...overloading.foo_dict (<type 'dict'>):\\n\\tprints the keys of x"
+ >>> foo ("text")
+ text
+ >>> foo (10) #calling the specialized foo
+ 0xa
+ >>> foo ({3:5, 6:7}) #calling the specialization foo_dict
+ [3, 6]
+ >>> foo_dict ({3:5, 6:7}) #with using a unique name, you still have the option of calling the function directly
+ [3, 6]
+ >>>
+ >>>
+ >>>
+ >>>
+ >>> #################
+ >>> #Multiple arguments, accessing the default, and function finding
+ >>> @overloaded
+ ... def two_arg (x, y):
+ ... print x,y
+ ...
+ >>> @two_arg.register(int, int)
+ ... def two_arg_int_int (x, y):
+ ... print hex(x), hex(y)
+ ...
+ >>> @two_arg.register(float, int)
+ ... def two_arg_float_int (x, y):
+ ... print x, hex(y)
+ ...
+ >>> @two_arg.register(int, float)
+ ... def two_arg_int_float (x, y):
+ ... print hex(x), y
+ ...
+ >>> two_arg.__doc__ # doctest: +ELLIPSIS
+ "...overloading.two_arg_int_int (<type 'int'>, <type 'int'>):\\n\\n...overloading.two_arg_float_int (<type 'float'>, <type 'int'>):\\n\\n...overloading.two_arg_int_float (<type 'int'>, <type 'float'>):"
+ >>> two_arg(9, 10)
+ 0x9 0xa
+ >>> two_arg(9.0, 10)
+ 9.0 0xa
+ >>> two_arg(15, 16.0)
+ 0xf 16.0
+ >>> two_arg.default_func(9, 10)
+ 9 10
+ >>> two_arg.find_func ((int, float)) == two_arg_int_float
+ True
+ >>> (int, float) in two_arg
+ True
+ >>> (str, int) in two_arg
+ False
+ >>>
+ >>>
+ >>>
+ >>> #################
+ >>> #wildcard
+ >>> @two_arg.register(AnyType, str)
+ ... def two_arg_any_str (x, y):
+ ... print x, y.lower()
+ ...
+ >>> two_arg("Hello", "World")
+ Hello world
+ >>> two_arg(500, "World")
+ 500 world
+ """
+
+ def __init__(self, default_func):
+ # Decorator to declare new overloaded function.
+ self.registry = {}
+ self.cache = {}
+ self.default_func = default_func
+ self.__name__ = self.default_func.__name__
+ self.__doc__ = self.default_func.__doc__
+ self.__dict__.update (self.default_func.__dict__)
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ return new.instancemethod(self, obj)
+
+ def register(self, *types):
+ """
+ Decorator to register an implementation for a specific set of types.
+
+ .register(t1, t2)(f) is equivalent to .register_func((t1, t2), f).
+ """
+
+ def helper(func):
+ self.register_func(types, func)
+
+ originalDoc = self.__doc__ if self.__doc__ is not None else ""
+ typeNames = ", ".join ([str(type) for type in types])
+ typeNames = "".join ([func.__module__+".", func.__name__, " (", typeNames, "):"])
+ overloadedDoc = ""
+ if func.__doc__ is not None:
+ overloadedDoc = textwrap.fill (func.__doc__, width=60, initial_indent="\t", subsequent_indent="\t")
+ self.__doc__ = "\n".join ([originalDoc, "", typeNames, overloadedDoc]).strip()
+
+ new_func = func
+
+ #Masking the function, so we want to take on its traits
+ if func.__name__ == self.__name__:
+ self.__dict__.update (func.__dict__)
+ new_func = self
+ return new_func
+
+ return helper
+
+ def register_func(self, types, func):
+ """Helper to register an implementation."""
+ self.registry[tuple(types)] = func
+ self.cache = {} # Clear the cache (later we can optimize this).
+
+ def __call__(self, *args):
+ """Call the overloaded function."""
+ types = tuple(map(type, args))
+ func = self.cache.get(types)
+ if func is None:
+ self.cache[types] = func = self.find_func(types)
+ return func(*args)
+
+ def __contains__ (self, types):
+ return self.find_func(types) is not self.default_func
+
+ def find_func(self, types):
+ """Find the appropriate overloaded function; don't call it.
+
+ @note This won't work for old-style classes or classes without __mro__
+ """
+ func = self.registry.get(types)
+ if func is not None:
+ # Easy case -- direct hit in registry.
+ return func
+
+ # Phillip Eby suggests to use issubclass() instead of __mro__.
+ # There are advantages and disadvantages.
+
+ # I can't help myself -- this is going to be intense functional code.
+ # Find all possible candidate signatures.
+ mros = tuple(inspect.getmro(t) for t in types)
+ n = len(mros)
+ candidates = [sig for sig in self.registry
+ if len(sig) == n and
+ all(t in mro for t, mro in zip(sig, mros))]
+
+ if not candidates:
+ # No match at all -- use the default function.
+ return self.default_func
+ elif len(candidates) == 1:
+ # Unique match -- that's an easy case.
+ return self.registry[candidates[0]]
+
+ # More than one match -- weed out the subordinate ones.
+
+ def dominates(dom, sub,
+ orders=tuple(dict((t, i) for i, t in enumerate(mro))
+ for mro in mros)):
+ # Predicate to decide whether dom strictly dominates sub.
+ # Strict domination is defined as domination without equality.
+ # The arguments dom and sub are type tuples of equal length.
+ # The orders argument is a precomputed auxiliary data structure
+ # giving dicts of ordering information corresponding to the
+ # positions in the type tuples.
+ # A type d dominates a type s iff order[d] <= order[s].
+ # A type tuple (d1, d2, ...) dominates a type tuple of equal length
+ # (s1, s2, ...) iff d1 dominates s1, d2 dominates s2, etc.
+ if dom is sub:
+ return False
+ return all(order[d] <= order[s] for d, s, order in zip(dom, sub, orders))
+
+ # I suppose I could inline dominates() but it wouldn't get any clearer.
+ candidates = [cand
+ for cand in candidates
+ if not any(dominates(dom, cand) for dom in candidates)]
+ if len(candidates) == 1:
+ # There's exactly one candidate left.
+ return self.registry[candidates[0]]
+
+ # Perhaps these multiple candidates all have the same implementation?
+ funcs = set(self.registry[cand] for cand in candidates)
+ if len(funcs) == 1:
+ return funcs.pop()
+
+ # No, the situation is irreducibly ambiguous.
+ raise TypeError("ambigous call; types=%r; candidates=%r" %
+ (types, candidates))
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+import inspect
+import contextlib
+import functools
+
+
+def TODO(func):
+ """
+ unittest test method decorator that ignores
+ exceptions raised by test
+
+ Used to annotate test methods for code that may
+ not be written yet. Ignores failures in the
+ annotated test method; fails if the text
+ unexpectedly succeeds.
+ !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+
+ Example:
+ >>> import unittest
+ >>> class ExampleTestCase(unittest.TestCase):
+ ... @TODO
+ ... def testToDo(self):
+ ... MyModule.DoesNotExistYet('boo')
+ ...
+ """
+
+ @functools.wraps(func)
+ def wrapper(*args, **kw):
+ try:
+ func(*args, **kw)
+ succeeded = True
+ except:
+ succeeded = False
+ assert succeeded is False, \
+ "%s marked TODO but passed" % func.__name__
+ return wrapper
+
+
+def PlatformSpecific(platformList):
+ """
+ unittest test method decorator that only
+ runs test method if os.name is in the
+ given list of platforms
+ !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+ Example:
+ >>> import unittest
+ >>> class ExampleTestCase(unittest.TestCase):
+ ... @PlatformSpecific(('mac', ))
+ ... def testMacOnly(self):
+ ... MyModule.SomeMacSpecificFunction()
+ ...
+ """
+
+ def decorator(func):
+ import os
+
+ @functools.wraps(func)
+ def wrapper(*args, **kw):
+ if os.name in platformList:
+ return func(*args, **kw)
+ return wrapper
+ return decorator
+
+
+def CheckReferences(func):
+ """
+ !author http://kbyanc.blogspot.com/2007/06/pythons-unittest-module-aint-that-bad.html
+ """
+
+ @functools.wraps(func)
+ def wrapper(*args, **kw):
+ refCounts = []
+ for i in range(5):
+ func(*args, **kw)
+ refCounts.append(XXXGetRefCount())
+ assert min(refCounts) != max(refCounts), "Reference counts changed - %r" % refCounts
+
+ return wrapper
+
+
+class expected(object):
+ """
+ >>> with expected(ZeroDivisionError):
+ ... 1 / 0
+ >>> with expected(AssertionError("expected ZeroDivisionError to have been thrown")):
+ ... with expected(ZeroDivisionError):
+ ... 1 / 2
+ Traceback (most recent call last):
+ File "/usr/lib/python2.5/doctest.py", line 1228, in __run
+ compileflags, 1) in test.globs
+ File "<doctest libraries.recipes.context.expected[1]>", line 3, in <module>
+ 1 / 2
+ File "/media/data/Personal/Development/bzr/Recollection-trunk/src/libraries/recipes/context.py", line 139, in __exit__
+ assert t is not None, ("expected {0:%s} to have been thrown" % (self._t.__name__))
+ AssertionError: expected {0:ZeroDivisionError} to have been thrown
+ >>> with expected(Exception("foo")):
+ ... raise Exception("foo")
+ >>> with expected(Exception("bar")):
+ ... with expected(Exception("foo")): # this won't catch it
+ ... raise Exception("bar")
+ ... assert False, "should not see me"
+ >>> with expected(Exception("can specify")):
+ ... raise Exception("can specify prefixes")
+ >>> with expected(Exception("Base class fun")):
+ ... raise IndexError("Base class fun")
+ """
+
+ def __init__(self, e):
+ if isinstance(e, Exception):
+ self._t, self._v = type(e), str(e)
+ elif isinstance(e, type):
+ self._t, self._v = e, ""
+ else:
+ raise Exception("usage: with expected(Exception): ... or "
+ "with expected(Exception(\"text\")): ...")
+
+ def __enter__(self):
+ try:
+ pass
+ except:
+ pass # this is a Python 3000 way of saying sys.exc_clear()
+
+ def __exit__(self, t, v, tb):
+ assert t is not None, ("expected {0:%s} to have been thrown" % (self._t.__name__))
+ return self._t in inspect.getmro(t) and str(v).startswith(self._v)
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+
+def flatten(elem, includeTail = False):
+ """
+ Recursively extract text content.
+
+ @note To get rid of all subelements to a given element, and keep just the text, you can do:
+ elem.text = flatten(elem); del elem[:]
+ """
+ text = elem.text or ""
+ for e in elem:
+ text += flatten(e)
+ if includeTail and e.tail:
+ text += e.tail
+ return text
+
+
+def append(elem, item):
+ """
+ Universal append to an Element
+ @param elem ElementTree.Element
+ @param item Either None, Str/Unicode, or ElementTree.Element
+ """
+ if item is None:
+ return
+
+ if isinstance(item, basestring):
+ if len(elem):
+ elem[-1].tail = (elem[-1].tail or "") + item
+ else:
+ elem.text = (elem.text or "") + item
+ else:
+ elem.append(item)
+
+
+def indent(elem, level=0, indentation=" "):
+ """
+ Add indentation to the data of in an ElementTree
+
+ >>> from xml.etree import ElementTree
+ >>> xmlRoot = ElementTree.fromstring("<xml><tree><bird /></tree></xml>")
+ >>> indent(xmlRoot)
+ >>> ElementTree.dump(xmlRoot)
+ <xml>
+ <tree>
+ <bird />
+ </tree>
+ </xml>
+ """
+
+ i = "\n" + level*indentation
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + indentation
+ for e in elem:
+ indent(e, level+1, indentation)
+ if not e.tail or not e.tail.strip():
+ e.tail = i + indentation
+ if not e.tail or not e.tail.strip():
+ e.tail = i
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = i
+
+
+if __name__ == "__main__":
+ import sys
+ from xml.etree import ElementTree
+ if len(sys.argv) == 3:
+ xml = ElementTree.parse(sys.argv[1])
+ indent(xml.getroot())
+ with open(sys.argv[2], "w") as source:
+ xml.write(source)
+ elif len(sys.argv) == 1:
+ xml = ElementTree.parse(sys.stdin)
+ indent(xml.getroot())
+ xml.write(sys.stdout)
--- /dev/null
+#!/usr/bin/env python
+
+
+import itertools
+import functools
+import decimal
+
+from libraries.recipes import overloading
+from libraries.recipes import algorithms
+
+
+@overloading.overloaded
+def serialize_value(value, base, renderer):
+ yield renderer(value, base)
+
+
+@serialize_value.register(complex, overloading.AnyType, overloading.AnyType)
+def serialize_complex(value, base, renderer):
+ if value.real == 0.0:
+ yield renderer(value.imag*1j, base)
+ elif value.imag == 0.0:
+ yield renderer(value.real, base)
+ else:
+ yield renderer(value.real, base)
+ yield renderer(value.imag*1j, base)
+ yield "+"
+
+
+def render_float(value):
+ return str(value)
+
+
+def render_float_dec(value):
+ floatText = str(value)
+ dec = decimal.Decimal(floatText)
+ return str(dec)
+
+
+def render_float_eng(value):
+ floatText = str(value)
+ dec = decimal.Decimal(floatText)
+ return dec.to_eng_string()
+
+
+def render_float_sci(value):
+ floatText = str(value)
+ dec = decimal.Decimal(floatText)
+ return dec.to_sci_string()
+
+
+def render_complex(floatRender):
+
+ def render_complex_real(value):
+ realRendered = floatRender(value.real)
+ imagRendered = floatRender(value.imag)
+ rendered = "%s+%sj" % (realRendered, imagRendered)
+ return rendered
+
+ return render_complex_real
+
+
+def _seperate_num(rendered, sep, count):
+ """
+ >>> _seperate_num("123", ",", 3)
+ '123'
+ >>> _seperate_num("123456", ",", 3)
+ '123,456'
+ >>> _seperate_num("1234567", ",", 3)
+ '1,234,567'
+ """
+ leadCount = len(rendered) % count
+ choppyRest = algorithms.itergroup(rendered[leadCount:], count)
+ rest = (
+ "".join(group)
+ for group in choppyRest
+ )
+ if 0 < leadCount:
+ lead = rendered[0:leadCount]
+ parts = itertools.chain((lead, ), rest)
+ else:
+ parts = rest
+ return sep.join(parts)
+
+
+def render_integer_oct(value, sep=""):
+ rendered = oct(int(value))
+ if 0 < len(sep):
+ assert rendered.startswith("0")
+ rendered = "0o%s" % _seperate_num(rendered[1:], sep, 3)
+ return rendered
+
+
+def render_integer_dec(value, sep=""):
+ rendered = str(int(value))
+ if 0 < len(sep):
+ rendered = "%s" % _seperate_num(rendered, sep, 3)
+ return rendered
+
+
+def render_integer_hex(value, sep=""):
+ rendered = hex(int(value))
+ if 0 < len(sep):
+ assert rendered.startswith("0x")
+ rendered = "0x%s" % _seperate_num(rendered[2:], sep, 3)
+ return rendered
+
+
+def set_render_int_seperator(renderer, sep):
+
+ @functools.wrap(renderer)
+ def render_with_sep(value):
+ return renderer(value, sep)
+
+ return render_with_sep
+
+
+class render_number(object):
+
+ def __init__(self,
+ ints = None,
+ f = None,
+ c = None,
+ ):
+ if ints is not None:
+ self.render_int = ints
+ else:
+ self.render_int = {
+ 2: render_integer_hex,
+ 8: render_integer_oct,
+ 10: render_integer_dec,
+ 16: render_integer_hex,
+ }
+ self.render_float = f if c is not None else render_float
+ self.render_complex = c if c is not None else self
+
+ def __call__(self, value, base):
+ return self.render(value, base)
+
+ @overloading.overloaded
+ def render(self, value, base):
+ return str(value)
+
+ @render.register(overloading.AnyType, int, overloading.AnyType)
+ def _render_int(self, value, base):
+ renderer = self.render_int.get(base, render_integer_dec)
+ return renderer(value)
+
+ @render.register(overloading.AnyType, float, overloading.AnyType)
+ def _render_float(self, value, base):
+ return self.render_float(value)
+
+ @render.register(overloading.AnyType, complex, overloading.AnyType)
+ def _render_complex(self, value, base):
+ return self.render_float(value)
+
+
+class Operation(object):
+
+ def __init__(self):
+ self.base = 10
+
+ def __str__(self):
+ raise NotImplementedError
+
+ def get_children(self):
+ return []
+
+ def serialize(self, renderer):
+ for child in self.get_children():
+ for childItem in child.serialize(renderer):
+ yield childItem
+
+ def simplify(self):
+ """
+ @returns an operation tree with all constant calculations performed and only variables left
+ """
+ raise NotImplementedError
+
+ def evaluate(self):
+ """
+ @returns a value that the tree represents, if it can't be evaluated,
+ then an exception is throwd
+ """
+ raise NotImplementedError
+
+ def __call__(self):
+ return self.evaluate()
+
+
+class Value(Operation):
+
+ def __init__(self, value, base):
+ super(Value, self).__init__()
+ self.value = value
+ self.base = base
+
+ def serialize(self, renderer):
+ for item in super(Value, self).serialize(renderer):
+ yield item
+ for component in serialize_value(self.value, self.base, renderer):
+ yield component
+
+ def __str__(self):
+ return str(self.value)
+
+ def simplify(self):
+ return self
+
+ def evaluate(self):
+ return self.value
+
+
+class Constant(Operation):
+
+ def __init__(self, name, valueNode):
+ super(Constant, self).__init__()
+ self.name = name
+ self.__valueNode = valueNode
+
+ def serialize(self, renderer):
+ for item in super(Constant, self).serialize(renderer):
+ yield item
+ yield self.name
+
+ def __str__(self):
+ return self.name
+
+ def simplify(self):
+ return self.__valueNode.simplify()
+
+ def evaluate(self):
+ return self.__valueNode.evaluate()
+
+
+class Variable(Operation):
+
+ def __init__(self, name):
+ super(Variable, self).__init__()
+ self.name = name
+
+ def serialize(self, renderer):
+ for item in super(Variable, self).serialize(renderer):
+ yield item
+ yield self.name
+
+ def __str__(self):
+ return self.name
+
+ def simplify(self):
+ return self
+
+ def evaluate(self):
+ raise KeyError
+
+
+class Function(Operation):
+
+ REP_FUNCTION = 0
+ REP_PREFIX = 1
+ REP_INFIX = 2
+ REP_POSTFIX = 3
+
+ _op = None
+ _rep = REP_FUNCTION
+ symbol = None
+ argumentCount = 0
+
+ def __init__(self, *args, **kwd):
+ super(Function, self).__init__()
+ self._args = args
+ self._kwd = kwd
+ self.__simple = self.__simplify()
+ self.__str = self.pretty_print(args, kwd)
+
+ def serialize(self, renderer):
+ for item in super(Function, self).serialize(renderer):
+ yield item
+ yield self.symbol
+
+ def get_children(self):
+ return (
+ arg
+ for arg in self._args
+ )
+
+ def __str__(self):
+ return self.__str
+
+ def simplify(self):
+ return self.__simple
+
+ def evaluate(self):
+ selfArgs = [arg.evaluate() for arg in self._args]
+ return Value(self._op(*selfArgs))
+
+ def __simplify(self):
+ selfArgs = [arg.simplify() for arg in self._args]
+ selfKwd = dict(
+ (name, arg.simplify())
+ for (name, arg) in self._kwd
+ )
+
+ try:
+ args = [arg.evaluate() for arg in selfArgs]
+ base = self.base
+ if base is None:
+ bases = [arg.base for arg in selfArgs]
+ base = bases[0]
+ result = self._op(*args)
+
+ node = Value(result, base)
+ except KeyError:
+ node = type(self)(*selfArgs, **selfKwd)
+
+ return node
+
+ @classmethod
+ def pretty_print(cls, args = None, kwds = None):
+ if args is None:
+ args = []
+ if kwds is None:
+ kwds = {}
+
+ if cls._rep == cls.REP_FUNCTION:
+ positional = (str(arg) for arg in args)
+ named = (
+ "%s=%s" % (str(key), str(value))
+ for (key, value) in kwds.iteritems()
+ )
+ return "%s(%s)" % (
+ cls.symbol,
+ ", ".join(itertools.chain(named, positional)),
+ )
+ elif cls._rep == cls.REP_PREFIX:
+ assert len(args) == 1
+ return "%s %s" % (cls.symbol, args[0])
+ elif cls._rep == cls.REP_POSTFIX:
+ assert len(args) == 1
+ return "%s %s" % (args[0], cls.symbol)
+ elif cls._rep == cls.REP_INFIX:
+ assert len(args) == 2
+ return "(%s %s %s)" % (
+ str(args[0]),
+ str(cls.symbol),
+ str(args[1]),
+ )
+ else:
+ raise AssertionError("Unsupported rep style")
+
+
+def generate_function(op, rep, style, numArgs):
+
+ class GenFunc(Function):
+
+ def __init__(self, *args, **kwd):
+ super(GenFunc, self).__init__(*args, **kwd)
+
+ _op = op
+ _rep = style
+ symbol = rep
+ argumentCount = numArgs
+
+ GenFunc.__name__ = op.__name__
+ return GenFunc
+
+
+def change_base(base, rep):
+
+ class GenFunc(Function):
+
+ def __init__(self, *args, **kwd):
+ super(GenFunc, self).__init__(*args, **kwd)
+ self.base = base
+
+ _op = lambda self, n: n
+ _rep = Function.REP_FUNCTION
+ symbol = rep
+ argumentCount = 1
+
+ GenFunc.__name__ = rep
+ return GenFunc
+
+
+@overloading.overloaded
+def render_operation(render_func, operation):
+ return str(operation)
+
+
+@render_operation.register(overloading.AnyType, Value)
+def render_value(render_func, operation):
+ return render_func(operation.value, operation.base)
+
+
+@render_operation.register(overloading.AnyType, Variable)
+@render_operation.register(overloading.AnyType, Constant)
+def render_variable(render_func, operation):
+ return operation.name
+
+
+@render_operation.register(overloading.AnyType, Function)
+def render_function(render_func, operation):
+ args = [
+ render_operation(render_func, arg)
+ for arg in operation.get_children()
+ ]
+ return operation.pretty_print(args)
--- /dev/null
+#!/usr/bin/env python
+
+
+from __future__ import with_statement
+
+
+import sys
+import os
+import inspect
+import ConfigParser
+
+from libraries import gtkpie
+from libraries import gtkpieboard
+from libraries.recipes import io
+import operation
+
+
+class CommandStackHandler(object):
+
+ def __init__(self, stack, command, operator):
+ self.command = command
+
+ self.__stack = stack
+ self.__operator = operator
+
+ def handler(self, commandName, activeModifiers):
+ self.__stack.apply_operation(self.__operator)
+
+
+class PieKeyboardPlugin(object):
+
+ def __init__(self, name, factory):
+ self.name = name
+ self.factory = factory
+ self.__handler = None
+
+ def setup(self, calcStack, style, boardHandler):
+ self.__handler = boardHandler
+
+ with open(self.factory.mapFile, "r") as mapfile:
+ boardTree = gtkpieboard.parse_keyboard_data("\n".join(mapfile.readlines()))
+
+ rows, columns = boardTree["dimensions"]
+ keyboardName = boardTree["name"]
+ keyTree = boardTree["keys"]
+
+ keyboard = gtkpieboard.PieKeyboard(style, rows, columns)
+ gtkpieboard.load_keyboard(keyboardName, keyTree, keyboard, self.__handler)
+
+ for commandName, operator in self.factory.commands.iteritems():
+ handler = CommandStackHandler(calcStack, commandName, operator)
+ self.__handler.register_command_handler(commandName, handler.handler)
+
+ return keyboard
+
+ def tear_down(self):
+ for commandName, operator in self.factory.commands.itervalues():
+ self.__handler.unregister_command_handler(commandName)
+
+ # Leave our self completely unusable
+ self.name = None
+ self.factory = None
+ self.__handler = None
+
+
+class PieKeyboardPluginFactory(object):
+
+ def __init__(self, pluginName, keyboardMapFile):
+ self.name = pluginName
+ self.mapFile = keyboardMapFile
+ self.commands = {}
+
+ def register_operation(self, commandName, operator):
+ self.commands[commandName] = operator
+
+ def construct_keyboard(self):
+ plugin = PieKeyboardPlugin(self.name, self)
+ return plugin
+
+
+class PluginManager(object):
+
+ def __init__(self, pluginType):
+ self._pluginType = pluginType
+ self._plugins = {}
+ self._enabled = set()
+
+ self.__searchPaths = []
+
+ def add_path(self, *paths):
+ self.__searchPaths.append(paths)
+ self.__scan(paths)
+
+ def rescan(self):
+ self._plugins = {}
+ self.__scan(self.__searchPaths)
+
+ def plugin_info(self, pluginId):
+ pluginData = self._plugins[pluginId]
+ return pluginData["name"], pluginData["version"], pluginData["description"]
+
+ def plugins(self):
+ for id, pluginData in self._plugins.iteritems():
+ yield id, pluginData["name"], pluginData["version"], pluginData["description"]
+
+ def enable_plugin(self, id):
+ assert id in self._plugins
+ self._load_module(id)
+ self._enabled.add(id)
+
+ def disable_plugin(self, id):
+ self._enabled.remove(id)
+
+ def lookup_plugin(self, name):
+ for id, data in self._plugins.iteritems():
+ if data["name"] == name:
+ return id
+
+ def _load_module(self, id):
+ pluginData = self._plugins[id]
+
+ if "module" not in pluginData:
+ pluginPath = pluginData["pluginpath"]
+ dataPath = pluginData["datapath"]
+ assert dataPath.endswith(".ini")
+
+ dataPath = io.relpath(pluginPath, dataPath)
+ pythonPath = dataPath[0:-len(".ini")]
+ modulePath = fspath_to_ipath(pythonPath, "")
+
+ sys.path.append(pluginPath)
+ try:
+ module = __import__(modulePath)
+ finally:
+ sys.path.remove(pluginPath)
+ pluginData["module"] = module
+ else:
+ # @todo Decide if want to call reload
+ module = pluginData["module"]
+
+ return module
+
+ def __scan(self, paths):
+ pluginDataFiles = find_plugins(paths, ".ini")
+
+ for pluginPath, pluginDataFile in pluginDataFiles:
+ config = ConfigParser.SafeConfigParser()
+ config.read(pluginDataFile)
+
+ name = config.get(self._pluginType, "name")
+ version = config.get(self._pluginType, "version")
+ description = config.get(self._pluginType, "description")
+
+ self._plugins[pluginDataFile] = {
+ "name": name,
+ "version": version,
+ "description": description,
+ "datapath": pluginDataFile,
+ "pluginpath": pluginPath,
+ }
+
+
+class ConstantPluginManager(PluginManager):
+
+ def __init__(self):
+ super(ConstantPluginManager, self).__init__("Constants")
+ self.__constants = {}
+ self.__constantsCache = {}
+ self.__isCacheDirty = False
+
+ def enable_plugin(self, id):
+ super(ConstantPluginManager, self).enable_plugin(id)
+ self.__constants[id] = dict(
+ extract_instance_from_plugin(self._plugins[id]["module"], operation.Operation)
+ )
+ self.__isCacheDirty = True
+
+ def disable_plugin(self, id):
+ super(ConstantPluginManager, self).disable_plugin(id)
+ self.__isCacheDirty = True
+
+ @property
+ def constants(self):
+ if self.__isCacheDirty:
+ self.__update_cache()
+ return self.__constantsCache
+
+ def __update_cache(self):
+ self.__constantsCache.clear()
+ for pluginId in self._enabled:
+ self.__constantsCache.update(self.__constants[pluginId])
+ self.__isCacheDirty = False
+
+
+class OperatorPluginManager(PluginManager):
+
+ def __init__(self):
+ super(OperatorPluginManager, self).__init__("Operator")
+ self.__operators = {}
+ self.__operatorsCache = {}
+ self.__isCacheDirty = False
+
+ def enable_plugin(self, id):
+ super(OperatorPluginManager, self).enable_plugin(id)
+ operators = (
+ extract_class_from_plugin(self._plugins[id]["module"], operation.Operation)
+ )
+ self.__operators[id] = dict(
+ (op.symbol, op)
+ for op in operators
+ )
+ self.__isCacheDirty = True
+
+ def disable_plugin(self, id):
+ super(OperatorPluginManager, self).disable_plugin(id)
+ self.__isCacheDirty = True
+
+ @property
+ def operators(self):
+ if self.__isCacheDirty:
+ self.__update_cache()
+ return self.__operatorsCache
+
+ def __update_cache(self):
+ self.__operatorsCache.clear()
+ for pluginId in self._enabled:
+ self.__operatorsCache.update(self.__operators[pluginId])
+ self.__isCacheDirty = False
+
+
+class KeyboardPluginManager(PluginManager):
+
+ def __init__(self):
+ super(KeyboardPluginManager, self).__init__("Keyboard")
+ self.__keyboards = {}
+ self.__keyboardsCache = {}
+ self.__isCacheDirty = False
+
+ def enable_plugin(self, id):
+ super(KeyboardPluginManager, self).enable_plugin(id)
+ keyboards = (
+ extract_instance_from_plugin(self._plugins[id]["module"], PieKeyboardPluginFactory)
+ )
+ self.__keyboards[id] = dict(
+ (board.name, board)
+ for boardVariableName, board in keyboards
+ )
+ self.__isCacheDirty = True
+
+ def disable_plugin(self, id):
+ super(KeyboardPluginManager, self).disable_plugin(id)
+ self.__isCacheDirty = True
+
+ @property
+ def keyboards(self):
+ if self.__isCacheDirty:
+ self.__update_cache()
+ return self.__keyboardsCache
+
+ def __update_cache(self):
+ self.__keyboardsCache.clear()
+ for pluginId in self._enabled:
+ self.__keyboardsCache.update(self.__keyboards[pluginId])
+ self.__isCacheDirty = False
+
+
+def fspath_to_ipath(fsPath, extension = ".py"):
+ """
+ >>> fspath_to_ipath("user/test/file.py")
+ 'user.test.file'
+ """
+ assert fsPath.endswith(extension)
+ CURRENT_DIR = "."+os.sep
+ CURRENT_DIR_LEN = len(CURRENT_DIR)
+ if fsPath.startswith(CURRENT_DIR):
+ fsPath = fsPath[CURRENT_DIR_LEN:]
+
+ if extension:
+ fsPath = fsPath[0:-len(extension)]
+ parts = fsPath.split(os.sep)
+ return ".".join(parts)
+
+
+def find_plugins(searchPaths, fileType=".py"):
+ pythonFiles = (
+ (path, os.path.join(root, file))
+ for path in searchPaths
+ for root, dirs, files in os.walk(path)
+ for file in files
+ if file.endswith(fileType)
+ )
+ return pythonFiles
+
+
+def extract_class_from_plugin(pluginModule, cls):
+ try:
+ for item in pluginModule.__dict__.itervalues():
+ try:
+ if cls in inspect.getmro(item):
+ yield item
+ except AttributeError:
+ pass
+ except AttributeError:
+ pass
+
+
+def extract_instance_from_plugin(pluginModule, cls):
+ try:
+ for name, item in pluginModule.__dict__.iteritems():
+ try:
+ if isinstance(item, cls):
+ yield name, item
+ except AttributeError:
+ pass
+ except AttributeError:
+ pass
--- /dev/null
+#!/usr/bin/env python
--- /dev/null
+[Operator]
+name=Alphabet
+version=0.1
+description=
+
+[Constants]
+name=Alphabet
+version=0.1
+description=
+
+[Keyboard]
+name=Alphabet
+version=0.1
+description=
--- /dev/null
+{
+ "name": "Alphabet",
+ "dimensions": (3, 3),
+ "keys": {
+ (0, 0): {
+ "CENTER": {"action": "e", "type": "text", "text": "E", },
+ "SOUTH": {"action": "q", "type": "text", "text": "Q", },
+ "EAST": {"action": "W", "type": "text", "text": "W", },
+ "showAllSlices": True,
+ },
+ (0, 1): {
+ "CENTER": {"action": "t", "type": "text", "text": "T", },
+ "WEST": {"action": "r", "type": "text", "text": "R", },
+ "EAST": {"action": "y", "type": "text", "text": "Y", },
+ "SOUTH": {"action": "u", "type": "text", "text": "U", },
+ "showAllSlices": True,
+ },
+ (0, 2): {
+ "CENTER": {"action": "i", "type": "text", "text": "I", },
+ "WEST": {"action": "o", "type": "text", "text": "O", },
+ "SOUTH": {"action": "p", "type": "text", "text": "P", },
+ "showAllSlices": True,
+ },
+ (1, 0): {
+ "CENTER": {"action": "a", "type": "text", "text": "A", },
+ "EAST": {"action": "s", "type": "text", "text": "S", },
+ "showAllSlices": True,
+ },
+ (1, 1): {
+ "CENTER": {"action": "h", "type": "text", "text": "H", },
+ "WEST": {"action": "d", "type": "text", "text": "D", },
+ "NORTH": {"action": "f", "type": "text", "text": "F", },
+ "EAST": {"action": "g", "type": "text", "text": "G", },
+ "SOUTH": {"action": "j", "type": "text", "text": "J", },
+ "showAllSlices": True,
+ },
+ (1, 2): {
+ "CENTER": {"action": "l", "type": "text", "text": "L", },
+ "WEST": {"action": "k", "type": "text", "text": "K", },
+ "showAllSlices": True,
+ },
+ (2, 0): {
+ "CENTER": {"action": "c", "type": "text", "text": "C", },
+ "NORTH": {"action": "z", "type": "text", "text": "Z", },
+ "EAST": {"action": "x", "type": "text", "text": "X", },
+ "showAllSlices": True,
+ },
+ (2, 1): {
+ "CENTER": {"action": "b", "type": "text", "text": "B", },
+ "NORTH": {"action": "v", "type": "text", "text": "V", },
+ "showAllSlices": True,
+ },
+ (2, 2): {
+ "CENTER": {"action": "n", "type": "text", "text": "N", },
+ "NORTH_WEST": {"action": "m", "type": "text", "text": "M", },
+ "showAllSlices": True,
+ },
+ },
+}
--- /dev/null
+"""
+Keyboard Origin:
+
+qwe rtyu iop
+as dfghj kl
+zxc vb nm
+
+e t i
+a h l
+c b n
+"""
+
+from __future__ import division
+
+import os
+import operator
+
+import operation
+
+import sys
+sys.path.append("../")
+import plugin_utils
+
+
+_MAP_FILE_PATH = os.path.join(os.path.dirname(__file__), "alphabet.map")
+PLUGIN = plugin_utils.PieKeyboardPluginFactory("Alphabet", _MAP_FILE_PATH)
--- /dev/null
+[Operator]
+name=Builtin
+version=0.1
+description=
+
+[Constants]
+name=Builtin
+version=0.1
+description=
+
+[Keyboard]
+name=Builtin
+version=0.1
+description=
--- /dev/null
+{
+ "name": "Builtins",
+ "dimensions": (4, 3),
+ "keys": {
+ (0, 0): {
+ "CENTER": {"action": "7", "type": "text", "text": "7", },
+ "showAllSlices": True,
+ },
+ (0, 1): {
+ "CENTER": {"action": "8", "type": "text", "text": "8", },
+ "SOUTH": {"action": "[**]", "type": "text", "text": "**", },
+ "showAllSlices": True,
+ },
+ (0, 2): {
+ "CENTER": {"action": "9", "type": "text", "text": "9", },
+ "showAllSlices": True,
+ },
+ (1, 0): {
+ "CENTER": {"action": "4", "type": "text", "text": "4", },
+ "showAllSlices": True,
+ },
+ (1, 1): {
+ "CENTER": {"action": "5", "type": "text", "text": "5", },
+ "EAST": {"action": "[+]", "type": "text", "text": "+", },
+ "WEST": {"action": "[-]", "type": "text", "text": "-", },
+ "NORTH": {"action": "[*]", "type": "text", "text": "*", },
+ "SOUTH": {"action": "[/]", "type": "text", "text": "/", },
+ "showAllSlices": True,
+ },
+ (1, 2): {
+ "CENTER": {"action": "6", "type": "text", "text": "6", },
+ "showAllSlices": True,
+ },
+ (2, 0): {
+ "CENTER": {"action": "1", "type": "text", "text": "1", },
+ "showAllSlices": True,
+ },
+ (2, 1): {
+ "CENTER": {"action": "2", "type": "text", "text": "2", },
+ "NORTH": {"action": "[abs]", "type": "text", "text": "abs", },
+ "showAllSlices": True,
+ },
+ (2, 2): {
+ "CENTER": {"action": "3", "type": "text", "text": "3", },
+ "showAllSlices": True,
+ },
+ (3, 0): {
+ "CENTER": {"action": "[push]", "type": "image", "path": "newline.png", },
+ "NORTH": {"action": "[unpush]", "type": "text", "text": "Undo", },
+ "NORTH_WEST": {"action": "[clear]", "type": "image", "path": "clear.png", },
+ "WEST": {"action": "[backspace]", "type": "image", "path": "backspace.png", },
+ "showAllSlices": False,
+ },
+ (3, 1): {
+ "CENTER": {"action": "0", "type": "text", "text": "0", },
+ "showAllSlices": True,
+ },
+ (3, 2): {
+ "CENTER": {"action": ".", "type": "text", "text": ".", },
+ "NORTH": {"action": "j", "type": "text", "text": "j", },
+ "showAllSlices": True,
+ },
+ },
+}
--- /dev/null
+from __future__ import division
+
+import os
+import operator
+import math
+
+import operation
+
+import sys
+sys.path.append("../")
+import plugin_utils
+
+
+_MAP_FILE_PATH = os.path.join(os.path.dirname(__file__), "builtins.map")
+PLUGIN = plugin_utils.PieKeyboardPluginFactory("Builtin", _MAP_FILE_PATH)
+
+addition = operation.generate_function(operator.add, "+", operation.Function.REP_INFIX, 2)
+subtraction = operation.generate_function(operator.sub, "-", operation.Function.REP_INFIX, 2)
+multiplication = operation.generate_function(operator.mul, "*", operation.Function.REP_INFIX, 2)
+trueDivision = operation.generate_function(operator.truediv, "/", operation.Function.REP_INFIX, 2)
+
+PLUGIN.register_operation("+", addition)
+PLUGIN.register_operation("-", subtraction)
+PLUGIN.register_operation("*", multiplication)
+PLUGIN.register_operation("/", trueDivision)
+
+exponentiation = operation.generate_function(operator.pow, "**", operation.Function.REP_INFIX, 2)
+abs = operation.generate_function(operator.abs, "abs", operation.Function.REP_FUNCTION, 1)
+#factorial = operation.generate_function(math.factorial, "!", operation.Function.REP_POSTFIX, 1)
+
+PLUGIN.register_operation("**", exponentiation)
+PLUGIN.register_operation("abs", abs)
--- /dev/null
+[Operator]
+name=Computer
+version=0.1
+description=
+
+[Constants]
+name=Computer
+version=0.1
+description=
+
+[Keyboard]
+name=Computer
+version=0.1
+description=
--- /dev/null
+{
+ "name": "Computer",
+ "dimensions": (3, 3),
+ "keys": {
+ (0, 0): {
+ "CENTER": {"action": "0x", "type": "text", "text": "0x", },
+ "NORTH": {"action": "[hex]", "type": "text", "text": "hex", },
+ "NORTH_WEST": {"action": "a", "type": "text", "text": "A", },
+ "WEST": {"action": "b", "type": "text", "text": "B", },
+ "SOUTH_WEST": {"action": "c", "type": "text", "text": "C", },
+ "NORTH_EAST": {"action": "d", "type": "text", "text": "D", },
+ "EAST": {"action": "e", "type": "text", "text": "E", },
+ "SOUTH_EAST": {"action": "f", "type": "text", "text": "F", },
+ "showAllSlices": True,
+ },
+ (0, 1): {
+ "CENTER": {"action": "0b", "type": "text", "text": "0b", },
+ "NORTH": {"action": "[oct]", "type": "text", "text": "oct", },
+ "showAllSlices": True,
+ },
+ (0, 2): {
+ "CENTER": {"action": "0o", "type": "text", "text": "0o", },
+ "showAllSlices": True,
+ },
+ (1, 0): {
+ "CENTER": {"action": "[&]", "type": "text", "text": "and", },
+ "showAllSlices": True,
+ },
+ (1, 1): {
+ "CENTER": {"action": "[|]", "type": "text", "text": "or", },
+ "SOUTH": {"action": "[~]", "type": "text", "text": "not", },
+ "showAllSlices": True,
+ },
+ (1, 2): {
+ "CENTER": {"action": "[^]", "type": "text", "text": "xor", },
+ "showAllSlices": True,
+ },
+ (2, 0): {
+ "CENTER": {"action": "[//]", "type": "text", "text": "//", },
+ "NORTH": {"action": "[%]", "type": "text", "text": "%", },
+ "showAllSlices": False,
+ },
+ (2, 1): {
+ "CENTER": {"action": "[dec]", "type": "text", "text": "dec", },
+ "showAllSlices": True,
+ },
+ (2, 2): {
+ "showAllSlices": False,
+ },
+ },
+}
--- /dev/null
+from __future__ import division
+
+import os
+import operator
+import math
+
+import operation
+
+import sys
+sys.path.append("../")
+import plugin_utils
+
+
+_MAP_FILE_PATH = os.path.join(os.path.dirname(__file__), "computer.map")
+PLUGIN = plugin_utils.PieKeyboardPluginFactory("Computer", _MAP_FILE_PATH)
+
+hex = operation.change_base(16, "hex")
+oct = operation.change_base(8, "oct")
+dec = operation.change_base(10, "dec")
+ceil = operation.generate_function(math.ceil, "ceil", operation.Function.REP_FUNCTION, 1)
+floor = operation.generate_function(math.floor, "floor", operation.Function.REP_FUNCTION, 1)
+
+PLUGIN.register_operation("hex", hex)
+PLUGIN.register_operation("oct", oct)
+PLUGIN.register_operation("dec", dec)
+PLUGIN.register_operation("ceil", ceil)
+PLUGIN.register_operation("floor", floor)
+
+floorDivision = operation.generate_function(operator.floordiv, "//", operation.Function.REP_INFIX, 2)
+modulo = operation.generate_function(operator.mod, "%", operation.Function.REP_INFIX, 2)
+
+PLUGIN.register_operation("//", floorDivision)
+PLUGIN.register_operation("%", modulo)
+
+bitAnd = operation.generate_function(operator.and_, "&", operation.Function.REP_INFIX, 2)
+bitOr = operation.generate_function(operator.or_, "|", operation.Function.REP_INFIX, 2)
+bitXor = operation.generate_function(operator.xor, "^", operation.Function.REP_INFIX, 2)
+bitInvert = operation.generate_function(operator.invert, "~", operation.Function.REP_PREFIX, 1)
+
+PLUGIN.register_operation("&", bitAnd)
+PLUGIN.register_operation("|", bitOr)
+PLUGIN.register_operation("^", bitXor)
+PLUGIN.register_operation("~", bitInvert)
--- /dev/null
+[Operator]
+name=Trigonometry
+version=0.1
+description=
+
+[Constants]
+name=Trigonometry
+version=0.1
+description=
+
+[Keyboard]
+name=Trigonometry
+version=0.1
+description=
--- /dev/null
+{
+ "name": "Trigonometry",
+ "dimensions": (3, 3),
+ "keys": {
+ (0, 0): {
+ "CENTER": {"action": "[sin]", "type": "text", "text": "sin", },
+ "NORTH": {"action": "[asin]", "type": "text", "text": "asin", },
+ "showAllSlices": False,
+ },
+ (0, 1): {
+ "CENTER": {"action": "[cos]", "type": "text", "text": "cos", },
+ "NORTH": {"action": "[acos]", "type": "text", "text": "acos", },
+ "showAllSlices": False,
+ },
+ (0, 2): {
+ "CENTER": {"action": "[tan]", "type": "text", "text": "tan", },
+ "NORTH": {"action": "[atan]", "type": "text", "text": "atan", },
+ "showAllSlices": False,
+ },
+ (1, 0): {
+ "CENTER": {"action": "[exp]", "type": "text", "text": "exp", },
+ "SOUTH": {"action": "[log]", "type": "text", "text": "log", },
+ "showAllSlices": True,
+ },
+ (1, 1): {
+ "CENTER": {"action": "pi", "type": "text", "text": "pi", },
+ "SOUTH": {"action": "e", "type": "text", "text": "e", },
+ "showAllSlices": True,
+ },
+ (1, 2): {
+ "CENTER": {"action": "[rad]", "type": "text", "text": "rad", },
+ "SOUTH": {"action": "[deg]", "type": "text", "text": "deg", },
+ "showAllSlices": True,
+ },
+ (2, 0): {
+ "CENTER": {"action": "[sinh]", "type": "text", "text": "sinh", },
+ "NORTH": {"action": "[asinh]", "type": "text", "text": "asinh", },
+ "showAllSlices": False,
+ },
+ (2, 1): {
+ "CENTER": {"action": "[cosh]", "type": "text", "text": "cosh", },
+ "NORTH": {"action": "[acosh]", "type": "text", "text": "acosh", },
+ "showAllSlices": False,
+ },
+ (2, 2): {
+ "CENTER": {"action": "[tanh]", "type": "text", "text": "tanh", },
+ "NORTH": {"action": "[atanh]", "type": "text", "text": "atanh", },
+ "showAllSlices": False,
+ },
+ },
+}
--- /dev/null
+from __future__ import division
+
+import os
+import operator
+import math
+import cmath
+
+import operation
+
+import sys
+sys.path.append("../")
+import plugin_utils
+
+
+_MAP_FILE_PATH = os.path.join(os.path.dirname(__file__), "trig.map")
+PLUGIN = plugin_utils.PieKeyboardPluginFactory("Trigonometry", _MAP_FILE_PATH)
+
+pi = operation.Constant("pi", operation.Value(math.pi, operation.render_float_eng))
+e = operation.Constant("e", operation.Value(math.e, operation.render_float_eng))
+
+def float_or_complex(float_func, complex_func):
+
+ def switching_func(self, *args, **kwd):
+ if any(
+ isinstance(arg, complex)
+ for arg in args
+ ):
+ return complex_func(*args, **kwd)
+ else:
+ return float_func(*args, **kwd)
+
+ switching_func.__name__ = complex_func.__name__
+ switching_func.__doc__ = complex_func.__doc__
+ return switching_func
+
+exp = operation.generate_function(float_or_complex(math.exp, cmath.exp), "exp", operation.Function.REP_FUNCTION, 1)
+log = operation.generate_function(float_or_complex(math.exp, cmath.exp), "log", operation.Function.REP_FUNCTION, 1)
+
+PLUGIN.register_operation("exp", exp)
+PLUGIN.register_operation("log", log)
+
+cos = operation.generate_function(float_or_complex(math.cos, cmath.cos), "cos", operation.Function.REP_FUNCTION, 1)
+acos = operation.generate_function(float_or_complex(math.acos, cmath.acos), "acos", operation.Function.REP_FUNCTION, 1)
+sin = operation.generate_function(float_or_complex(math.sin, cmath.sin), "sin", operation.Function.REP_FUNCTION, 1)
+asin = operation.generate_function(float_or_complex(math.asin, cmath.asin), "asin", operation.Function.REP_FUNCTION, 1)
+tan = operation.generate_function(float_or_complex(math.tan, cmath.tan), "tan", operation.Function.REP_FUNCTION, 1)
+atan = operation.generate_function(float_or_complex(math.atan, cmath.atan), "atan", operation.Function.REP_FUNCTION, 1)
+
+PLUGIN.register_operation("cos", cos)
+PLUGIN.register_operation("acos", acos)
+PLUGIN.register_operation("sin", sin)
+PLUGIN.register_operation("asin", asin)
+PLUGIN.register_operation("tan", tan)
+PLUGIN.register_operation("atan", atan)
+
+cosh = operation.generate_function(float_or_complex(math.cosh, cmath.cosh), "cosh", operation.Function.REP_FUNCTION, 1)
+acosh = operation.generate_function(cmath.acosh, "acosh", operation.Function.REP_FUNCTION, 1)
+sinh = operation.generate_function(float_or_complex(math.sinh, cmath.sinh), "sinh", operation.Function.REP_FUNCTION, 1)
+asinh = operation.generate_function(cmath.asinh, "asinh", operation.Function.REP_FUNCTION, 1)
+tanh = operation.generate_function(float_or_complex(math.tanh, cmath.tanh), "tanh", operation.Function.REP_FUNCTION, 1)
+atanh = operation.generate_function(cmath.atanh, "atanh", operation.Function.REP_FUNCTION, 1)
+
+PLUGIN.register_operation("cosh", cosh)
+PLUGIN.register_operation("acosh", acosh)
+PLUGIN.register_operation("sinh", sinh)
+PLUGIN.register_operation("asinh", asinh)
+PLUGIN.register_operation("tanh", tanh)
+PLUGIN.register_operation("atanh", atanh)
+
+deg = operation.generate_function(math.degrees, "deg", operation.Function.REP_FUNCTION, 1)
+rad = operation.generate_function(math.radians, "rad", operation.Function.REP_FUNCTION, 1)
+
+PLUGIN.register_operation("deg", deg)
+PLUGIN.register_operation("rad", rad)
+
+# In 2.6
+#phase = operation.generate_function(cmath.phase, "phase", operation.Function.REP_FUNCTION, 1)
+#polar = operation.generate_function(cmath.polar, "polar", operation.Function.REP_FUNCTION, 1)
+#rect = operation.generate_function(cmath.rect, "rect", operation.Function.REP_FUNCTION, 1)
+
--- /dev/null
+# lint Python modules using external checkers.
+#
+# This is the main checker controling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+#disable-msg-cat=
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+disable-msg=W0403,W0612,W0613,C0103,C0111,C0301
+
+[REPORTS]
+
+# set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Include message's id in output
+include-ids=no
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note).You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=no
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+acquired-members=REQUEST,acl_users,aq_parent
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=1
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+# @note Limiting this to the most extreme cases
+max-line-length=100
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='\t'