diff --git a/.idea/Py.iml b/.idea/Py.iml new file mode 100644 index 0000000..a34a857 --- /dev/null +++ b/.idea/Py.iml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6402894 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..0847ce8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..7b4a422 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1376297153461 + 1376297153461 + + + + + + + + + + + + + diff --git a/Absolute Book/Python Programming for the Absolute Beginner, 3rd Edition/Python Programming for the Absolute Beginner, 3rd Edition.pdf b/Absolute Book/Python Programming for the Absolute Beginner, 3rd Edition/Python Programming for the Absolute Beginner, 3rd Edition.pdf new file mode 100644 index 0000000..86f0f1b Binary files /dev/null and b/Absolute Book/Python Programming for the Absolute Beginner, 3rd Edition/Python Programming for the Absolute Beginner, 3rd Edition.pdf differ diff --git a/Absolute Book/Python Programming for the Absolute Beginner, 3rd Edition/~uTorrentPartFile_C923F5.dat b/Absolute Book/Python Programming for the Absolute Beginner, 3rd Edition/~uTorrentPartFile_C923F5.dat new file mode 100644 index 0000000..6b807c4 Binary files /dev/null and b/Absolute Book/Python Programming for the Absolute Beginner, 3rd Edition/~uTorrentPartFile_C923F5.dat differ diff --git a/Absolute Book/py3e_software/livewires/build/lib/livewires/__init__.py b/Absolute Book/py3e_software/livewires/build/lib/livewires/__init__.py new file mode 100755 index 0000000..686cdae --- /dev/null +++ b/Absolute Book/py3e_software/livewires/build/lib/livewires/__init__.py @@ -0,0 +1,29 @@ +# Copyright Richard Crook and Gareth McCaughan. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither name of Scripture Union nor LiveWires nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCRIPTURE UNION +# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Absolute Book/py3e_software/livewires/build/lib/livewires/color.py b/Absolute Book/py3e_software/livewires/build/lib/livewires/color.py new file mode 100755 index 0000000..34ce27d --- /dev/null +++ b/Absolute Book/py3e_software/livewires/build/lib/livewires/color.py @@ -0,0 +1,46 @@ +# Copyright Richard Crook and Gareth McCaughan. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither name of Scripture Union nor Live Wires nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCRIPTURE UNION +# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +### Default colour names, as RGB triplets + +red = (255,0,0) +green = (0,255,0) +blue = (0,0,255) +black = (0,0,0) +white = (255,255,255) +dark_red = (127,0,0) +dark_green = (0,102,0) +dark_blue = (0,0,127) +dark_gray = (76,76,76) +gray = (127,127,127) +light_gray = (178,178,178) +yellow = (229,204,0) +brown = (127,89,0) +pink = (255,0,204) +purple = (153,0,178) diff --git a/Absolute Book/py3e_software/livewires/build/lib/livewires/games.py b/Absolute Book/py3e_software/livewires/build/lib/livewires/games.py new file mode 100755 index 0000000..a379776 --- /dev/null +++ b/Absolute Book/py3e_software/livewires/build/lib/livewires/games.py @@ -0,0 +1,894 @@ +############################################################################### +### Games support module for LiveWires using pygame. +### +### $Revision: 1.7 $ -- $Date: 2001/10/27 17:43:51 $ +############################################################################### +# Copyright Richard Crook, Gareth McCaughan, Rhodri James, Neil Turton +# and Paul Wright. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither name of Scripture Union nor LiveWires nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCRIPTURE UNION +# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### +############################################################################### +# Modified by Michael Dawson +# 5/24/05 +# +# Restructured Classes +# - created a single Sprite class (no multiple inheritance) +# - added properties to classes (getter and setter methods still available) +# - added Question class to get user keyboard input +# - added Mouse class for mouse input +# - added Keyboard class for keyboard input +# - added Music class to access music channel +# +# Revised Animation Class +# - now receives only one list of images to animate +# - images now displayed in order, from first frame to last (not the reverse) +# - n_repeats represents number of cycles to display (not number of frames) +# +# "Americanized" Spelling +# - 'colour' is now 'color' +############################################################################### + +import pygame, pygame.image, pygame.mixer, pygame.font, pygame.transform +import pygame.draw +from pygame.locals import * + +pygame.init() + + +############################################################################### +## Error classes ############################################################## +############################################################################### + +class GamesError(Exception): pass + + +############################################################################### +## Mouse class ################################################################ +############################################################################### + +class Mouse(object): + #------Properties--------# + + ## position + def get_position(self): + return pygame.mouse.get_pos() + + def set_position(self, new_position): + pygame.mouse.set_pos(new_position) + + position = property(get_position, set_position) + + ## x + def get_x(self): + return pygame.mouse.get_pos()[0] + + def set_x(self, new_x): + current_y = pygame.mouse.get_pos()[1] + pygame.mouse.set_pos( (new_x, current_y) ) + + x = property(get_x, set_x) + + ## y + def get_y(self): + return pygame.mouse.get_pos()[1] + + def set_y(self, new_y): + current_mouse_x = pygame.mouse.get_pos()[0] + pygame.mouse.set_pos( (current_x, new_y) ) + + y = property(get_y, set_y) + + ## is visible + def set_is_visible(self, new_visibility): + pygame.mouse.set_visible(new_visibility) + is_visible = property(fset = set_is_visible) + + def is_pressed(self, button_number): + return pygame.mouse.get_pressed()[button_number] == 1 + + +############################################################################### +## Keyboard class ############################################################# +############################################################################### + +class Keyboard(object): + def is_pressed(self, key): + return pygame.key.get_pressed()[key] == 1 + + +############################################################################### +## Music class ################################################################ +############################################################################### + +class Music(object): + def load(self, filename): + pygame.mixer.music.load(filename) + + def play(self, loop=0): + pygame.mixer.music.play(loop) + + def fadeout(self, millisec): + pygame.mixer.music.fadeout(millisec) + + def stop(self): + pygame.mixer.music.stop() + + +############################################################################### +## Screen class ############################################################### +############################################################################### +## +## The Screen object represents the playing area. Since we can have +## only one screen under pygame, it's just a handy container for stuff +## +############################################################################### + +class Screen(object): + + initialized = 0 + + def __init__ (self, width=640, height=480, fps=50): + # Bomb if you try this more than once + if Screen.initialized: + raise GamesError("Cannot have more than on Screen object") + + Screen.initialized = 1 + + # Create the pygame display + #self._display = pygame.display.set_mode ((width, height), HWSURFACE) + self._display = pygame.display.set_mode ((width, height)) + self._width = width + self._height = height + self._background = self._display.convert() + + # Initialize a list of objects in play + self._objects = [] + # Initialize list dirty rectangles to be repainted + self._dirtyrects = [] + + # Time when we should draw the next frame + self._next_tick = 0 + + # Frames per second screen will be updated + self._fps = fps + + #------Properties--------# + + ## width + def get_width(self): + return self._width + + width = property(get_width) + + ## height + def get_height(self): + return self._height + + height = property(get_height) + + ## fps + def get_fps(self): + return self._fps + + fps = property(get_fps) + + ## background + def get_background(self): + return self._background + + def set_background(self, new_background): + """ + Set the background to the surface provided. Note that the + surface should not have transparency set, or weird things + will happen. + """ + self._background = pygame.Surface((self._width, self._height)) + for x in range(0, self._width, new_background.get_width()): + for y in range(0, self._height, new_background.get_height()): + self._background.blit(new_background, (x, y)) + + self._display.blit(self._background, (0,0)) + pygame.display.update() + + background = property(get_background, set_background) + + ## all objects + def get_all_objects(self): + """ + Returns a list of all the Sprites on the Screen. + """ + return self._objects[:] + all_objects = property(get_all_objects) + + ## event_grab + def get_event_grab(self): + return pygame.event.get_grab() + + def set_event_grab(self, new_status): + pygame.event.set_grab(new_status) + + event_grab = property(get_event_grab, set_event_grab) + + def tick(self): + """ + If you override the tick method in a subclass of the Screen + class, you can specify actions which are carried out every + tick. + """ + pass + + def keypress(self, key): + """ + If you override the keypress method, you will be able to + handle individual keypresses instead of dealing with the + keys held down as in the standard library + """ + pass + + def handle_events(self): + """ + If you override this method in a subclass of the Screen + class, you can specify how to handle different kinds of + events. However you must handle the quit condition! + """ + events = pygame.event.get() + for event in events: + if event.type == QUIT: + self.quit() + elif event.type == KEYDOWN: + if event.key == K_ESCAPE: + self.quit() + else: + self.keypress(event.key) + + def quit(self): + """ + Calling this method will stop the main loop from running and + make the graphics window disappear. + """ + self._exit = 1 + + def clear(self): + """ + Destroy all objects on this Screen. + """ + for object in self._objects[:]: + object.destroy() + self._objects = [] + + def _update_display(self): + """ + Get the actual display in sync with reality. + """ + pygame.display.update(self._dirtyrects) + self._dirtyrects = [] + + def mainloop(self): + """ + Run the pygame main loop. This will animate the objects on the + screen and call their tick methods every tick. + """ + self._exit = 0 + + while not self._exit: + self._wait_frame() + + for object in self._objects: + object._erase() + + # Take a copy of the _objects list as it may get changed in place. + for object in self._objects[:]: + if object._tickable: + object._tick() + + self.tick() + + for object in self._objects: + object._draw() + + self._update_display() + pygame.display.flip() + + self.handle_events() + + # Throw away any pending events. + pygame.event.get() + + def _wait_frame (self): + "Wait for the correct fps time to expire" + this_tick = pygame.time.get_ticks() + if this_tick < self._next_tick: + pygame.time.delay(int(self._next_tick+0.5) - this_tick) + self._next_tick = this_tick + (1000./self._fps) + + def overlapping_objects(self, rectangle): + """ + Return list of all sprites which overlap given rectangle. + """ + rect = pygame.Rect (rectangle) + + rect_list = [] + for obj in self._objects: + rect_list.append (obj._rect) + + indices = rect.collidelistall (rect_list) + + over_objects = [] + for index in indices: + if (self._objects[index]).is_collideable: + over_objects.append (self._objects [index]) + + return over_objects + + def _elevate(self, it, above=None): + """ + Elevates an object to the top of the stack, or above the specified + object. + """ + # This makes sure we're always in a consistent state. + objects = self._objects[:] + # Remove the object from the list. + objects.remove(it) + if above == None: + # Put it on top (the end). + objects.append(it) + else: + # Put the object after . + idx = 1+objects.index(above) + objects[idx:idx]=[it] + # Install the new list. + self._objects = objects + + def _lower(self, object, below=None): + """ + Lower an object to the bottom of the stack, or below the specified + object. + """ + # This makes sure we're always in a consistent state. + objects = self._objects[:] + objects.remove(it) + if below == None: + # Put the object on the beginning (bottom) of the list. + self._objects = [it]+objects + else: + # Put the object before (below) the aboject. + idx = objects.index(below) + objects[idx:idx]=[it] + self._objects = objects + + def add(self, sprite): + self._objects.append(sprite) + + def remove(self, sprite): + try: + self._objects.remove(sprite) + except ValueError: + # Already done it: happens in some games, not an error. + pass + + def blit_and_dirty (self, source_surf, dest_pos): + """ + You probably won't need to use this method in your own programs, + as |Sprite| and its sub-classes know how to draw themselves on + the screen. You'd need to use method if you wanted to draw an + image on the screen which wasn't an |Sprite|. + + This method blits (draws, taking account of transparency) the + given source surface |source_surf| to the screen at the position + given by |dest_pos|. + + It then remembers the place where the surface was drawn as + ``dirty''. This means that when the display is updated on the + next tick, this part of it will be redrawn. + """ + rect = self._display.blit(source_surf, dest_pos) + self._dirtyrects.append(rect) + + + def blit_background(self, rect): + """ + This method draws the background over the given rectangle, and + marks that rectangle as ``dirty'' (see the |blit_and_dirty| + method for what that means). It's used to erase an object before + moving it. You shouldn't need to call it yourself. + """ + rect = self._display.blit(self._background, rect, rect) + self._dirtyrects.append(rect) + + +############################################################################### +## Sprite class ############################################################### +############################################################################### +## ## +## Sprite represents a graphical object on the screen. Sprites ## +## can be moved, rotated, deleted, and maybe have other things done to them. ## +## ## +############################################################################### + +class Sprite(object): + def __init__(self, image, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + interval=1, is_collideable=True): + + if not Screen.initialized: + raise GamesError("Screen object must be intialized before any Sprite object") + + self._surface = image + self._orig_surface = image # Surface before any rotation + self._rect = self._surface.get_rect() + + self.position = (x, y) + + if top != None: + self.top = top + if bottom != None: + self.bottom = bottom + if left != None: + self.left = left + if right != None: + self.right = right + + self.velocity = (dx, dy) + + self._angle = angle % 360 + if self._angle != 0: + self._rotate() + + self.is_collideable = is_collideable + + self._interval = interval + self._tickable = 1 + self._next = 0 + + self._gone = 0 + + def __del__(self): + if screen and not self._gone: + self.destroy() + + def _draw(self): + """ + Draw object on screen by blitting the image onto the screen. + """ + screen.blit_and_dirty(self._surface, self._rect) + + def _erase(self): + """ + Erase object from screen by blitting the background over where + it was. + """ + screen.blit_background(self._rect) + + def _replace(self, new_surface): + x, y = self.position + self._surface = new_surface + self._rect = self._surface.get_rect() + self.position = (x, y) + + def _rotate(self): + self._replace(pygame.transform.rotate(self._orig_surface, -self._angle)) + + def _tick(self): + self._next = self._next + 1 + if self._next >= self._interval: + self._next = 0 + self.tick() + if self._dx or self._dy: + self.position = ( (self._x + self._dx), (self._y + self._dy) ) + self.update() + + def start (self): + self._tickable = 1 + self._next = 0 + + def stop (self): + self._tickable = 0 + + def update(self): + pass + + def tick(self): + pass + + def overlaps(self, other): + if not self.is_collideable or not other.is_collideable: + return False + else: + return self._rect.colliderect(other._rect) + + def elevate(self, above=None): + """ + Elevate an object to the top of the stack, or above the specified + object. + """ + screen._elevate(self, above) + + def lower(self, below=None): + """ + Lower an object to the bottom of the stack, or below the specified + object. + """ + screen._lower(self, below) + + def destroy(self): + """ + Erase object from screen and remove it from the list of objects + maintained by games module. + """ + self._erase() + screen.remove(self) + self._gone = 1 + + #------Properties--------# + + ## x + def get_x(self): + return self._x + def set_x(self, new_x): + self._x = new_x + self._rect.centerx = int(self._x) + x = property(get_x, set_x) + + ## y + def get_y(self): + return self._y + def set_y(self, new_y): + self._y = new_y + self._rect.centery = int(self._y) + y = property(get_y, set_y) + + ## position + def get_position(self): + return ( (self.x, self.y) ) + def set_position(self, new_position): + self.x, self.y = new_position + position = property(get_position, set_position) + + ## dx + def get_dx(self): + return self._dx + def set_dx(self, new_dx): + self._dx = new_dx + dx = property(get_dx, set_dx) + + ## dy + def get_dy(self): + return self._dy + def set_dy(self, new_dy): + self._dy = new_dy + dy = property(get_dy, set_dy) + + ## velocity + def get_velocity(self): + return ( (self.dx, self.dy) ) + def set_velocity (self, new_velocity): + self.dx, self.dy = new_velocity + velocity = property(get_velocity, set_velocity) + + ## left + def get_left(self): + return self._rect.left + def set_left(self, new_left): + self._rect.left = new_left + self._x = self._rect.centerx + left = property(get_left, set_left) + + ## right + def get_right(self): + return self._rect.right + def set_right(self, new_right): + self._rect.right = new_right + self._x = self._rect.centerx + right = property(get_right, set_right) + + ## top + def get_top(self): + return self._rect.top + def set_top(self, new_top): + self._rect.top = new_top + self._y = self._rect.centery + top = property(get_top, set_top) + + ## bottom + def get_bottom(self): + return self._rect.bottom + def set_bottom(self, new_bottom): + self._rect.bottom = new_bottom + self._y = self._rect.centery + bottom = property(get_bottom, set_bottom) + + ## angle + def get_angle(self): + return self._angle + def set_angle(self, new_angle): + self._angle = new_angle % 360 + self._rotate() + angle = property(get_angle, set_angle) + + ## image + def get_image(self): + return self._orig_surface + def set_image(self, new_image): + self._orig_surface = new_image + if self._angle != 0: + self._rotate() + else: + self._replace(new_image) + image = property(get_image, set_image) + + ## height + def get_height(self): + return self._surface.get_height() + height = property(get_height) + + ## width + def get_width(self): + return self._surface.get_width() + width = property(get_width) + + ## is_collideable + def get_is_collideable(self): + return self._is_collideable + def set_is_collideable(self, new_status): + self._is_collideable = new_status + is_collideable = property(get_is_collideable, set_is_collideable) + + + ## overlapping_sprites + def get_overlapping_sprites(self): + overlapping = screen.overlapping_objects(self._rect) + if self in overlapping: + overlapping.remove(self) + return overlapping + overlapping_sprites = property(get_overlapping_sprites) + + ## interval + def get_interval(self): + return self._interval + def set_interval(self, new_interval): + self._interval = new_interval + interval = property(get_interval, set_interval) + + +class Text(Sprite): + """ + Alphanumeric values displayed on the screen. + """ + def __init__(self, value, size, color, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + interval=1, is_collideable=True): + self._size = size + self._color = color + self._value = value + self._font = pygame.font.Font(None, self._size) + Sprite.__init__(self, self._create_surface(), angle, + x, y, + top, bottom, left, right, + dx, dy, + interval, is_collideable) + + def _create_surface(self): + return self._font.render(str(self._value), 1, self._color) + + #------Properties--------# + + ## value + def get_value(self): + return self._value + + def set_value(self, new_value): + if new_value != self._value: + self._value = new_value + self.image = self._create_surface() + + value = property(get_value, set_value) + + ## color + def get_color(self): + return self._color + + def set_color(self, new_color): + if new_color != self._color: + self._color = new_color + surface = self._create_surface() + self.image = surface + + color = property(get_color, set_color) + + ## size + def get_size(self): + return self._size + + def set_size(self, new_size): + if new_size != self._size: + self._size = new_size + self._font = pygame.font.Font(None, self._size) + surface = self._create_surface() + self.image = surface + + size = property(get_size, set_size) + + +class Question(Text): + def __init__(self, value, size, color, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + interval=1, is_collideable=True, responses=()): + Text.__init__(self, value, size, color, angle, + x, y, + top, bottom, left, right, + dx, dy, + interval, is_collideable) + self.responses = responses + + def tick(self): + for key, action in self.responses: + if keyboard.is_pressed(key): + action() + + +class Message(Text): + def __init__(self, value, size, color, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + lifetime=0, is_collideable=True, after_death=None): + Text.__init__(self, value, size, color, angle, + x, y, + top, bottom, left, right, + dx, dy, + lifetime, is_collideable) + self._after_death = after_death + + def tick(self): + if self._after_death: + self._after_death() + self.stop() + self.destroy() + + +class Animation(Sprite): + """ + An image that changes every repeat_interval ticks. + The n_repeats parameter is the number of complete animation cycles to show. + If n_repeats <= 0, the animation will repeat forever. + You can give list of filenames or list of images. + """ + def __init__(self, images, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + repeat_interval=1, n_repeats=0, is_collideable=True): + + if images and type(images[0]) is type(""): + images = load_animation(images) + + self.images = images + if self.images == []: + raise GamesError("An animation with no images is illegal.") + + self.n_repeats = n_repeats or -1 + if self.n_repeats > 0: + self.n_repeats = (self.n_repeats * len(self.images)) + + first_image = self.next_image() + + Sprite.__init__(self, self.next_image(), angle, + x, y, + top, bottom, left, right, + dx, dy, + repeat_interval, is_collideable) + + def next_image(self): + if self.n_repeats==0: return None + if self.n_repeats>0: self.n_repeats -= 1 + new_image = self.images[0] + self.images = self.images[1:] + [self.images[0]] + return new_image + + def tick(self): + new_image = self.next_image() + if new_image is None: + self.destroy() + else: + self.image = new_image + + +############################################################################### +## Utility Functions +############################################################################### +def load_image(filename, transparent=True): + """Loads an image, prepares it for play. Returns a pygame.Surface object + which you can give as the "image" parameter to Sprite. + + filename -- the filename of the image to load + transparent -- whether the background of the image should be transparent. + Defaults to true. + The background color is taken as the color of the pixel + at (0,0) in the image. + """ + try: + surface = pygame.image.load(filename) + except pygame.error: + raise GamesError( 'Could not load image "%s" %s'%(filename, pygame.get_error()) ) + if transparent: + corner = surface.get_at((0, 0)) + surface.set_colorkey(corner, RLEACCEL) + return surface.convert() + +def scale_image(image, x_scale, y_scale=None): + if y_scale is None: y_scale = x_scale + (x_size, y_size) = image.get_size() + x_size = x_size * x_scale + y_size = y_size * y_scale + return pygame.transform.scale(image, (x_size, y_size)) + +def load_animation(filenames, transparent=1): + """ + Loads a number of files. Receives file names. Returns corresponding file objects + needed by the Animation constructor. + """ + def _(name, transparent=transparent): + try: surface = pygame.image.load(name) + except pygame.error: + raise GamesError( 'Could not load animation frame "%s": %s' % (name, pygame.get_error()) ) + if transparent: + surface.set_colorkey(surface.get_at((0,0)), RLEACCEL) + return surface.convert() + files = list(map(_, filenames)) + return files + +def load_sound(filename): + """ + Load a sound file, returning a Sound object. + """ + return pygame.mixer.Sound(filename) + + +############################################################################### +## Initialization Function +############################################################################### +def init(screen_width = 640, screen_height = 480, fps = 50): + global screen + screen = Screen(screen_width, screen_height, fps) + +mouse = Mouse() +keyboard = Keyboard() +music = Music() + + + + + + diff --git a/Absolute Book/py3e_software/livewires/livewires/__init__.py b/Absolute Book/py3e_software/livewires/livewires/__init__.py new file mode 100755 index 0000000..686cdae --- /dev/null +++ b/Absolute Book/py3e_software/livewires/livewires/__init__.py @@ -0,0 +1,29 @@ +# Copyright Richard Crook and Gareth McCaughan. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither name of Scripture Union nor LiveWires nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCRIPTURE UNION +# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Absolute Book/py3e_software/livewires/livewires/__init__.pyc b/Absolute Book/py3e_software/livewires/livewires/__init__.pyc new file mode 100755 index 0000000..468355a Binary files /dev/null and b/Absolute Book/py3e_software/livewires/livewires/__init__.pyc differ diff --git a/Absolute Book/py3e_software/livewires/livewires/color.py b/Absolute Book/py3e_software/livewires/livewires/color.py new file mode 100755 index 0000000..34ce27d --- /dev/null +++ b/Absolute Book/py3e_software/livewires/livewires/color.py @@ -0,0 +1,46 @@ +# Copyright Richard Crook and Gareth McCaughan. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither name of Scripture Union nor Live Wires nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCRIPTURE UNION +# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +### Default colour names, as RGB triplets + +red = (255,0,0) +green = (0,255,0) +blue = (0,0,255) +black = (0,0,0) +white = (255,255,255) +dark_red = (127,0,0) +dark_green = (0,102,0) +dark_blue = (0,0,127) +dark_gray = (76,76,76) +gray = (127,127,127) +light_gray = (178,178,178) +yellow = (229,204,0) +brown = (127,89,0) +pink = (255,0,204) +purple = (153,0,178) diff --git a/Absolute Book/py3e_software/livewires/livewires/color.pyc b/Absolute Book/py3e_software/livewires/livewires/color.pyc new file mode 100755 index 0000000..b1c27d6 Binary files /dev/null and b/Absolute Book/py3e_software/livewires/livewires/color.pyc differ diff --git a/Absolute Book/py3e_software/livewires/livewires/games.py b/Absolute Book/py3e_software/livewires/livewires/games.py new file mode 100755 index 0000000..a379776 --- /dev/null +++ b/Absolute Book/py3e_software/livewires/livewires/games.py @@ -0,0 +1,894 @@ +############################################################################### +### Games support module for LiveWires using pygame. +### +### $Revision: 1.7 $ -- $Date: 2001/10/27 17:43:51 $ +############################################################################### +# Copyright Richard Crook, Gareth McCaughan, Rhodri James, Neil Turton +# and Paul Wright. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither name of Scripture Union nor LiveWires nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCRIPTURE UNION +# OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +############################################################################### +############################################################################### +# Modified by Michael Dawson +# 5/24/05 +# +# Restructured Classes +# - created a single Sprite class (no multiple inheritance) +# - added properties to classes (getter and setter methods still available) +# - added Question class to get user keyboard input +# - added Mouse class for mouse input +# - added Keyboard class for keyboard input +# - added Music class to access music channel +# +# Revised Animation Class +# - now receives only one list of images to animate +# - images now displayed in order, from first frame to last (not the reverse) +# - n_repeats represents number of cycles to display (not number of frames) +# +# "Americanized" Spelling +# - 'colour' is now 'color' +############################################################################### + +import pygame, pygame.image, pygame.mixer, pygame.font, pygame.transform +import pygame.draw +from pygame.locals import * + +pygame.init() + + +############################################################################### +## Error classes ############################################################## +############################################################################### + +class GamesError(Exception): pass + + +############################################################################### +## Mouse class ################################################################ +############################################################################### + +class Mouse(object): + #------Properties--------# + + ## position + def get_position(self): + return pygame.mouse.get_pos() + + def set_position(self, new_position): + pygame.mouse.set_pos(new_position) + + position = property(get_position, set_position) + + ## x + def get_x(self): + return pygame.mouse.get_pos()[0] + + def set_x(self, new_x): + current_y = pygame.mouse.get_pos()[1] + pygame.mouse.set_pos( (new_x, current_y) ) + + x = property(get_x, set_x) + + ## y + def get_y(self): + return pygame.mouse.get_pos()[1] + + def set_y(self, new_y): + current_mouse_x = pygame.mouse.get_pos()[0] + pygame.mouse.set_pos( (current_x, new_y) ) + + y = property(get_y, set_y) + + ## is visible + def set_is_visible(self, new_visibility): + pygame.mouse.set_visible(new_visibility) + is_visible = property(fset = set_is_visible) + + def is_pressed(self, button_number): + return pygame.mouse.get_pressed()[button_number] == 1 + + +############################################################################### +## Keyboard class ############################################################# +############################################################################### + +class Keyboard(object): + def is_pressed(self, key): + return pygame.key.get_pressed()[key] == 1 + + +############################################################################### +## Music class ################################################################ +############################################################################### + +class Music(object): + def load(self, filename): + pygame.mixer.music.load(filename) + + def play(self, loop=0): + pygame.mixer.music.play(loop) + + def fadeout(self, millisec): + pygame.mixer.music.fadeout(millisec) + + def stop(self): + pygame.mixer.music.stop() + + +############################################################################### +## Screen class ############################################################### +############################################################################### +## +## The Screen object represents the playing area. Since we can have +## only one screen under pygame, it's just a handy container for stuff +## +############################################################################### + +class Screen(object): + + initialized = 0 + + def __init__ (self, width=640, height=480, fps=50): + # Bomb if you try this more than once + if Screen.initialized: + raise GamesError("Cannot have more than on Screen object") + + Screen.initialized = 1 + + # Create the pygame display + #self._display = pygame.display.set_mode ((width, height), HWSURFACE) + self._display = pygame.display.set_mode ((width, height)) + self._width = width + self._height = height + self._background = self._display.convert() + + # Initialize a list of objects in play + self._objects = [] + # Initialize list dirty rectangles to be repainted + self._dirtyrects = [] + + # Time when we should draw the next frame + self._next_tick = 0 + + # Frames per second screen will be updated + self._fps = fps + + #------Properties--------# + + ## width + def get_width(self): + return self._width + + width = property(get_width) + + ## height + def get_height(self): + return self._height + + height = property(get_height) + + ## fps + def get_fps(self): + return self._fps + + fps = property(get_fps) + + ## background + def get_background(self): + return self._background + + def set_background(self, new_background): + """ + Set the background to the surface provided. Note that the + surface should not have transparency set, or weird things + will happen. + """ + self._background = pygame.Surface((self._width, self._height)) + for x in range(0, self._width, new_background.get_width()): + for y in range(0, self._height, new_background.get_height()): + self._background.blit(new_background, (x, y)) + + self._display.blit(self._background, (0,0)) + pygame.display.update() + + background = property(get_background, set_background) + + ## all objects + def get_all_objects(self): + """ + Returns a list of all the Sprites on the Screen. + """ + return self._objects[:] + all_objects = property(get_all_objects) + + ## event_grab + def get_event_grab(self): + return pygame.event.get_grab() + + def set_event_grab(self, new_status): + pygame.event.set_grab(new_status) + + event_grab = property(get_event_grab, set_event_grab) + + def tick(self): + """ + If you override the tick method in a subclass of the Screen + class, you can specify actions which are carried out every + tick. + """ + pass + + def keypress(self, key): + """ + If you override the keypress method, you will be able to + handle individual keypresses instead of dealing with the + keys held down as in the standard library + """ + pass + + def handle_events(self): + """ + If you override this method in a subclass of the Screen + class, you can specify how to handle different kinds of + events. However you must handle the quit condition! + """ + events = pygame.event.get() + for event in events: + if event.type == QUIT: + self.quit() + elif event.type == KEYDOWN: + if event.key == K_ESCAPE: + self.quit() + else: + self.keypress(event.key) + + def quit(self): + """ + Calling this method will stop the main loop from running and + make the graphics window disappear. + """ + self._exit = 1 + + def clear(self): + """ + Destroy all objects on this Screen. + """ + for object in self._objects[:]: + object.destroy() + self._objects = [] + + def _update_display(self): + """ + Get the actual display in sync with reality. + """ + pygame.display.update(self._dirtyrects) + self._dirtyrects = [] + + def mainloop(self): + """ + Run the pygame main loop. This will animate the objects on the + screen and call their tick methods every tick. + """ + self._exit = 0 + + while not self._exit: + self._wait_frame() + + for object in self._objects: + object._erase() + + # Take a copy of the _objects list as it may get changed in place. + for object in self._objects[:]: + if object._tickable: + object._tick() + + self.tick() + + for object in self._objects: + object._draw() + + self._update_display() + pygame.display.flip() + + self.handle_events() + + # Throw away any pending events. + pygame.event.get() + + def _wait_frame (self): + "Wait for the correct fps time to expire" + this_tick = pygame.time.get_ticks() + if this_tick < self._next_tick: + pygame.time.delay(int(self._next_tick+0.5) - this_tick) + self._next_tick = this_tick + (1000./self._fps) + + def overlapping_objects(self, rectangle): + """ + Return list of all sprites which overlap given rectangle. + """ + rect = pygame.Rect (rectangle) + + rect_list = [] + for obj in self._objects: + rect_list.append (obj._rect) + + indices = rect.collidelistall (rect_list) + + over_objects = [] + for index in indices: + if (self._objects[index]).is_collideable: + over_objects.append (self._objects [index]) + + return over_objects + + def _elevate(self, it, above=None): + """ + Elevates an object to the top of the stack, or above the specified + object. + """ + # This makes sure we're always in a consistent state. + objects = self._objects[:] + # Remove the object from the list. + objects.remove(it) + if above == None: + # Put it on top (the end). + objects.append(it) + else: + # Put the object after . + idx = 1+objects.index(above) + objects[idx:idx]=[it] + # Install the new list. + self._objects = objects + + def _lower(self, object, below=None): + """ + Lower an object to the bottom of the stack, or below the specified + object. + """ + # This makes sure we're always in a consistent state. + objects = self._objects[:] + objects.remove(it) + if below == None: + # Put the object on the beginning (bottom) of the list. + self._objects = [it]+objects + else: + # Put the object before (below) the aboject. + idx = objects.index(below) + objects[idx:idx]=[it] + self._objects = objects + + def add(self, sprite): + self._objects.append(sprite) + + def remove(self, sprite): + try: + self._objects.remove(sprite) + except ValueError: + # Already done it: happens in some games, not an error. + pass + + def blit_and_dirty (self, source_surf, dest_pos): + """ + You probably won't need to use this method in your own programs, + as |Sprite| and its sub-classes know how to draw themselves on + the screen. You'd need to use method if you wanted to draw an + image on the screen which wasn't an |Sprite|. + + This method blits (draws, taking account of transparency) the + given source surface |source_surf| to the screen at the position + given by |dest_pos|. + + It then remembers the place where the surface was drawn as + ``dirty''. This means that when the display is updated on the + next tick, this part of it will be redrawn. + """ + rect = self._display.blit(source_surf, dest_pos) + self._dirtyrects.append(rect) + + + def blit_background(self, rect): + """ + This method draws the background over the given rectangle, and + marks that rectangle as ``dirty'' (see the |blit_and_dirty| + method for what that means). It's used to erase an object before + moving it. You shouldn't need to call it yourself. + """ + rect = self._display.blit(self._background, rect, rect) + self._dirtyrects.append(rect) + + +############################################################################### +## Sprite class ############################################################### +############################################################################### +## ## +## Sprite represents a graphical object on the screen. Sprites ## +## can be moved, rotated, deleted, and maybe have other things done to them. ## +## ## +############################################################################### + +class Sprite(object): + def __init__(self, image, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + interval=1, is_collideable=True): + + if not Screen.initialized: + raise GamesError("Screen object must be intialized before any Sprite object") + + self._surface = image + self._orig_surface = image # Surface before any rotation + self._rect = self._surface.get_rect() + + self.position = (x, y) + + if top != None: + self.top = top + if bottom != None: + self.bottom = bottom + if left != None: + self.left = left + if right != None: + self.right = right + + self.velocity = (dx, dy) + + self._angle = angle % 360 + if self._angle != 0: + self._rotate() + + self.is_collideable = is_collideable + + self._interval = interval + self._tickable = 1 + self._next = 0 + + self._gone = 0 + + def __del__(self): + if screen and not self._gone: + self.destroy() + + def _draw(self): + """ + Draw object on screen by blitting the image onto the screen. + """ + screen.blit_and_dirty(self._surface, self._rect) + + def _erase(self): + """ + Erase object from screen by blitting the background over where + it was. + """ + screen.blit_background(self._rect) + + def _replace(self, new_surface): + x, y = self.position + self._surface = new_surface + self._rect = self._surface.get_rect() + self.position = (x, y) + + def _rotate(self): + self._replace(pygame.transform.rotate(self._orig_surface, -self._angle)) + + def _tick(self): + self._next = self._next + 1 + if self._next >= self._interval: + self._next = 0 + self.tick() + if self._dx or self._dy: + self.position = ( (self._x + self._dx), (self._y + self._dy) ) + self.update() + + def start (self): + self._tickable = 1 + self._next = 0 + + def stop (self): + self._tickable = 0 + + def update(self): + pass + + def tick(self): + pass + + def overlaps(self, other): + if not self.is_collideable or not other.is_collideable: + return False + else: + return self._rect.colliderect(other._rect) + + def elevate(self, above=None): + """ + Elevate an object to the top of the stack, or above the specified + object. + """ + screen._elevate(self, above) + + def lower(self, below=None): + """ + Lower an object to the bottom of the stack, or below the specified + object. + """ + screen._lower(self, below) + + def destroy(self): + """ + Erase object from screen and remove it from the list of objects + maintained by games module. + """ + self._erase() + screen.remove(self) + self._gone = 1 + + #------Properties--------# + + ## x + def get_x(self): + return self._x + def set_x(self, new_x): + self._x = new_x + self._rect.centerx = int(self._x) + x = property(get_x, set_x) + + ## y + def get_y(self): + return self._y + def set_y(self, new_y): + self._y = new_y + self._rect.centery = int(self._y) + y = property(get_y, set_y) + + ## position + def get_position(self): + return ( (self.x, self.y) ) + def set_position(self, new_position): + self.x, self.y = new_position + position = property(get_position, set_position) + + ## dx + def get_dx(self): + return self._dx + def set_dx(self, new_dx): + self._dx = new_dx + dx = property(get_dx, set_dx) + + ## dy + def get_dy(self): + return self._dy + def set_dy(self, new_dy): + self._dy = new_dy + dy = property(get_dy, set_dy) + + ## velocity + def get_velocity(self): + return ( (self.dx, self.dy) ) + def set_velocity (self, new_velocity): + self.dx, self.dy = new_velocity + velocity = property(get_velocity, set_velocity) + + ## left + def get_left(self): + return self._rect.left + def set_left(self, new_left): + self._rect.left = new_left + self._x = self._rect.centerx + left = property(get_left, set_left) + + ## right + def get_right(self): + return self._rect.right + def set_right(self, new_right): + self._rect.right = new_right + self._x = self._rect.centerx + right = property(get_right, set_right) + + ## top + def get_top(self): + return self._rect.top + def set_top(self, new_top): + self._rect.top = new_top + self._y = self._rect.centery + top = property(get_top, set_top) + + ## bottom + def get_bottom(self): + return self._rect.bottom + def set_bottom(self, new_bottom): + self._rect.bottom = new_bottom + self._y = self._rect.centery + bottom = property(get_bottom, set_bottom) + + ## angle + def get_angle(self): + return self._angle + def set_angle(self, new_angle): + self._angle = new_angle % 360 + self._rotate() + angle = property(get_angle, set_angle) + + ## image + def get_image(self): + return self._orig_surface + def set_image(self, new_image): + self._orig_surface = new_image + if self._angle != 0: + self._rotate() + else: + self._replace(new_image) + image = property(get_image, set_image) + + ## height + def get_height(self): + return self._surface.get_height() + height = property(get_height) + + ## width + def get_width(self): + return self._surface.get_width() + width = property(get_width) + + ## is_collideable + def get_is_collideable(self): + return self._is_collideable + def set_is_collideable(self, new_status): + self._is_collideable = new_status + is_collideable = property(get_is_collideable, set_is_collideable) + + + ## overlapping_sprites + def get_overlapping_sprites(self): + overlapping = screen.overlapping_objects(self._rect) + if self in overlapping: + overlapping.remove(self) + return overlapping + overlapping_sprites = property(get_overlapping_sprites) + + ## interval + def get_interval(self): + return self._interval + def set_interval(self, new_interval): + self._interval = new_interval + interval = property(get_interval, set_interval) + + +class Text(Sprite): + """ + Alphanumeric values displayed on the screen. + """ + def __init__(self, value, size, color, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + interval=1, is_collideable=True): + self._size = size + self._color = color + self._value = value + self._font = pygame.font.Font(None, self._size) + Sprite.__init__(self, self._create_surface(), angle, + x, y, + top, bottom, left, right, + dx, dy, + interval, is_collideable) + + def _create_surface(self): + return self._font.render(str(self._value), 1, self._color) + + #------Properties--------# + + ## value + def get_value(self): + return self._value + + def set_value(self, new_value): + if new_value != self._value: + self._value = new_value + self.image = self._create_surface() + + value = property(get_value, set_value) + + ## color + def get_color(self): + return self._color + + def set_color(self, new_color): + if new_color != self._color: + self._color = new_color + surface = self._create_surface() + self.image = surface + + color = property(get_color, set_color) + + ## size + def get_size(self): + return self._size + + def set_size(self, new_size): + if new_size != self._size: + self._size = new_size + self._font = pygame.font.Font(None, self._size) + surface = self._create_surface() + self.image = surface + + size = property(get_size, set_size) + + +class Question(Text): + def __init__(self, value, size, color, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + interval=1, is_collideable=True, responses=()): + Text.__init__(self, value, size, color, angle, + x, y, + top, bottom, left, right, + dx, dy, + interval, is_collideable) + self.responses = responses + + def tick(self): + for key, action in self.responses: + if keyboard.is_pressed(key): + action() + + +class Message(Text): + def __init__(self, value, size, color, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + lifetime=0, is_collideable=True, after_death=None): + Text.__init__(self, value, size, color, angle, + x, y, + top, bottom, left, right, + dx, dy, + lifetime, is_collideable) + self._after_death = after_death + + def tick(self): + if self._after_death: + self._after_death() + self.stop() + self.destroy() + + +class Animation(Sprite): + """ + An image that changes every repeat_interval ticks. + The n_repeats parameter is the number of complete animation cycles to show. + If n_repeats <= 0, the animation will repeat forever. + You can give list of filenames or list of images. + """ + def __init__(self, images, angle=0, + x=0, y=0, + top=None, bottom=None, left=None, right=None, + dx=0, dy=0, + repeat_interval=1, n_repeats=0, is_collideable=True): + + if images and type(images[0]) is type(""): + images = load_animation(images) + + self.images = images + if self.images == []: + raise GamesError("An animation with no images is illegal.") + + self.n_repeats = n_repeats or -1 + if self.n_repeats > 0: + self.n_repeats = (self.n_repeats * len(self.images)) + + first_image = self.next_image() + + Sprite.__init__(self, self.next_image(), angle, + x, y, + top, bottom, left, right, + dx, dy, + repeat_interval, is_collideable) + + def next_image(self): + if self.n_repeats==0: return None + if self.n_repeats>0: self.n_repeats -= 1 + new_image = self.images[0] + self.images = self.images[1:] + [self.images[0]] + return new_image + + def tick(self): + new_image = self.next_image() + if new_image is None: + self.destroy() + else: + self.image = new_image + + +############################################################################### +## Utility Functions +############################################################################### +def load_image(filename, transparent=True): + """Loads an image, prepares it for play. Returns a pygame.Surface object + which you can give as the "image" parameter to Sprite. + + filename -- the filename of the image to load + transparent -- whether the background of the image should be transparent. + Defaults to true. + The background color is taken as the color of the pixel + at (0,0) in the image. + """ + try: + surface = pygame.image.load(filename) + except pygame.error: + raise GamesError( 'Could not load image "%s" %s'%(filename, pygame.get_error()) ) + if transparent: + corner = surface.get_at((0, 0)) + surface.set_colorkey(corner, RLEACCEL) + return surface.convert() + +def scale_image(image, x_scale, y_scale=None): + if y_scale is None: y_scale = x_scale + (x_size, y_size) = image.get_size() + x_size = x_size * x_scale + y_size = y_size * y_scale + return pygame.transform.scale(image, (x_size, y_size)) + +def load_animation(filenames, transparent=1): + """ + Loads a number of files. Receives file names. Returns corresponding file objects + needed by the Animation constructor. + """ + def _(name, transparent=transparent): + try: surface = pygame.image.load(name) + except pygame.error: + raise GamesError( 'Could not load animation frame "%s": %s' % (name, pygame.get_error()) ) + if transparent: + surface.set_colorkey(surface.get_at((0,0)), RLEACCEL) + return surface.convert() + files = list(map(_, filenames)) + return files + +def load_sound(filename): + """ + Load a sound file, returning a Sound object. + """ + return pygame.mixer.Sound(filename) + + +############################################################################### +## Initialization Function +############################################################################### +def init(screen_width = 640, screen_height = 480, fps = 50): + global screen + screen = Screen(screen_width, screen_height, fps) + +mouse = Mouse() +keyboard = Keyboard() +music = Music() + + + + + + diff --git a/Absolute Book/py3e_software/livewires/livewires/games.pyc b/Absolute Book/py3e_software/livewires/livewires/games.pyc new file mode 100755 index 0000000..642f18f Binary files /dev/null and b/Absolute Book/py3e_software/livewires/livewires/games.pyc differ diff --git a/Absolute Book/py3e_software/livewires/readme.txt b/Absolute Book/py3e_software/livewires/readme.txt new file mode 100755 index 0000000..0c7769f --- /dev/null +++ b/Absolute Book/py3e_software/livewires/readme.txt @@ -0,0 +1,26 @@ + THE LIVEWIRES PACKAGE 3.0 + + +Dependencies +------------ +To install the livewires package, you must first install Python. To +use the livewires package, you must first install pygame. + + +Installation +------------ +Run the setup.bat file by double-clicking it. If you get a message that +Python can't be found, you may need to update your PATH to include the +folder in which you installed Python. + + +Testing +------- +Start the Python interpreter and type: + +from livewires import games, color + +If this line produces no errors, you've successfully installed the +package. If you get an error that refers to pygame, you may not have +successfully installed pygame. pygame is required to use livewires. + diff --git a/Absolute Book/py3e_software/livewires/setup.bat b/Absolute Book/py3e_software/livewires/setup.bat new file mode 100755 index 0000000..f2e8ce1 --- /dev/null +++ b/Absolute Book/py3e_software/livewires/setup.bat @@ -0,0 +1,2 @@ +setup.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_software/livewires/setup.py b/Absolute Book/py3e_software/livewires/setup.py new file mode 100755 index 0000000..1520df5 --- /dev/null +++ b/Absolute Book/py3e_software/livewires/setup.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +from distutils.core import setup +import sys + +# Default to installing if no commands are given +if len (sys.argv) == 1: + script_args = ["install"] +else: + script_args = sys.argv [1:] + + + +setup (name = "LiveWires", + version = "2.0", + description = "LiveWires package provides resources for people learning Python. It is intended for use with the LiveWires Python Course", + author = "Richard Crook, Gareth McCaughan, Paul Wright, Rhodri James, Neil Turton", + author_email = "python@livewires.org.uk", + url = "http://www.livewires.org.uk/python/", + packages = ['livewires'], + script_args = script_args + ) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_software/pygame/pygame-1.9.1.win32-py3.1.msi b/Absolute Book/py3e_software/pygame/pygame-1.9.1.win32-py3.1.msi new file mode 100755 index 0000000..0e28359 Binary files /dev/null and b/Absolute Book/py3e_software/pygame/pygame-1.9.1.win32-py3.1.msi differ diff --git a/Absolute Book/py3e_software/pygame/readme.txt b/Absolute Book/py3e_software/pygame/readme.txt new file mode 100755 index 0000000..9428359 --- /dev/null +++ b/Absolute Book/py3e_software/pygame/readme.txt @@ -0,0 +1,34 @@ + THE PYGAME PACKAGE + 1.9.1 for Python 3.1.x + + +Dependencies +------------ +To install the pygame package for Windows, you must first install +Python. + + +Installation +------------ +To install pygame, simply run the pygame set-up program, +pygame-1.9.1.win32-py3.1.msi, by double-clicking it, and follow +the prompts. + +If you get a message that Python can't be found, you may need to +update your PATH to include the folder in which you installed Python. + + +Testing +------- +Start the Python interpreter and type: + +import pygame + +If this line produces no errors, you've successfully installed the +package. + + +Official Site +------------- +For more information, visit the official pygame web site at +http://www.pygame.org. diff --git a/Absolute Book/py3e_software/python/python-3.1.1.msi b/Absolute Book/py3e_software/python/python-3.1.1.msi new file mode 100755 index 0000000..4908620 Binary files /dev/null and b/Absolute Book/py3e_software/python/python-3.1.1.msi differ diff --git a/Absolute Book/py3e_software/python/readme.txt b/Absolute Book/py3e_software/python/readme.txt new file mode 100755 index 0000000..2af0592 --- /dev/null +++ b/Absolute Book/py3e_software/python/readme.txt @@ -0,0 +1,29 @@ + THE PYTHON LANGUAGE + 3.1.1 + + +Dependencies +------------ +Although Python is cross-platform, the file that accompanies these +instructions will only work with Windows. If you have another +operating system, please see the official Python web site. + + +Installation +------------ +To install Python, simply run the Python set-up program, +python-3.1.1.msi, by double-clicking it, and follow the prompts. +You may accept all default settings. + + +Testing +------- +From the Start menu, choose Programs, Python 3.1, choose IDLE (Python GUI). +If IDLE does not appear, you may need to update your PATH to include the +folder in which you installed Python. + + +Official Site +------------- +For more information, visit the official Python web site at +http://www.python.org. diff --git a/Absolute Book/py3e_source/chapter01/game_over.py b/Absolute Book/py3e_source/chapter01/game_over.py new file mode 100755 index 0000000..26a455c --- /dev/null +++ b/Absolute Book/py3e_source/chapter01/game_over.py @@ -0,0 +1,6 @@ +# Game Over +# Demonstrates the print function + +print("Game Over") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/fancy_credits.py b/Absolute Book/py3e_source/chapter02/fancy_credits.py new file mode 100755 index 0000000..0e451b5 --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/fancy_credits.py @@ -0,0 +1,19 @@ +# Fancy Credits +# Demonstrates escape sequences + +print("\t\t\tFancy Credits") + +print("\t\t\t \\ \\ \\ \\ \\ \\ \\") +print("\t\t\t\tby") +print("\t\t\tMichael Dawson") +print("\t\t\t \\ \\ \\ \\ \\ \\ \\") + +print("\nSpecial thanks goes out to:") +print("My hair stylist, Henry \'The Great,\' who never says \"can\'t.\"") + +# sound the system bell +print("\a") + +input("\n\nPress the enter key to exit.") + + diff --git a/Absolute Book/py3e_source/chapter02/game_over2.py b/Absolute Book/py3e_source/chapter02/game_over2.py new file mode 100755 index 0000000..7d2d3cf --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/game_over2.py @@ -0,0 +1,34 @@ +# Game Over - Version 2 +# Demonstrates the use of quotes in strings + +print("Program 'Game Over' 2.0") + +print("Same", "message", "as before") + +print("Just", + "a bit", + "bigger") + +print("Here", end=" ") +print("it is...") + +print( + """ + _____ ___ ___ ___ _____ + / ___| / | / |/ | | ___| + | | / /| | / /| /| | | |__ + | | _ / ___ | / / |__/ | | | __| + | |_| | / / | | / / | | | |___ + \_____/ /_/ |_| /_/ |_| |_____| + + _____ _ _ _____ _____ + / _ \ | | / / | ___| | _ \ + | | | | | | / / | |__ | |_| | + | | | | | | / / | __| | _ / + | |_| | | |/ / | |___ | | \ \ + \_____/ |___/ |_____| |_| \_\ + + """ + ) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/greeter.py b/Absolute Book/py3e_source/chapter02/greeter.py new file mode 100755 index 0000000..7706614 --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/greeter.py @@ -0,0 +1,10 @@ +# Greeter +# Demonstrates the use of a variable + +name = "Larry" + +print(name) + +print("Hi,", name) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/personal_greeter.py b/Absolute Book/py3e_source/chapter02/personal_greeter.py new file mode 100755 index 0000000..e78db1a --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/personal_greeter.py @@ -0,0 +1,10 @@ +# Personal Greeter +# Demonstrates getting user input + +name = input("Hi. What's your name? ") + +print(name) + +print("Hi,", name) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/quotation_manipulation.py b/Absolute Book/py3e_source/chapter02/quotation_manipulation.py new file mode 100755 index 0000000..a172823 --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/quotation_manipulation.py @@ -0,0 +1,25 @@ +# Quotation Manipulation +# Demonstrates string methods + +# quote from IBM Chairman, Thomas Watson, in 1943 +quote = "I think there is a world market for maybe five computers." + +print("Original quote:") +print(quote) + +print("\nIn uppercase:") +print(quote.upper()) + +print("\nIn lowercase:") +print(quote.lower()) + +print("\nAs a title:") +print(quote.title()) + +print("\nWith a minor replacement:") +print(quote.replace("five", "millions of")) + +print("\nOriginal quote is still:") +print(quote) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/silly_strings.py b/Absolute Book/py3e_source/chapter02/silly_strings.py new file mode 100755 index 0000000..f00613f --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/silly_strings.py @@ -0,0 +1,19 @@ +# Silly Strings +# Demonstrates string concatenation and repetition + +print("You can concatenate two " + "strings with the '+' operator.") + +print("\nThis string " + "may not " + "seem terr" + "ibly impressive. " \ + + "But what " + "you don't know" + " is that\n" + "it's one real" \ + + "l" + "y" + " long string, created from the concatenation " \ + + "of " + "twenty-two\n" + "different strings, broken across " \ + + "six lines." + " Now are you" + " impressed? " + "Okay,\n" \ + + "this " + "one " + "long" + " string is now over!") + +print("\nIf you really like a string, you can repeat it. For example,") +print("who doesn't like pie? That's right, nobody. But if you really") +print("like it, you should say it like you mean it:") +print("Pie" * 10) + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter02/trust_fund_bad.py b/Absolute Book/py3e_source/chapter02/trust_fund_bad.py new file mode 100755 index 0000000..83f2383 --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/trust_fund_bad.py @@ -0,0 +1,31 @@ +# Trust Fund Buddy - Bad +# Demonstrates a logical error + +print( +""" + Trust Fund Buddy + +Totals your monthly spending so that your trust fund doesn't run out +(and you're forced to get a real job). + +Please enter the requested, monthly costs. Since you're rich, ignore pennies +and use only dollar amounts. + +""" +) + +car = input("Lamborghini Tune-Ups: ") +rent = input("Manhattan Apartment: ") +jet = input("Private Jet Rental: ") +gifts = input("Gifts: ") +food = input("Dining Out: ") +staff = input("Staff (butlers, chef, driver, assistant): ") +guru = input("Personal Guru and Coach: ") +games = input("Computer Games: ") + +total = car + rent + jet + gifts + food + staff + guru + games + +print("\nGrand Total:", total) + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter02/trust_fund_good.py b/Absolute Book/py3e_source/chapter02/trust_fund_good.py new file mode 100755 index 0000000..1a279ab --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/trust_fund_good.py @@ -0,0 +1,32 @@ +# Trust Fund Buddy - Good +# Demonstrates type conversion + +print( +""" + Trust Fund Buddy + +Totals your monthly spending so that your trust fund doesn't run out +(and you're forced to get a real job). + +Please enter the requested, monthly costs. Since you're rich, ignore pennies +and use only dollar amounts. + +""" +) + +car = input("Lamborghini Tune-Ups: ") +car = int(car) + +rent = int(input("Manhattan Apartment: ")) +jet = int(input("Private Jet Rental: ")) +gifts = int(input("Gifts: ")) +food = int(input("Dining Out: ")) +staff = int(input("Staff (butlers, chef, driver, assistant): ")) +guru = int(input("Personal Guru and Coach: ") ) +games = int(input("Computer Games: ")) + +total = car + rent + jet + gifts + food + staff + guru + games + +print("\nGrand Total:", total) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/useless_trivia.py b/Absolute Book/py3e_source/chapter02/useless_trivia.py new file mode 100755 index 0000000..44ec3c0 --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/useless_trivia.py @@ -0,0 +1,32 @@ +# Useless Trivia +# +# Gets personal information from the user and then +# prints true but useless information about him or her + +name = input("Hi. What's your name? ") + +age = input("How old are you? ") +age = int(age) + +weight = int(input("Okay, last question. How many pounds do you weigh? ")) + +print("\nIf poet ee cummings were to email you, he'd address you as", + name.lower()) +print("But if ee were mad, he'd call you", name.upper()) + +called = name * 5 +print("\nIf a small child were trying to get your attention",) +print("your name would become:") +print(called) + +seconds = age * 365 * 24 * 60 * 60 +print("\nYou're over", seconds, "seconds old.") + +moon_weight = weight / 6 +print("\nDid you know that on the moon you would weigh only", + moon_weight, "pounds?") + +sun_weight = weight * 27.1 +print("On the sun, you'd weigh", sun_weight, "(but, ah... not for long).") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter02/word_problems.py b/Absolute Book/py3e_source/chapter02/word_problems.py new file mode 100755 index 0000000..e04c8e4 --- /dev/null +++ b/Absolute Book/py3e_source/chapter02/word_problems.py @@ -0,0 +1,29 @@ +# Word Problems +# Demonstrates numbers and math + +print("If a 2000 pound pregnant hippo gives birth to a 100 pound calf,") +print("but then eats 50 pounds of food, how much does she weigh?") +input("Press the enter key to find out.") +print("2000 - 100 + 50 =", 2000 - 100 + 50) + +print("\nIf an adventurer returns from a successful quest and buys each of") +print("6 companions 3 bottles of ale, how many bottles are purchased?") +input("Press the enter key to find out.") +print("6 * 3 =", 6 * 3) + +print("\nIf a restaurant check comes to 19 dollars with tip, and you and") +print("your friends split it evenly 4 ways, how much do you each throw in?") +input("Press the enter key to find out.") +print("19 / 4 =", 19 / 4) + +print("\nIf a group of 4 pirates finds a chest full of 107 gold coins, and") +print("they divide the booty evenly, how many whole coins does each get?") +input("Press the enter key to find out.") +print("107 // 4 =", 107 // 4) + +print("\nIf that same group of 4 pirates evenly divides the chest full") +print("of 107 gold coins, how many coins are left over?") +input("Press the enter key to find out.") +print("107 % 4 =", 107 % 4) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/craps_roller.py b/Absolute Book/py3e_source/chapter03/craps_roller.py new file mode 100755 index 0000000..708ed58 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/craps_roller.py @@ -0,0 +1,14 @@ +# Craps Roller +# Demonstrates random number generation + +import random + +# generate random numbers 1 - 6 +die1 = random.randint(1, 6) +die2 = random.randrange(6) + 1 + +total = die1 + die2 + +print("You rolled a", die1, "and a", die2, "for a total of", total) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/exclusive_network.py b/Absolute Book/py3e_source/chapter03/exclusive_network.py new file mode 100755 index 0000000..b0de8a1 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/exclusive_network.py @@ -0,0 +1,35 @@ +# Exclusive Network +# Demonstrates logical operators and compound conditions + +print("\tExclusive Computer Network") +print("\t\tMembers only!\n") + +security = 0 + +username = "" +while not username: + username = input("Username: ") + +password = "" +while not password: + password = input("Password: ") + +if username == "M.Dawson" and password == "secret": + print("Hi, Mike.") + security = 5 +elif username == "S.Meier" and password == "civilization": + print("Hey, Sid.") + security = 3 +elif username == "S.Miyamoto" and password == "mariobros": + print("What's up, Shigeru?") + security = 3 +elif username == "W.Wright" and password == "thesims": + print("How goes it, Will?") + security = 3 +elif username == "guest" or password == "guest": + print("Welcome, guest.") + security = 1 +else: + print("Login failed. You're not so exclusive.\n") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/finicky_counter.py b/Absolute Book/py3e_source/chapter03/finicky_counter.py new file mode 100755 index 0000000..798e4b4 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/finicky_counter.py @@ -0,0 +1,15 @@ +# Finicky Counter +# Demonstrates the break and continue statements + +count = 0 +while True: + count += 1 + # end loop if count greater than 10 + if count > 10: + break + # skip 5 + if count == 5: + continue + print(count) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/granted_or_denied.py b/Absolute Book/py3e_source/chapter03/granted_or_denied.py new file mode 100755 index 0000000..68c4f28 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/granted_or_denied.py @@ -0,0 +1,14 @@ +# Granted or Denied +# Demonstrates an else clause + +print("Welcome to System Security Inc.") +print("-- where security is our middle name\n") + +password = input("Enter your password: ") + +if password == "secret": + print("Access Granted") +else: + print("Access Denied") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/guess_my_ number.py b/Absolute Book/py3e_source/chapter03/guess_my_ number.py new file mode 100755 index 0000000..bd62a17 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/guess_my_ number.py @@ -0,0 +1,32 @@ +# Guess My Number +# +# The computer picks a random number between 1 and 100 +# The player tries to guess it and the computer lets +# the player know if the guess is too high, too low +# or right on the money + +import random + +print("\tWelcome to 'Guess My Number'!") +print("\nI'm thinking of a number between 1 and 100.") +print("Try to guess it in as few attempts as possible.\n") + +# set the initial values +the_number = random.randint(1, 100) +guess = int(input("Take a guess: ")) +tries = 1 + +# guessing loop +while guess != the_number: + if guess > the_number: + print("Lower...") + else: + print("Higher...") + + guess = int(input("Take a guess: ")) + tries += 1 + +print("You guessed it! The number was", the_number) +print("And it only took you", tries, "tries!\n") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/losing_battle-bad.py b/Absolute Book/py3e_source/chapter03/losing_battle-bad.py new file mode 100755 index 0000000..56838ae --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/losing_battle-bad.py @@ -0,0 +1,23 @@ +# Losing Battle +# Demonstrates the dreaded infinite loop + +print("Your lone hero is surrounded by a massive army of trolls.") +print("Their decaying green bodies stretch out, melting into the horizon.") +print("Your hero unsheathes his sword for the last fight of his life.\n") + +health = 10 +trolls = 0 +damage = 3 + +while health != 0: + trolls += 1 + health -= damage + + print("Your hero swings and defeats an evil troll, " \ + "but takes", damage, "damage points.\n") + +print("Your hero fought valiantly and defeated", trolls, "trolls.") +print("But alas, your hero is no more.") + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter03/losing_battle-good.py b/Absolute Book/py3e_source/chapter03/losing_battle-good.py new file mode 100755 index 0000000..7462250 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/losing_battle-good.py @@ -0,0 +1,23 @@ +# Losing Battle +# Avoids the dreaded infinite loop + +print("Your lone hero is surrounded by a massive army of trolls.") +print("Their decaying green bodies stretch out, melting into the horizon.") +print("Your hero unsheathes his sword for the last fight of his life.\n") + +health = 10 +trolls = 0 +damage = 3 + +while health > 0: + trolls += 1 + health -= damage + + print("Your hero swings and defeats an evil troll, " \ + "but takes", damage, "damage points.\n") + +print("Your hero fought valiantly and defeated", trolls, "trolls.") +print("But alas, your hero is no more.") + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter03/maitre_d.py b/Absolute Book/py3e_source/chapter03/maitre_d.py new file mode 100755 index 0000000..3dfb33a --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/maitre_d.py @@ -0,0 +1,15 @@ +# Maitre D' +# Demonstrates treating a value as a condition + +print("Welcome to the Chateau D' Food") +print("It seems we are quite full this evening.\n") + +money = int(input("How many dollars do you slip the Maitre D'? ")) + +if money: + print("Ah, I am reminded of a table. Right this way.") +else: + print("Please, sit. It may be a while.") + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter03/mood_computer.py b/Absolute Book/py3e_source/chapter03/mood_computer.py new file mode 100755 index 0000000..bccfbec --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/mood_computer.py @@ -0,0 +1,61 @@ +# Mood Computer +# Demonstrates the elif clause + +import random + +print("I sense your energy. Your true emotions are coming across my screen.") +print("You are...") + +mood = random.randint(1, 3) + +if mood == 1: + # happy + print( \ + """ + ----------- + | | + | O O | + | < | + | | + | . . | + | `...` | + ----------- + """) +elif mood == 2: + # neutral + print( \ + """ + ----------- + | | + | O O | + | < | + | | + | ------ | + | | + ----------- + """) +elif mood == 3: + # sad + print( \ + """ + ----------- + | | + | O O | + | < | + | | + | .'. | + | ' ' | + ----------- + """) +else: + print("Illegal mood value! (You must be in a really bad mood).") + +print("...today.") + +input("\n\nPress the enter key to exit.") + + + + + + diff --git a/Absolute Book/py3e_source/chapter03/password.py b/Absolute Book/py3e_source/chapter03/password.py new file mode 100755 index 0000000..44c8e13 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/password.py @@ -0,0 +1,12 @@ + m # Password +# Demonstrates the if statement + +print("Welcome to System Security Inc.") +print("-- where security is our middle name\n") + +password = input("Enter your password: ") + +if password == "secret": + print("Access Granted") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter03/three_year-old.py b/Absolute Book/py3e_source/chapter03/three_year-old.py new file mode 100755 index 0000000..7dc7459 --- /dev/null +++ b/Absolute Book/py3e_source/chapter03/three_year-old.py @@ -0,0 +1,14 @@ +# Three Year-Old Simulator +# Demonstrates the while loop + +print("\tWelcome to the 'Three-Year-Old Simulator'\n") +print("This program simulates a conversation with a three-year-old child.") +print("Try to stop the madness.\n") + +response = "" +while response != "Because.": + response = input("Why?\n") + +print("Oh. Okay.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter04/counter.py b/Absolute Book/py3e_source/chapter04/counter.py new file mode 100755 index 0000000..b0bd42e --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/counter.py @@ -0,0 +1,18 @@ +# Counter +# Demonstrates the range() function + +print("Counting:") +for i in range(10): + print(i, end=" ") + +print("\n\nCounting by fives:") +for i in range(0, 50, 5): + print(i, end=" ") + +print("\n\nCounting backwards:") +for i in range(10, 0, -1): + print(i, end=" ") + +input("\n\nPress the enter key to exit.\n") + + diff --git a/Absolute Book/py3e_source/chapter04/hero's_inventory.py b/Absolute Book/py3e_source/chapter04/hero's_inventory.py new file mode 100755 index 0000000..7eb8eef --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/hero's_inventory.py @@ -0,0 +1,28 @@ +# Hero's Inventory +# Demonstrates tuple creation + +# create an empty tuple +inventory = () + +# treat the tuple as a condition +if not inventory: + print("You are empty-handed.") + +input("\nPress the enter key to continue.") + +# create a tuple with some items +inventory = ("sword", + "armor", + "shield", + "healing potion") + +# print the tuple +print("\nThe tuple inventory is:") +print(inventory) + +# print each element in the tuple +print("\nYour items:") +for item in inventory: + print(item) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter04/hero's_inventory2.py b/Absolute Book/py3e_source/chapter04/hero's_inventory2.py new file mode 100755 index 0000000..9c3e464 --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/hero's_inventory2.py @@ -0,0 +1,45 @@ +# Hero's Inventory 2.0 +# Demonstrates tuples + +# create a tuple with some items and display with a for loop +inventory = ("sword", + "armor", + "shield", + "healing potion") +print("Your items:") +for item in inventory: + print(item) + +input("\nPress the enter key to continue.") + +# get the length of a tuple +print("You have", len(inventory), "items in your possession.") + +input("\nPress the enter key to continue.") + +# test for membership with in +if "healing potion" in inventory: + print("You will live to fight another day.") + +# display one item through an index +index = int(input("\nEnter the index number for an item in inventory: ")) +print("At index", index, "is", inventory[index]) + +# display a slice +start = int(input("\nEnter the index number to begin a slice: ")) +finish = int(input("Enter the index number to end the slice: ")) +print("inventory[", start, ":", finish, "] is", end=" ") +print(inventory[start:finish]) + +input("\nPress the enter key to continue.") + +# concatenate two tuples +chest = ("gold", "gems") +print("You find a chest. It contains:") +print(chest) +print("You add the contents of the chest to your inventory.") +inventory += chest +print("Your inventory is now:") +print(inventory) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter04/loopy_string.py b/Absolute Book/py3e_source/chapter04/loopy_string.py new file mode 100755 index 0000000..45a254e --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/loopy_string.py @@ -0,0 +1,10 @@ +# Loopy String +# Demonstrates the for loop with a string + +word = input("Enter a word: ") + +print("\nHere's each letter in your word:") +for letter in word: + print(letter) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter04/message_analyzer.py b/Absolute Book/py3e_source/chapter04/message_analyzer.py new file mode 100755 index 0000000..e2e196f --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/message_analyzer.py @@ -0,0 +1,15 @@ +# Message Analyzer +# Demonstrates the len() function and the in operator + +message = input("Enter a message: ") + +print("\nThe length of your message is:", len(message)) + +print("\nThe most common letter in the English language, 'e',") +if "e" in message: + print("is in your message.") +else: + print("is not in your message.") + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter04/no_vowels.py b/Absolute Book/py3e_source/chapter04/no_vowels.py new file mode 100755 index 0000000..d131682 --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/no_vowels.py @@ -0,0 +1,17 @@ +# No Vowels +# Demonstrates creating new strings with a for loop + +message = input("Enter a message: ") +new_message = "" +VOWELS = "aeiou" + +print() +for letter in message: + if letter.lower() not in VOWELS: + new_message += letter + print("A new string has been created:", new_message) + +print("\nYour message without vowels is:", new_message) + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter04/pizza_slicer.py b/Absolute Book/py3e_source/chapter04/pizza_slicer.py new file mode 100755 index 0000000..244ae39 --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/pizza_slicer.py @@ -0,0 +1,35 @@ +# Pizza Slicer +# Demonstrates string slicing + +word = "pizza" + +print( +""" + Slicing 'Cheat Sheet' + + 0 1 2 3 4 5 + +---+---+---+---+---+ + | p | i | z | z | a | + +---+---+---+---+---+ +-5 -4 -3 -2 -1 + +""" +) + +print("Enter the beginning and ending index for your slice of 'pizza'.") +print("Press the enter key at 'Begin' to exit.") + +start = None +while start != "": + start = (input("\nStart: ")) + + if start: + start = int(start) + + finish = int(input("Finish: ")) + + print("word[", start, ":", finish, "] is", end=" ") + print(word[start:finish]) + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter04/random_access.py b/Absolute Book/py3e_source/chapter04/random_access.py new file mode 100755 index 0000000..f65538d --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/random_access.py @@ -0,0 +1,16 @@ +# Random Access +# Demonstrates string indexing + +import random + +word = "index" +print("The word is: ", word, "\n") + +high = len(word) +low = -len(word) + +for i in range(10): + position = random.randrange(low, high) + print("word[", position, "]\t", word[position]) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter04/word_jumble.py b/Absolute Book/py3e_source/chapter04/word_jumble.py new file mode 100755 index 0000000..4dd95b3 --- /dev/null +++ b/Absolute Book/py3e_source/chapter04/word_jumble.py @@ -0,0 +1,43 @@ +# Word Jumble +# +# The computer picks a random word and then "jumbles" it +# The player has to guess the original word + +import random + +# create a sequence of words to choose from +WORDS = ("python", "jumble", "easy", "difficult", "answer", "xylophone") +# pick one word randomly from the sequence +word = random.choice(WORDS) +# create a variable to use later to see if the guess is correct +correct = word + +# create a jumbled version of the word +jumble ="" +while word: + position = random.randrange(len(word)) + jumble += word[position] + word = word[:position] + word[(position + 1):] + +# start the game +print( +""" + Welcome to Word Jumble! + + Unscramble the letters to make a word. +(Press the enter key at the prompt to quit.) +""" +) +print("The jumble is:", jumble) + +guess = input("\nYour guess: ") +while guess != correct and guess != "": + print("Sorry, that's not it.") + guess = input("Your guess: ") + +if guess == correct: + print("That's it! You guessed it!\n") + +print("Thanks for playing.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter05/geek_translator.py b/Absolute Book/py3e_source/chapter05/geek_translator.py new file mode 100755 index 0000000..8f10156 --- /dev/null +++ b/Absolute Book/py3e_source/chapter05/geek_translator.py @@ -0,0 +1,75 @@ +# Geek Translator +# Demonstrates using dictionaries + +geek = {"404": "clueless. From the web error message 404, meaning page not found.", + "Googling": "searching the Internet for background information on a person.", + "Keyboard Plaque" : "the collection of debris found in computer keyboards.", + "Link Rot" : "the process by which web page links become obsolete.", + "Percussive Maintenance" : "the act of striking an electronic device to make it work.", + "Uninstalled" : "being fired. Especially popular during the dot-bomb era."} + +choice = None +while choice != "0": + + print( + """ + Geek Translator + + 0 - Quit + 1 - Look Up a Geek Term + 2 - Add a Geek Term + 3 - Redefine a Geek Term + 4 - Delete a Geek Term + """ + ) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # get a definition + elif choice == "1": + term = input("What term do you want me to translate?: ") + if term in geek: + definition = geek[term] + print("\n", term, "means", definition) + else: + print("\nSorry, I don't know", term) + + # add a term-definition pair + elif choice == "2": + term = input("What term do you want me to add?: ") + if term not in geek: + definition = input("\nWhat's the definition?: ") + geek[term] = definition + print("\n", term, "has been added.") + else: + print("\nThat term already exists! Try redefining it.") + + # redefine an existing term + elif choice == "3": + term = input("What term do you want me to redefine?: ") + if term in geek: + definition = input("What's the new definition?: ") + geek[term] = definition + print("\n", term, "has been redefined.") + else: + print("\nThat term doesn't exist! Try adding it.") + + # delete a term-definition pair + elif choice == "4": + term = input("What term do you want me to delete?: ") + if term in geek: + del geek[term] + print("\nOkay, I deleted", term) + else: + print("\nI can't do that!", term, "doesn't exist in the dictionary.") + + # some unknown choice + else: + print("\nSorry, but", choice, "isn't a valid choice.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter05/hangman.py b/Absolute Book/py3e_source/chapter05/hangman.py new file mode 100755 index 0000000..fa5595a --- /dev/null +++ b/Absolute Book/py3e_source/chapter05/hangman.py @@ -0,0 +1,160 @@ +# Hangman Game +# +# The classic game of Hangman. The computer picks a random word +# and the player wrong to guess it, one letter at a time. If the player +# can't guess the word in time, the little stick figure gets hanged. + +# imports +import random + +# constants +HANGMAN = ( +""" + ------ + | | + | + | + | + | + | + | + | +---------- +""", +""" + ------ + | | + | O + | + | + | + | + | + | +---------- +""", +""" + ------ + | | + | O + | -+- + | + | + | + | + | +---------- +""", +""" + ------ + | | + | O + | /-+- + | + | + | + | + | +---------- +""", +""" + ------ + | | + | O + | /-+-/ + | + | + | + | + | +---------- +""", +""" + ------ + | | + | O + | /-+-/ + | | + | + | + | + | +---------- +""", +""" + ------ + | | + | O + | /-+-/ + | | + | | + | | + | | + | +---------- +""", +""" + ------ + | | + | O + | /-+-/ + | | + | | + | | | + | | | + | +---------- +""") + +MAX_WRONG = len(HANGMAN) - 1 +WORDS = ("OVERUSED", "CLAM", "GUAM", "TAFFETA", "PYTHON") + +# initialize variables +word = random.choice(WORDS) # the word to be guessed +so_far = "-" * len(word) # one dash for each letter in word to be guessed +wrong = 0 # number of wrong guesses player has made +used = [] # letters already guessed + + +print("Welcome to Hangman. Good luck!") + +while wrong < MAX_WRONG and so_far != word: + print(HANGMAN[wrong]) + print("\nYou've used the following letters:\n", used) + print("\nSo far, the word is:\n", so_far) + + guess = input("\n\nEnter your guess: ") + guess = guess.upper() + + while guess in used: + print("You've already guessed the letter", guess) + guess = input("Enter your guess: ") + guess = guess.upper() + + used.append(guess) + + if guess in word: + print("\nYes!", guess, "is in the word!") + + # create a new so_far to include guess + new = "" + for i in range(len(word)): + if guess == word[i]: + new += guess + else: + new += so_far[i] + so_far = new + + else: + print("\nSorry,", guess, "isn't in the word.") + wrong += 1 + +if wrong == MAX_WRONG: + print(HANGMAN[wrong]) + print("\nYou've been hanged!") +else: + print("\nYou guessed it!") + +print("\nThe word was", word) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter05/hero's_inventory3.py b/Absolute Book/py3e_source/chapter05/hero's_inventory3.py new file mode 100755 index 0000000..620631b --- /dev/null +++ b/Absolute Book/py3e_source/chapter05/hero's_inventory3.py @@ -0,0 +1,74 @@ +# Hero's Inventory 3.0 +# Demonstrates lists + +# create a list with some items and display with a for loop +inventory = ["sword", "armor", "shield", "healing potion"] +print("Your items:") +for item in inventory: + print(item) + +input("\nPress the enter key to continue.") + +# get the length of a list +print("You have", len(inventory), "items in your possession.") + +input("\nPress the enter key to continue.") + +# test for membership with in +if "healing potion" in inventory: + print("You will live to fight another day.") + +# display one item through an index +index = int(input("\nEnter the index number for an item in inventory: ")) +print("At index", index, "is", inventory[index]) + +# display a slice +start = int(input("\nEnter the index number to begin a slice: ")) +finish = int(input("Enter the index number to end the slice: ")) +print("inventory[", start, ":", finish, "] is", end=" ") +print(inventory[start:finish]) + +input("\nPress the enter key to continue.") + +# concatenate two lists +chest = ["gold", "gems"] +print("You find a chest which contains:") +print(chest) +print("You add the contents of the chest to your inventory.") +inventory += chest +print("Your inventory is now:") +print(inventory) + +input("\nPress the enter key to continue.") + +# assign by index +print("You trade your sword for a crossbow.") +inventory[0] = "crossbow" +print("Your inventory is now:") +print(inventory) + +input("\nPress the enter key to continue.") + +# assign by slice +print("You use your gold and gems to buy an orb of future telling.") +inventory[4:6] = ["orb of future telling"] +print("Your inventory is now:") +print(inventory) + +input("\nPress the enter key to continue.") + +# delete an element +print("In a great battle, your shield is destroyed.") +del inventory[2] +print("Your inventory is now:") +print(inventory) + +input("\nPress the enter key to continue.") + +# delete a slice +print("Your crossbow and armor are stolen by thieves.") +del inventory[:2] +print("Your inventory is now:") +print(inventory) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter05/high_scores.py b/Absolute Book/py3e_source/chapter05/high_scores.py new file mode 100755 index 0000000..b294ef2 --- /dev/null +++ b/Absolute Book/py3e_source/chapter05/high_scores.py @@ -0,0 +1,55 @@ +# High Scores +# Demonstrates list methods + +scores = [] + +choice = None +while choice != "0": + + print( + """ + High Scores + + 0 - Exit + 1 - Show Scores + 2 - Add a Score + 3 - Remove a Score + 4 - Sort Scores + """ + ) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # list high-score table + elif choice == "1": + print("High Scores") + for score in scores: + print(score) + + # add a score + elif choice == "2": + score = int(input("What score did you get?: ")) + scores.append(score) + + # remove a score + elif choice == "3": + score = int(input("Remove which score?: ")) + if score in scores: + scores.remove(score) + else: + print(score, "isn't in the high scores list.") + + # sort scores + elif choice == "4": + scores.sort(reverse=True) + + # some unknown choice + else: + print("Sorry, but", choice, "isn't a valid choice.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter05/high_scores2.py b/Absolute Book/py3e_source/chapter05/high_scores2.py new file mode 100755 index 0000000..a200748 --- /dev/null +++ b/Absolute Book/py3e_source/chapter05/high_scores2.py @@ -0,0 +1,47 @@ +# High Scores 2.0 +# Demonstrates nested sequences + +scores = [] + +choice = None +while choice != "0": + + print( + """ + High Scores 2.0 + + 0 - Quit + 1 - List Scores + 2 - Add a Score + """ + ) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # display high-score table + elif choice == "1": + print("High Scores\n") + print("NAME\tSCORE") + for entry in scores: + score, name = entry + print(name, "\t", score) + + # add a score + elif choice == "2": + name = input("What is the player's name?: ") + score = int(input("What score did the player get?: ")) + entry = (score, name) + scores.append(entry) + scores.sort(reverse=True) + scores = scores[:5] # keep only top 5 scores + + # some unknown choice + else: + print("Sorry, but", choice, "isn't a valid choice.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter06/birthday_wishes.py b/Absolute Book/py3e_source/chapter06/birthday_wishes.py new file mode 100755 index 0000000..c1fdfbe --- /dev/null +++ b/Absolute Book/py3e_source/chapter06/birthday_wishes.py @@ -0,0 +1,27 @@ +# Birthday Wishes +# Demonstrates keyword arguments and default parameter values + +# positional parameters +def birthday1(name, age): + print("Happy birthday,", name, "!", " I hear you're", age, "today.\n") + +# parameters with default values +def birthday2(name = "Jackson", age = 1): + print("Happy birthday,", name, "!", " I hear you're", age, "today.\n") + + +birthday1("Jackson", 1) +birthday1(1, "Jackson") +birthday1(name = "Jackson", age = 1) +birthday1(age = 1, name = "Jackson") + +birthday2() +birthday2(name = "Katherine") +birthday2(age = 12) +birthday2(name = "Katherine", age = 12) +birthday2("Katherine", 12) + +input("\n\nPress the enter key to exit.") + + + diff --git a/Absolute Book/py3e_source/chapter06/global_reach.py b/Absolute Book/py3e_source/chapter06/global_reach.py new file mode 100755 index 0000000..dd31d03 --- /dev/null +++ b/Absolute Book/py3e_source/chapter06/global_reach.py @@ -0,0 +1,40 @@ +# Global Reach +# Demonstrates global variables + +def read_global(): + print("From inside the local scope of read_global(), value is:", value) + +def shadow_global(): + value = -10 + print("From inside the local scope of shadow_global(), value is:", value) + +def change_global(): + global value + value = -10 + print("From inside the local scope of change_global(), value is:", value) + +# main +# value is a global variable because we're in the global scope here +value = 10 +print("In the global scope, value has been set to:", value, "\n") + +read_global() +print("Back in the global scope, value is still:", value, "\n") + +shadow_global() +print("Back in the global scope, value is still:", value, "\n") + +change_global() +print("Back in the global scope, value has now changed to:", value) + +input("\n\nPress the enter key to exit.") + + + + + + + + + + diff --git a/Absolute Book/py3e_source/chapter06/instructions.py b/Absolute Book/py3e_source/chapter06/instructions.py new file mode 100755 index 0000000..c36ae7f --- /dev/null +++ b/Absolute Book/py3e_source/chapter06/instructions.py @@ -0,0 +1,31 @@ +# Instructions +# Demonstrates programmer-created functions + +def instructions(): + """Display game instructions.""" + print( + """ + Welcome to the greatest intellectual challenge of all time: Tic-Tac-Toe. + This will be a showdown between your human brain and my silicon processor. + + You will make your move known by entering a number, 0 - 8. The number + will correspond to the board position as illustrated: + + 0 | 1 | 2 + --------- + 3 | 4 | 5 + --------- + 6 | 7 | 8 + + Prepare yourself, human. The ultimate battle is about to begin. \n + """ + ) + +# main +print("Here are the instructions to the Tic-Tac-Toe game:") +instructions() +print("Here they are again:") +instructions() +print("You probably understand the game by now.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter06/receive_and_return.py b/Absolute Book/py3e_source/chapter06/receive_and_return.py new file mode 100755 index 0000000..567e239 --- /dev/null +++ b/Absolute Book/py3e_source/chapter06/receive_and_return.py @@ -0,0 +1,27 @@ +# Receive and Return +# Demonstrates parameters and return values + +def display(message): + print(message) + +def give_me_five(): + five = 5 + return five + +def ask_yes_no(question): + """Ask a yes or no question.""" + response = None + while response not in ("y", "n"): + response = input(question).lower() + return response + +# main +display("Here's a message for you.\n") + +number = give_me_five() +print("Here's what I got from give_me_five():", number) + +answer = ask_yes_no("\nPlease enter 'y' or 'n': ") +print("Thanks for entering:", answer) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter06/tic-tac-toe.py b/Absolute Book/py3e_source/chapter06/tic-tac-toe.py new file mode 100755 index 0000000..d8909b2 --- /dev/null +++ b/Absolute Book/py3e_source/chapter06/tic-tac-toe.py @@ -0,0 +1,208 @@ +# Tic-Tac-Toe +# Plays the game of tic-tac-toe against a human opponent + +# global constants +X = "X" +O = "O" +EMPTY = " " +TIE = "TIE" +NUM_SQUARES = 9 + + +def display_instruct(): + """Display game instructions.""" + print( + """ + Welcome to the greatest intellectual challenge of all time: Tic-Tac-Toe. + This will be a showdown between your human brain and my silicon processor. + + You will make your move known by entering a number, 0 - 8. The number + will correspond to the board position as illustrated: + + 0 | 1 | 2 + --------- + 3 | 4 | 5 + --------- + 6 | 7 | 8 + + Prepare yourself, human. The ultimate battle is about to begin. \n + """ + ) + + +def ask_yes_no(question): + """Ask a yes or no question.""" + response = None + while response not in ("y", "n"): + response = input(question).lower() + return response + + +def ask_number(question, low, high): + """Ask for a number within a range.""" + response = None + while response not in range(low, high): + response = int(input(question)) + return response + + +def pieces(): + """Determine if player or computer goes first.""" + go_first = ask_yes_no("Do you require the first move? (y/n): ") + if go_first == "y": + print("\nThen take the first move. You will need it.") + human = X + computer = O + else: + print("\nYour bravery will be your undoing... I will go first.") + computer = X + human = O + return computer, human + + +def new_board(): + """Create new game board.""" + board = [] + for square in range(NUM_SQUARES): + board.append(EMPTY) + return board + + +def display_board(board): + """Display game board on screen.""" + print("\n\t", board[0], "|", board[1], "|", board[2]) + print("\t", "---------") + print("\t", board[3], "|", board[4], "|", board[5]) + print("\t", "---------") + print("\t", board[6], "|", board[7], "|", board[8], "\n") + + +def legal_moves(board): + """Create list of legal moves.""" + moves = [] + for square in range(NUM_SQUARES): + if board[square] == EMPTY: + moves.append(square) + return moves + + +def winner(board): + """Determine the game winner.""" + WAYS_TO_WIN = ((0, 1, 2), + (3, 4, 5), + (6, 7, 8), + (0, 3, 6), + (1, 4, 7), + (2, 5, 8), + (0, 4, 8), + (2, 4, 6)) + + for row in WAYS_TO_WIN: + if board[row[0]] == board[row[1]] == board[row[2]] != EMPTY: + winner = board[row[0]] + return winner + + if EMPTY not in board: + return TIE + + return None + + +def human_move(board, human): + """Get human move.""" + legal = legal_moves(board) + move = None + while move not in legal: + move = ask_number("Where will you move? (0 - 8):", 0, NUM_SQUARES) + if move not in legal: + print("\nThat square is already occupied, foolish human. Choose another.\n") + print("Fine...") + return move + + +def computer_move(board, computer, human): + """Make computer move.""" + # make a copy to work with since function will be changing list + board = board[:] + # the best positions to have, in order + BEST_MOVES = (4, 0, 2, 6, 8, 1, 3, 5, 7) + + print("I shall take square number", end=" ") + + # if computer can win, take that move + for move in legal_moves(board): + board[move] = computer + if winner(board) == computer: + print(move) + return move + # done checking this move, undo it + board[move] = EMPTY + + # if human can win, block that move + for move in legal_moves(board): + board[move] = human + if winner(board) == human: + print(move) + return move + # done checkin this move, undo it + board[move] = EMPTY + + # since no one can win on next move, pick best open square + for move in BEST_MOVES: + if move in legal_moves(board): + print(move) + return move + + +def next_turn(turn): + """Switch turns.""" + if turn == X: + return O + else: + return X + + +def congrat_winner(the_winner, computer, human): + """Congratulate the winner.""" + if the_winner != TIE: + print(the_winner, "won!\n") + else: + print("It's a tie!\n") + + if the_winner == computer: + print("As I predicted, human, I am triumphant once more. \n" \ + "Proof that computers are superior to humans in all regards.") + + elif the_winner == human: + print("No, no! It cannot be! Somehow you tricked me, human. \n" \ + "But never again! I, the computer, so swear it!") + + elif the_winner == TIE: + print("You were most lucky, human, and somehow managed to tie me. \n" \ + "Celebrate today... for this is the best you will ever achieve.") + + +def main(): + display_instruct() + computer, human = pieces() + turn = X + board = new_board() + display_board(board) + + while not winner(board): + if turn == human: + move = human_move(board, human) + board[move] = human + else: + move = computer_move(board, computer, human) + board[move] = computer + display_board(board) + turn = next_turn(turn) + + the_winner = winner(board) + congrat_winner(the_winner, computer, human) + + +# start the program +main() +input("\n\nPress the enter key to quit.") diff --git a/Absolute Book/py3e_source/chapter07/handle_it.py b/Absolute Book/py3e_source/chapter07/handle_it.py new file mode 100755 index 0000000..db0855d --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/handle_it.py @@ -0,0 +1,50 @@ +# Handle It +# Demonstrates handling exceptions + +# try/except +try: + num = float(input("Enter a number: ")) +except: + print("Something went wrong!") + +# specifying exception type +try: + num = float(input("\nEnter a number: ")) +except ValueError: + print("That was not a number!") + +# handle multiple exception types +print() +for value in (None, "Hi!"): + try: + print("Attempting to convert", value, "-->", end=" ") + print(float(value)) + except (TypeError, ValueError): + print("Something went wrong!") + +print() +for value in (None, "Hi!"): + try: + print("Attempting to convert", value, "-->", end=" ") + print(float(value)) + except TypeError: + print("I can only convert a string or a number!") + except ValueError: + print("I can only convert a string of digits!") + +# get an exception's argument +try: + num = float(input("\nEnter a number: ")) +except ValueError as e: + print("That was not a number! Or as Python would say...") + print(e) + +# try/except/else +try: + num = float(input("\nEnter a number: ")) +except ValueError: + print("That was not a number!") +else: + print("You entered the number", num) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter07/pickle_it.py b/Absolute Book/py3e_source/chapter07/pickle_it.py new file mode 100755 index 0000000..1392191 --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/pickle_it.py @@ -0,0 +1,39 @@ +# Pickle It +# Demonstrates pickling and shelving data + +import pickle, shelve + +print("Pickling lists.") +variety = ["sweet", "hot", "dill"] +shape = ["whole", "spear", "chip"] +brand = ["Claussen", "Heinz", "Vlassic"] +f = open("pickles1.dat", "wb") +pickle.dump(variety, f) +pickle.dump(shape, f) +pickle.dump(brand, f) +f.close() + +print("\nUnpickling lists.") +f = open("pickles1.dat", "rb") +variety = pickle.load(f) +shape = pickle.load(f) +brand = pickle.load(f) +print(variety) +print(shape) +print(brand) +f.close() + +print("\nShelving lists.") +s = shelve.open("pickles2.dat") +s["variety"] = ["sweet", "hot", "dill"] +s["shape"] = ["whole", "spear", "chip"] +s["brand"] = ["Claussen", "Heinz", "Vlassic"] +s.sync() # make sure data is written + +print("\nRetrieving lists from a shelved file:") +print("brand -", s["brand"]) +print("shape -", s["shape"]) +print("variety -", s["variety"]) +s.close() + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter07/read_it.py b/Absolute Book/py3e_source/chapter07/read_it.py new file mode 100755 index 0000000..f66bd66 --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/read_it.py @@ -0,0 +1,48 @@ +# Read It +# Demonstrates reading from a text file + +print("Opening and closing the file.") +text_file = open("read_it.txt", "r") +text_file.close() + +print("\nReading characters from the file.") +text_file = open("read_it.txt", "r") +print(text_file.read(1)) +print(text_file.read(5)) +text_file.close() + +print("\nReading the entire file at once.") +text_file = open("read_it.txt", "r") +whole_thing = text_file.read() +print(whole_thing) +text_file.close() + +print("\nReading characters from a line.") +text_file = open("read_it.txt", "r") +print(text_file.readline(1)) +print(text_file.readline(5)) +text_file.close() + +print("\nReading one line at a time.") +text_file = open("read_it.txt", "r") +print(text_file.readline()) +print(text_file.readline()) +print(text_file.readline()) +text_file.close() + +print("\nReading the entire file into a list.") +text_file = open("read_it.txt", "r") +lines = text_file.readlines() +print(lines) +print(len(lines)) +for line in lines: + print(line) +text_file.close() + +print("\nLooping through the file, line by line.") +text_file = open("read_it.txt", "r") +for line in text_file: + print(line) +text_file.close() + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter07/read_it.txt b/Absolute Book/py3e_source/chapter07/read_it.txt new file mode 100755 index 0000000..1d0f902 --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/read_it.txt @@ -0,0 +1,3 @@ +Line 1 +This is line 2 +That makes this line 3 diff --git a/Absolute Book/py3e_source/chapter07/trivia.txt b/Absolute Book/py3e_source/chapter07/trivia.txt new file mode 100755 index 0000000..b94839c --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/trivia.txt @@ -0,0 +1,41 @@ +An Episode You Can't Refuse +On the Run With a Mammal +Let's say you turn state's evidence and need to "get on the lamb." If you wait /too long, what will happen? +You'll end up on the sheep +You'll end up on the cow +You'll end up on the goat +You'll end up on the emu +1 +A lamb is just a young sheep. +The Godfather Will Get Down With You Now +Let's say you have an audience with the Godfather of Soul. How would it be /smart to address him? +Mr. Richard +Mr. Domino +Mr. Brown +Mr. Checker +3 +James Brown is the Godfather of Soul. +That's Gonna Cost Ya +If you paid the Mob protection money in rupees, what business would you most /likely be insuring? +Your tulip farm in Holland +Your curry powder factory in India +Your vodka distillery in Russian +Your army knife warehouse in Switzerland +2 +The Rupee is the standard monetary unit of India. +Keeping It the Family +If your mother's father's sister's son was in "The Family," how are you /related to the mob? +By your first cousin once removed +By your first cousin twice removed +By your second cousin once removed +By your second cousin twice removed +1 +Your mother's father's sister is her aunt -- and her son is your /mother's first cousin. Since you and your mother are exactly one generation /apart, her first cousin is your first cousin once removed. +A Maid Man +If you were to literally launder your money, but didn't want the green in your /bills to run, what temperature should you use? +Hot +Warm +Tepid +Cold +4 +According to my detergent bottle, cold is best for colors that might run. \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter07/trivia_challenge.py b/Absolute Book/py3e_source/chapter07/trivia_challenge.py new file mode 100755 index 0000000..b426351 --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/trivia_challenge.py @@ -0,0 +1,82 @@ +# Trivia Challenge +# Trivia game that reads a plain text file + +import sys + +def open_file(file_name, mode): + """Open a file.""" + try: + the_file = open(file_name, mode) + except IOError as e: + print("Unable to open the file", file_name, "Ending program.\n", e) + input("\n\nPress the enter key to exit.") + sys.exit() + else: + return the_file + +def next_line(the_file): + """Return next line from the trivia file, formatted.""" + line = the_file.readline() + line = line.replace("/", "\n") + return line + +def next_block(the_file): + """Return the next block of data from the trivia file.""" + category = next_line(the_file) + + question = next_line(the_file) + + answers = [] + for i in range(4): + answers.append(next_line(the_file)) + + correct = next_line(the_file) + if correct: + correct = correct[0] + + explanation = next_line(the_file) + + return category, question, answers, correct, explanation + +def welcome(title): + """Welcome the player and get his/her name.""" + print("\t\tWelcome to Trivia Challenge!\n") + print("\t\t", title, "\n") + +def main(): + trivia_file = open_file("trivia.txt", "r") + title = next_line(trivia_file) + welcome(title) + score = 0 + + # get first block + category, question, answers, correct, explanation = next_block(trivia_file) + while category: + # ask a question + print(category) + print(question) + for i in range(4): + print("\t", i + 1, "-", answers[i]) + + # get answer + answer = input("What's your answer?: ") + + # check answer + if answer == correct: + print("\nRight!", end=" ") + score += 1 + else: + print("\nWrong.", end=" ") + print(explanation) + print("Score:", score, "\n\n") + + # get next block + category, question, answers, correct, explanation = next_block(trivia_file) + + trivia_file.close() + + print("That was the last question!") + print("You're final score is", score) + +main() +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter07/write_it.py b/Absolute Book/py3e_source/chapter07/write_it.py new file mode 100755 index 0000000..a6fa7aa --- /dev/null +++ b/Absolute Book/py3e_source/chapter07/write_it.py @@ -0,0 +1,29 @@ +# Write It +# Demonstrates writing to a text file + +print("Creating a text file with the write() method.") +text_file = open("write_it.txt", "w") +text_file.write("Line 1\n") +text_file.write("This is line 2\n") +text_file.write("That makes this line 3\n") +text_file.close() + +print("\nReading the newly created file.") +text_file = open("write_it.txt", "r") +print(text_file.read()) +text_file.close() + +print("\nCreating a text file with the writelines() method.") +text_file = open("write_it.txt", "w") +lines = ["Line 1\n", + "This is line 2\n", + "That makes this line 3\n"] +text_file.writelines(lines) +text_file.close() + +print("\nReading the newly created file.") +text_file = open("write_it.txt", "r") +print(text_file.read()) +text_file.close() + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/attribute_critter.py b/Absolute Book/py3e_source/chapter08/attribute_critter.py new file mode 100755 index 0000000..b75fd14 --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/attribute_critter.py @@ -0,0 +1,31 @@ +# Attribute Critter +# Demonstrates creating and accessing object attributes + +class Critter(object): + """A virtual pet""" + def __init__(self, name): + print("A new critter has been born!") + self.name = name + + def __str__(self): + rep = "Critter object\n" + rep += "name: " + self.name + "\n" + return rep + + def talk(self): + print("Hi. I'm", self.name, "\n") + +# main +crit1 = Critter("Poochie") +crit1.talk() + +crit2 = Critter("Randolph") +crit2.talk() + +print("Printing crit1:") +print(crit1) + +print("Directly accessing crit1.name:") +print(crit1.name) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/classy_critter.py b/Absolute Book/py3e_source/chapter08/classy_critter.py new file mode 100755 index 0000000..d32e977 --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/classy_critter.py @@ -0,0 +1,31 @@ +# Classy Critter +# Demonstrates class attributes and static methods + +class Critter(object): + """A virtual pet""" + total = 0 + + @staticmethod + def status(): + print("\nThe total number of critters is", Critter.total) + + def __init__(self, name): + print("A critter has been born!") + self.name = name + Critter.total += 1 + +#main +print("Accessing the class attribute Critter.total:", end=" ") +print(Critter.total) + +print("\nCreating critters.") +crit1 = Critter("critter 1") +crit2 = Critter("critter 2") +crit3 = Critter("critter 3") + +Critter.status() + +print("\nAccessing the class attribute through an object:", end= " ") +print(crit1.total) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/constructor_critter.py b/Absolute Book/py3e_source/chapter08/constructor_critter.py new file mode 100755 index 0000000..d124aeb --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/constructor_critter.py @@ -0,0 +1,19 @@ +# Constructor Critter +# Demonstrates constructors + +class Critter(object): + """A virtual pet""" + def __init__(self): + print("A new critter has been born!") + + def talk(self): + print("\nHi. I'm an instance of class Critter.") + +# main +crit1 = Critter() +crit2 = Critter() + +crit1.talk() +crit2.talk() + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/critter_caretaker.py b/Absolute Book/py3e_source/chapter08/critter_caretaker.py new file mode 100755 index 0000000..cb63c70 --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/critter_caretaker.py @@ -0,0 +1,87 @@ +# Critter Caretaker +# A virtual pet to care for + +class Critter(object): + """A virtual pet""" + def __init__(self, name, hunger = 0, boredom = 0): + self.name = name + self.hunger = hunger + self.boredom = boredom + + def __pass_time(self): + self.hunger += 1 + self.boredom += 1 + + @property + def mood(self): + unhappiness = self.hunger + self.boredom + if unhappiness < 5: + m = "happy" + elif 5 <= unhappiness <= 10: + m = "okay" + elif 11 <= unhappiness <= 15: + m = "frustrated" + else: + m = "mad" + return m + + def talk(self): + print("I'm", self.name, "and I feel", self.mood, "now.\n") + self.__pass_time() + + def eat(self, food = 4): + print("Brruppp. Thank you.") + self.hunger -= food + if self.hunger < 0: + self.hunger = 0 + self.__pass_time() + + def play(self, fun = 4): + print("Wheee!") + self.boredom -= fun + if self.boredom < 0: + self.boredom = 0 + self.__pass_time() + + +def main(): + crit_name = input("What do you want to name your critter?: ") + crit = Critter(crit_name) + + choice = None + while choice != "0": + print \ + (""" + Critter Caretaker + + 0 - Quit + 1 - Listen to your critter + 2 - Feed your critter + 3 - Play with your critter + """) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # listen to your critter + elif choice == "1": + crit.talk() + + # feed your critter + elif choice == "2": + crit.eat() + + # play with your critter + elif choice == "3": + crit.play() + + # some unknown choice + else: + print("\nSorry, but", choice, "isn't a valid choice.") + +main() +("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/private_critter.py b/Absolute Book/py3e_source/chapter08/private_critter.py new file mode 100755 index 0000000..c948b6a --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/private_critter.py @@ -0,0 +1,27 @@ +# Private Critter +# Demonstrates private variables and methods + +class Critter(object): + """A virtual pet""" + def __init__(self, name, mood): + print("A new critter has been born!") + self.name = name # public attribute + self.__mood = mood # private attribute + + def talk(self): + print("\nI'm", self.name) + print("Right now I feel", self.__mood, "\n") + + def __private_method(self): + print("This is a private method.") + + def public_method(self): + print("This is a public method.") + self.__private_method() + +# main +crit = Critter(name = "Poochie", mood = "happy") +crit.talk() +crit.public_method() + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/property_critter.py b/Absolute Book/py3e_source/chapter08/property_critter.py new file mode 100755 index 0000000..54d437f --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/property_critter.py @@ -0,0 +1,42 @@ +# Property Critter +# Demonstrates properties + +class Critter(object): + """A virtual pet""" + def __init__(self, name): + print("A new critter has been born!") + self.__name = name + + @property + def name(self): + return self.__name + + @name.setter + def name(self, new_name): + if new_name == "": + print("A critter's name can't be the empty string.") + else: + self.__name = new_name + print("Name change successful.") + + def talk(self): + print("\nHi, I'm", self.name) + +# main +crit = Critter("Poochie") +crit.talk() + +print("\nMy critter's name is:", end= " ") +print(crit.name) + +print("\nAttempting to change my critter's name to Randolph...") +crit.name = "Randolph" +print("My critter's name is:", end= " ") +print(crit.name) + +print("\nAttempting to change my critter's name to the empty string...") +crit.name = "" +print("My critter's name is:", end= " ") +print(crit.name) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter08/simple_critter.py b/Absolute Book/py3e_source/chapter08/simple_critter.py new file mode 100755 index 0000000..130175e --- /dev/null +++ b/Absolute Book/py3e_source/chapter08/simple_critter.py @@ -0,0 +1,13 @@ +# Simple Critter +# Demonstrates a basic class and object + +class Critter(object): + """A virtual pet""" + def talk(self): + print("Hi. I'm an instance of class Critter.") + +# main +crit = Critter() +crit.talk() + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter09/alien_blaster.py b/Absolute Book/py3e_source/chapter09/alien_blaster.py new file mode 100755 index 0000000..a77a796 --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/alien_blaster.py @@ -0,0 +1,24 @@ +# Alien Blaster +# Demonstrates object interaction + +class Player(object): + """ A player in a shooter game. """ + def blast(self, enemy): + print("The player blasts an enemy.\n") + enemy.die() + +class Alien(object): + """ An alien in a shooter game. """ + def die(self): + print("The alien gasps and says, 'Oh, this is it. This is the big one. \n" \ + "Yes, it's getting dark now. Tell my 1.6 million larvae that I loved them... \n" \ + "Good-bye, cruel universe.'") + +# main +print("\t\tDeath of an Alien\n") + +hero = Player() +invader = Alien() +hero.blast(invader) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter09/blackjack.py b/Absolute Book/py3e_source/chapter09/blackjack.py new file mode 100755 index 0000000..f0c2863 --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/blackjack.py @@ -0,0 +1,195 @@ +# Blackjack +# From 1 to 7 players compete against a dealer + +import cards, games + +class BJ_Card(cards.Card): + """ A Blackjack Card. """ + ACE_VALUE = 1 + + @property + def value(self): + if self.is_face_up: + v = BJ_Card.RANKS.index(self.rank) + 1 + if v > 10: + v = 10 + else: + v = None + return v + +class BJ_Deck(cards.Deck): + """ A Blackjack Deck. """ + def populate(self): + for suit in BJ_Card.SUITS: + for rank in BJ_Card.RANKS: + self.cards.append(BJ_Card(rank, suit)) + + +class BJ_Hand(cards.Hand): + """ A Blackjack Hand. """ + def __init__(self, name): + super(BJ_Hand, self).__init__() + self.name = name + + def __str__(self): + rep = self.name + ":\t" + super(BJ_Hand, self).__str__() + if self.total: + rep += "(" + str(self.total) + ")" + return rep + + @property + def total(self): + # if a card in the hand has value of None, then total is None + for card in self.cards: + if not card.value: + return None + + # add up card values, treat each Ace as 1 + t = 0 + for card in self.cards: + t += card.value + + # determine if hand contains an Ace + contains_ace = False + for card in self.cards: + if card.value == BJ_Card.ACE_VALUE: + contains_ace = True + + # if hand contains Ace and total is low enough, treat Ace as 11 + if contains_ace and t <= 11: + # add only 10 since we've already added 1 for the Ace + t += 10 + + return t + + def is_busted(self): + return self.total > 21 + + +class BJ_Player(BJ_Hand): + """ A Blackjack Player. """ + def is_hitting(self): + response = games.ask_yes_no("\n" + self.name + ", do you want a hit? (Y/N): ") + return response == "y" + + def bust(self): + print(self.name, "busts.") + self.lose() + + def lose(self): + print(self.name, "loses.") + + def win(self): + print(self.name, "wins.") + + def push(self): + print(self.name, "pushes.") + + +class BJ_Dealer(BJ_Hand): + """ A Blackjack Dealer. """ + def is_hitting(self): + return self.total < 17 + + def bust(self): + print(self.name, "busts.") + + def flip_first_card(self): + first_card = self.cards[0] + first_card.flip() + + +class BJ_Game(object): + """ A Blackjack Game. """ + def __init__(self, names): + self.players = [] + for name in names: + player = BJ_Player(name) + self.players.append(player) + + self.dealer = BJ_Dealer("Dealer") + + self.deck = BJ_Deck() + self.deck.populate() + self.deck.shuffle() + + @property + def still_playing(self): + sp = [] + for player in self.players: + if not player.is_busted(): + sp.append(player) + return sp + + def __additional_cards(self, player): + while not player.is_busted() and player.is_hitting(): + self.deck.deal([player]) + print(player) + if player.is_busted(): + player.bust() + + def play(self): + # deal initial 2 cards to everyone + self.deck.deal(self.players + [self.dealer], per_hand = 2) + self.dealer.flip_first_card() # hide dealer's first card + for player in self.players: + print(player) + print(self.dealer) + + # deal additional cards to players + for player in self.players: + self.__additional_cards(player) + + self.dealer.flip_first_card() # reveal dealer's first + + if not self.still_playing: + # since all players have busted, just show the dealer's hand + print(self.dealer) + else: + # deal additional cards to dealer + print(self.dealer) + self.__additional_cards(self.dealer) + + if self.dealer.is_busted(): + # everyone still playing wins + for player in self.still_playing: + player.win() + else: + # compare each player still playing to dealer + for player in self.still_playing: + if player.total > self.dealer.total: + player.win() + elif player.total < self.dealer.total: + player.lose() + else: + player.push() + + # remove everyone's cards + for player in self.players: + player.clear() + self.dealer.clear() + + +def main(): + print("\t\tWelcome to Blackjack!\n") + + names = [] + number = games.ask_number("How many players? (1 - 7): ", low = 1, high = 8) + for i in range(number): + name = input("Enter player name: ") + names.append(name) + print() + + game = BJ_Game(names) + + again = None + while again != "n": + game.play() + again = games.ask_yes_no("\nDo you want to play again?: ") + + +main() +input("\n\nPress the enter key to exit.") + + + diff --git a/Absolute Book/py3e_source/chapter09/cards.py b/Absolute Book/py3e_source/chapter09/cards.py new file mode 100755 index 0000000..b2c44fa --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/cards.py @@ -0,0 +1,73 @@ +# Cards Module +# Basic classes for a game with playing cards + +class Card(object): + """ A playing card. """ + RANKS = ["A", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "J", "Q", "K"] + SUITS = ["c", "d", "h", "s"] + + def __init__(self, rank, suit, face_up = True): + self.rank = rank + self.suit = suit + self.is_face_up = face_up + + def __str__(self): + if self.is_face_up: + rep = self.rank + self.suit + else: + rep = "XX" + return rep + + def flip(self): + self.is_face_up = not self.is_face_up + +class Hand(object): + """ A hand of playing cards. """ + def __init__(self): + self.cards = [] + + def __str__(self): + if self.cards: + rep = "" + for card in self.cards: + rep += str(card) + "\t" + else: + rep = "" + return rep + + def clear(self): + self.cards = [] + + def add(self, card): + self.cards.append(card) + + def give(self, card, other_hand): + self.cards.remove(card) + other_hand.add(card) + +class Deck(Hand): + """ A deck of playing cards. """ + def populate(self): + for suit in Card.SUITS: + for rank in Card.RANKS: + self.add(Card(rank, suit)) + + def shuffle(self): + import random + random.shuffle(self.cards) + + def deal(self, hands, per_hand = 1): + for rounds in range(per_hand): + for hand in hands: + if self.cards: + top_card = self.cards[0] + self.give(top_card, hand) + else: + print("Can't continue deal. Out of cards!") + + + +if __name__ == "__main__": + print("This is a module with classes for playing cards.") + input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter09/games.py b/Absolute Book/py3e_source/chapter09/games.py new file mode 100755 index 0000000..1f229f2 --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/games.py @@ -0,0 +1,33 @@ +# Games +# Demonstrates module creation + +class Player(object): + """ A player for a game. """ + def __init__(self, name, score = 0): + self.name = name + self.score = score + + def __str__(self): + rep = self.name + ":\t" + str(self.score) + return rep + +def ask_yes_no(question): + """Ask a yes or no question.""" + response = None + while response not in ("y", "n"): + response = input(question).lower() + return response + +def ask_number(question, low, high): + """Ask for a number within a range.""" + response = None + while response not in range(low, high): + response = int(input(question)) + return response + + +if __name__ == "__main__": + print("You ran this module directly (and did not 'import' it).") + input("\n\nPress the enter key to exit.") + + diff --git a/Absolute Book/py3e_source/chapter09/playing_cards.py b/Absolute Book/py3e_source/chapter09/playing_cards.py new file mode 100755 index 0000000..d0bfb2d --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/playing_cards.py @@ -0,0 +1,85 @@ +# Playing Cards +# Demonstrates combining objects + +class Card(object): + """ A playing card. """ + RANKS = ["A", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "J", "Q", "K"] + SUITS = ["c", "d", "h", "s"] + + def __init__(self, rank, suit): + self.rank = rank + self.suit = suit + + def __str__(self): + rep = self.rank + self.suit + return rep + + +class Hand(object): + """ A hand of playing cards. """ + def __init__(self): + self.cards = [] + + def __str__(self): + if self.cards: + rep = "" + for card in self.cards: + rep += str(card) + " " + else: + rep = "" + return rep + + def clear(self): + self.cards = [] + + def add(self, card): + self.cards.append(card) + + def give(self, card, other_hand): + self.cards.remove(card) + other_hand.add(card) + + +# main +card1 = Card(rank = "A", suit = "c") +print("Printing a Card object:") +print(card1) + +card2 = Card(rank = "2", suit = "c") +card3 = Card(rank = "3", suit = "c") +card4 = Card(rank = "4", suit = "c") +card5 = Card(rank = "5", suit = "c") +print("\nPrinting the rest of the objects individually:") +print(card2) +print(card3) +print(card4) +print(card5) + +my_hand = Hand() +print("\nPrinting my hand before I add any cards:") +print(my_hand) + +my_hand.add(card1) +my_hand.add(card2) +my_hand.add(card3) +my_hand.add(card4) +my_hand.add(card5) +print("\nPrinting my hand after adding 5 cards:") +print(my_hand) + +your_hand = Hand() +my_hand.give(card1, your_hand) +my_hand.give(card2, your_hand) +print("\nGave the first two cards from my hand to your hand.") +print("Your hand:") +print(your_hand) +print("My hand:") +print(my_hand) + +my_hand.clear() +print("\nMy hand after clearing it:") +print(my_hand) + +input("\n\nPress the enter key to exit.") + diff --git a/Absolute Book/py3e_source/chapter09/playing_cards2.py b/Absolute Book/py3e_source/chapter09/playing_cards2.py new file mode 100755 index 0000000..f61238b --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/playing_cards2.py @@ -0,0 +1,95 @@ +# Playing Cards 2.0 +# Demonstrates inheritance - class extension + +class Card(object): + """ A playing card. """ + RANKS = ["A", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "J", "Q", "K"] + SUITS = ["c", "d", "h", "s"] + + def __init__(self, rank, suit): + self.rank = rank + self.suit = suit + + def __str__(self): + rep = self.rank + self.suit + return rep + +class Hand(object): + """ A hand of playing cards. """ + def __init__(self): + self.cards = [] + + def __str__(self): + if self.cards: + rep = "" + for card in self.cards: + rep += str(card) + "\t" + else: + rep = "" + return rep + + def clear(self): + self.cards = [] + + def add(self, card): + self.cards.append(card) + + def give(self, card, other_hand): + self.cards.remove(card) + other_hand.add(card) + +class Deck(Hand): + """ A deck of playing cards. """ + def populate(self): + for suit in Card.SUITS: + for rank in Card.RANKS: + self.add(Card(rank, suit)) + + def shuffle(self): + import random + random.shuffle(self.cards) + + def deal(self, hands, per_hand = 1): + for rounds in range(per_hand): + for hand in hands: + if self.cards: + top_card = self.cards[0] + self.give(top_card, hand) + else: + print("Can't continue deal. Out of cards!") + + +# main +deck1 = Deck() +print("Created a new deck.") +print("Deck:") +print(deck1) + +deck1.populate() +print("\nPopulated the deck.") +print("Deck:") +print(deck1) + +deck1.shuffle() +print("\nShuffled the deck.") +print("Deck:") +print(deck1) + +my_hand = Hand() +your_hand = Hand() +hands = [my_hand, your_hand] +deck1.deal(hands, per_hand = 5) +print("\nDealt 5 cards to my hand and your hand.") +print("My hand:") +print(my_hand) +print("Your hand:") +print(your_hand) +print("Deck:") +print(deck1) + +deck1.clear() +print("\nCleared the deck.") +print("Deck:", deck1) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter09/playing_cards3.py b/Absolute Book/py3e_source/chapter09/playing_cards3.py new file mode 100755 index 0000000..c0de8b7 --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/playing_cards3.py @@ -0,0 +1,60 @@ +# Playing Cards 3.0 +# Demonstrates inheritance - overriding methods + +class Card(object): + """ A playing card. """ + RANKS = ["A", "2", "3", "4", "5", "6", "7", + "8", "9", "10", "J", "Q", "K"] + SUITS = ["c", "d", "h", "s"] + + def __init__(self, rank, suit): + self.rank = rank + self.suit = suit + + def __str__(self): + rep = self.rank + self.suit + return rep + + +class Unprintable_Card(Card): + """ A Card that won't reveal its rank or suit when printed. """ + def __str__(self): + return "" + + +class Positionable_Card(Card): + """ A Card that can be face up or face down. """ + def __init__(self, rank, suit, face_up = True): + super(Positionable_Card, self).__init__(rank, suit) + self.is_face_up = face_up + + def __str__(self): + if self.is_face_up: + rep = super(Positionable_Card, self).__str__() + else: + rep = "XX" + return rep + + def flip(self): + self.is_face_up = not self.is_face_up + + +#main +card1 = Card("A", "c") +card2 = Unprintable_Card("A", "d") +card3 = Positionable_Card("A", "h") + +print("Printing a Card object:") +print(card1) + +print("\nPrinting an Unprintable_Card object:") +print(card2) + +print("\nPrinting a Positionable_Card object:") +print(card3) +print("Flipping the Positionable_Card object.") +card3.flip() +print("Printing the Positionable_Card object:") +print(card3) + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter09/simple_game.py b/Absolute Book/py3e_source/chapter09/simple_game.py new file mode 100755 index 0000000..3e9f853 --- /dev/null +++ b/Absolute Book/py3e_source/chapter09/simple_game.py @@ -0,0 +1,25 @@ +# Simple Game +# Demonstrates importing modules + +import games, random + +print("Welcome to the world's simplest game!\n") + +again = None +while again != "n": + players = [] + num = games.ask_number(question = "How many players? (2 - 5): ", + low = 2, high = 5) + for i in range(num): + name = input("Player name: ") + score = random.randrange(100) + 1 + player = games.Player(name, score) + players.append(player) + + print("\nHere are the game results:") + for player in players: + print(player) + + again = games.ask_yes_no("\nDo you want to play again? (y/n): ") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter10/click_counter.bat b/Absolute Book/py3e_source/chapter10/click_counter.bat new file mode 100755 index 0000000..5996c33 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/click_counter.bat @@ -0,0 +1,2 @@ +click_counter.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/click_counter.py b/Absolute Book/py3e_source/chapter10/click_counter.py new file mode 100755 index 0000000..2c9745b --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/click_counter.py @@ -0,0 +1,34 @@ +# Click Counter +# Demonstrates binding an event with an event handler + +from tkinter import * + +class Application(Frame): + """ GUI application which counts button clicks. """ + def __init__(self, master): + """ Initialize the frame. """ + super(Application, self).__init__(master) + self.grid() + self.bttn_clicks = 0 # the number of button clicks + self.create_widget() + + def create_widget(self): + """ Create button which displays number of clicks. """ + self.bttn = Button(self) + self.bttn["text"]= "Total Clicks: 0" + self.bttn["command"] = self.update_count + self.bttn.grid() + + def update_count(self): + """ Increase click count and display new total. """ + self.bttn_clicks += 1 + self.bttn["text"] = "Total Clicks: " + str(self.bttn_clicks) + +# main +root = Tk() +root.title("Click Counter") +root.geometry("200x50") + +app = Application(root) + +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/labeler.bat b/Absolute Book/py3e_source/chapter10/labeler.bat new file mode 100755 index 0000000..568eaef --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/labeler.bat @@ -0,0 +1,2 @@ +labeler.py +pause diff --git a/Absolute Book/py3e_source/chapter10/labeler.py b/Absolute Book/py3e_source/chapter10/labeler.py new file mode 100755 index 0000000..6e59dc9 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/labeler.py @@ -0,0 +1,20 @@ +# Labeler +# Demonstrates a label + +from tkinter import * + +# create the root window +root = Tk() +root.title("Labeler") +root.geometry("200x50") + +# create a frame in the window to hold other widgets +app = Frame(root) +app.grid() + +# create a label in the frame +lbl = Label(app, text = "I'm a label!") +lbl.grid() + +# kick off the window's event loop +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/lazy_buttons.bat b/Absolute Book/py3e_source/chapter10/lazy_buttons.bat new file mode 100755 index 0000000..dabf12e --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/lazy_buttons.bat @@ -0,0 +1,2 @@ +lazy_buttons.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/lazy_buttons.py b/Absolute Book/py3e_source/chapter10/lazy_buttons.py new file mode 100755 index 0000000..27f1326 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/lazy_buttons.py @@ -0,0 +1,30 @@ +# Lazy Buttons +# Demonstrates creating buttons + +from tkinter import * + +# create a root window +root = Tk() +root.title("Lazy Buttons") +root.geometry("200x85") + +# create a frame in the window to hold other widgets +app = Frame(root) +app.grid() + +# create a button in the frame +bttn1 = Button(app, text = "I do nothing!") +bttn1.grid() + +# create a second button in the frame +bttn2 = Button(app) +bttn2.grid() +bttn2.configure(text = "Me too!") + +# create a third button in the frame +bttn3 = Button(app) +bttn3.grid() +bttn3["text"]= "Same here!" + +# kick off the root window's event loop +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/lazy_buttons2.bat b/Absolute Book/py3e_source/chapter10/lazy_buttons2.bat new file mode 100755 index 0000000..b66f453 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/lazy_buttons2.bat @@ -0,0 +1,2 @@ +lazy_buttons2.py +pause diff --git a/Absolute Book/py3e_source/chapter10/lazy_buttons2.py b/Absolute Book/py3e_source/chapter10/lazy_buttons2.py new file mode 100755 index 0000000..c893f6a --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/lazy_buttons2.py @@ -0,0 +1,35 @@ +# Lazy Buttons 2 +# Demonstrates using a class with Tkinter + +from tkinter import * + +class Application(Frame): + """ A GUI application with three buttons. """ + def __init__(self, master): + """ Initialize the Frame. """ + super(Application, self).__init__(master) + self.grid() + self.create_widgets() + + def create_widgets(self): + """ Create three buttons that do nothing. """ + # create first button + self.bttn1 = Button(self, text = "I do nothing!") + self.bttn1.grid() + + # create second button + self.bttn2 = Button(self) + self.bttn2.grid() + self.bttn2.configure(text = "Me too!") + + # create third button + self.bttn3 = Button(self) + self.bttn3.grid() + self.bttn3["text"] = "Same here!" + +# main +root = Tk() +root.title("Lazy Buttons 2") +root.geometry("200x85") +app = Application(root) +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/longevity (2).bat b/Absolute Book/py3e_source/chapter10/longevity (2).bat new file mode 100644 index 0000000..1a45b48 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/longevity (2).bat @@ -0,0 +1,2 @@ +longevity.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/longevity.bat b/Absolute Book/py3e_source/chapter10/longevity.bat new file mode 100755 index 0000000..1a45b48 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/longevity.bat @@ -0,0 +1,2 @@ +longevity.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/longevity.py b/Absolute Book/py3e_source/chapter10/longevity.py new file mode 100755 index 0000000..2925ad5 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/longevity.py @@ -0,0 +1,55 @@ +# Longevity +# Demonstrates text and entry widgets, and the grid layout manager + +from tkinter import * + +class Application(Frame): + """ GUI application which can reveal the secret of longevity. """ + def __init__(self, master): + """ Initialize the frame. """ + super(Application, self).__init__(master) + self.grid() + self.create_widgets() + + def create_widgets(self): + """ Create button, text, and entry widgets. """ + # create instruction label + self.inst_lbl = Label(self, text = "Enter password for the secret of longevity") + self.inst_lbl.grid(row = 0, column = 0, columnspan = 2, sticky = W) + + # create label for password + self.pw_lbl = Label(self, text = "Password: ") + self.pw_lbl.grid(row = 1, column = 0, sticky = W) + + # create entry widget to accept password + self.pw_ent = Entry(self) + self.pw_ent.grid(row = 1, column = 1, sticky = W) + + # create submit button + self.submit_bttn = Button(self, text = "Submit", command = self.reveal) + self.submit_bttn.grid(row = 2, column = 0, sticky = W) + + # create text widget to display message + self.secret_txt = Text(self, width = 35, height = 5, wrap = WORD) + self.secret_txt.grid(row = 3, column = 0, columnspan = 2, sticky = W) + + def reveal(self): + """ Display message based on password. """ + contents = self.pw_ent.get() + if contents == "secret": + message = "Here's the secret to living to 100: live to 99 " \ + "and then be VERY careful." + else: + message = "That's not the correct password, so I can't share " \ + "the secret with you." + self.secret_txt.delete(0.0, END) + self.secret_txt.insert(0.0, message) + +# main +root = Tk() +root.title("Longevity") +root.geometry("300x150") + +app = Application(root) + +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/mad_lib.bat b/Absolute Book/py3e_source/chapter10/mad_lib.bat new file mode 100755 index 0000000..ed237f7 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/mad_lib.bat @@ -0,0 +1,2 @@ +mad_lib.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/mad_lib.py b/Absolute Book/py3e_source/chapter10/mad_lib.py new file mode 100755 index 0000000..9628df5 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/mad_lib.py @@ -0,0 +1,143 @@ +# Mad Lib +# Create a story based on user input + +from tkinter import * + +class Application(Frame): + """ GUI application that creates a story based on user input. """ + def __init__(self, master): + """ Initialize Frame. """ + super(Application, self).__init__(master) + self.grid() + self.create_widgets() + + def create_widgets(self): + """ Create widgets to get story information and to display story. """ + # create instruction label + Label(self, + text = "Enter information for a new story" + ).grid(row = 0, column = 0, columnspan = 2, sticky = W) + + # create a label and text entry for the name of a person + Label(self, + text = "Person: " + ).grid(row = 1, column = 0, sticky = W) + self.person_ent = Entry(self) + self.person_ent.grid(row = 1, column = 1, sticky = W) + + # create a label and text entry for a plural noun + Label(self, + text = "Plural Noun:" + ).grid(row = 2, column = 0, sticky = W) + self.noun_ent = Entry(self) + self.noun_ent.grid(row = 2, column = 1, sticky = W) + + # create a label and text entry for a verb + Label(self, + text = "Verb:" + ).grid(row = 3, column = 0, sticky = W) + self.verb_ent = Entry(self) + self.verb_ent.grid(row = 3, column = 1, sticky = W) + + # create a label for adjectives check buttons + Label(self, + text = "Adjective(s):" + ).grid(row = 4, column = 0, sticky = W) + + # create itchy check button + self.is_itchy = BooleanVar() + Checkbutton(self, + text = "itchy", + variable = self.is_itchy + ).grid(row = 4, column = 1, sticky = W) + + # create joyous check button + self.is_joyous = BooleanVar() + Checkbutton(self, + text = "joyous", + variable = self.is_joyous + ).grid(row = 4, column = 2, sticky = W) + + # create electric check button + self.is_electric = BooleanVar() + Checkbutton(self, + text = "electric", + variable = self.is_electric + ).grid(row = 4, column = 3, sticky = W) + + # create a label for body parts radio buttons + Label(self, + text = "Body Part:" + ).grid(row = 5, column = 0, sticky = W) + + # create variable for single, body part + self.body_part = StringVar() + self.body_part.set(None) + + # create body part radio buttons + body_parts = ["bellybutton", "big toe", "medulla oblongata"] + column = 1 + for part in body_parts: + Radiobutton(self, + text = part, + variable = self.body_part, + value = part + ).grid(row = 5, column = column, sticky = W) + column += 1 + + # create a submit button + Button(self, + text = "Click for story", + command = self.tell_story + ).grid(row = 6, column = 0, sticky = W) + + self.story_txt = Text(self, width = 75, height = 10, wrap = WORD) + self.story_txt.grid(row = 7, column = 0, columnspan = 4) + + def tell_story(self): + """ Fill text box with new story based on user input. """ + # get values from the GUI + person = self.person_ent.get() + noun = self.noun_ent.get() + verb = self.verb_ent.get() + adjectives = "" + if self.is_itchy.get(): + adjectives += "itchy, " + if self.is_joyous.get(): + adjectives += "joyous, " + if self.is_electric.get(): + adjectives += "electric, " + body_part = self.body_part.get() + + # create the story + story = "The famous explorer " + story += person + story += " had nearly given up a life-long quest to find The Lost City of " + story += noun.title() + story += " when one day, the " + story += noun + story += " found " + story += person + ". " + story += "A strong, " + story += adjectives + story += "peculiar feeling overwhelmed the explorer. " + story += "After all this time, the quest was finally over. A tear came to " + story += person + "'s " + story += body_part + ". " + story += "And then, the " + story += noun + story += " promptly devoured " + story += person + ". " + story += "The moral of the story? Be careful what you " + story += verb + story += " for." + + # display the story + self.story_txt.delete(0.0, END) + self.story_txt.insert(0.0, story) + +# main +root = Tk() +root.title("Mad Lib") +app = Application(root) +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/movie_chooser.bat b/Absolute Book/py3e_source/chapter10/movie_chooser.bat new file mode 100755 index 0000000..39d0969 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/movie_chooser.bat @@ -0,0 +1,2 @@ +movie_chooser.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/movie_chooser.py b/Absolute Book/py3e_source/chapter10/movie_chooser.py new file mode 100755 index 0000000..8099781 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/movie_chooser.py @@ -0,0 +1,73 @@ +# Movie Chooser +# Demonstrates check buttons + +from tkinter import * + +class Application(Frame): + """ GUI Application for favorite movie types. """ + def __init__(self, master): + super(Application, self).__init__(master) + self.grid() + self.create_widgets() + + def create_widgets(self): + """ Create widgets for movie type choices. """ + # create description label + Label(self, + text = "Choose your favorite movie types" + ).grid(row = 0, column = 0, sticky = W) + + # create instruction label + Label(self, + text = "Select all that apply:" + ).grid(row = 1, column = 0, sticky = W) + + # create Comedy check button + self.likes_comedy = BooleanVar() + Checkbutton(self, + text = "Comedy", + variable = self.likes_comedy, + command = self.update_text + ).grid(row = 2, column = 0, sticky = W) + + # create Drama check button + self.likes_drama = BooleanVar() + Checkbutton(self, + text = "Drama", + variable = self.likes_drama, + command = self.update_text + ).grid(row = 3, column = 0, sticky = W) + + # create Romance check button + self.likes_romance = BooleanVar() + Checkbutton(self, + text = "Romance", + variable = self.likes_romance, + command = self.update_text + ).grid(row = 4, column = 0, sticky = W) + + # create text field to display results + self.results_txt = Text(self, width = 40, height = 5, wrap = WORD) + self.results_txt.grid(row = 5, column = 0, columnspan = 3) + + def update_text(self): + """ Update text widget and display user's favorite movie types. """ + likes = "" + + if self.likes_comedy.get(): + likes += "You like comedic movies.\n" + + if self.likes_drama.get(): + likes += "You like dramatic movies.\n" + + if self.likes_romance.get(): + likes += "You like romantic movies." + + self.results_txt.delete(0.0, END) + self.results_txt.insert(0.0, likes) + +# main +root = Tk() +root.title("Movie Chooser") +app = Application(root) +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/movie_chooser2.bat b/Absolute Book/py3e_source/chapter10/movie_chooser2.bat new file mode 100755 index 0000000..a26639d --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/movie_chooser2.bat @@ -0,0 +1,2 @@ +movie_chooser2.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter10/movie_chooser2.py b/Absolute Book/py3e_source/chapter10/movie_chooser2.py new file mode 100755 index 0000000..4be44f1 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/movie_chooser2.py @@ -0,0 +1,70 @@ +# Movie Chooser 2 +# Demonstrates radio buttons + +from tkinter import * + +class Application(Frame): + """ GUI Application for favorite movie type. """ + def __init__(self, master): + """ Initialize Frame. """ + super(Application, self).__init__(master) + self.grid() + self.create_widgets() + + def create_widgets(self): + """ Create widgets for movie type choices. """ + # create description label + Label(self, + text = "Choose your favorite type of movie" + ).grid(row = 0, column = 0, sticky = W) + + # create instruction label + Label(self, + text = "Select one:" + ).grid(row = 1, column = 0, sticky = W) + + # create variable for single, favorite type of movie + self.favorite = StringVar() + self.favorite.set(None) + + # create Comedy radio button + Radiobutton(self, + text = "Comedy", + variable = self.favorite, + value = "comedy.", + command = self.update_text + ).grid(row = 2, column = 0, sticky = W) + + # create Drama radio button + Radiobutton(self, + text = "Drama", + variable = self.favorite, + value = "drama.", + command = self.update_text + ).grid(row = 3, column = 0, sticky = W) + + # create Romance radio button + Radiobutton(self, + text = "Romance", + variable = self.favorite, + value = "romance.", + command = self.update_text + ).grid(row = 4, column = 0, sticky = W) + + # create text field to display result + self.results_txt = Text(self, width = 40, height = 5, wrap = WORD) + self.results_txt.grid(row = 5, column = 0, columnspan = 3) + + def update_text(self): + """ Update text area and display user's favorite movie type. """ + message = "Your favorite type of movie is " + message += self.favorite.get() + + self.results_txt.delete(0.0, END) + self.results_txt.insert(0.0, message) + +# main +root = Tk() +root.title("Movie Chooser 2") +app = Application(root) +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter10/simple_gui.bat b/Absolute Book/py3e_source/chapter10/simple_gui.bat new file mode 100755 index 0000000..977ecdd --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/simple_gui.bat @@ -0,0 +1,2 @@ +simple_gui.py +pause diff --git a/Absolute Book/py3e_source/chapter10/simple_gui.py b/Absolute Book/py3e_source/chapter10/simple_gui.py new file mode 100755 index 0000000..c012461 --- /dev/null +++ b/Absolute Book/py3e_source/chapter10/simple_gui.py @@ -0,0 +1,14 @@ +# Simple GUI +# Demonstrates creating a window + +from tkinter import * + +# create the root window +root = Tk() + +# modify the window +root.title("Simple GUI") +root.geometry("200x100") + +# kick off the window's event-loop +root.mainloop() diff --git a/Absolute Book/py3e_source/chapter11/Thumbs.db b/Absolute Book/py3e_source/chapter11/Thumbs.db new file mode 100755 index 0000000..3ff1306 Binary files /dev/null and b/Absolute Book/py3e_source/chapter11/Thumbs.db differ diff --git a/Absolute Book/py3e_source/chapter11/background_image.bat b/Absolute Book/py3e_source/chapter11/background_image.bat new file mode 100755 index 0000000..8ccaf92 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/background_image.bat @@ -0,0 +1,2 @@ +background_image.py +pause diff --git a/Absolute Book/py3e_source/chapter11/background_image.py b/Absolute Book/py3e_source/chapter11/background_image.py new file mode 100755 index 0000000..3f31673 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/background_image.py @@ -0,0 +1,11 @@ +# Background Image +# Demonstrates setting the background image of a graphics screen + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +wall_image = games.load_image("wall.jpg", transparent = False) +games.screen.background = wall_image + +games.screen.mainloop() diff --git a/Absolute Book/py3e_source/chapter11/big_score.bat b/Absolute Book/py3e_source/chapter11/big_score.bat new file mode 100755 index 0000000..60d633d --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/big_score.bat @@ -0,0 +1,2 @@ +big_score.py +pause diff --git a/Absolute Book/py3e_source/chapter11/big_score.py b/Absolute Book/py3e_source/chapter11/big_score.py new file mode 100755 index 0000000..b2ae2f3 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/big_score.py @@ -0,0 +1,18 @@ +# Big Score +# Demonstrates displaying text on a graphics screen + +from livewires import games, color + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +wall_image = games.load_image("wall.jpg", transparent = False) +games.screen.background = wall_image + +score = games.Text(value = 1756521, + size = 60, + color = color.black, + x = 550, + y = 30) +games.screen.add(score) + +games.screen.mainloop() diff --git a/Absolute Book/py3e_source/chapter11/bouncing_pizza.bat b/Absolute Book/py3e_source/chapter11/bouncing_pizza.bat new file mode 100755 index 0000000..05d8432 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/bouncing_pizza.bat @@ -0,0 +1,2 @@ +bouncing_pizza.py +pause diff --git a/Absolute Book/py3e_source/chapter11/bouncing_pizza.py b/Absolute Book/py3e_source/chapter11/bouncing_pizza.py new file mode 100755 index 0000000..872a4f9 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/bouncing_pizza.py @@ -0,0 +1,33 @@ +# Bouncing Pizza +# Demonstrates dealing with screen boundaries + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +class Pizza(games.Sprite): + """ A bouncing pizza. """ + def update(self): + """ Reverse a velocity component if edge of screen reached. """ + if self.right > games.screen.width or self.left < 0: + self.dx = -self.dx + + if self.bottom > games.screen.height or self.top < 0: + self.dy = -self.dy + +def main(): + wall_image = games.load_image("wall.jpg", transparent = False) + games.screen.background = wall_image + + pizza_image = games.load_image("pizza.bmp") + the_pizza = Pizza(image = pizza_image, + x = games.screen.width/2, + y = games.screen.height/2, + dx = 1, + dy = 1) + games.screen.add(the_pizza) + + games.screen.mainloop() + +# kick it off! +main() diff --git a/Absolute Book/py3e_source/chapter11/chef.bmp b/Absolute Book/py3e_source/chapter11/chef.bmp new file mode 100755 index 0000000..3c46ebe Binary files /dev/null and b/Absolute Book/py3e_source/chapter11/chef.bmp differ diff --git a/Absolute Book/py3e_source/chapter11/moving_pan.bat b/Absolute Book/py3e_source/chapter11/moving_pan.bat new file mode 100755 index 0000000..59bb227 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/moving_pan.bat @@ -0,0 +1,2 @@ +moving_pan.py +pause diff --git a/Absolute Book/py3e_source/chapter11/moving_pan.py b/Absolute Book/py3e_source/chapter11/moving_pan.py new file mode 100755 index 0000000..080e31e --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/moving_pan.py @@ -0,0 +1,32 @@ +# Moving Pan +# Demonstrates mouse input + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +class Pan(games.Sprite): + """" A pan controlled by the mouse. """ + def update(self): + """ Move to mouse coordinates. """ + self.x = games.mouse.x + self.y = games.mouse.y + +def main(): + wall_image = games.load_image("wall.jpg", transparent = False) + games.screen.background = wall_image + + pan_image = games.load_image("pan.bmp") + the_pan = Pan(image = pan_image, + x = games.mouse.x, + y = games.mouse.y) + games.screen.add(the_pan) + + games.mouse.is_visible = False + + games.screen.event_grab = True + + games.screen.mainloop() + +# kick it off! +main() diff --git a/Absolute Book/py3e_source/chapter11/moving_pizza.bat b/Absolute Book/py3e_source/chapter11/moving_pizza.bat new file mode 100755 index 0000000..e4f0ae8 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/moving_pizza.bat @@ -0,0 +1,2 @@ +moving_pizza.py +pause diff --git a/Absolute Book/py3e_source/chapter11/moving_pizza.py b/Absolute Book/py3e_source/chapter11/moving_pizza.py new file mode 100755 index 0000000..653edd4 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/moving_pizza.py @@ -0,0 +1,20 @@ +# Moving Pizza +# Demonstrates sprite velocities + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +wall_image = games.load_image("wall.jpg", transparent = False) +games.screen.background = wall_image + +pizza_image = games.load_image("pizza.bmp") +the_pizza = games.Sprite(image = pizza_image, + x = games.screen.width/2, + y = games.screen.height/2, + dx = 1, + dy = 1) +games.screen.add(the_pizza) + +games.screen.mainloop() + diff --git a/Absolute Book/py3e_source/chapter11/new_graphics_window.bat b/Absolute Book/py3e_source/chapter11/new_graphics_window.bat new file mode 100755 index 0000000..7efc786 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/new_graphics_window.bat @@ -0,0 +1,2 @@ +new_graphics_window.py +pause diff --git a/Absolute Book/py3e_source/chapter11/new_graphics_window.py b/Absolute Book/py3e_source/chapter11/new_graphics_window.py new file mode 100755 index 0000000..918055c --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/new_graphics_window.py @@ -0,0 +1,8 @@ +# New Graphics Window +# Demonstrates creating a graphics window + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +games.screen.mainloop() diff --git a/Absolute Book/py3e_source/chapter11/pan.bmp b/Absolute Book/py3e_source/chapter11/pan.bmp new file mode 100755 index 0000000..a2a02b1 Binary files /dev/null and b/Absolute Book/py3e_source/chapter11/pan.bmp differ diff --git a/Absolute Book/py3e_source/chapter11/pizza.bmp b/Absolute Book/py3e_source/chapter11/pizza.bmp new file mode 100755 index 0000000..bd1a63f Binary files /dev/null and b/Absolute Book/py3e_source/chapter11/pizza.bmp differ diff --git a/Absolute Book/py3e_source/chapter11/pizza_panic.bat b/Absolute Book/py3e_source/chapter11/pizza_panic.bat new file mode 100755 index 0000000..70962f4 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/pizza_panic.bat @@ -0,0 +1,2 @@ +pizza_panic.py +pause diff --git a/Absolute Book/py3e_source/chapter11/pizza_panic.py b/Absolute Book/py3e_source/chapter11/pizza_panic.py new file mode 100755 index 0000000..c80cfc2 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/pizza_panic.py @@ -0,0 +1,137 @@ +# Pizza Panic +# Player must catch falling pizzas before they hit the ground + +from livewires import games, color +import random + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Pan(games.Sprite): + """ + A pan controlled by player to catch falling pizzas. + """ + image = games.load_image("pan.bmp") + + def __init__(self): + """ Initialize Pan object and create Text object for score. """ + super(Pan, self).__init__(image = Pan.image, + x = games.mouse.x, + bottom = games.screen.height) + + self.score = games.Text(value = 0, size = 25, color = color.black, + top = 5, right = games.screen.width - 10) + games.screen.add(self.score) + + def update(self): + """ Move to mouse x position. """ + self.x = games.mouse.x + + if self.left < 0: + self.left = 0 + + if self.right > games.screen.width: + self.right = games.screen.width + + self.check_catch() + + def check_catch(self): + """ Check if catch pizzas. """ + for pizza in self.overlapping_sprites: + self.score.value += 10 + self.score.right = games.screen.width - 10 + pizza.handle_caught() + + +class Pizza(games.Sprite): + """ + A pizza which falls to the ground. + """ + image = games.load_image("pizza.bmp") + speed = 1 + + def __init__(self, x, y = 90): + """ Initialize a Pizza object. """ + super(Pizza, self).__init__(image = Pizza.image, + x = x, y = y, + dy = Pizza.speed) + + def update(self): + """ Check if bottom edge has reached screen bottom. """ + if self.bottom > games.screen.height: + self.end_game() + self.destroy() + + def handle_caught(self): + """ Destroy self if caught. """ + self.destroy() + + def end_game(self): + """ End the game. """ + end_message = games.Message(value = "Game Over", + size = 90, + color = color.red, + x = games.screen.width/2, + y = games.screen.height/2, + lifetime = 5 * games.screen.fps, + after_death = games.screen.quit) + games.screen.add(end_message) + + +class Chef(games.Sprite): + """ + A chef which moves left and right, dropping pizzas. + """ + image = games.load_image("chef.bmp") + + def __init__(self, y = 55, speed = 2, odds_change = 200): + """ Initialize the Chef object. """ + super(Chef, self).__init__(image = Chef.image, + x = games.screen.width / 2, + y = y, + dx = speed) + + self.odds_change = odds_change + self.time_til_drop = 0 + + def update(self): + """ Determine if direction needs to be reversed. """ + if self.left < 0 or self.right > games.screen.width: + self.dx = -self.dx + elif random.randrange(self.odds_change) == 0: + self.dx = -self.dx + + self.check_drop() + + + def check_drop(self): + """ Decrease countdown or drop pizza and reset countdown. """ + if self.time_til_drop > 0: + self.time_til_drop -= 1 + else: + new_pizza = Pizza(x = self.x) + games.screen.add(new_pizza) + + # set buffer to approx 30% of pizza height, regardless of pizza speed + self.time_til_drop = int(new_pizza.height * 1.3 / Pizza.speed) + 1 + + +def main(): + """ Play the game. """ + wall_image = games.load_image("wall.jpg", transparent = False) + games.screen.background = wall_image + + the_chef = Chef() + games.screen.add(the_chef) + + the_pan = Pan() + games.screen.add(the_pan) + + games.mouse.is_visible = False + + games.screen.event_grab = True + games.screen.mainloop() + +# start it up! +main() + diff --git a/Absolute Book/py3e_source/chapter11/pizza_sprite.bat b/Absolute Book/py3e_source/chapter11/pizza_sprite.bat new file mode 100755 index 0000000..c2c51a0 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/pizza_sprite.bat @@ -0,0 +1,2 @@ +pizza_sprite.py +pause diff --git a/Absolute Book/py3e_source/chapter11/pizza_sprite.py b/Absolute Book/py3e_source/chapter11/pizza_sprite.py new file mode 100755 index 0000000..7613e24 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/pizza_sprite.py @@ -0,0 +1,15 @@ +# Pizza Sprite +# Demonstrates creating a sprite + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +wall_image = games.load_image("wall.jpg", transparent = False) +games.screen.background = wall_image + +pizza_image = games.load_image("pizza.bmp") +pizza = games.Sprite(image = pizza_image, x = 320, y = 240) +games.screen.add(pizza) + +games.screen.mainloop() diff --git a/Absolute Book/py3e_source/chapter11/slippery_pizza.bat b/Absolute Book/py3e_source/chapter11/slippery_pizza.bat new file mode 100755 index 0000000..d9fc825 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/slippery_pizza.bat @@ -0,0 +1,2 @@ +slippery_pizza.py +pause diff --git a/Absolute Book/py3e_source/chapter11/slippery_pizza.py b/Absolute Book/py3e_source/chapter11/slippery_pizza.py new file mode 100755 index 0000000..5b5adeb --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/slippery_pizza.py @@ -0,0 +1,55 @@ +# Slippery Pizza Program +# Demonstrates testing for sprite collisions + +from livewires import games +import random + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Pan(games.Sprite): + """" A pan controlled by the mouse. """ + def update(self): + """ Move to mouse position. """ + self.x = games.mouse.x + self.y = games.mouse.y + self.check_collide() + + def check_collide(self): + """ Check for collision with pizza. """ + for pizza in self.overlapping_sprites: + pizza.handle_collide() + + +class Pizza(games.Sprite): + """" A slippery pizza. """ + def handle_collide(self): + """ Move to a random screen location. """ + self.x = random.randrange(games.screen.width) + self.y = random.randrange(games.screen.height) + + +def main(): + wall_image = games.load_image("wall.jpg", transparent = False) + games.screen.background = wall_image + + pizza_image = games.load_image("pizza.bmp") + pizza_x = random.randrange(games.screen.width) + pizza_y = random.randrange(games.screen.height) + the_pizza = Pizza(image = pizza_image, x = pizza_x, y = pizza_y) + games.screen.add(the_pizza) + + pan_image = games.load_image("pan.bmp") + the_pan = Pan(image = pan_image, + x = games.mouse.x, + y = games.mouse.y) + games.screen.add(the_pan) + + games.mouse.is_visible = False + + games.screen.event_grab = True + + games.screen.mainloop() + +# kick it off! +main() diff --git a/Absolute Book/py3e_source/chapter11/wall.jpg b/Absolute Book/py3e_source/chapter11/wall.jpg new file mode 100644 index 0000000..45a7ad0 Binary files /dev/null and b/Absolute Book/py3e_source/chapter11/wall.jpg differ diff --git a/Absolute Book/py3e_source/chapter11/you_won.bat b/Absolute Book/py3e_source/chapter11/you_won.bat new file mode 100755 index 0000000..17d8771 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/you_won.bat @@ -0,0 +1,2 @@ +you_won.py +pause diff --git a/Absolute Book/py3e_source/chapter11/you_won.py b/Absolute Book/py3e_source/chapter11/you_won.py new file mode 100755 index 0000000..4a26025 --- /dev/null +++ b/Absolute Book/py3e_source/chapter11/you_won.py @@ -0,0 +1,20 @@ +# You Won +# Demonstrates displaying a message + +from livewires import games, color + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +wall_image = games.load_image("wall.jpg", transparent = False) +games.screen.background = wall_image + +won_message = games.Message(value = "You won!", + size = 100, + color = color.red, + x = games.screen.width/2, + y = games.screen.height/2, + lifetime = 250, + after_death = games.screen.quit) +games.screen.add(won_message) + +games.screen.mainloop() diff --git a/Absolute Book/py3e_source/chapter12/asteroid_big.bmp b/Absolute Book/py3e_source/chapter12/asteroid_big.bmp new file mode 100755 index 0000000..efd7146 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/asteroid_big.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/asteroid_med.bmp b/Absolute Book/py3e_source/chapter12/asteroid_med.bmp new file mode 100755 index 0000000..c48387e Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/asteroid_med.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/asteroid_small.bmp b/Absolute Book/py3e_source/chapter12/asteroid_small.bmp new file mode 100755 index 0000000..397a779 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/asteroid_small.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/astrocrash01.bat b/Absolute Book/py3e_source/chapter12/astrocrash01.bat new file mode 100755 index 0000000..c44ba74 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash01.bat @@ -0,0 +1,2 @@ +astrocrash01.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash01.py b/Absolute Book/py3e_source/chapter12/astrocrash01.py new file mode 100755 index 0000000..e6d3a75 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash01.py @@ -0,0 +1,63 @@ +# Astrocrash01 +# Get asteroids moving on the screen + +import random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Asteroid(games.Sprite): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def update(self): + """ Wrap around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + games.screen.mainloop() + +# kick it off! +main() + diff --git a/Absolute Book/py3e_source/chapter12/astrocrash02.bat b/Absolute Book/py3e_source/chapter12/astrocrash02.bat new file mode 100755 index 0000000..209fb9c --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash02.bat @@ -0,0 +1,2 @@ +astrocrash02.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash02.py b/Absolute Book/py3e_source/chapter12/astrocrash02.py new file mode 100755 index 0000000..ccad6c0 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash02.py @@ -0,0 +1,82 @@ +# Astrocrash02 +# Get asteroids moving on the screen + +import random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Asteroid(games.Sprite): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def update(self): + """ Wrap around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +class Ship(games.Sprite): + """ The player's ship. """ + image = games.load_image("ship.bmp") + ROTATION_STEP = 3 + + def update(self): + """ Rotate based on keys pressed. """ + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + # create the ship + the_ship = Ship(image = Ship.image, + x = games.screen.width/2, + y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +# kick it off! +main() + diff --git a/Absolute Book/py3e_source/chapter12/astrocrash03.bat b/Absolute Book/py3e_source/chapter12/astrocrash03.bat new file mode 100755 index 0000000..cbdec9e --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash03.bat @@ -0,0 +1,2 @@ +astrocrash03.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash03.py b/Absolute Book/py3e_source/chapter12/astrocrash03.py new file mode 100755 index 0000000..62738d4 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash03.py @@ -0,0 +1,107 @@ +# Astrocrash03 +# Get ship moving + +import math, random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Asteroid(games.Sprite): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def update(self): + """ Wrap around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +class Ship(games.Sprite): + """ The player's ship. """ + image = games.load_image("ship.bmp") + sound = games.load_sound("thrust.wav") + ROTATION_STEP = 3 + VELOCITY_STEP = .03 + + def update(self): + """ Rotate and thrust based on keys pressed. """ + # rotate based on left and right arrow keys + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + # apply thrust based on up arrow key + if games.keyboard.is_pressed(games.K_UP): + Ship.sound.play() + + # change velocity components based on ship's angle + angle = self.angle * math.pi / 180 # convert to radians + self.dx += Ship.VELOCITY_STEP * math.sin(angle) + self.dy += Ship.VELOCITY_STEP * -math.cos(angle) + + # wrap the ship around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + # create the ship + the_ship = Ship(image = Ship.image, + x = games.screen.width/2, + y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +# kick it off! +main() + diff --git a/Absolute Book/py3e_source/chapter12/astrocrash04.bat b/Absolute Book/py3e_source/chapter12/astrocrash04.bat new file mode 100755 index 0000000..5f64005 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash04.bat @@ -0,0 +1,2 @@ +astrocrash04.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash04.py b/Absolute Book/py3e_source/chapter12/astrocrash04.py new file mode 100755 index 0000000..4833d65 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash04.py @@ -0,0 +1,164 @@ +# Astrocrash04 +# Get ship firing missiles + +import math, random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Asteroid(games.Sprite): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def update(self): + """ Wrap around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +class Ship(games.Sprite): + """ The player's ship. """ + image = games.load_image("ship.bmp") + sound = games.load_sound("thrust.wav") + ROTATION_STEP = 3 + VELOCITY_STEP = .03 + + def update(self): + """ Rotate and thrust based on keys pressed. """ + # rotate based on left and right arrow keys + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + # apply thrust based on up arrow key + if games.keyboard.is_pressed(games.K_UP): + Ship.sound.play() + + # change velocity components based on ship's angle + angle = self.angle * math.pi / 180 # convert to radians + self.dx += Ship.VELOCITY_STEP * math.sin(angle) + self.dy += Ship.VELOCITY_STEP * -math.cos(angle) + + # wrap the ship around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + # fire missile if spacebar pressed + if games.keyboard.is_pressed(games.K_SPACE): + new_missile = Missile(self.x, self.y, self.angle) + games.screen.add(new_missile) + + +class Missile(games.Sprite): + """ A missile launched by the player's ship. """ + image = games.load_image("missile.bmp") + sound = games.load_sound("missile.wav") + BUFFER = 40 + VELOCITY_FACTOR = 7 + LIFETIME = 40 + + def __init__(self, ship_x, ship_y, ship_angle): + """ Initialize missile sprite. """ + Missile.sound.play() + + # convert to radians + angle = ship_angle * math.pi / 180 + + # calculate missile's starting position + buffer_x = Missile.BUFFER * math.sin(angle) + buffer_y = Missile.BUFFER * -math.cos(angle) + x = ship_x + buffer_x + y = ship_y + buffer_y + + # calculate missile's velocity components + dx = Missile.VELOCITY_FACTOR * math.sin(angle) + dy = Missile.VELOCITY_FACTOR * -math.cos(angle) + + # create the missile + super(Missile, self).__init__(image = Missile.image, + x = x, y = y, + dx = dx, dy = dy) + self.lifetime = Missile.LIFETIME + + def update(self): + """ Move the missile. """ + # if lifetime is up, destroy the missile + self.lifetime -= 1 + if self.lifetime == 0: + self.destroy() + + # wrap the missile around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + # create the ship + the_ship = Ship(image = Ship.image, + x = games.screen.width/2, + y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +# kick it off! +main() + diff --git a/Absolute Book/py3e_source/chapter12/astrocrash05.bat b/Absolute Book/py3e_source/chapter12/astrocrash05.bat new file mode 100755 index 0000000..b815610 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash05.bat @@ -0,0 +1,2 @@ +astrocrash05.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash05.py b/Absolute Book/py3e_source/chapter12/astrocrash05.py new file mode 100755 index 0000000..3bbf7bd --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash05.py @@ -0,0 +1,173 @@ +# Astrocrash05 +# Limiting missile fire rate + +import math, random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Asteroid(games.Sprite): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def update(self): + """ Wrap around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +class Ship(games.Sprite): + """ The player's ship. """ + image = games.load_image("ship.bmp") + sound = games.load_sound("thrust.wav") + ROTATION_STEP = 3 + VELOCITY_STEP = .03 + MISSILE_DELAY = 25 + + def __init__(self, x, y): + """ Initialize ship sprite. """ + super(Ship, self).__init__(image = Ship.image, x = x, y = y) + self.missile_wait = 0 + + def update(self): + """ Rotate and thrust based on keys pressed. """ + # rotate based on left and right arrow keys + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + # apply thrust based on up arrow key + if games.keyboard.is_pressed(games.K_UP): + Ship.sound.play() + + # change velocity components based on ship's angle + angle = self.angle * math.pi / 180 # convert to radians + self.dx += Ship.VELOCITY_STEP * math.sin(angle) + self.dy += Ship.VELOCITY_STEP * -math.cos(angle) + + # wrap the ship around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + # if waiting until the ship can fire next, decrease wait + if self.missile_wait > 0: + self.missile_wait -= 1 + + # fire missile if spacebar pressed and missile wait is over + if games.keyboard.is_pressed(games.K_SPACE) and self.missile_wait == 0: + new_missile = Missile(self.x, self.y, self.angle) + games.screen.add(new_missile) + self.missile_wait = Ship.MISSILE_DELAY + + +class Missile(games.Sprite): + """ A missile launched by the player's ship. """ + image = games.load_image("missile.bmp") + sound = games.load_sound("missile.wav") + BUFFER = 40 + VELOCITY_FACTOR = 7 + LIFETIME = 40 + + def __init__(self, ship_x, ship_y, ship_angle): + """ Initialize missile sprite. """ + Missile.sound.play() + + # convert to radians + angle = ship_angle * math.pi / 180 + + # calculate missile's starting position + buffer_x = Missile.BUFFER * math.sin(angle) + buffer_y = Missile.BUFFER * -math.cos(angle) + x = ship_x + buffer_x + y = ship_y + buffer_y + + # calculate missile's velocity components + dx = Missile.VELOCITY_FACTOR * math.sin(angle) + dy = Missile.VELOCITY_FACTOR * -math.cos(angle) + + # create the missile + super(Missile, self).__init__(image = Missile.image, + x = x, y = y, + dx = dx, dy = dy) + self.lifetime = Missile.LIFETIME + + def update(self): + """ Move the missile. """ + # if lifetime is up, destroy the missile + self.lifetime -= 1 + if self.lifetime == 0: + self.destroy() + + # wrap the missile around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + # create the ship + the_ship = Ship(x = games.screen.width/2, y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +# kick it off! +main() + diff --git a/Absolute Book/py3e_source/chapter12/astrocrash06.bat b/Absolute Book/py3e_source/chapter12/astrocrash06.bat new file mode 100755 index 0000000..4c59529 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash06.bat @@ -0,0 +1,2 @@ +astrocrash06.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash06.py b/Absolute Book/py3e_source/chapter12/astrocrash06.py new file mode 100755 index 0000000..74e4570 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash06.py @@ -0,0 +1,204 @@ +# Astrocrash06 +# Handling collisions + +import math, random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Asteroid(games.Sprite): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + SPAWN = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def update(self): + """ Wrap around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + def die(self): + """ Destroy asteroid. """ + # if asteroid isn't small, replace with two smaller asteroids + if self.size != Asteroid.SMALL: + for i in range(Asteroid.SPAWN): + new_asteroid = Asteroid(x = self.x, + y = self.y, + size = self.size - 1) + games.screen.add(new_asteroid) + self.destroy() + + +class Ship(games.Sprite): + """ The player's ship. """ + image = games.load_image("ship.bmp") + sound = games.load_sound("thrust.wav") + ROTATION_STEP = 3 + VELOCITY_STEP = .03 + MISSILE_DELAY = 25 + + def __init__(self, x, y): + """ Initialize ship sprite. """ + super(Ship, self).__init__(image = Ship.image, x = x, y = y) + self.missile_wait = 0 + + def update(self): + """ Rotate and thrust based on keys pressed. """ + # rotate based on left and right arrow keys + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + # apply thrust based on up arrow key + if games.keyboard.is_pressed(games.K_UP): + Ship.sound.play() + + # change velocity components based on ship's angle + angle = self.angle * math.pi / 180 # convert to radians + self.dx += Ship.VELOCITY_STEP * math.sin(angle) + self.dy += Ship.VELOCITY_STEP * -math.cos(angle) + + # wrap the ship around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + # if waiting until the ship can fire next, decrease wait + if self.missile_wait > 0: + self.missile_wait -= 1 + + # fire missile if spacebar pressed and missile wait is over + if games.keyboard.is_pressed(games.K_SPACE) and self.missile_wait == 0: + new_missile = Missile(self.x, self.y, self.angle) + games.screen.add(new_missile) + self.missile_wait = Ship.MISSILE_DELAY + + # check if ship overlaps any other object + if self.overlapping_sprites: + for sprite in self.overlapping_sprites: + sprite.die() + self.die() + + def die(self): + """ Destroy ship. """ + self.destroy() + + +class Missile(games.Sprite): + """ A missile launched by the player's ship. """ + image = games.load_image("missile.bmp") + sound = games.load_sound("missile.wav") + BUFFER = 40 + VELOCITY_FACTOR = 7 + LIFETIME = 40 + + def __init__(self, ship_x, ship_y, ship_angle): + """ Initialize missile sprite. """ + Missile.sound.play() + + # convert to radians + angle = ship_angle * math.pi / 180 + + # calculate missile's starting position + buffer_x = Missile.BUFFER * math.sin(angle) + buffer_y = Missile.BUFFER * -math.cos(angle) + x = ship_x + buffer_x + y = ship_y + buffer_y + + # calculate missile's velocity components + dx = Missile.VELOCITY_FACTOR * math.sin(angle) + dy = Missile.VELOCITY_FACTOR * -math.cos(angle) + + # create the missile + super(Missile, self).__init__(image = Missile.image, + x = x, y = y, + dx = dx, dy = dy) + self.lifetime = Missile.LIFETIME + + def update(self): + """ Move the missile. """ + # if lifetime is up, destroy the missile + self.lifetime -= 1 + if self.lifetime == 0: + self.destroy() + + # wrap the missile around screen + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + # check if missile overlaps any other object + if self.overlapping_sprites: + for sprite in self.overlapping_sprites: + sprite.die() + self.die() + + def die(self): + """ Destroy the missile. """ + self.destroy() + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + # create the ship + the_ship = Ship(x = games.screen.width/2, y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +# kick it off! +main() diff --git a/Absolute Book/py3e_source/chapter12/astrocrash07.bat b/Absolute Book/py3e_source/chapter12/astrocrash07.bat new file mode 100755 index 0000000..6f493e6 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash07.bat @@ -0,0 +1,2 @@ +astrocrash07.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash07.py b/Absolute Book/py3e_source/chapter12/astrocrash07.py new file mode 100755 index 0000000..1aa4d20 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash07.py @@ -0,0 +1,209 @@ +# Astrocrash07 +# Add explosions + +import math, random +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Wrapper(games.Sprite): + """ A sprite that wraps around the screen. """ + def update(self): + """ Wrap sprite around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + def die(self): + """ Destroy self. """ + self.destroy() + + +class Collider(Wrapper): + """ A Wrapper that can collide with another object. """ + def update(self): + """ Check for overlapping sprites. """ + super(Collider, self).update() + + if self.overlapping_sprites: + for sprite in self.overlapping_sprites: + sprite.die() + self.die() + + def die(self): + """ Destroy self and leave explosion behind. """ + new_explosion = Explosion(x = self.x, y = self.y) + games.screen.add(new_explosion) + self.destroy() + + +class Asteroid(Wrapper): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + SPAWN = 2 + + def __init__(self, x, y, size): + """ Initialize asteroid sprite. """ + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.size = size + + def die(self): + """ Destroy asteroid. """ + # if asteroid isn't small, replace with two smaller asteroids + if self.size != Asteroid.SMALL: + for i in range(Asteroid.SPAWN): + new_asteroid = Asteroid(x = self.x, + y = self.y, + size = self.size - 1) + games.screen.add(new_asteroid) + + super(Asteroid, self).die() + + +class Ship(Collider): + """ The player's ship. """ + image = games.load_image("ship.bmp") + sound = games.load_sound("thrust.wav") + ROTATION_STEP = 3 + VELOCITY_STEP = .03 + MISSILE_DELAY = 25 + + def __init__(self, x, y): + """ Initialize ship sprite. """ + super(Ship, self).__init__(image = Ship.image, x = x, y = y) + self.missile_wait = 0 + + def update(self): + """ Rotate, thrust and fire missiles based on keys pressed. """ + super(Ship, self).update() + + # rotate based on left and right arrow keys + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + # apply thrust based on up arrow key + if games.keyboard.is_pressed(games.K_UP): + Ship.sound.play() + + # change velocity components based on ship's angle + angle = self.angle * math.pi / 180 # convert to radians + self.dx += Ship.VELOCITY_STEP * math.sin(angle) + self.dy += Ship.VELOCITY_STEP * -math.cos(angle) + + # if waiting until the ship can fire next, decrease wait + if self.missile_wait > 0: + self.missile_wait -= 1 + + # fire missile if spacebar pressed and missile wait is over + if games.keyboard.is_pressed(games.K_SPACE) and self.missile_wait == 0: + new_missile = Missile(self.x, self.y, self.angle) + games.screen.add(new_missile) + self.missile_wait = Ship.MISSILE_DELAY + + +class Missile(Collider): + """ A missile launched by the player's ship. """ + image = games.load_image("missile.bmp") + sound = games.load_sound("missile.wav") + BUFFER = 40 + VELOCITY_FACTOR = 7 + LIFETIME = 40 + + def __init__(self, ship_x, ship_y, ship_angle): + """ Initialize missile sprite. """ + Missile.sound.play() + + # convert to radians + angle = ship_angle * math.pi / 180 + + # calculate missile's starting position + buffer_x = Missile.BUFFER * math.sin(angle) + buffer_y = Missile.BUFFER * -math.cos(angle) + x = ship_x + buffer_x + y = ship_y + buffer_y + + # calculate missile's velocity components + dx = Missile.VELOCITY_FACTOR * math.sin(angle) + dy = Missile.VELOCITY_FACTOR * -math.cos(angle) + + # create the missile + super(Missile, self).__init__(image = Missile.image, + x = x, y = y, + dx = dx, dy = dy) + self.lifetime = Missile.LIFETIME + + def update(self): + """ Move the missile. """ + super(Missile, self).update() + + # if lifetime is up, destroy the missile + self.lifetime -= 1 + if self.lifetime == 0: + self.destroy() + + +class Explosion(games.Animation): + """ Explosion animation. """ + sound = games.load_sound("explosion.wav") + images = ["explosion1.bmp", + "explosion2.bmp", + "explosion3.bmp", + "explosion4.bmp", + "explosion5.bmp", + "explosion6.bmp", + "explosion7.bmp", + "explosion8.bmp", + "explosion9.bmp"] + + def __init__(self, x, y): + super(Explosion, self).__init__(images = Explosion.images, + x = x, y = y, + repeat_interval = 4, n_repeats = 1, + is_collideable = False) + Explosion.sound.play() + + +def main(): + # establish background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # create 8 asteroids + for i in range(8): + x = random.randrange(games.screen.width) + y = random.randrange(games.screen.height) + size = random.choice([Asteroid.SMALL, Asteroid.MEDIUM, Asteroid.LARGE]) + new_asteroid = Asteroid(x = x, y = y, size = size) + games.screen.add(new_asteroid) + + # create the ship + the_ship = Ship(x = games.screen.width/2, y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +# kick it off! +main() diff --git a/Absolute Book/py3e_source/chapter12/astrocrash08.bat b/Absolute Book/py3e_source/chapter12/astrocrash08.bat new file mode 100755 index 0000000..532482e --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash08.bat @@ -0,0 +1,2 @@ +astrocrash08.py +pause diff --git a/Absolute Book/py3e_source/chapter12/astrocrash08.py b/Absolute Book/py3e_source/chapter12/astrocrash08.py new file mode 100755 index 0000000..5d69268 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/astrocrash08.py @@ -0,0 +1,323 @@ +# Astrocrash08 +# Add Game object for complete program + +import math, random +from livewires import games, color + +games.init(screen_width = 640, screen_height = 480, fps = 50) + + +class Wrapper(games.Sprite): + """ A sprite that wraps around the screen. """ + def update(self): + """ Wrap sprite around screen. """ + if self.top > games.screen.height: + self.bottom = 0 + + if self.bottom < 0: + self.top = games.screen.height + + if self.left > games.screen.width: + self.right = 0 + + if self.right < 0: + self.left = games.screen.width + + def die(self): + """ Destroy self. """ + self.destroy() + + +class Collider(Wrapper): + """ A Wrapper that can collide with another object. """ + def update(self): + """ Check for overlapping sprites. """ + super(Collider, self).update() + + if self.overlapping_sprites: + for sprite in self.overlapping_sprites: + sprite.die() + self.die() + + def die(self): + """ Destroy self and leave explosion behind. """ + new_explosion = Explosion(x = self.x, y = self.y) + games.screen.add(new_explosion) + self.destroy() + + +class Asteroid(Wrapper): + """ An asteroid which floats across the screen. """ + SMALL = 1 + MEDIUM = 2 + LARGE = 3 + images = {SMALL : games.load_image("asteroid_small.bmp"), + MEDIUM : games.load_image("asteroid_med.bmp"), + LARGE : games.load_image("asteroid_big.bmp") } + + SPEED = 2 + SPAWN = 2 + POINTS = 30 + + total = 0 + + def __init__(self, game, x, y, size): + """ Initialize asteroid sprite. """ + Asteroid.total += 1 + + super(Asteroid, self).__init__( + image = Asteroid.images[size], + x = x, y = y, + dx = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size, + dy = random.choice([1, -1]) * Asteroid.SPEED * random.random()/size) + + self.game = game + self.size = size + + def die(self): + """ Destroy asteroid. """ + Asteroid.total -= 1 + + self.game.score.value += int(Asteroid.POINTS / self.size) + self.game.score.right = games.screen.width - 10 + + # if asteroid isn't small, replace with two smaller asteroids + if self.size != Asteroid.SMALL: + for i in range(Asteroid.SPAWN): + new_asteroid = Asteroid(game = self.game, + x = self.x, + y = self.y, + size = self.size - 1) + games.screen.add(new_asteroid) + + # if all asteroids are gone, advance to next level + if Asteroid.total == 0: + self.game.advance() + + super(Asteroid, self).die() + + +class Ship(Collider): + """ The player's ship. """ + image = games.load_image("ship.bmp") + sound = games.load_sound("thrust.wav") + ROTATION_STEP = 3 + VELOCITY_STEP = .03 + VELOCITY_MAX = 3 + MISSILE_DELAY = 25 + + def __init__(self, game, x, y): + """ Initialize ship sprite. """ + super(Ship, self).__init__(image = Ship.image, x = x, y = y) + self.game = game + self.missile_wait = 0 + + def update(self): + """ Rotate, thrust and fire missiles based on keys pressed. """ + super(Ship, self).update() + + # rotate based on left and right arrow keys + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= Ship.ROTATION_STEP + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += Ship.ROTATION_STEP + + # apply thrust based on up arrow key + if games.keyboard.is_pressed(games.K_UP): + Ship.sound.play() + + # change velocity components based on ship's angle + angle = self.angle * math.pi / 180 # convert to radians + self.dx += Ship.VELOCITY_STEP * math.sin(angle) + self.dy += Ship.VELOCITY_STEP * -math.cos(angle) + + # cap velocity in each direction + self.dx = min(max(self.dx, -Ship.VELOCITY_MAX), Ship.VELOCITY_MAX) + self.dy = min(max(self.dy, -Ship.VELOCITY_MAX), Ship.VELOCITY_MAX) + + # if waiting until the ship can fire next, decrease wait + if self.missile_wait > 0: + self.missile_wait -= 1 + + # fire missile if spacebar pressed and missile wait is over + if games.keyboard.is_pressed(games.K_SPACE) and self.missile_wait == 0: + new_missile = Missile(self.x, self.y, self.angle) + games.screen.add(new_missile) + self.missile_wait = Ship.MISSILE_DELAY + + def die(self): + """ Destroy ship and end the game. """ + self.game.end() + super(Ship, self).die() + + +class Missile(Collider): + """ A missile launched by the player's ship. """ + image = games.load_image("missile.bmp") + sound = games.load_sound("missile.wav") + BUFFER = 40 + VELOCITY_FACTOR = 7 + LIFETIME = 40 + + def __init__(self, ship_x, ship_y, ship_angle): + """ Initialize missile sprite. """ + Missile.sound.play() + + # convert to radians + angle = ship_angle * math.pi / 180 + + # calculate missile's starting position + buffer_x = Missile.BUFFER * math.sin(angle) + buffer_y = Missile.BUFFER * -math.cos(angle) + x = ship_x + buffer_x + y = ship_y + buffer_y + + # calculate missile's velocity components + dx = Missile.VELOCITY_FACTOR * math.sin(angle) + dy = Missile.VELOCITY_FACTOR * -math.cos(angle) + + # create the missile + super(Missile, self).__init__(image = Missile.image, + x = x, y = y, + dx = dx, dy = dy) + self.lifetime = Missile.LIFETIME + + def update(self): + """ Move the missile. """ + super(Missile, self).update() + + # if lifetime is up, destroy the missile + self.lifetime -= 1 + if self.lifetime == 0: + self.destroy() + + +class Explosion(games.Animation): + """ Explosion animation. """ + sound = games.load_sound("explosion.wav") + images = ["explosion1.bmp", + "explosion2.bmp", + "explosion3.bmp", + "explosion4.bmp", + "explosion5.bmp", + "explosion6.bmp", + "explosion7.bmp", + "explosion8.bmp", + "explosion9.bmp"] + + def __init__(self, x, y): + super(Explosion, self).__init__(images = Explosion.images, + x = x, y = y, + repeat_interval = 4, n_repeats = 1, + is_collideable = False) + Explosion.sound.play() + + +class Game(object): + """ The game itself. """ + def __init__(self): + """ Initialize Game object. """ + # set level + self.level = 0 + + # load sound for level advance + self.sound = games.load_sound("level.wav") + + # create score + self.score = games.Text(value = 0, + size = 30, + color = color.white, + top = 5, + right = games.screen.width - 10, + is_collideable = False) + games.screen.add(self.score) + + # create player's ship + self.ship = Ship(game = self, + x = games.screen.width/2, + y = games.screen.height/2) + games.screen.add(self.ship) + + def play(self): + """ Play the game. """ + # begin theme music + games.music.load("theme.mid") + games.music.play(-1) + + # load and set background + nebula_image = games.load_image("nebula.jpg") + games.screen.background = nebula_image + + # advance to level 1 + self.advance() + + # start play + games.screen.mainloop() + + def advance(self): + """ Advance to the next game level. """ + self.level += 1 + + # amount of space around ship to preserve when creating asteroids + BUFFER = 150 + + # create new asteroids + for i in range(self.level): + # calculate an x and y at least BUFFER distance from the ship + + # choose minimum distance along x-axis and y-axis + x_min = random.randrange(BUFFER) + y_min = BUFFER - x_min + + # choose distance along x-axis and y-axis based on minimum distance + x_distance = random.randrange(x_min, games.screen.width - x_min) + y_distance = random.randrange(y_min, games.screen.height - y_min) + + # calculate location based on distance + x = self.ship.x + x_distance + y = self.ship.y + y_distance + + # wrap around screen, if necessary + x %= games.screen.width + y %= games.screen.height + + # create the asteroid + new_asteroid = Asteroid(game = self, + x = x, y = y, + size = Asteroid.LARGE) + games.screen.add(new_asteroid) + + # display level number + level_message = games.Message(value = "Level " + str(self.level), + size = 40, + color = color.yellow, + x = games.screen.width/2, + y = games.screen.width/10, + lifetime = 3 * games.screen.fps, + is_collideable = False) + games.screen.add(level_message) + + # play new level sound (except at first level) + if self.level > 1: + self.sound.play() + + def end(self): + """ End the game. """ + # show 'Game Over' for 5 seconds + end_message = games.Message(value = "Game Over", + size = 90, + color = color.red, + x = games.screen.width/2, + y = games.screen.height/2, + lifetime = 5 * games.screen.fps, + after_death = games.screen.quit, + is_collideable = False) + games.screen.add(end_message) + + +def main(): + astrocrash = Game() + astrocrash.play() + +# kick it off! +main() diff --git a/Absolute Book/py3e_source/chapter12/explosion.bat b/Absolute Book/py3e_source/chapter12/explosion.bat new file mode 100755 index 0000000..d819252 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/explosion.bat @@ -0,0 +1,2 @@ +explosion.py +pause diff --git a/Absolute Book/py3e_source/chapter12/explosion.py b/Absolute Book/py3e_source/chapter12/explosion.py new file mode 100755 index 0000000..67b9580 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/explosion.py @@ -0,0 +1,28 @@ +# Explosion +# Demonstrates creating an animation + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +nebula_image = games.load_image("nebula.jpg", transparent = 0) +games.screen.background = nebula_image + +explosion_files = ["explosion1.bmp", + "explosion2.bmp", + "explosion3.bmp", + "explosion4.bmp", + "explosion5.bmp", + "explosion6.bmp", + "explosion7.bmp", + "explosion8.bmp", + "explosion9.bmp"] + +explosion = games.Animation(images = explosion_files, + x = games.screen.width/2, + y = games.screen.height/2, + n_repeats = 0, + repeat_interval = 5) +games.screen.add(explosion) + +games.screen.mainloop() diff --git a/Absolute Book/py3e_source/chapter12/explosion.wav b/Absolute Book/py3e_source/chapter12/explosion.wav new file mode 100755 index 0000000..bf95b22 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion.wav differ diff --git a/Absolute Book/py3e_source/chapter12/explosion1.bmp b/Absolute Book/py3e_source/chapter12/explosion1.bmp new file mode 100755 index 0000000..c9e8ced Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion1.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion2.bmp b/Absolute Book/py3e_source/chapter12/explosion2.bmp new file mode 100755 index 0000000..c89845f Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion2.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion3.bmp b/Absolute Book/py3e_source/chapter12/explosion3.bmp new file mode 100755 index 0000000..9a084e3 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion3.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion4.bmp b/Absolute Book/py3e_source/chapter12/explosion4.bmp new file mode 100755 index 0000000..8357230 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion4.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion5.bmp b/Absolute Book/py3e_source/chapter12/explosion5.bmp new file mode 100755 index 0000000..0813768 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion5.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion6.bmp b/Absolute Book/py3e_source/chapter12/explosion6.bmp new file mode 100755 index 0000000..b755966 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion6.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion7.bmp b/Absolute Book/py3e_source/chapter12/explosion7.bmp new file mode 100755 index 0000000..7f2ccca Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion7.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion8.bmp b/Absolute Book/py3e_source/chapter12/explosion8.bmp new file mode 100755 index 0000000..2cf2b19 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion8.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/explosion9.bmp b/Absolute Book/py3e_source/chapter12/explosion9.bmp new file mode 100755 index 0000000..50034bd Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/explosion9.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/image credit.txt b/Absolute Book/py3e_source/chapter12/image credit.txt new file mode 100755 index 0000000..50fef7b --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/image credit.txt @@ -0,0 +1,9 @@ +Image Credit for nebula.jpg +--------------------------- + +Keyhole Nebula +Image Credit: NASA, The Hubble Heritage Team (AURA/STScI) +Image Type:Astronomical +STScI-PRC2000-06 +http://hubblesite.org/ + diff --git a/Absolute Book/py3e_source/chapter12/level.wav b/Absolute Book/py3e_source/chapter12/level.wav new file mode 100755 index 0000000..9711a89 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/level.wav differ diff --git a/Absolute Book/py3e_source/chapter12/missile.bmp b/Absolute Book/py3e_source/chapter12/missile.bmp new file mode 100755 index 0000000..e9e2fb2 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/missile.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/missile.wav b/Absolute Book/py3e_source/chapter12/missile.wav new file mode 100755 index 0000000..a83e5c2 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/missile.wav differ diff --git a/Absolute Book/py3e_source/chapter12/nebula.jpg b/Absolute Book/py3e_source/chapter12/nebula.jpg new file mode 100644 index 0000000..f8db30a Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/nebula.jpg differ diff --git a/Absolute Book/py3e_source/chapter12/read_key.bat b/Absolute Book/py3e_source/chapter12/read_key.bat new file mode 100755 index 0000000..6b45428 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/read_key.bat @@ -0,0 +1,2 @@ +read_key.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter12/read_key.py b/Absolute Book/py3e_source/chapter12/read_key.py new file mode 100755 index 0000000..b614a89 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/read_key.py @@ -0,0 +1,33 @@ +# Read Key +# Demonstrates reading the keyboard + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +class Ship(games.Sprite): + """ A moving ship. """ + def update(self): + """ Move ship based on keys pressed. """ + if games.keyboard.is_pressed(games.K_w): + self.y -= 1 + if games.keyboard.is_pressed(games.K_s): + self.y += 1 + if games.keyboard.is_pressed(games.K_a): + self.x -= 1 + if games.keyboard.is_pressed(games.K_d): + self.x += 1 + +def main(): + nebula_image = games.load_image("nebula.jpg", transparent = False) + games.screen.background = nebula_image + + ship_image = games.load_image("ship.bmp") + the_ship = Ship(image = ship_image, + x = games.screen.width/2, + y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +main() diff --git a/Absolute Book/py3e_source/chapter12/rotate_sprite.bat b/Absolute Book/py3e_source/chapter12/rotate_sprite.bat new file mode 100755 index 0000000..b1ce8dc --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/rotate_sprite.bat @@ -0,0 +1,2 @@ +rotate_sprite.py +pause \ No newline at end of file diff --git a/Absolute Book/py3e_source/chapter12/rotate_sprite.py b/Absolute Book/py3e_source/chapter12/rotate_sprite.py new file mode 100755 index 0000000..a7922f5 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/rotate_sprite.py @@ -0,0 +1,38 @@ +# Rotate Sprite +# Demonstrates rotating a sprite + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +class Ship(games.Sprite): + """ A rotating ship. """ + def update(self): + """ Rotate based on keys pressed. """ + if games.keyboard.is_pressed(games.K_RIGHT): + self.angle += 1 + if games.keyboard.is_pressed(games.K_LEFT): + self.angle -= 1 + + if games.keyboard.is_pressed(games.K_1): + self.angle = 0 + if games.keyboard.is_pressed(games.K_2): + self.angle = 90 + if games.keyboard.is_pressed(games.K_3): + self.angle = 180 + if games.keyboard.is_pressed(games.K_4): + self.angle = 270 + +def main(): + nebula_image = games.load_image("nebula.jpg", transparent = False) + games.screen.background = nebula_image + + ship_image = games.load_image("ship.bmp") + the_ship = Ship(image = ship_image, + x = games.screen.width/2, + y = games.screen.height/2) + games.screen.add(the_ship) + + games.screen.mainloop() + +main() diff --git a/Absolute Book/py3e_source/chapter12/ship.bmp b/Absolute Book/py3e_source/chapter12/ship.bmp new file mode 100755 index 0000000..04590a9 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/ship.bmp differ diff --git a/Absolute Book/py3e_source/chapter12/sound_and_music.bat b/Absolute Book/py3e_source/chapter12/sound_and_music.bat new file mode 100755 index 0000000..a617ec0 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/sound_and_music.bat @@ -0,0 +1,2 @@ +sound_and_music.py +pause diff --git a/Absolute Book/py3e_source/chapter12/sound_and_music.py b/Absolute Book/py3e_source/chapter12/sound_and_music.py new file mode 100755 index 0000000..b318e76 --- /dev/null +++ b/Absolute Book/py3e_source/chapter12/sound_and_music.py @@ -0,0 +1,74 @@ +# Sound and Music +# Demonstrates playing sound and music files + +from livewires import games + +games.init(screen_width = 640, screen_height = 480, fps = 50) + +# load a sound file +missile_sound = games.load_sound("missile.wav") + +# load the music file +games.music.load("theme.mid") + +choice = None +while choice != "0": + + print( + """ + Sound and Music + + 0 - Quit + 1 - Play missile sound + 2 - Loop missile sound + 3 - Stop missile sound + 4 - Play theme music + 5 - Loop theme music + 6 - Stop theme music + """ + ) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # play missile sound + elif choice == "1": + missile_sound.play() + print("Playing missile sound.") + + # loop missile sound + elif choice == "2": + loop = int(input("Loop how many extra times? (-1 = forever): ")) + missile_sound.play(loop) + print("Looping missile sound.") + + # stop missile sound + elif choice == "3": + missile_sound.stop() + print("Stopping missile sound.") + + # play theme music + elif choice == "4": + games.music.play() + print("Playing theme music.") + + # loop theme music + elif choice == "5": + loop = int(input("Loop how many extra times? (-1 = forever): ")) + games.music.play(loop) + print("Looping theme music.") + + # stop theme music + elif choice == "6": + games.music.stop() + print("Stopping theme music.") + + # some unknown choice + else: + print("\nSorry, but", choice, "isn't a valid choice.") + +input("\n\nPress the enter key to exit.") diff --git a/Absolute Book/py3e_source/chapter12/theme.mid b/Absolute Book/py3e_source/chapter12/theme.mid new file mode 100755 index 0000000..32d12d5 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/theme.mid differ diff --git a/Absolute Book/py3e_source/chapter12/thrust.wav b/Absolute Book/py3e_source/chapter12/thrust.wav new file mode 100755 index 0000000..0aee731 Binary files /dev/null and b/Absolute Book/py3e_source/chapter12/thrust.wav differ diff --git a/Learning Python Programming - Working Files/Chapter 1/No Work Files.rtf b/Learning Python Programming - Working Files/Chapter 1/No Work Files.rtf new file mode 100644 index 0000000..b1be15b --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 1/No Work Files.rtf @@ -0,0 +1,7 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\margl1440\margr1440\vieww9000\viewh8400\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural + +\f0\fs24 \cf0 No work files for this lesson...} \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 10/listcomp.py b/Learning Python Programming - Working Files/Chapter 10/listcomp.py new file mode 100644 index 0000000..3243e0d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 10/listcomp.py @@ -0,0 +1,9 @@ +#N = range(1,101) +#EN = [x for x in N if x % 2 == 0] +#print(EN) +sent = "now is the time for all good people to come to the aid " +sent += "of their party" +words = sent.split(' ') +wlen = [(word, len(word)) for word in words] +for i in wlen: + print(i) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 10/listcomp1.py b/Learning Python Programming - Working Files/Chapter 10/listcomp1.py new file mode 100644 index 0000000..d4f6604 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 10/listcomp1.py @@ -0,0 +1,10 @@ +#grades = [71, 81, 77, 84] +#print(grades) +#for i in range(len(grades)): +# grades[i] = grades[i] + 5 +#grades = [grade + 5 for grade in grades] +#print(grades) +words = ['NOW','IS','THE','TIME'] +print(words) +words = [word.lower() for word in words] +print(words) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 10/listcomp2.py b/Learning Python Programming - Working Files/Chapter 10/listcomp2.py new file mode 100644 index 0000000..87cc7cb --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 10/listcomp2.py @@ -0,0 +1,8 @@ +#file = open('grades.txt') +#grades = file.readlines() +#print(grades) +#for i in range(len(grades)): +# grades[i] = grades[i].rstrip() +#print(grades) +grades = [grade.rstrip() for grade in open('grades.txt')] +print(grades) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/chap11ex1.py b/Learning Python Programming - Working Files/Chapter 11/chap11ex1.py new file mode 100644 index 0000000..df6da50 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/chap11ex1.py @@ -0,0 +1,20 @@ +#0-240 0% +#241-480 15% +#481- 28% +def tax(amount): + if amount <= 240: + return 0 + elif amount > 240 and amount <= 480: + return amount * .15 + else: + return amount * .28 + +def netpay(grosspay): + return grosspay - tax(grosspay) + +#print("Enter amount: ") +#amount = int(input()) +#print("The tax is " + str(tax(amount))) +print("Enter gross pay: ") +gp = int(input()) +print("Net pay is " + str(netpay(gp))) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/chap11ex2.py b/Learning Python Programming - Working Files/Chapter 11/chap11ex2.py new file mode 100644 index 0000000..efee441 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/chap11ex2.py @@ -0,0 +1,9 @@ +def fact(number): + product = 1 + for i in range(1,number+1): + product *= i + return product + +print("Enter a number: ") +num = int(input()) +print(str(num) + "! equals " + str(fact(num))) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/functions.txt b/Learning Python Programming - Working Files/Chapter 11/functions.txt new file mode 100644 index 0000000..5fc3013 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/functions.txt @@ -0,0 +1,13 @@ +Functions + +definition: a piece of source code, separate from the larger program, +that performs a specific task + +Reasons to use functions: + +1. Reduce complex tasks into simpler tasks +2. Eliminate duplicate code +3. Code reuse +4. Distribute tasks to multiple programmers +5. Hide implementation details - abstraction +6. Improves debugging by improving traceability \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/functions1.py b/Learning Python Programming - Working Files/Chapter 11/functions1.py new file mode 100644 index 0000000..cc5c7f0 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/functions1.py @@ -0,0 +1,12 @@ +def square(number): + return number * number + +def numVowels(string): + string = string.lower() + count = 0 + for i in range(len(string)): + if string[i] == "a" or string[i] == "e" or \ + string[i] == "i" or string[i] == "o" or \ + string[i] == "u": + count += 1 #count = count + 1 + return count \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/functions2.py b/Learning Python Programming - Working Files/Chapter 11/functions2.py new file mode 100644 index 0000000..1fbe266 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/functions2.py @@ -0,0 +1,20 @@ +def square(number): + return number * number + +def numVowels(string): + string = string.lower() + count = 0 + for i in range(len(string)): + if string[i] == "a" or string[i] == "e" or \ + string[i] == "i" or string[i] == "o" or \ + string[i] == "u": + count += 1 #count = count + 1 + return count + +#print("Enter a number: ") +#num = int(input()) +#numsquared = square(num) +#print(str(num) + " squared = " + str(numsquared)) +print("Enter a string: ") +strng = input() +print("There are " + str(numVowels(strng)) + " vowels in the string.") \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/predicate.py b/Learning Python Programming - Working Files/Chapter 11/predicate.py new file mode 100644 index 0000000..dbeb399 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/predicate.py @@ -0,0 +1,19 @@ +def isVowel(letter): + if letter == "a" or letter == "e" or \ + letter == "i" or letter == "o" or \ + letter == "u": + return True + else: + return False + +def numVowels(string): + string = string.lower() + count = 0 + for i in range(len(string)): + if isVowel(string[i]): + count += 1 #count = count + 1 + return count + +print("Enter a string: ") +strng = input() +print("There are " + str(numVowels(strng)) + " vowels in the string.") \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 11/tempconv.py b/Learning Python Programming - Working Files/Chapter 11/tempconv.py new file mode 100644 index 0000000..9ae1ca5 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 11/tempconv.py @@ -0,0 +1,18 @@ +def ftoc(temp): + return (temp-32.0) * (5.0/9.0) + +def ctof(temp): + return temp * (9.0/5.0) + 32.0 + +def convert(temp, toTemp): + if toTemp.lower() == "c": + return ftoc(temp) + else: + return ctof(temp) + +print("Enter a temperature: ") +temp = int(input()) +print("Enter the scale to convert to: ") +scale = input() +converted = convert(temp, scale) +print(temp, converted, scale) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/anony.py b/Learning Python Programming - Working Files/Chapter 12/anony.py new file mode 100644 index 0000000..a44ba5b --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/anony.py @@ -0,0 +1,11 @@ +#anonymous function +#lambda form + +#def square(number): +# return number * number + +#square = lambda x: x*x +#print(square(2)) +numbers = [1,2,3,4] +numberssq = list(map(lambda x:x*x, numbers)) +print(numberssq) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/chap12ex1.py b/Learning Python Programming - Working Files/Chapter 12/chap12ex1.py new file mode 100644 index 0000000..d225c02 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/chap12ex1.py @@ -0,0 +1,9 @@ +def countLetters(words): + if len(words) < 1: + return 0 + else: + return len(words[0]) + countLetters(words[1:]) + +sentence = ['now','is','the','time','for','all','good','people'] +print(sentence) +print(countLetters(sentence)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/chap12ex2.py b/Learning Python Programming - Working Files/Chapter 12/chap12ex2.py new file mode 100644 index 0000000..6fdd80c --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/chap12ex2.py @@ -0,0 +1,18 @@ +# in my humble opinion +#IMHO + +def first(word): + return word[0] + +def acronym(words): + acro = '' + acro = acro.join(list(map(first, words))).upper() + return acro + +words = ['in','my','humble','opinion'] +#acro = list(map(first, words)) +#print(acro) +#acro = '' +#acro = acro.join(list(map(first, words))).upper() +acro = acronym(words) +print(acro) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/funcobjs.py b/Learning Python Programming - Working Files/Chapter 12/funcobjs.py new file mode 100644 index 0000000..add6104 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/funcobjs.py @@ -0,0 +1,13 @@ +def square(number): + return number * number + +num = 2 +sqnum = square(num) +sqnumber = square +sqnum = sqnumber(2) +print(sqnum) + +#map higher-order function +numbers = [1,2,3,4] +numberssq = list(map(square, numbers)) +print(numberssq) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/highorder.py b/Learning Python Programming - Working Files/Chapter 12/highorder.py new file mode 100644 index 0000000..71bf34b --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/highorder.py @@ -0,0 +1,25 @@ +def square(number): + return number * number + +def even(number): + if number % 2 == 0: + return True + else: + return False + +def sum(x,y): + return x + y + +#numbers = [1,2,3] +#print(numbers) +#numberssq = list(map(square, numbers)) +#print(numberssq) +#numbers = list(range(1,11)) +#print(numbers) +#evens = list(filter(even, numbers)) +#print(evens) +import functools +numbers = list(range(1,11)) +print(numbers) +sum = functools.reduce(sum, numbers) +print("The sum is of the range is " + str(sum)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/recursion2.py b/Learning Python Programming - Working Files/Chapter 12/recursion2.py new file mode 100644 index 0000000..5a5c246 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/recursion2.py @@ -0,0 +1,20 @@ +#explode('hello')->'h e l l o' +#removeDups('aabbcc')->'abc' +def explode(word): + if len(word) <= 1: + return word + else: + return word[0] + ' ' + explode(word[1:]) + +def removeDups(word): + if len(word) <= 1: + return word + elif word[0]==word[1]: + return removeDups(word[1:]) + else: + return word[0] + removeDups(word[1:]) + +#print(explode('hello')) +word = 'aabbbccccdd' +print(word) +print(removeDups(word)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 12/recursive1.py b/Learning Python Programming - Working Files/Chapter 12/recursive1.py new file mode 100644 index 0000000..7f1f6dc --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 12/recursive1.py @@ -0,0 +1,9 @@ +#5! = 5 * 4 * 3 * 2 * 1 +#5! = 5 * 4! +def fact(number): + if number <= 1: + return 1 + else: + return number * fact(number-1) + +print(fact(10)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 13/chap13ex1.py b/Learning Python Programming - Working Files/Chapter 13/chap13ex1.py new file mode 100644 index 0000000..482c7bc --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 13/chap13ex1.py @@ -0,0 +1,24 @@ +import math + +def square(number): + return number * number + +def sqrt(number): + return sqrtHelper(1.0, number) + +def sqrtHelper(guess, number): + if (closeEnough(guess, number)): + return guess + else: + return sqrtHelper(improve(guess, number), number) + +def closeEnough(guess, number): + return (math.fabs((square(guess)) - number) < 0.001) + +def improve(guess, x): + return average(guess, (x / guess)) + +def average(x, y): + return (x + y) / 2 + +print(sqrt(144)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 13/chap13ex1p2.py b/Learning Python Programming - Working Files/Chapter 13/chap13ex1p2.py new file mode 100644 index 0000000..aa99d77 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 13/chap13ex1p2.py @@ -0,0 +1,37 @@ +#def sqrt(number): +# return sqrtHelper(1.0, number) + +#def sqrtHelper(guess, number): +# if (closeEnough(guess, number)): +# return guess +# else: +# return sqrtHelper(improve(guess, number), number) + +#def closeEnough(guess, number): +# return (math.fabs((square(guess)) - number) < 0.001) + +#def improve(guess, x): +# return average(guess, (x / guess)) + +import math + +def average(x, y): + return (x + y) / 2 + +def square(number): + return number * number + +def sqrt(number): + def closeEnough(guess): + return (math.fabs((square(guess)) - number) < 0.001) + def improve(guess): + return average(guess, (number / guess)) + def sqrtHelper(guess): + if (closeEnough(guess)): + return guess + else: + return sqrtHelper(improve(guess)) + return sqrtHelper(1.0) + +print(sqrt(144)) + diff --git a/Learning Python Programming - Working Files/Chapter 13/global.py b/Learning Python Programming - Working Files/Chapter 13/global.py new file mode 100644 index 0000000..592f9b9 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 13/global.py @@ -0,0 +1,9 @@ +# scope +# global / local + +def getNumber(): + print(number) + +number = 1 +print(number) +getNumber() \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 13/local.py b/Learning Python Programming - Working Files/Chapter 13/local.py new file mode 100644 index 0000000..14ba8f3 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 13/local.py @@ -0,0 +1,6 @@ +def square(number): + squared = number * number + return squared + +print(square(2)) +print("Squared (defined in square function): " + str(squared)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 13/nested.py b/Learning Python Programming - Working Files/Chapter 13/nested.py new file mode 100644 index 0000000..6e5b01d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 13/nested.py @@ -0,0 +1,13 @@ +import math + +def hypotenuse(s1, s2): + def square(num): + return num * num + return math.sqrt(square(s1) + square(s2)) + +print("Enter the length of side 1: ") +side1 = int(input()) +print("Enter the length of side 2: ") +side2 = int(input()) +hyp = hypotenuse(side1, side2) +print("The hypotenuse is " + str(hyp)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/builtin.py b/Learning Python Programming - Working Files/Chapter 14/builtin.py new file mode 100644 index 0000000..53f77d1 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/builtin.py @@ -0,0 +1,8 @@ +import os +import math + +files = os.popen("dir *.py") +for file in files: + print(file, end='') + +print(math.fabs(-123.45)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/namespace.py b/Learning Python Programming - Working Files/Chapter 14/namespace.py new file mode 100644 index 0000000..9b022ae --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/namespace.py @@ -0,0 +1,12 @@ +import newton + +def square(number): + print("not from the newton module") + return number * number + + +num = 12 +print("Square from newton.py: ") +print(newton.square(num)) +print("Square from main program: ") +print(square(num)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/newton.py b/Learning Python Programming - Working Files/Chapter 14/newton.py new file mode 100644 index 0000000..7340cb6 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/newton.py @@ -0,0 +1,19 @@ +import math + +def average(x, y): + return (x + y) / 2 + +def square(number): + return number * number + +def sqrt(number): + def closeEnough(guess): + return (math.fabs((square(guess)) - number) < 0.001) + def improve(guess): + return average(guess, (number / guess)) + def sqrtHelper(guess): + if (closeEnough(guess)): + return guess + else: + return sqrtHelper(improve(guess)) + return sqrtHelper(1.0) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/newtontest.py b/Learning Python Programming - Working Files/Chapter 14/newtontest.py new file mode 100644 index 0000000..b933aea --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/newtontest.py @@ -0,0 +1,6 @@ +from newton import * + +print("Enter a number: ") +number = int(input()) +print(sqrt(number)) +print(average(144,9)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/newtontest2.py b/Learning Python Programming - Working Files/Chapter 14/newtontest2.py new file mode 100644 index 0000000..bf046d9 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/newtontest2.py @@ -0,0 +1,9 @@ +from newton import average, square + +num1 = 199 +num2 = 78 +#print("The average is " + str(average(num1, num2))) +#print(sqrt(9)) +avg = average(num1, num2) +print("The average is " + str(avg)) +print("The square of the average " + str(square(avg))) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/sphere.py b/Learning Python Programming - Working Files/Chapter 14/sphere.py new file mode 100644 index 0000000..8559e7c --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/sphere.py @@ -0,0 +1,7 @@ +pi = 3.14159 + +def area(radius): + return 4 * pi * (radius * radius) + +def volume(radius): + return (4.0/3.0) * pi * (radius * radius * radius) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 14/spheretest.py b/Learning Python Programming - Working Files/Chapter 14/spheretest.py new file mode 100644 index 0000000..a0460a3 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 14/spheretest.py @@ -0,0 +1,9 @@ +#from sphere import * +import sphere + +print("Enter the radius of the sphere: ") +radius = int(input()) +#print("The area is " + str(area(radius))) +#print("The volume is " + str(volume(radius))) +print("The area is " + str(sphere.area(radius))) +print("The volume is " + str(sphere.volume(radius))) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 15/name.py b/Learning Python Programming - Working Files/Chapter 15/name.py new file mode 100644 index 0000000..2da82f1 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 15/name.py @@ -0,0 +1,8 @@ +class Name: + #constructor method - instantiation + def __init__(self, first, middle, last): + self.first = first + self.middle = middle + self.last = last + +aName = Name('Mary','Elizabeth','Smith') \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 15/name1.py b/Learning Python Programming - Working Files/Chapter 15/name1.py new file mode 100644 index 0000000..669d918 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 15/name1.py @@ -0,0 +1,21 @@ +class Name: + #constructor method - instantiation + def __init__(self, first, middle, last): + self.first = first + self.middle = middle + self.last = last + + #to-string method + def __str__(self): + return self.first + ' ' + self.middle + ' ' + self.last + + def lastFirst(self): + return self.last + ', ' + self.first + ' ' + self.middle + + def initials(self): + return self.first[0] + self.middle[0] + self.last[0] + +aName = Name('Mary','Elizabeth','Smith') +print('aName is ' + str(aName)) +print(aName.lastFirst()) +print(aName.initials()) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 15/person.py b/Learning Python Programming - Working Files/Chapter 15/person.py new file mode 100644 index 0000000..4320fe6 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 15/person.py @@ -0,0 +1,23 @@ +class Person: + def __init__(self, name, sex, age): + self.name = name + self.sex = sex + self.age = age + + def __str__(self): + return self.name + ' ' + self.sex + ' ' + str(self.age) + + def changeName(self, name): + self.name = name + + def changeAge(self): + self.age = self.age + 1 + +person1 = Person('Jane Doe','F',23) +person2 = Person('Bob Smith','M',55) +print('Person 1: ' + str(person1)) +person1.changeAge() +print('Person 1: ' + str(person1)) +person1.changeName('Jane Brown') +print('Person 1: ' + str(person1)) +print(person2) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 15/sphere1.py b/Learning Python Programming - Working Files/Chapter 15/sphere1.py new file mode 100644 index 0000000..f1ed15c --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 15/sphere1.py @@ -0,0 +1,5 @@ +class Sphere: + def __init__(self, radius): + self.radius = radius + +sp = Sphere(4) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 15/student.py b/Learning Python Programming - Working Files/Chapter 15/student.py new file mode 100644 index 0000000..ed4fe8d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 15/student.py @@ -0,0 +1,21 @@ +class Student: + #fields - name, id, grades(list) + grades = [] + def __init__(self, name, id): + self.name = name + self.id = id + + def addGrade(self, grade): + self.grades.append(grade) + + def showGrades(self): + grds = '' + for grade in self.grades: + grds += str(grade) + ' ' + return grds + +stu1 = Student('Jones','123') +stu1.addGrade(88) +stu1.addGrade(84) +stu1.addGrade(91) +print(stu1.showGrades()) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 15/student1.py b/Learning Python Programming - Working Files/Chapter 15/student1.py new file mode 100644 index 0000000..538cdc7 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 15/student1.py @@ -0,0 +1,33 @@ +class Student: + #fields - name, id, grades(list) + grades = [] + def __init__(self, name, id): + self.name = name + self.id = id + + def addGrade(self, grade): + self.grades.append(grade) + + def showGrades(self): + grds = '' + for grade in self.grades: + grds += str(grade) + ' ' + return grds + + def __str__(self): + return "Name: " + self.name + "\n" + \ + "Id: " + self.id + "\n" + \ + "Grades: " + self.showGrades() + + def average(self): + total = 0 + for grade in self.grades: + total += grade + return total / len(self.grades) + +stu1 = Student('Jones','123') +stu1.addGrade(88) +stu1.addGrade(84) +stu1.addGrade(91) +print(stu1) +print("Average: " + str(stu1.average())) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 16/account.py b/Learning Python Programming - Working Files/Chapter 16/account.py new file mode 100644 index 0000000..6e6e89d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 16/account.py @@ -0,0 +1,37 @@ +class Account: + def __init__(self, acctNumber, balance): + self.acctNumber = acctNumber + self.balance = balance + + def __str__(self): + return "Account number: " + str(self.acctNumber) + "\n" + \ + "Balance: " + str(self.balance) + +class Checking(Account): + def __init__(self, acctNumber, balance, fee): + Account.__init__(self, acctNumber, balance) + self.fee = fee + + def __str__(self): + retStr = "Account type: Checking\n"; + retStr += Account.__str__(self) + return retStr + + def getFee(self): + return self.fee + + def deposit(self, amount): + self.balance += amount + + def withdraw(self, amount): + if amount > self.balance: + print("Insufficient funds.") + else: + self.balance = self.balance - amount - self.fee + +ca = Checking("123", 500, .50) +print(ca) +ca.withdraw(100) +print(ca) +ca.deposit(200) +print(ca) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 16/employee.py b/Learning Python Programming - Working Files/Chapter 16/employee.py new file mode 100644 index 0000000..8098d55 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 16/employee.py @@ -0,0 +1,36 @@ +class Employee: + def __init__(self, name, payRate): + self.name = name + self.payRate = payRate + + def __str__(self): + return self.name + ", " + str(self.payRate) + + def pay(self, hoursWorked): + return self.payRate * hoursWorked + +class Manager(Employee): + def __init__(self, name, payRate, isSalaried): + Employee.__init__(self, name, payRate) + self.salaried = isSalaried + + def __str__(self): + retStr = Employee.__str__(self) + retStr += " salaried: " + str(self.salaried) + return retStr + + def pay(self, hoursWorked): + if self.salaried: + return self.payRate + else: + return self.payRate * hoursWorked + +e1 = Employee("John Jones", 10.00) +print(e1) +print("Gross pay: " + str(e1.pay(40))) +m1 = Manager("Jane Smith", 1200, True) +print(m1) +print("Gross pay: " + str(m1.pay(40))) +m2 = Manager("Jim Brown", 20.00, False) +print(m2) +print("Gross pay: " + str(m2.pay(40))) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 16/rectangle.py b/Learning Python Programming - Working Files/Chapter 16/rectangle.py new file mode 100644 index 0000000..0686e34 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 16/rectangle.py @@ -0,0 +1,31 @@ +class Shape: + def __init__(self, xcor, ycor): + self.x = xcor + self.y = ycor + + def __str__(self): + return 'x: ' + str(self.x) + 'y: ' + str(self.y) + + def move(self, x1, y1): + self.x = self.x + x1 + self.y = self.y + y1 + +class Rectangle(Shape): + def __init__(self, xcor, ycor, width, height): + Shape.__init__(self, xcor, ycor) + self.width = width + self.height = height + + def __str__(self): + retStr = Shape.__str__(self) + retStr += ' width: ' + str(self.width) + \ + ' height: ' + str(self.height) + return retStr + +rec = Rectangle(5,10,8,9) +print(rec) +rec.move(10,12) +print(rec) + + + diff --git a/Learning Python Programming - Working Files/Chapter 16/savings.py b/Learning Python Programming - Working Files/Chapter 16/savings.py new file mode 100644 index 0000000..c411d4d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 16/savings.py @@ -0,0 +1,62 @@ +class Account: + def __init__(self, acctNumber, balance): + self.acctNumber = acctNumber + self.balance = balance + + def __str__(self): + return "Account number: " + str(self.acctNumber) + "\n" + \ + "Balance: " + str(self.balance) + + def deposit(self, amount): + self.balance += amount + +class Checking(Account): + def __init__(self, acctNumber, balance, fee): + Account.__init__(self, acctNumber, balance) + self.fee = fee + + def __str__(self): + retStr = "Account type: Checking\n" + retStr += Account.__str__(self) + return retStr + + def getFee(self): + return self.fee + + def withdraw(self, amount): + if amount > self.balance: + print("Insufficient funds.") + else: + self.balance = self.balance - amount - self.fee + +class Savings(Account): + def __init__(self, acctNumber, balance): + Account.__init__(self, acctNumber, balance) + + def __str__(self): + retStr = "Account type: Savings\n" + retStr += Account.__str__(self) + return retStr + + def withdraw(self, amount): + if amount > self.balance: + print("Insufficient funds.") + else: + self.balance = self.balance - amount + +ca = Checking("123", 500, .50) +print(ca) +ca.withdraw(100) +print(ca) +ca.deposit(200) +print(ca) +sa = Savings("456", 1000) +print(sa) +sa.withdraw(250) +print(sa) +sa.deposit(125) +print(sa) +accounts = [ca, sa] +print("Displaying all accounts: ") +for i in range(0, len(accounts)): + print(accounts[i]) diff --git a/Learning Python Programming - Working Files/Chapter 17/except.py b/Learning Python Programming - Working Files/Chapter 17/except.py new file mode 100644 index 0000000..a3835f7 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 17/except.py @@ -0,0 +1,7 @@ +#print("Enter a numerator: ") +#numer = int(input()) +#print("Enter a denominator: ") +#denom = int(input()) +#quotient = numer / denom +#print(quotient) +file = open('blah.txt','r') \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 17/exceptex1.py b/Learning Python Programming - Working Files/Chapter 17/exceptex1.py new file mode 100644 index 0000000..1420cf9 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 17/exceptex1.py @@ -0,0 +1,38 @@ +def calc(op1, op2, op): + if op == '+': + return op1 + op2 + elif op == '-': + return op1 - op2 + elif op == '*': + return op1 * op2 + elif op == '/': + return op1 / op2 + +import os + +print("Enter file name to open: ") +name = input() +while not os.path.isfile(name): + print("File does not exist.") + print("Enter file name to open: ") + name = input() +file = open(name, 'r') +for line in file: + print(line,end='') + +#cont = 'y' +#while cont != 'n': +# print("Enter the first number: ") +# num1 = int(input()) +# print("Enter the second number: ") +# num2 = int(input()) +# print("Enter operation: ") +# op = input() +# if op == '/' and num2 == 0: +# print("Cannot divide by zero.") +# continue +# else: +# print(calc(num1, num2, op)) +# print("Do you want to continue(y/n)?") +# cont = input() + diff --git a/Learning Python Programming - Working Files/Chapter 17/finally.py b/Learning Python Programming - Working Files/Chapter 17/finally.py new file mode 100644 index 0000000..393d4a8 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 17/finally.py @@ -0,0 +1,13 @@ +try: + print("Enter a file name: ") + name = input() + file = open(name, 'w') + display(file) +except: + print("Error with file.") + print("Enter a file name: ") + name = input() + file = open(name, 'w') + display(file) +finally: + file.close() \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 17/raise.py b/Learning Python Programming - Working Files/Chapter 17/raise.py new file mode 100644 index 0000000..40b996a --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 17/raise.py @@ -0,0 +1,13 @@ +class Rational: + def __init__(self, x, y): + numer = x + if y == 0: + raise ZeroDivisionError() + else: + denom = y + +try: + rat1 = Rational(4,1) + rat2 = Rational(3,0) +except: + print("Cannot have a rational number with 0 for denominator.") \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 17/try.py b/Learning Python Programming - Working Files/Chapter 17/try.py new file mode 100644 index 0000000..a4a268c --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 17/try.py @@ -0,0 +1,21 @@ +try: + print("Enter the name of a file: ") + name = input() + file = open(name, 'r') + #print("Enter a numerator: ") + #numer = int(input()) + #print("Enter a denominator: ") + #denom = int(input()) + #quotient = numer / denom + #print(quotient) +except IOError: + print("Cannot open file.") + print("Enter the name of the file to open: ") + name = input() + file = open(name, 'r') +except ZeroDivisionError: + #print("Cannot divide by zero.") + #print("Enter a new denominator: ") + #denom = int(input()) + #quotient = numer / denom + #print(quotient) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 18/line.py b/Learning Python Programming - Working Files/Chapter 18/line.py new file mode 100644 index 0000000..77df026 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 18/line.py @@ -0,0 +1,28 @@ +class Point: + def __init__(self,x,y): + self.point = (x,y) + + def __str__(self): + return "x: " + str(self.point[0]) + " y: " + str(self.point[1]) + + def setLocation(self,x,y): + self.point = (x,y) +#composition - has-a +class Line: + def __init__(self, p1, p2): + self.point1 = p1 + self.point2 = p2 + + def __str__(self): + return "Point 1: " + str(self.point1) + "\n" + \ + "Point 2: " + str(self.point2) + +p1 = Point(1,2) +print(p1) +p1.setLocation(10,20) +print(p1) +p2 = Point(8,9) +line1 = Line(p1, p2) +print(line1) +p2.setLocation(12,22) +print(line1) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 18/numwords.py b/Learning Python Programming - Working Files/Chapter 18/numwords.py new file mode 100644 index 0000000..5e513c4 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 18/numwords.py @@ -0,0 +1,15 @@ +sentence = "now is the time for all good people to come" +sentence += " to the aid of their party" +words = sentence.split(' ') +words = sorted(words) +print("Sentence in sorted order:\n") +print(words) +numWords = {} +for i in range(0, len(words)): + if words[i] in numWords: + numWords[words[i]] += 1 + else: + numWords[words[i]] = 1 +print("Word list and count: \n") +for key in numWords.keys(): + print(key, numWords[key]) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 18/takedrop.py b/Learning Python Programming - Working Files/Chapter 18/takedrop.py new file mode 100644 index 0000000..48e2159 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 18/takedrop.py @@ -0,0 +1,17 @@ +def take(num, lyst): + rlist = [] + for i in range(0,num): + rlist.append(lyst[i]) + return rlist + +def drop(num, lyst): + rlist = [] + for i in range(num, len(lyst)): + rlist.append(lyst[i]) + return rlist + +names = ['Raymond','Cynthia','David','Jennifer','Clayton'] +somenames = take(-3,names) +print(somenames) +names = drop(-3,names) +print(names) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 2/No Work Files.rtf b/Learning Python Programming - Working Files/Chapter 2/No Work Files.rtf new file mode 100644 index 0000000..b1be15b --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 2/No Work Files.rtf @@ -0,0 +1,7 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf350 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\margl1440\margr1440\vieww9000\viewh8400\viewkind0 +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural\pardirnatural + +\f0\fs24 \cf0 No work files for this lesson...} \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 3/chap0301.py b/Learning Python Programming - Working Files/Chapter 3/chap0301.py new file mode 100644 index 0000000..8c854ae --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 3/chap0301.py @@ -0,0 +1 @@ +print('Hello, world!') \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 3/chap3ex1.py b/Learning Python Programming - Working Files/Chapter 3/chap3ex1.py new file mode 100644 index 0000000..fa5889d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 3/chap3ex1.py @@ -0,0 +1,4 @@ +#print("hello,", end='') +#print(" world!") +print("hello,\tworld!") +print("hello,\nworld!") \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 4/grades.txt b/Learning Python Programming - Working Files/Chapter 4/grades.txt new file mode 100644 index 0000000..4d59e1a --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 4/grades.txt @@ -0,0 +1,8 @@ +Raymond 92 +Cynthia 83 +Terrill 64 +Jennifer 75 +Clayton 88 +David 91 +Bryan 100 +Danny 99 \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 5/add.py b/Learning Python Programming - Working Files/Chapter 5/add.py new file mode 100644 index 0000000..68f5b16 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 5/add.py @@ -0,0 +1,5 @@ +print("Enter a number: ") +number1 = int(raw_input()) +print("Enter another number: ") +number2 = int(raw_input()) +print("The sum is: " + str(number1+number2)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 5/thanks.py b/Learning Python Programming - Working Files/Chapter 5/thanks.py new file mode 100644 index 0000000..03fefd6 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 5/thanks.py @@ -0,0 +1,19 @@ +print("What is the name of the gift giver?") +name = raw_input() +print("What is the present they gave you?") +present = raw_input() +print("How old were you on your birthday?") +age = int(raw_input()) +print("What is your name?") +yourName = raw_input() +print("Dear " + name + ", ") +print("") +print("Thank you for the " + present + ". ") +print("I really like it. I can't believe ") +print("I am already " + str(age) + " years old, but ") +print("it does not feel much different than being ") +print(str(age-1) + ".") +print("") +print("Sincerely,") +print("") +print(yourName) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 6/activity.py b/Learning Python Programming - Working Files/Chapter 6/activity.py new file mode 100644 index 0000000..53cda62 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 6/activity.py @@ -0,0 +1,14 @@ +message = "The recommended activity is " +print("Enter the temperature: ") +temp = int(raw_input()) +if temp > 85: + message = message + "swimming." +elif temp >= 70: + message = message + "tennis." +elif temp >= 32: + message = message + "golf." +elif temp >= 0: + message = message + "dancing." +else: + message = message + "sitting by the fire." +print(message) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 6/guess.py b/Learning Python Programming - Working Files/Chapter 6/guess.py new file mode 100644 index 0000000..6c5b41b --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 6/guess.py @@ -0,0 +1,18 @@ +answer = "Watson" +print("Here is a guessing game. You get three tries.") +print("What is the name of the computer that played on Jeopardy?") +response = raw_input() +if response == answer: + print("That is right!") +else: + print("Sorry. Guess again: ") + response = raw_input() + if response == answer: + print("That is right!") + else: + print("Sorry. One more guess: ") + response = raw_input() + if response == answer: + print("That is right!") + else: + print("Sorry. No more guesses. The answer is " + answer + ".") \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 6/ifelif.py b/Learning Python Programming - Working Files/Chapter 6/ifelif.py new file mode 100644 index 0000000..2f3c483 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 6/ifelif.py @@ -0,0 +1,15 @@ +print("Enter a numeric grade: ") +grade = int(raw_input()) +if grade >= 90: + letterGrade = "A" +elif grade >= 80: + letterGrade = "B" +elif grade >= 70: + letterGrade = "C" +elif grade >= 60: + letterGrade = "D" +elif grade <= 59: + letterGrade = "F" +else: + print("Did not recognize input") +print("Your letter grade is " + letterGrade) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 6/ifelse.py b/Learning Python Programming - Working Files/Chapter 6/ifelse.py new file mode 100644 index 0000000..1c0980a --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 6/ifelse.py @@ -0,0 +1,8 @@ +print("Enter hours worked: ") +hoursWorked = int(raw_input()) +rate = 25.00 +if hoursWorked > 40: + grossPay = (40 * rate) + ((hoursWorked - 40) * (rate * 1.5)) +else: + grossPay = hoursWorked * rate +print("Gross pay: " + str(grossPay)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 6/ifstatement.py b/Learning Python Programming - Working Files/Chapter 6/ifstatement.py new file mode 100644 index 0000000..3835e22 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 6/ifstatement.py @@ -0,0 +1,8 @@ +print("Enter hours worked: ") +hoursWorked = int(raw_input()) +rate = 25.00 +if hoursWorked > 40: + grossPay = (40 * rate) + ((hoursWorked - 40) * (rate * 1.5)) +if hoursWorked <= 40: + grossPay = hoursWorked * rate +print("Gross pay: " + str(grossPay)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 7/break.py b/Learning Python Programming - Working Files/Chapter 7/break.py new file mode 100644 index 0000000..228ca76 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/break.py @@ -0,0 +1,13 @@ +number = 0 +total = 0 +average = 0.0 +count = 0 +while True: + print("Enter a number: ") + number = float(raw_input()) + if number == -1: + break + total = total + number + count = count + 1 +average = total / count +print("Average: " + str(average)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 7/chap7ex1.py b/Learning Python Programming - Working Files/Chapter 7/chap7ex1.py new file mode 100644 index 0000000..1028665 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/chap7ex1.py @@ -0,0 +1,15 @@ +tries = 0 +answer = "Watson" +while(tries <= 3): + print("What is the name of the computer that played on Jeopardy?") + response = raw_input() + tries = tries + 1 + if (response == "Watson"): + print("That is right!") + break; + elif (tries == 3): + print("Sorry. The answer is Watson.") + break; + else: + print("Sorry. Try again.") + \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 7/chap7ex2.py b/Learning Python Programming - Working Files/Chapter 7/chap7ex2.py new file mode 100644 index 0000000..d3c7195 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/chap7ex2.py @@ -0,0 +1,23 @@ +op1 = 0.0 +op2 = 0.0 +op = '' +while op1 != 'q': + print("Enter first number (q to quit): ") + op1 = raw_input() + if op1 == 'q': + break; + op1 = float(op1) + print("Enter second number: ") + op2 = float(raw_input()) + print("Enter an operation (+,-,*,/): ") + op = raw_input() + if op == '+': + print(op1 + op2) + elif op == '-': + print(op1 - op2) + elif op == '*': + print(op1 * op2) + elif op == '/': + print(op1 / op2) + else: + print("Did not recognize operator.") \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 7/continue.py b/Learning Python Programming - Working Files/Chapter 7/continue.py new file mode 100644 index 0000000..24deb7a --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/continue.py @@ -0,0 +1,10 @@ +numer = 0 +denom = 0 +while denom != -1: + print("Enter a numerator: ") + numer = float(raw_input()) + print("Enter a denominator: ") + denom = float(raw_input()) + if denom == 0: + continue + print(numer / denom) diff --git a/Learning Python Programming - Working Files/Chapter 7/readfile.py b/Learning Python Programming - Working Files/Chapter 7/readfile.py new file mode 100644 index 0000000..b46a9ab --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/readfile.py @@ -0,0 +1,16 @@ +#inFile = open('text.txt', 'r') +#line = inFile.readline() +#print(line) +#line = inFile.readline() +#print(line) +count = 0 +total = 0 +inFile = open('grades.txt', 'r') +grade = inFile.readline() +while (grade): + print(grade) + count = count+1 + total = total + int(grade) + grade = inFile.readline() +average = total / count +print("Average: " + str(average)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 7/whilecount.py b/Learning Python Programming - Working Files/Chapter 7/whilecount.py new file mode 100644 index 0000000..5569909 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/whilecount.py @@ -0,0 +1,15 @@ +#examples of count-controlled loops +#sum = 0 +#number = 1 +#while number <= 10: +# sum = sum + number +# number = number + 1 +#print("The sum is " + str(sum)) +balance = 5000 +rate = 1.02 +year = 1 +while year <= 10: + balance = balance * rate + print("Year: " + str(year) + ": " + str(balance)) + year = year + 1 + diff --git a/Learning Python Programming - Working Files/Chapter 7/whileevent.py b/Learning Python Programming - Working Files/Chapter 7/whileevent.py new file mode 100644 index 0000000..766f7f3 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/whileevent.py @@ -0,0 +1,12 @@ +average = 0.0 +total = 0 +count = 0 +print("Enter a grade (-1 to quit): ") +grade = int(raw_input()) +while grade != -1: + total = total + grade + count = count + 1 + print("Enter a grade (-1 to quit): ") + grade = int(raw_input()) +average = total / count +print("Average grade: " + str(average)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 7/whileintro.py b/Learning Python Programming - Working Files/Chapter 7/whileintro.py new file mode 100644 index 0000000..e830254 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/whileintro.py @@ -0,0 +1,4 @@ +number = 1 +while number <= 10: + print(number) + number = number + 1 diff --git a/Learning Python Programming - Working Files/Chapter 7/writefile.py b/Learning Python Programming - Working Files/Chapter 7/writefile.py new file mode 100644 index 0000000..55cb094 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 7/writefile.py @@ -0,0 +1,14 @@ +#outFile = open('text.txt', 'w') +#outFile.write('this is line 1\n') +#outFile.write('this is line 2\n') +#outFile.close() +outFile = open('grades.txt', 'w') +grade = 0 +print("Enter a grade (q to quit): ") +grade = raw_input() +while (grade != 'q'): + outFile.write(grade + '\n') + print("Enter a grade (q to quit): ") + grade = raw_input() + +outFile.close() \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/chap8ex1.py b/Learning Python Programming - Working Files/Chapter 8/chap8ex1.py new file mode 100644 index 0000000..bf809e7 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/chap8ex1.py @@ -0,0 +1,9 @@ +# 5! = 5*4*3*2*1 = 120 +# 6! = 6 * 5! +#factorial computation iteratively +print("Enter a number: ") +num = int(input()) +fact = 1 +for i in range(1, num+1): + fact = fact * i +print(str(num) + "! = " + str(fact)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/chap8ex2.py b/Learning Python Programming - Working Files/Chapter 8/chap8ex2.py new file mode 100644 index 0000000..711a6f2 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/chap8ex2.py @@ -0,0 +1,7 @@ +bar = "" +for grade in open('grades.txt'): + for i in range(1, int(grade)+1): + if i % 5 == 0: + bar = bar + "*" + print(bar, i) + bar = "" \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/fordict.py b/Learning Python Programming - Working Files/Chapter 8/fordict.py new file mode 100644 index 0000000..42493d4 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/fordict.py @@ -0,0 +1,5 @@ +numbers = {'Cynthia':'2356', 'Raymond':'2345', 'David':'2373'} +#print(numbers.keys()) +#print(numbers.values()) +for key in numbers.keys(): + print(key + " extension is: " + numbers[key]) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/forfile.py b/Learning Python Programming - Working Files/Chapter 8/forfile.py new file mode 100644 index 0000000..59da965 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/forfile.py @@ -0,0 +1,15 @@ +#inFile = open('text.txt', 'r') +#line = inFile.readline() +#while(line): +# print(line, end='') +# line = inFile.readline() +#for line in open('text.txt'): +# print(line, end='') +sum = 0 +count = 0 +for grade in open('grades.txt'): + print(grade, end='') + sum = sum + int(grade) + count = count + 1 +average = sum / count +print("Average: " + str(average)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/forintro1.py b/Learning Python Programming - Working Files/Chapter 8/forintro1.py new file mode 100644 index 0000000..9af0f39 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/forintro1.py @@ -0,0 +1,7 @@ +#for i in [1,2,3,4,5]: +# print(i) +numbers = [1,2,3,4,5] +sum = 0 +for x in numbers: + sum = sum + x +print(sum) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/forintro2.py b/Learning Python Programming - Working Files/Chapter 8/forintro2.py new file mode 100644 index 0000000..348c2b0 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/forintro2.py @@ -0,0 +1,10 @@ +#word = "hello world!" +#for letter in word: +# print(letter) +sentence = "now is the time for all good people to come to the aid" +count = 0 +for letter in sentence: + if letter == 'a' or letter == 'e' or letter == 'i' or letter == 'u' \ + or letter == 'o': + count = count + 1 +print("The number of vowels is " + str(count)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/forlist.py b/Learning Python Programming - Working Files/Chapter 8/forlist.py new file mode 100644 index 0000000..f031a2d --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/forlist.py @@ -0,0 +1,5 @@ +numbers = [1,2,3,4,5,6,7,8,9,10] +#for number in numbers: +# print(number) +for i in range(0,len(numbers),3): + print(numbers[i]) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 8/fortuples.py b/Learning Python Programming - Working Files/Chapter 8/fortuples.py new file mode 100644 index 0000000..39590b8 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 8/fortuples.py @@ -0,0 +1,14 @@ +#numbers = (1,2,3,4,5) +#sum = 0 +#for num in numbers: +# sum = sum + num +# print(num) +#print("The sum is " + str(sum)) +words = ("now","is","time","the") +for word in words: + print(word) +max = 0 +for i in range(1,len(words)): + if len(words[i]) > len(words[max]): + max = i +print("The longest word is " + words[max]) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 9/chap9ex1.py b/Learning Python Programming - Working Files/Chapter 9/chap9ex1.py new file mode 100644 index 0000000..e0af8ac --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 9/chap9ex1.py @@ -0,0 +1,8 @@ +square = ((10,8), (10,23), (25,23), (25,8)) +#for points in square: +# print(points) +squareit = iter(square) +print(next(squareit)) +print(next(squareit)) +print(next(squareit)) +print(next(squareit)) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 9/dictioniter.py b/Learning Python Programming - Working Files/Chapter 9/dictioniter.py new file mode 100644 index 0000000..3cb5365 --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 9/dictioniter.py @@ -0,0 +1,8 @@ +grades = {'Cynthia':88, 'David':77, 'Clayton':99} +#for key in grades.keys(): +# print(key, grades[key]) +it = iter(grades) +print(next(it)) +print(next(it)) +for key in grades: + print(key, grades[key]) \ No newline at end of file diff --git a/Learning Python Programming - Working Files/Chapter 9/iternext.py b/Learning Python Programming - Working Files/Chapter 9/iternext.py new file mode 100644 index 0000000..404c48b --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 9/iternext.py @@ -0,0 +1,7 @@ +#numbers = [1,2,3] +#it = iter(numbers) +#print(next(it)) +#print(next(it)) +#print(next(it)) +fileIt = open('grades.txt', 'r') +print(next(fileIt), end='') diff --git a/Learning Python Programming - Working Files/Chapter 9/otheriters.py b/Learning Python Programming - Working Files/Chapter 9/otheriters.py new file mode 100644 index 0000000..baaabfe --- /dev/null +++ b/Learning Python Programming - Working Files/Chapter 9/otheriters.py @@ -0,0 +1,15 @@ +#numbers = range(1,11) +#it = iter(numbers) +#print(next(it)) +import os +files = os.popen('dir *.py') +fileit = iter(files) +print(next(fileit), end='') +print(next(fileit)) +print(next(fileit)) +print(next(fileit)) +print(next(fileit)) +print(next(fileit)) +print(next(fileit)) +print(next(fileit)) +print(next(fileit)) \ No newline at end of file diff --git a/Number.py b/Number.py new file mode 100644 index 0000000..a8f014d --- /dev/null +++ b/Number.py @@ -0,0 +1,10 @@ +x = int(input("Please enter an integer: ")) +if x < 0: + x = 0 + print('Negative changed to zero') +elif x == 0: + print('Zero') +elif x == 1: + print('Single') +else: + print('More') \ No newline at end of file diff --git a/PyCharm/.idea/.name b/PyCharm/.idea/.name new file mode 100644 index 0000000..f5ca06b --- /dev/null +++ b/PyCharm/.idea/.name @@ -0,0 +1 @@ +PyCharm \ No newline at end of file diff --git a/PyCharm/.idea/PyCharm.iml b/PyCharm/.idea/PyCharm.iml new file mode 100644 index 0000000..e5c8f96 --- /dev/null +++ b/PyCharm/.idea/PyCharm.iml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/PyCharm/.idea/codeStyleSettings.xml b/PyCharm/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..9178b38 --- /dev/null +++ b/PyCharm/.idea/codeStyleSettings.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/PyCharm/.idea/encodings.xml b/PyCharm/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/PyCharm/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/PyCharm/.idea/misc.xml b/PyCharm/.idea/misc.xml new file mode 100644 index 0000000..6402894 --- /dev/null +++ b/PyCharm/.idea/misc.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/PyCharm/.idea/modules.xml b/PyCharm/.idea/modules.xml new file mode 100644 index 0000000..6f20852 --- /dev/null +++ b/PyCharm/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/PyCharm/.idea/other.xml b/PyCharm/.idea/other.xml new file mode 100644 index 0000000..9d2e7e9 --- /dev/null +++ b/PyCharm/.idea/other.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/PyCharm/.idea/scopes/scope_settings.xml b/PyCharm/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/PyCharm/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/PyCharm/.idea/testrunner.xml b/PyCharm/.idea/testrunner.xml new file mode 100644 index 0000000..0d97b74 --- /dev/null +++ b/PyCharm/.idea/testrunner.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/PyCharm/.idea/vcs.xml b/PyCharm/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/PyCharm/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/PyCharm/.idea/workspace.xml b/PyCharm/.idea/workspace.xml new file mode 100644 index 0000000..52823f4 --- /dev/null +++ b/PyCharm/.idea/workspace.xml @@ -0,0 +1,590 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Buildout + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1376297074443 + 1376297074443 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + file://$PROJECT_DIR$/../charCreate.py + 21 + + + file://$PROJECT_DIR$/../charCreate.py + 124 + + + file://$PROJECT_DIR$/../whosYourDaddy.py + 8 + + + file://$PROJECT_DIR$/../whosYourDaddy.py + 80 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Wing/Absolute/Absolute b/Wing/Absolute/Absolute new file mode 100644 index 0000000..9e4ec6b --- /dev/null +++ b/Wing/Absolute/Absolute @@ -0,0 +1,7 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +proj.file-type = 'shared' diff --git a/Wing/Absolute/Absolute.wpr b/Wing/Absolute/Absolute.wpr new file mode 100644 index 0000000..7f9140f --- /dev/null +++ b/Wing/Absolute/Absolute.wpr @@ -0,0 +1,13 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +proj.directory-list = [{'dirloc': loc('../..'), + 'excludes': (), + 'filter': u'*', + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}] +proj.file-type = 'shared' diff --git a/Wing/Absolute/Absolute.wpu b/Wing/Absolute/Absolute.wpu new file mode 100644 index 0000000..20a74dc --- /dev/null +++ b/Wing/Absolute/Absolute.wpu @@ -0,0 +1,463 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file : User-specific branch # +################################################################## +[user attributes] +debug.breakpoints = {loc('../../charCreate.py'): {22: (0, + None, + 1, + 0)}} +debug.err-values = {loc('../../charCreate.py'): {}, + loc('../../whosYourDaddy.py'): {}} +debug.show-args-dialog = {loc('../../charCreate.py'): False, + loc('../../whosYourDaddy.py'): False} +guimgr.overall-gui-state = {'windowing-policy': 'combined-window', + 'windows': [{'name': 'ZFo90pOQwVnZz3zMm1X6r9hbLF'\ + 'gtg2t1', + 'size-state': 'maximized', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': None, + 'current_pages': [0, + 1], + 'full-screen': False, + 'notebook_display': 'normal', + 'notebook_percent': 0.25, + 'override_title': None, + 'pagelist': [('project', + 'tall', + 0, + {'tree-state': {'file-sort-method': 'by name', + 'list-files-first': False, + 'tree-states': {'deep': {'column-widths': None, + 'expanded-nodes': [(0,)], + 'selected-nodes': [(0, + 27)], + 'top-node': (0, + 11)}}, + 'tree-style': 'deep'}}), + ('browser', + 'tall', + 0, + {'all_tree_states': {u'By Module': {'column-w'\ + 'idths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': None}}, + 'browse_mode': u'By Module', + 'follow-selection': False, + 'sort_mode': 'Alphabetically', + 'visibility_options': {u'Derived Classes': False, + u'Imported': False, + u'Modules': True}}), + ('snippets', + 'tall', + 0, + {'tree-states': {'__all__': [], + u'c': [], + u'django': [], + u'html': [], + u'py': []}}), + ('source-assistant', + 'tall', + 2, + {'docstring-during-complete': False, + 'wrap-lines': True}), + ('debug-stack', + 'tall', + 1, + {'codeline-mode': 'below'}), + ('indent', + 'tall', + 2, + {})], + 'primary_view_state': {'area': 'wide', + 'constraint': None, + 'current_pages': [0, + 2], + 'notebook_display': 'normal', + 'notebook_percent': 0.30000000000000004, + 'override_title': None, + 'pagelist': [('batch-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'Create/Edit _Filters...', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': '', + 'fReverse': False, + 'fSearchText': '', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': False}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('interactive-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'All Source Files', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': '', + 'fReverse': False, + 'fSearchText': '', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': False}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {'added-files': [], + 'filter': u'', + 'recent-filters': None, + 'sort-order': 'alpha'}), + ('debug-data', + 'wide', + 0, + {}), + ('debug-breakpoints', + 'wide', + 0, + {'tree-state': []}), + ('python-shell', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 2, + 'folded-linenos': [], + 'history': {None: ['wydd\n']}, + 'launch-id': None, + 'sel-line': 11, + 'sel-line-start': 430, + 'selection_end': 430, + 'selection_start': 430}), + ('bookmarks', + 'wide', + 1, + {}), + ('debug-io', + 'wide', + 1, + {'attrib-starts': [], + 'first-line': 84, + 'folded-linenos': [], + 'sel-line': 94, + 'sel-line-start': 2024, + 'selection_end': 2032, + 'selection_start': 2032}), + ('debug-probe', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 4, + 'folded-linenos': [], + 'history': {u'file:/Users/charleswade/Documents/Programming/Py/whosYourDaddy.py': [''\ + 'wydd\n', + 'wydd["Greg Cave"] = "Bobby Kenedy"\n', + 'wydd["Greg Cave"]\n', + 'wydd["Bobby Kenedy"]\n']}, + 'launch-id': None, + 'sel-line': 11, + 'sel-line-start': 330, + 'selection_end': 330, + 'selection_start': 330}), + ('debug-watch', + 'wide', + 1, + {'node-states': [], + 'tree-state': {'column-widths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': (0,)}}), + ('debug-modules', + 'wide', + 1, + {})], + 'primary_view_state': {'editor_states': ({'bookmarks': ([(loc('../../charCreate.py'), + {'attrib-starts': [], + 'first-line': 38, + 'folded-linenos': [], + 'sel-line': 31, + 'sel-line-start': 714, + 'selection_end': 716, + 'selection_start': 716}, + 1376951519.363224), + (loc('../../charCreate.py'), + {'attrib-starts': [], + 'first-line': 3, + 'folded-linenos': [], + 'sel-line': 8, + 'sel-line-start': 379, + 'selection_end': 379, + 'selection_start': 379}, + 1376951590.462128), + (loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_find_and_load', + 1558)], + 'first-line': 1534, + 'folded-linenos': [], + 'sel-line': 1560, + 'sel-line-start': 55443, + 'selection_end': 55443, + 'selection_start': 55443}, + 1376951619.645789), + (loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_find_and_load', + 1558)], + 'first-line': 1545, + 'folded-linenos': [], + 'sel-line': 1561, + 'sel-line-start': 55452, + 'selection_end': 55452, + 'selection_start': 55452}, + 1376951635.078308), + (loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_get_module_lock', + 265)], + 'first-line': 264, + 'folded-linenos': [], + 'sel-line': 269, + 'sel-line-start': 7903, + 'selection_end': 7903, + 'selection_start': 7903}, + 1376951640.182056), + (loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_get_module_lock', + 265)], + 'first-line': 264, + 'folded-linenos': [], + 'sel-line': 270, + 'sel-line-start': 7919, + 'selection_end': 7919, + 'selection_start': 7919}, + 1376951641.834643), + (loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_get_module_lock', + 265)], + 'first-line': 264, + 'folded-linenos': [], + 'sel-line': 271, + 'sel-line-start': 7928, + 'selection_end': 7928, + 'selection_start': 7928}, + 1376951642.461141), + (loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_get_module_lock', + 265)], + 'first-line': 264, + 'folded-linenos': [], + 'sel-line': 271, + 'sel-line-start': 7928, + 'selection_end': 7928, + 'selection_start': 7928}, + 1376951644.456159), + [loc('../../../../../../../Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/importlib/_bootstrap.py'), + {'attrib-starts': [('_get_module_lock', + 265)], + 'first-line': 264, + 'folded-linenos': [], + 'sel-line': 272, + 'sel-line-start': 7965, + 'selection_end': 7965, + 'selection_start': 7965}, + 1376951647.635761], + (loc('../../charCreate.py'), + {'attrib-starts': [], + 'first-line': 59, + 'folded-linenos': [], + 'sel-line': 8, + 'sel-line-start': 379, + 'selection_end': 379, + 'selection_start': 379}, + 1376951665.669731), + (loc('../../charCreate.py'), + {'attrib-starts': [], + 'first-line': 1, + 'folded-linenos': [], + 'sel-line': 60, + 'sel-line-start': 1584, + 'selection_end': 1623, + 'selection_start': 1623}, + 1376951721.857441), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 47, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1377139824.20591), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 12, + 'viewer-state': {'history': [-1, + 27, + 1, + 10, + 9, + 12], + 'history-pos': 5, + 'index': 12, + 'top': 0}}, + 1377139830.521163), + (loc('../../charCreate.py'), + {'attrib-starts': [], + 'first-line': 1, + 'folded-linenos': [], + 'sel-line': 60, + 'sel-line-start': 1584, + 'selection_end': 1623, + 'selection_start': 1623}, + 1377139835.11741), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 3, + 'sel-line-start': 267, + 'selection_end': 267, + 'selection_start': 267}, + 1377139842.050118), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 67, + 'folded-linenos': [], + 'sel-line': 81, + 'sel-line-start': 2562, + 'selection_end': 2593, + 'selection_start': 2593}, + 1377139926.575305), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 3, + 'folded-linenos': [], + 'sel-line': 8, + 'sel-line-start': 399, + 'selection_end': 399, + 'selection_start': 399}, + 1377140409.503246), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 3, + 'folded-linenos': [], + 'sel-line': 8, + 'sel-line-start': 399, + 'selection_end': 399, + 'selection_start': 399}, + 1377140433.93693), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 3, + 'folded-linenos': [], + 'sel-line': 8, + 'sel-line-start': 399, + 'selection_end': 399, + 'selection_start': 399}, + 1377140492.815499), + [loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 3, + 'folded-linenos': [], + 'sel-line': 8, + 'sel-line-start': 399, + 'selection_end': 399, + 'selection_start': 399}, + 1377140501.749871]], + 19), + 'current-loc': loc('../../whosYourDaddy.py'), + 'editor-state-list': [(loc('../../charCreate.py'), + {'attrib-starts': [], + 'first-line': 1, + 'folded-linenos': [], + 'sel-line': 60, + 'sel-line-start': 1584, + 'selection_end': 1623, + 'selection_start': 1623}), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 12, + 'viewer-state': {'history': [-1, + 27, + 1, + 10, + 9, + 12], + 'history-pos': 5, + 'index': 12, + 'top': 0}}), + (loc('../../whosYourDaddy.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 72, + 'sel-line-start': 2189, + 'selection_end': 2199, + 'selection_start': 2199})], + 'has-focus': True, + 'locked': False}, + [loc('../../charCreate.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('../../whosYourDaddy.py')]), + 'open_files': [u'../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx', + u'../../charCreate.py', + u'../../whosYourDaddy.py']}, + 'saved_notebook_display': None, + 'split_percents': {0: 0.5}, + 'splits': 2, + 'tab_location': 'top', + 'user_data': {}}, + 'saved_notebook_display': None, + 'split_percents': {0: 0.47700394218134035}, + 'splits': 2, + 'tab_location': 'left', + 'user_data': {}}, + 'window-alloc': (0, + 22, + 1420, + 798)}]} +guimgr.recent-documents = [loc('../../whosYourDaddy.py'), + loc('../../charCreate.py')] +proj.env-vars = {None: ('default', + [u''])} +proj.pyexec = {None: ('custom', + u'/Library/Frameworks/Python.framework/Versions/3.3/bin/python3.3-32')} diff --git a/Wing/wing.wpr b/Wing/wing.wpr new file mode 100644 index 0000000..6c6211d --- /dev/null +++ b/Wing/wing.wpr @@ -0,0 +1,13 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +proj.directory-list = [{'dirloc': loc('..'), + 'excludes': (), + 'filter': u'*', + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}] +proj.file-type = 'shared' diff --git a/Wing/wing.wpu b/Wing/wing.wpu new file mode 100644 index 0000000..d46ca6a --- /dev/null +++ b/Wing/wing.wpu @@ -0,0 +1,698 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file : User-specific branch # +################################################################## +[user attributes] +debug.breakpoints = {loc('../coinFlip.py'): {17: (0, + None, + 1, + 0)}} +debug.err-values = {loc('../coinFlip.py'): {}} +debug.show-args-dialog = {loc('../coinFlip.py'): False} +guimgr.overall-gui-state = {'windowing-policy': 'combined-window', + 'windows': [{'name': 'ZFo90pOQwVnZz3zMm1X6r9hbLF'\ + 'gtg2t1', + 'size-state': 'maximized', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': None, + 'current_pages': [0, + 1], + 'full-screen': False, + 'notebook_display': 'normal', + 'notebook_percent': 0.25, + 'override_title': None, + 'pagelist': [('project', + 'tall', + 0, + {'tree-state': {'file-sort-method': 'by name', + 'list-files-first': False, + 'tree-states': {'deep': {'column-widths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': (0,)}}, + 'tree-style': 'deep'}}), + ('browser', + 'tall', + 0, + {'all_tree_states': {u'By Module': {'column-w'\ + 'idths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': None}}, + 'browse_mode': u'By Module', + 'follow-selection': False, + 'sort_mode': 'Alphabetically', + 'visibility_options': {u'Derived Classes': False, + u'Imported': False, + u'Modules': True}}), + ('snippets', + 'tall', + 0, + {'tree-states': {'__all__': [], + u'c': [], + u'django': [], + u'html': [], + u'py': []}}), + ('source-assistant', + 'tall', + 2, + {'docstring-during-complete': False, + 'wrap-lines': True}), + ('debug-stack', + 'tall', + 1, + {'codeline-mode': 'below'})], + 'primary_view_state': {'area': 'wide', + 'constraint': None, + 'current_pages': [4, + 3], + 'notebook_display': 'normal', + 'notebook_percent': 0.30000000000000004, + 'override_title': None, + 'pagelist': [('batch-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'Create/Edit _Filters...', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': '', + 'fReverse': False, + 'fSearchText': '', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': False}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('interactive-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'All Source Files', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': '', + 'fReverse': False, + 'fSearchText': '', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': False}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {}), + ('debug-data', + 'wide', + 0, + {}), + ('debug-breakpoints', + 'wide', + 0, + {}), + ('python-shell', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'history': {}, + 'launch-id': None, + 'sel-line': 3, + 'sel-line-start': 173, + 'selection_end': 173, + 'selection_start': 173}), + ('bookmarks', + 'wide', + 1, + {}), + ('debug-io', + 'wide', + 1, + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}), + ('debug-probe', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'history': {}, + 'launch-id': None, + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}), + ('debug-watch', + 'wide', + 1, + {'node-states': [], + 'tree-state': {'column-widths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': (0,)}}), + ('debug-modules', + 'wide', + 1, + {})], + 'primary_view_state': {'editor_states': ({'bookmarks': ([(loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 10, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 17, + 'index': 10, + 'top': 0}}, + 1376220989.669613), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 11, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 18, + 'index': 11, + 'top': 237}}, + 1376221027.682615), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 12, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 19, + 'index': 12, + 'top': 1164}}, + 1376221099.357377), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 13, + 'viewer-state': {'history': [0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14], + 'history-pos': 19, + 'index': 13, + 'top': 1014}}, + 1376221463.509194), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 14, + 'viewer-state': {'history': [1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15], + 'history-pos': 19, + 'index': 14, + 'top': 669}}, + 1376221474.365549), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 15, + 'viewer-state': {'history': [2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16], + 'history-pos': 19, + 'index': 15, + 'top': 257}}, + 1376221943.255603), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 16, + 'viewer-state': {'history': [3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17], + 'history-pos': 19, + 'index': 16, + 'top': 815}}, + 1376222052.864093), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 17, + 'viewer-state': {'history': [4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18], + 'history-pos': 19, + 'index': 17, + 'top': 0}}, + 1376222065.306892), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 18, + 'viewer-state': {'history': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19], + 'history-pos': 19, + 'index': 18, + 'top': 751}}, + 1376222097.122038), + (loc('wing/wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1376222180.354958), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 18, + 'viewer-state': {'history': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19], + 'history-pos': 19, + 'index': 18, + 'top': 751}}, + 1376222182.596582), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 19, + 'viewer-state': {'history': [4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20], + 'history-pos': 19, + 'index': 19, + 'top': 0}}, + 1376222183.319316), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 20, + 'viewer-state': {'history': [5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21], + 'history-pos': 19, + 'index': 20, + 'top': 0}}, + 1376222185.716818), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 21, + 'viewer-state': {'history': [6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22], + 'history-pos': 19, + 'index': 21, + 'top': 0}}, + 1376222186.796465), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 22, + 'viewer-state': {'history': [7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23], + 'history-pos': 19, + 'index': 22, + 'top': 0}}, + 1376222187.415277), + (loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 23, + 'viewer-state': {'history': [8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24], + 'history-pos': 19, + 'index': 23, + 'top': 0}}, + 1376222189.394995), + [loc('../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 23, + 'viewer-state': {'history': [8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24], + 'history-pos': 19, + 'index': 23, + 'top': 0}}, + 1376222191.082565], + (loc('../coinFlip.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 23, + 'sel-line-start': 401, + 'selection_end': 401, + 'selection_start': 401}, + 1376222385.472251), + (loc('../coinFlip.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 16, + 'sel-line-start': 332, + 'selection_end': 332, + 'selection_start': 332}, + 1376223319.356258), + [loc('../coinFlip.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 16, + 'sel-line-start': 332, + 'selection_end': 332, + 'selection_start': 332}, + 1376223328.011472]], + 19), + 'current-loc': loc('../coinFlip.py'), + 'editor-state-list': [(loc('../coinFlip.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 16, + 'sel-line-start': 332, + 'selection_end': 332, + 'selection_start': 332})], + 'has-focus': False, + 'locked': False}, + [loc('../coinFlip.py')]), + 'open_files': [u'../coinFlip.py']}, + 'saved_notebook_display': None, + 'split_percents': {0: 0.5}, + 'splits': 2, + 'tab_location': 'top', + 'user_data': {}}, + 'saved_notebook_display': None, + 'split_percents': {0: 0.47700394218134035}, + 'splits': 2, + 'tab_location': 'left', + 'user_data': {}}, + 'window-alloc': (0, + 22, + 1420, + 798)}]} +guimgr.recent-documents = [loc('../coinFlip.py')] diff --git a/Wing/wing/README.txt b/Wing/wing/README.txt new file mode 100644 index 0000000..e6c813e --- /dev/null +++ b/Wing/wing/README.txt @@ -0,0 +1,4 @@ +This directory contains some simple code examples that are used with +the Wing IDE tutorial. To read the tutorial, select it from the Help +menu in the IDE. + diff --git a/Wing/wing/example1.py b/Wing/wing/example1.py new file mode 100644 index 0000000..251bf9c --- /dev/null +++ b/Wing/wing/example1.py @@ -0,0 +1,84 @@ +# This is example code for use with the Wing IDE tutorial, which +# is accessible from the Help menu of the IDE + +# NOTE: This code is incomplete and contains deliberate errors +# to illustrate features when working through the tutorial + +import os +import sys +import stat +import time +import urllib + +# NOTE: This import will not work unless you follow the directions +# in the tutorial to set your PYTHONPATH +from path_example import ParseRDFNews, kCannedData + +#---------------------------------------------------------------------- +def PromptToContinue(msg="Press Enter to Continue: "): + """Prompt to continue and wait for the user to press Enter""" + if sys.hexversion >= 0x03000000: + x = input(msg) + else: + x = raw_input(msg) + +#----------------------------------------------------------------------- +def GetItemCount(): + """This gets the number of items to use in this example""" + return 5 + +#----------------------------------------------------------------------- +def ReadPythonNews(count, force=0): + """Reads news and project list from python.org news channel""" + + newscache = 'newscache.rdf' + + txt = None + if not force and os.path.exists(newscache): + mtime = os.stat(newscache).st_mtime + if time.time() - mtime < 60 * 60 * 24: + f = open(newscache) + txt = f.read() + f.close() + + if txt is None: + try: + svc = urllib.urlopen("http://www.python.org/channews.rdf") + txt = svc.read() + svc.close() + except: + return kCannedData + f = open(newscache, 'w') + f.write(txt) + f.close() + + if len(txt) == 0: + return [] + + news = ParseRDFNews(txt) + + return news[:count] + +#----------------------------------------------------------------------- +def PrintAsText(news): + """Print Python news in plain text format""" + + for date, event, url in news: + print("%s -- %s (%s)" % (date, event, url)) + +#----------------------------------------------------------------------- +def PrintAsHTML(news): + """Print Python news in simple HTML format""" + + for date, event, url in news: + # NOTE: The line below contains a deliberate typo + print('

%s %s

' % (data, url, event)) + + +########################################################################## +# Enter code according to the tutorial here: + +news = ReadPythonNews(GetItemCount()) +PrintAsText(news) +PromptToContinue() +PrintAsHTML(news) diff --git a/Wing/wing/example2.py b/Wing/wing/example2.py new file mode 100644 index 0000000..520ed98 --- /dev/null +++ b/Wing/wing/example2.py @@ -0,0 +1,25 @@ +# This is example code for use with the Wing IDE tutorial, which +# is accessible from the Help menu of the IDE + +import os + +print("Starting up...") +print("This code runs outside of the debugger") + +# A breakpoint on the following line will not be reached +x = "test value" + +print("Now starting the debugger and attaching to the IDE") +import wingdbstub + +if 'WINGDB_ACTIVE' in os.environ: + print("Success starting debug") +else: + print("Failed to start debug... continuing without debug") + +# Set a breakpoint on the following line; it should be reached +# if debugging started successfully +print x + +print("Done") + diff --git a/Wing/wing/newscache.rdf b/Wing/wing/newscache.rdf new file mode 100644 index 0000000..d524bee --- /dev/null +++ b/Wing/wing/newscache.rdf @@ -0,0 +1,295 @@ + + + + + Python News + http://www.python.org/ + Python-related news and announcements. + Python is an interpreted, interactive, object-oriented + programming language. + + + Python logo + http://www.python.org/images/python-logo.gif + http://www.python.org/ + + + + + http://www.python.org/news/index.html#Sat3August20132300-0800 + Python 3.4.0 alpha 1 has been released + + + + + +

The first alpha for Python 3.4, Python 3.4.0a1, has been released.

]]>
+ Sat, 3 August 2013, 23:00 -0800 +
+ + http://www.python.org/news/index.html#Thu13June20131143-0400 + EuroPython 2014/2015 Conference Team Call for Site Proposals + + + + + +

The EuroPython Society announced the Call for Proposals for EuroPython, to collect proposals from teams volunteering to organize the EuroPython conference in 2014-2015.

]]>
+ Thu, 13 June 2013, 11:43 -0400 +
+ + http://www.python.org/news/index.html#Wed15May201322300100 + Python 3.2.5 and 3.3.2 have been released + + + + + +

Python 3.2.5 and Python 3.3.2 regression fix releases have been released.

]]>
+ Wed, 15 May 2013, 22:30 +0100 +
+ + http://www.python.org/news/index.html#Wed15May20131100-0600 + Python 2.7.5 released + + + + + +

Python 2.7.5 has been released.

]]>
+ Wed, 15 May 2013, 11:00 -0600 +
+ + http://www.python.org/news/index.html#Fri10May20131615-0400 + PyOhio 2013 Call for Proposals due June 1st + + + + + +

PyOhio (July 27-28, 2013, Columbus, OH, USA) has released its Call for Proposals, due June 1. See http://pyohio.org/call-for-proposals/.

]]>
+ Fri, 10 May 2013, 16:15 -0400 +
+ + http://www.python.org/news/index.html#Sat6April201322300200 + Python 3.2.4 and 3.3.1 have been released + + + + + +

Python 3.2.4 and Python 3.3.1 final have been released.

]]>
+ Sat, 6 April 2013, 22:30 +0200 +
+ + http://www.python.org/news/index.html#Sat6April20131100-0500 + Python 2.7.4 released + + + + + +

Python 2.7.4 has been released.

]]>
+ Sat, 6 April 2013, 11:00 -0500 +
+ + http://www.python.org/news/index.html#Mon25March201311000200 + Release candidates for Python 3.2.4 and 3.3.1 have been released + + + + + +

Release candidates for Python 3.2.4 and Python 3.3.1 have been released. Please test!

]]>
+ Mon, 25 March 2013, 11:00 +0200 +
+ + http://www.python.org/news/index.html#Sat23March20131100-0500 + A release candidate for Python 2.7.4 has been released + + + + + +

A Release candidate for Python 2.7.4 has been released. Please test!

]]>
+ Sat, 23 March 2013, 11:00 -0500 +
+ + http://www.python.org/news/index.html#Fri22March201321000200 + Python trademark dispute resolved! + + + + + +

The Python Software Foundation thanks the community for their support during our European Union trademark dispute. We have settled the dispute and have now obtained the trademark within the EU, and our opponent has ceased using the Python name for their services. More details are available on the PSF blog.

]]>
+ Fri, 22 March 2013, 21:00 +0200 +
+ + http://www.python.org/news/index.html#Thu14February201321000200 + Python trademark at risk in Europe: We need your help! + + + + + +

The Python Software Foundation is requesting the assistance of anyone in the community who works in a company that has a presence in an EU Community Member State. The Python trademark is at risk, more details are available on the PSF blog. Thanks.

]]>
+ Thu, 14 February 2013, 21:00 +0200 +
+ + http://www.python.org/news/index.html#Fri25January201321000200 + Python wiki server online again after recovery + + + + + +

The Python wiki server is back online, after an attack on January 5 2013. All passwords were reset, so you will have to use the password recovery function to get a new password. Please see the wiki attack description page for more details.

]]>
+ Fri, 25 January 2013, 21:00 +0200 +
+ + http://www.python.org/news/index.html#Mon7January201320000200 + http://wiki.python.org taken down for recovery + + + + + +

The Python wiki server has been taken offline for recovery operations. Please see the PSF blog for details and updates.

]]>
+ Mon, 7 January 2013, 20:00 +0200 +
+ + http://www.python.org/news/index.html#Sat29September201218000200 + Python 3.3.0 released + + + + + +

Python 3.3.0 has been released.

]]>
+ Sat, 29 September 2012, 18:00 +0200 +
+ + http://www.python.org/news/index.html#Mon24Sep201208000200 + Third rc for Python 3.3.0 released + + + + + +

The third release candidate for Python 3.3.0 has been released for testing.

]]>
+ Mon, 24 Sep 2012, 08:00 +0200 +
+ + http://www.python.org/news/index.html#Fri14Sep201200450100 + Python Software Foundation announces Distinguished Service Award + + + + + +

The Python Software Foundation announces the creation of the Distinguished Service Award, with the inaugural honor given posthumously to John Hunter.

]]>
+ Fri, 14 Sep 2012, 00:45 +0100 +
+ + http://www.python.org/news/index.html#Tue11Sep201200450100 + ConFoo conference in Canada, February 25th - March 13th + + + + + +

ConFoo is a multi technology conference dedicated to web development in Canada which takes place on February 27th - March 1st 2013. We're looking for Python talks and our call for papers is open.

]]>
+ Tue, 11 Sep 2012, 00:45 +0100 +
+ + http://www.python.org/news/index.html#Sun9Sep201212000200 + Second rc for Python 3.3.0 released + + + + + +

The second release candidate for Python 3.3.0 has been released for testing.

]]>
+ Sun, 9 Sep 2012, 12:00 +0200 +
+ + http://www.python.org/news/index.html#Sat24Aug201222000200 + First rc for Python 3.3.0 released + + + + + +

The first release candidate for Python 3.3.0 has been released for testing.

]]>
+ Sat, 24 Aug 2012, 22:00 +0200 +
+ + http://www.python.org/news/index.html#Sun12Aug20121045-0500 + Fifth annual pyArkansas conference to be held + + + + + +

The fifth annual pyArkansas conference will be held on October 27, 2012 in Conway, Arkansas. See http://www.pyarkansas.org/ for more details.

]]>
+ Sun, 12 Aug 2012, 10:45 -0500 +
+ + http://www.python.org/news/index.html#Sun12Aug201217000200 + Second beta for Python 3.3.0 released + + + + + +

The second beta release for Python 3.3.0 has been released for testing.

]]>
+ Sun, 12 Aug 2012, 17:00 +0200 +
+ + http://www.python.org/news/index.html#Wed27Jun201208000200 + First beta for Python 3.3.0 released + + + + + +

The first beta release for Python 3.3.0 has been released for testing.

]]>
+ Wed, 27 Jun 2012, 08:00 +0200 +
+ + http://www.python.org/news/index.html#Thu31May201222000200 + Fourth alpha for Python 3.3.0 released + + + + + +

The fourth alpha release for Python 3.3.0 has been released for testing.

]]>
+ Thu, 31 May 2012, 22:00 +0200 +
+ + http://www.python.org/news/index.html#Thu31May201210000200 + Request for Proposal: Redesign python.org + + + + + +

The PSF has published an RFP for redesigning python.org. We look forward to seeing what the community can produce.

]]>
+ Thu, 31 May 2012, 10:00 +0200 +
+ + http://www.python.org/news/index.html#Fri12May201214510000 + PyGotham Deadline for Proposals + + + + + +

The deadline for proposals for PyGotham II, June 8-9 is May 30. Please submit proposals to the pygotham.org site.

]]>
+ Fri, 12 May 2012, 14:51 +0000 +
+ +
+
diff --git a/Wing/wing/subdir/path_example.py b/Wing/wing/subdir/path_example.py new file mode 100644 index 0000000..3a0dbbf --- /dev/null +++ b/Wing/wing/subdir/path_example.py @@ -0,0 +1,66 @@ +# This file is located in a sub-directory to illustrate setting +# PYTHONPATH in the tutorial, which is accessible from the +# Help menu in Wing IDE + +import xml.sax +import xml.sax.handler + +#----------------------------------------------------------------------- +class CHandler(xml.sax.handler.ContentHandler): + """Parser handler class for parsing news from python.org""" + + def __init__(self): + xml.sax.handler.ContentHandler.__init__(self) + self.fItems = [] + self.__fCurItem = None + self.__fText = [] + + def startElement(self, name, attrs): + if name == 'item': + self.__fCurItem = {} + self.__fText = [] + + def endElement(self, name): + if name == 'item': + self.fItems.append(self.__fCurItem) + self.__fCurItem = None + elif self.__fCurItem is not None: + self.__fCurItem[name] = ''.join(self.__fText).strip() + + def characters(self, content): + self.__fText.append(content) + +#----------------------------------------------------------------------- +def ParseRDFNews(txt): + """Utility to parse XML from the python.org RDF news feed""" + + news = [] + + try: + try: + h = CHandler() + p = xml.sax.parseString(txt, h) + except: + # This is a common formatting error + txt = txt.replace('&', '&') + h = CHandler() + p = xml.sax.parseString(txt, h) + except: + return kCannedData + + for item in h.fItems: + d = ' '.join(item['pubDate'].split()[:4]) + news.append([d, item['title'], item['guid']]) + + return news + +#----------------------------------------------------------------------- +# Canned data to use when no internet connection is available +kCannedData = [ + ['June 13, 2013 11:43 AM', 'EuroPython 2014/2015 Conference Team Call for Site Proposals', 'http://www.python.org/news/index.html#Thu13June20131143-0400'], + ['May 15, 2013 5:30 PM', 'Python 3.2.5 and Python 3.3.2 have been released', 'http://www.python.org/news/index.html#Wed15May201322300100'], + ['May 15, 2013 1:00 PM', 'Python 2.7.5 released', 'http://www.python.org/news/index.html#Wed15May20131100-0600'], + ['May 10, 2013 4:15 PM', 'PyOhio 2013 Call for Proposals due June 1st', 'http://www.python.org/news/index.html#Fri10May20131615-0400'], + ['April 6, 2013 4:30 PM', 'Python 3.2.4 and 3.3.1 have been released', 'http://www.python.org/news/index.html#Sat6April201322300200'], +] + diff --git a/Wing/wing/subdir/path_example.pyc b/Wing/wing/subdir/path_example.pyc new file mode 100644 index 0000000..82ac367 Binary files /dev/null and b/Wing/wing/subdir/path_example.pyc differ diff --git a/Wing/wing/test_example1.py b/Wing/wing/test_example1.py new file mode 100644 index 0000000..94c7c92 --- /dev/null +++ b/Wing/wing/test_example1.py @@ -0,0 +1,46 @@ +# This is example code for use with the Wing IDE tutorial, which +# is accessible from the Help menu of the IDE + +import time +import unittest +import example1 + + +class CWindowTests(unittest.TestCase): + """ Tests for example1.py. """ + + #----------------------------------------------------------------------- + def setUp(self): + + self.start_time = time.time() + + #----------------------------------------------------------------------- + def tearDown(self): + + print("Time elapsed:", time.time()-self.start_time) + + #----------------------------------------------------------------------- + def testGetItemCount(self): + + assert example1.GetItemCount() == 5 + + #---------------------------------------------------------------------- + def testReadPythonNews(self): + + + news = example1.ReadPythonNews(5) + assert len(news) == 5 + + for news_item in news: + assert len(news_item) == 3 and isinstance(news_item, list) + for field in news_item: + assert field is not None + + #---------------------------------------------------------------------- + def testFailure(self): + + print("testing") + print("about to fail") + + assert 0, "Mock test failure" + \ No newline at end of file diff --git a/Wing/wing/tutorial.wpr b/Wing/wing/tutorial.wpr new file mode 100644 index 0000000..b532839 --- /dev/null +++ b/Wing/wing/tutorial.wpr @@ -0,0 +1,13 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +proj.directory-list = [{'dirloc': loc('.'), + 'excludes': (), + 'filter': u'*', + 'include_hidden': False, + 'recursive': True, + 'watch_for_changes': True}] +proj.file-type = 'shared' diff --git a/Wing/wing/tutorial.wpu b/Wing/wing/tutorial.wpu new file mode 100644 index 0000000..bdd838a --- /dev/null +++ b/Wing/wing/tutorial.wpu @@ -0,0 +1,1377 @@ +#!wing +#!version=5.0 +################################################################## +# Wing IDE project file : User-specific branch # +################################################################## +[user attributes] +debug.breakpoints = {loc('example1.py'): {36: (0, + None, + 1, + 0)}, + loc('example2.py'): {10: (0, + None, + 1, + 0), + 22: (0, + None, + 1, + 0)}} +debug.err-values = {None: {}, + loc('example1.py'): {}} +debug.show-args-dialog = {loc('example1.py'): False} +gui.perspective-auto = True +gui.perspective-current = 'debug' +gui.perspectives = (1, + {'debug': ({}, + {'windows': [{'name': 'ZFo90pOQwVnZz3zMm1X6r9'\ + 'hbLFgtg2t1', + 'size-state': '', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': 'perspective_tools_only', + 'current_pages': [0, + 0], + 'full-screen': True, + 'notebook_display': 'normal', + 'notebook_percent': 0.25, + 'override_title': None, + 'pagelist': [('project', + 'tall', + 0, + {}), + ('browser', + 'tall', + 0, + {}), + ('snippets', + 'tall', + 0, + {}), + ('source-assistant', + 'tall', + 2, + {}), + ('debug-stack', + 'tall', + 1, + {})], + 'primary_view_state': {'area': 'wide', + 'constraint': 'perspective_tools_only', + 'current_pages': [2, + 4], + 'notebook_display': 'normal', + 'notebook_percent': 0.30000000000000004, + 'override_title': None, + 'pagelist': [('batch-search', + 'wide', + 0, + {}), + ('interactive-search', + 'wide', + 0, + {}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {}), + ('debug-data', + 'wide', + 0, + {}), + ('debug-breakpoints', + 'wide', + 0, + {}), + ('python-shell', + 'wide', + 2, + {'launch-id': None}), + ('bookmarks', + 'wide', + 1, + {}), + ('debug-io', + 'wide', + 1, + {}), + ('debug-probe', + 'wide', + 2, + {'launch-id': None}), + ('debug-watch', + 'wide', + 1, + {}), + ('debug-modules', + 'wide', + 1, + {})], + 'primary_view_state': {'editor_states': ('horizontal', + 0.5, + ({'locked': False}, + [loc('example1.py'), + loc('example2.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('wingdbstub.py')]), + ({'locked': False}, + [loc('example1.py'), + loc('example2.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('wingdbstub.py')]))}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.5}, + 'splits': 2}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.47700394218134035}, + 'splits': 2}, + 'window-alloc': (8, + 22, + 1420, + 798)}]}), + 'edit': ({}, + {'windows': [{'name': 'ZFo90pOQwVnZz3zMm1X6r9h'\ + 'bLFgtg2t1', + 'size-state': '', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': 'perspective_tools_only', + 'current_pages': [0, + 1], + 'full-screen': True, + 'notebook_display': 'normal', + 'notebook_percent': 0.25, + 'override_title': None, + 'pagelist': [('project', + 'tall', + 0, + {}), + ('browser', + 'tall', + 0, + {}), + ('snippets', + 'tall', + 0, + {}), + ('source-assistant', + 'tall', + 2, + {}), + ('indent', + 'tall', + 2, + {})], + 'primary_view_state': {'area': 'wide', + 'constraint': 'perspective_tools_only', + 'current_pages': [0, + 4], + 'notebook_display': 'normal', + 'notebook_percent': 0.30000000000000004, + 'override_title': None, + 'pagelist': [('batch-search', + 'wide', + 0, + {}), + ('interactive-search', + 'wide', + 0, + {}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {}), + ('python-shell', + 'wide', + 2, + {'launch-id': None}), + ('bookmarks', + 'wide', + 1, + {}), + ('messages', + 'wide', + 2, + {}), + ('os-command', + 'wide', + 1, + {}), + ('debug-watch', + 'wide', + 1, + {})], + 'primary_view_state': {'editor_states': ('horizontal', + 0.5, + ({'locked': False}, + [loc('example1.py'), + loc('example2.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('wingdbstub.py')]), + ({'locked': False}, + [loc('example1.py'), + loc('example2.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('wingdbstub.py')]))}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.5}, + 'splits': 2}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.47700394218134035}, + 'splits': 2}, + 'window-alloc': (8, + 22, + 1420, + 798)}]}), + 'user': ({}, + {'windows': [{'name': 'ZFo90pOQwVnZz3zMm1X6r9h'\ + 'bLFgtg2t1', + 'size-state': '', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': 'perspective_tools_only', + 'current_pages': [0, + 1], + 'full-screen': True, + 'notebook_display': 'normal', + 'notebook_percent': 0.25, + 'override_title': None, + 'pagelist': [('project', + 'tall', + 0, + {}), + ('browser', + 'tall', + 0, + {}), + ('snippets', + 'tall', + 0, + {}), + ('uses', + 'tall', + 0, + {}), + ('debug-stack', + 'tall', + 1, + {}), + ('source-assistant', + 'tall', + 2, + {}), + ('indent', + 'tall', + 2, + {})], + 'primary_view_state': {'area': 'wide', + 'constraint': 'perspective_tools_only', + 'current_pages': [0, + 0], + 'notebook_display': 'normal', + 'notebook_percent': 0.30000000000000004, + 'override_title': None, + 'pagelist': [('batch-search', + 'wide', + 0, + {}), + ('interactive-search', + 'wide', + 0, + {}), + ('debug-data', + 'wide', + 0, + {}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('debug-breakpoints', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {}), + ('debug-io', + 'wide', + 1, + {}), + ('debug-probe', + 'wide', + 2, + {}), + ('debug-watch', + 'wide', + 1, + {}), + ('debug-modules', + 'wide', + 1, + {}), + ('python-shell', + 'wide', + 2, + {'launch-id': None}), + ('bookmarks', + 'wide', + 1, + {}), + ('messages', + 'wide', + 2, + {}), + ('os-command', + 'wide', + 1, + {})], + 'primary_view_state': {'editor_states': ('horizontal', + 0.5, + ({'locked': False}, + [loc('example1.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('subdir/path_example.py')]), + ({'locked': False}, + [loc('example1.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('subdir/path_example.py')]))}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.5}, + 'splits': 2}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.47700394218134035}, + 'splits': 2}, + 'window-alloc': (8, + 22, + 1420, + 798)}]})}) +guimgr.overall-gui-state = {'windowing-policy': 'combined-window', + 'windows': [{'name': 'ZFo90pOQwVnZz3zMm1X6r9hbLF'\ + 'gtg2t1', + 'size-state': '', + 'type': 'dock', + 'view': {'area': 'tall', + 'constraint': None, + 'current_pages': [0, + 0], + 'full-screen': True, + 'notebook_display': 'normal', + 'notebook_percent': 0.25, + 'override_title': None, + 'pagelist': [('project', + 'tall', + 0, + {'tree-state': {'file-sort-method': 'by name', + 'list-files-first': False, + 'tree-states': {'deep': {'column-widths': None, + 'expanded-nodes': [(0,)], + 'selected-nodes': [(6,)], + 'top-node': (0,)}}, + 'tree-style': 'deep'}}), + ('browser', + 'tall', + 0, + {'all_tree_states': {u'By Module': {'column-w'\ + 'idths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': [('generic attribute', + loc('example1.py'), + '')]}}, + 'browse_mode': u'By Module', + 'follow-selection': False, + 'sort_mode': 'Alphabetically', + 'visibility_options': {u'Derived Classes': False, + u'Imported': False, + u'Modules': True}}), + ('snippets', + 'tall', + 0, + {'tree-states': {'__all__': [], + u'c': [], + u'django': [], + u'html': [], + u'py': []}}), + ('source-assistant', + 'tall', + 2, + {'docstring-during-complete': False, + 'wrap-lines': True}), + ('debug-stack', + 'tall', + 1, + {'codeline-mode': 'below'})], + 'primary_view_state': {'area': 'wide', + 'constraint': None, + 'current_pages': [2, + 4], + 'notebook_display': 'normal', + 'notebook_percent': 0.30000000000000004, + 'override_title': None, + 'pagelist': [('batch-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'Create/Edit _Filters...', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': '', + 'fReverse': False, + 'fSearchText': '', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': False}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('interactive-search', + 'wide', + 0, + {'fScope': {'fFileSetName': u'All Source Files', + 'fLocation': None, + 'fRecursive': True, + 'fType': 'project-files'}, + 'fSearchSpec': {'fEndPos': None, + 'fIncludeLinenos': True, + 'fInterpretBackslashes': False, + 'fMatchCase': False, + 'fOmitBinary': True, + 'fRegexFlags': 46, + 'fReplaceText': '', + 'fReverse': False, + 'fSearchText': '', + 'fStartPos': 0, + 'fStyle': 'text', + 'fWholeWords': False, + 'fWrap': True}, + 'fUIOptions': {'fAutoBackground': True, + 'fFilePrefix': 'short-file', + 'fFindAfterReplace': True, + 'fInSelection': False, + 'fIncremental': True, + 'fReplaceOnDisk': False, + 'fShowFirstMatch': False, + 'fShowLineno': True, + 'fShowReplaceWidgets': False}, + 'replace-entry-expanded': False, + 'search-entry-expanded': False}), + ('debug-exceptions', + 'wide', + 0, + {}), + ('testing', + 'wide', + 0, + {}), + ('debug-data', + 'wide', + 0, + {}), + ('debug-breakpoints', + 'wide', + 0, + {}), + ('python-shell', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 1, + 'folded-linenos': [], + 'history': {}, + 'launch-id': None, + 'sel-line': 12, + 'sel-line-start': 444, + 'selection_end': 444, + 'selection_start': 444}), + ('bookmarks', + 'wide', + 1, + {}), + ('debug-io', + 'wide', + 1, + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 6, + 'sel-line-start': 654, + 'selection_end': 654, + 'selection_start': 654}), + ('debug-probe', + 'wide', + 2, + {'attrib-starts': [], + 'first-line': 16, + 'folded-linenos': [], + 'history': {u'file:/Users/charleswade/Documents/Programming/Py/Wing/wing/example1.py': [''\ + 'news[0][0]\n', + "news[0][0] = '2013-06-15'\n", + 'PrintAsText(news)\n', + "def PrintAsHTML(news):\n for date, event, url in news:\n print("\ + "'

%s %s

' % (date, url, event))\n\n", + 'PrintAsHTML(news)\n', + 'testvar = test\n', + "testvar = 'test'\n"]}, + 'launch-id': None, + 'sel-line': 24, + 'sel-line-start': 1722, + 'selection_end': 1722, + 'selection_start': 1722}), + ('debug-watch', + 'wide', + 1, + {'node-states': [], + 'tree-state': {'column-widths': None, + 'expanded-nodes': [], + 'selected-nodes': [], + 'top-node': ()}}), + ('debug-modules', + 'wide', + 1, + {})], + 'primary_view_state': {'editor_states': ('horizontal', + 0.5, + ({'bookmarks': ([[loc('example1.py'), + {'attrib-starts': [('PrintAsHTML', + 69)], + 'first-line': 65, + 'folded-linenos': [], + 'sel-line': 74, + 'sel-line-start': 2116, + 'selection_end': 2116, + 'selection_start': 2116}, + 1376221522.512876], + (loc('subdir/path_example.py'), + {'attrib-starts': [('ParseRDFNews', + 33)], + 'first-line': 20, + 'folded-linenos': [], + 'sel-line': 36, + 'sel-line-start': 1102, + 'selection_end': 1108, + 'selection_start': 1104}, + 1376221568.201493), + (loc('example2.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1376221571.801987), + (loc('wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 110, + 'folded-linenos': [], + 'sel-line': 116, + 'sel-line-start': 4839, + 'selection_end': 4890, + 'selection_start': 4890}, + 1376221621.705719), + (loc('example2.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 19, + 'sel-line-start': 490, + 'selection_end': 505, + 'selection_start': 505}, + 1376221695.084647), + (loc('wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 98, + 'folded-linenos': [], + 'sel-line': 116, + 'sel-line-start': 4839, + 'selection_end': 4851, + 'selection_start': 4851}, + 1376221814.782679), + (loc('example1.py'), + {'attrib-starts': [], + 'first-line': 49, + 'folded-linenos': [], + 'sel-line': 9, + 'sel-line-start': 269, + 'selection_end': 280, + 'selection_start': 280}, + 1376221848.856767), + (loc('example1.py'), + {'attrib-starts': [('PrintAsHTML', + 69)], + 'first-line': 51, + 'folded-linenos': [], + 'sel-line': 69, + 'sel-line-start': 1956, + 'selection_end': 1971, + 'selection_start': 1960}, + 1376221885.48187), + (loc('wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 262, + 'folded-linenos': [], + 'sel-line': 116, + 'sel-line-start': 4839, + 'selection_end': 4851, + 'selection_start': 4851}, + 1376221886.151243), + (loc('example2.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 19, + 'sel-line-start': 490, + 'selection_end': 505, + 'selection_start': 505}, + 1376221892.852138), + (loc('example1.py'), + {'attrib-starts': [('PrintAsHTML', + 69)], + 'first-line': 52, + 'folded-linenos': [], + 'sel-line': 74, + 'sel-line-start': 2116, + 'selection_end': 2116, + 'selection_start': 2116}, + 1376221897.371401), + (loc('example2.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 20, + 'sel-line-start': 553, + 'selection_end': 555, + 'selection_start': 555}, + 1376221899.367135), + (loc('example1.py'), + {'attrib-starts': [('PrintAsHTML', + 69)], + 'first-line': 5, + 'folded-linenos': [], + 'sel-line': 75, + 'sel-line-start': 2187, + 'selection_end': 2188, + 'selection_start': 2188}, + 1376221966.896683), + (loc('example1.py'), + {'attrib-starts': [('ReadPythonNews', + 30)], + 'first-line': 17, + 'folded-linenos': [], + 'sel-line': 35, + 'sel-line-start': 1110, + 'selection_end': 1110, + 'selection_start': 1110}, + 1376222005.831297), + (loc('example1.py'), + {'attrib-starts': [], + 'first-line': 53, + 'folded-linenos': [], + 'sel-line': 80, + 'sel-line-start': 2314, + 'selection_end': 2314, + 'selection_start': 2314}, + 1376222010.634972), + (loc('example1.py'), + {'attrib-starts': [('ReadPythonNews', + 30)], + 'first-line': 30, + 'folded-linenos': [], + 'sel-line': 35, + 'sel-line-start': 1110, + 'selection_end': 1110, + 'selection_start': 1110}, + 1376222013.741925), + (loc('example1.py'), + {'attrib-starts': [], + 'first-line': 53, + 'folded-linenos': [], + 'sel-line': 80, + 'sel-line-start': 2314, + 'selection_end': 2314, + 'selection_start': 2314}, + 1376222014.335348), + (loc('example1.py'), + {'attrib-starts': [('ReadPythonNews', + 30)], + 'first-line': 30, + 'folded-linenos': [], + 'sel-line': 35, + 'sel-line-start': 1110, + 'selection_end': 1110, + 'selection_start': 1110}, + 1376222015.812135), + (loc('example1.py'), + {'attrib-starts': [('PrintAsHTML', + 69)], + 'first-line': 47, + 'folded-linenos': [], + 'sel-line': 74, + 'sel-line-start': 2116, + 'selection_end': 2116, + 'selection_start': 2116}, + 1376222035.135109), + [loc('example1.py'), + {'attrib-starts': [('PrintAsHTML', + 69)], + 'first-line': 47, + 'folded-linenos': [], + 'sel-line': 74, + 'sel-line-start': 2116, + 'selection_end': 2116, + 'selection_start': 2116}, + 1376222038.687039]], + 19), + 'current-loc': loc('example1.py'), + 'editor-state-list': [(loc('example1.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 14, + 'sel-line-start': 405, + 'selection_end': 448, + 'selection_start': 448}), + (loc('example2.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 20, + 'sel-line-start': 553, + 'selection_end': 555, + 'selection_start': 555}), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': -1, + 'viewer-state': {'history': [-1], + 'history-pos': 0, + 'index': -1, + 'top': 0}}), + (loc('wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 262, + 'folded-linenos': [], + 'sel-line': 116, + 'sel-line-start': 4839, + 'selection_end': 4851, + 'selection_start': 4851})], + 'has-focus': False, + 'locked': False}, + [loc('example1.py'), + loc('example2.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('wingdbstub.py')]), + ({'bookmarks': ([(loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 9, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 14, + 'index': 9, + 'top': 0}}, + 1376220290.04181), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 8, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 15, + 'index': 8, + 'top': 1123}}, + 1376220290.413288), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 9, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 16, + 'index': 9, + 'top': 486}}, + 1376220506.15679), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 10, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 17, + 'index': 10, + 'top': 0}}, + 1376220989.669613), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 11, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 18, + 'index': 11, + 'top': 237}}, + 1376221027.682615), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 12, + 'viewer-state': {'history': [-1, + 0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13], + 'history-pos': 19, + 'index': 12, + 'top': 1164}}, + 1376221099.357377), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 13, + 'viewer-state': {'history': [0, + 1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14], + 'history-pos': 19, + 'index': 13, + 'top': 1014}}, + 1376221463.509194), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 14, + 'viewer-state': {'history': [1, + 2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15], + 'history-pos': 19, + 'index': 14, + 'top': 669}}, + 1376221474.365549), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 15, + 'viewer-state': {'history': [2, + 3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16], + 'history-pos': 19, + 'index': 15, + 'top': 257}}, + 1376221943.255603), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 16, + 'viewer-state': {'history': [3, + 4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17], + 'history-pos': 19, + 'index': 16, + 'top': 815}}, + 1376222052.864093), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 17, + 'viewer-state': {'history': [4, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18], + 'history-pos': 19, + 'index': 17, + 'top': 0}}, + 1376222065.306892), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 18, + 'viewer-state': {'history': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19], + 'history-pos': 19, + 'index': 18, + 'top': 751}}, + 1376222097.122038), + (loc('wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0}, + 1376222180.354958), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 18, + 'viewer-state': {'history': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19], + 'history-pos': 19, + 'index': 18, + 'top': 751}}, + 1376222182.596582), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 19, + 'viewer-state': {'history': [4, + 5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20], + 'history-pos': 19, + 'index': 19, + 'top': 0}}, + 1376222183.319316), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 20, + 'viewer-state': {'history': [5, + 6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21], + 'history-pos': 19, + 'index': 20, + 'top': 0}}, + 1376222185.716818), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 21, + 'viewer-state': {'history': [6, + 7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22], + 'history-pos': 19, + 'index': 21, + 'top': 0}}, + 1376222186.796465), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 22, + 'viewer-state': {'history': [7, + 8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23], + 'history-pos': 19, + 'index': 22, + 'top': 0}}, + 1376222187.415277), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 23, + 'viewer-state': {'history': [8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24], + 'history-pos': 19, + 'index': 23, + 'top': 0}}, + 1376222189.394995), + [loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 23, + 'viewer-state': {'history': [8, + 9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24], + 'history-pos': 19, + 'index': 23, + 'top': 0}}, + 1376222191.082565]], + 19), + 'current-loc': loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + 'editor-state-list': [(loc('example1.py'), + {}), + (loc('example2.py'), + {}), + (loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + {'displayed-index': 24, + 'viewer-state': {'history': [9, + 10, + 9, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24], + 'history-pos': 19, + 'index': 24, + 'top': 0}}), + (loc('wingdbstub.py'), + {'attrib-starts': [], + 'first-line': 0, + 'folded-linenos': [], + 'sel-line': 0, + 'sel-line-start': 0, + 'selection_end': 0, + 'selection_start': 0})], + 'has-focus': True, + 'locked': False}, + [loc('example1.py'), + loc('example2.py'), + loc('../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx'), + loc('wingdbstub.py')])), + 'open_files': [u'../../../../../../../Applications/WingIDE.app/Contents/MacOS/resources/doc/en/TOC.idx', + u'example2.py', + u'example1.py', + u'wingdbstub.py']}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.5}, + 'splits': 2, + 'tab_location': 'top', + 'user_data': {}}, + 'saved_notebook_display': 'normal', + 'split_percents': {0: 0.47700394218134035}, + 'splits': 2, + 'tab_location': 'left', + 'user_data': {}}, + 'window-alloc': (8, + 22, + 1420, + 798)}]} +guimgr.recent-documents = [loc('wingdbstub.py'), + loc('example1.py'), + loc('example2.py')] +proj.env-vars = {None: ('default', + [u''])} +proj.pypath = {None: ('custom', + u'/Users/charleswade/Documents/Programming/Py/Wing/wing/subdir')} diff --git a/Wing/wing/wingdbstub.py b/Wing/wing/wingdbstub.py new file mode 100755 index 0000000..b3c19dc --- /dev/null +++ b/Wing/wing/wingdbstub.py @@ -0,0 +1,276 @@ +######################################################################### +""" wingdbstub.py -- Debug stub for debuggifying Python programs + +Copyright (c) 1999-2001, Archaeopteryx Software, Inc. All rights reserved. + +Written by Stephan R.A. Deibel and John P. Ehresman + +Usage: +----- + +This is the file that Wing DB users copy into their python project +directory if they want to be able to debug programs that are launched +outside of the IDE (e.g., CGI scripts, in response to a browser page +load). + +To use this, edit the configuration values below to match your +Wing IDE installation and requirements of your project. + +Then, add the following line to your code: + + import wingdbstub + +Debugging will start immediately after this import statements. + +Next make sure that your IDE is running and that it's configured to accept +connections from the host the debug program will be running on. + +Now, invoking your python file should run the code within the debugger. +Note, however, that Wing will not stop in the code unless a breakpoint +is set. + +If the debug process is started before the IDE, or is not listening +at the time this module is imported then the program will run with +debugging until an attach request is seen. Attaching only works +if the .wingdebugpw file is present; see the manual for details. + +One win32, you either need to edit WINGHOME in this script or +pass in an environment variable called WINGHOME that points to +the Wing IDE installation directory. + +""" +######################################################################### + +import sys +import os +import imp + + +#------------------------------------------------------------------------ +# Default configuration values: Note that the named environment +# variables, if set, will override these settings. + +# Set this to 1 to disable all debugging; 0 to enable debugging +# (WINGDB_DISABLED environment variable) +kWingDebugDisabled = 0 + +# Host:port of the IDE within which to debug: As configured in the IDE +# with the Server Port preference +# (WINGDB_HOSTPORT environment variable) +kWingHostPort = 'localhost:50005' + +# Port on which to listen for connection requests, so that the +# IDE can (re)attach to the debug process after it has started +# This is only used when the debug process is not attached to +# an IDE or the IDE has dropped its connection. The configured +# port can optionally be added to the IDE's Common Attach Hosts +# preference. Note that a random port is used instead if this +# port is already in use! +# (WINGDB_ATTACHPORT environment variable) +kAttachPort = '50015' + +# Set this to a filename to log verbose information about the debugger +# internals to a file. If the file does not exist, it will be created +# as long as its enclosing directory exists and is writeable. Use +# "" or "". Note that "" may cause problems +# on win32 if the debug process is not running in a console. +# (WINGDB_LOGFILE environment variable) +kLogFile = None + +# Set to get a tremendous amount of logging from the debugger internals +# (WINGDB_LOGVERYVERBOSE) +kLogVeryVerbose = 0 + +# Set this to 1 when debugging embedded scripts in an environment that +# creates and reuses a Python instance across multiple script invocations: +# It turns off automatic detection of program quit so that the debug session +# can span multiple script invocations. When this is turned on, you may +# need to call ProgramQuit() on the debugger object to shut down the +# debugger cleanly when your application exits or discards the Python +# instance. If multiple Python instances are created in the same run, +# only the first one will be able to debug unless it terminates debug +# and the environment variable WINGDB_ACTIVE is unset before importing +# this module in the second or later Python instance. See the Wing +# IDE manual for details. +kEmbedded = 0 + +# Path to search for the debug password file and the name of the file +# to use. The password file contains the encryption type and connect +# password for all connections to the IDE and must match the wingdebugpw +# file in the profile dir used by the IDE. Any entry of '$' +# is replaced by the wing user profile directory for the user that the +# current process is running as +# (WINGDB_PWFILEPATH environment variable) +kPWFilePath = [os.path.dirname(__file__), '$'] +kPWFileName = 'wingdebugpw' + +# Whether to exit if the debugger fails to run or to connect with an IDE +# for whatever reason +kExitOnFailure = 0 + +#------------------------------------------------------------------------ +# Find Wing debugger installation location + +# Edit this to point to your Wing installation or set to None to use env WINGHOME +# On OS X this must be set to the Contents/MacOS directory within the Wing IDE +# application bundle (for example, /Applications/WingIDE5.app/Contents/MacOS) +WINGHOME = '/Applications/WingIDE.app/Contents/MacOS' + +if sys.hexversion >= 0x03000000: + def has_key(o, key): + return key in o +else: + def has_key(o, key): + return o.has_key(key) + +# Check environment: Must have WINGHOME defined if still == None +if WINGHOME == None: + if has_key(os.environ, 'WINGHOME'): + WINGHOME=os.environ['WINGHOME'] + else: + sys.stdout.write("*******************************************************************\n") + sys.stdout.write("Error: Could not find Wing installation! You must set WINGHOME or edit\n") + sys.stdout.write("wingdbstub.py where indicated to point it to the location where\n") + sys.stdout.write("Wing IDE is installed.\n") + sys.exit(1) + +# The user settings dir where per-user settings & patches are located. Will be +# set from environment if left as None +kUserSettingsDir = None +if kUserSettingsDir is None: + kUserSettingsDir = os.environ.get('WINGDB_USERSETTINGS') + +def _ImportWingdb(winghome, user_settings=None): + """ Find & import wingdb module. """ + + try: + exec_dict = {} + execfile(os.path.join(winghome, 'bin', '_patchsupport.py'), exec_dict) + find_matching = exec_dict['FindMatching'] + dir_list = find_matching('bin', winghome, user_settings) + except Exception: + dir_list = [] + dir_list.extend([os.path.join(WINGHOME, 'bin'), os.path.join(WINGHOME, 'src')]) + for path in dir_list: + try: + f, p, d = imp.find_module('wingdb', [path]) + try: + return imp.load_module('wingdb', f, p, d) + finally: + if f is not None: + f.close() + break + except ImportError: + pass + +#------------------------------------------------------------------------ +# Set debugger if it hasn't been set -- this is to handle module reloading +# In the reload case, the debugger variable will be set to something +try: + debugger +except NameError: + debugger = None + +# Start debugging if not disabled and this module has never been imported +# before +if (not kWingDebugDisabled and debugger is None + and not has_key(os.environ, 'WINGDB_DISABLED') and + not has_key(os.environ, 'WINGDB_ACTIVE')): + + exit_on_fail = 0 + + try: + # Obtain exit if fails value + exit_on_fail = os.environ.get('WINGDB_EXITONFAILURE', kExitOnFailure) + + # Obtain configuration for log file to use, if any + logfile = os.environ.get('WINGDB_LOGFILE', kLogFile) + if logfile == '-' or logfile == None or len(logfile.strip()) == 0: + logfile = None + + very_verbose_log = os.environ.get('WINGDB_LOGVERYVERBOSE', kLogVeryVerbose) + if type(very_verbose_log) == type('') and very_verbose_log.strip() == '': + very_verbose_log = 0 + + # Determine remote host/port where the IDE is running + hostport = os.environ.get('WINGDB_HOSTPORT', kWingHostPort) + colonpos = hostport.find(':') + host = hostport[:colonpos] + port = int(hostport[colonpos+1:]) + + # Determine port to listen on locally for attach requests + attachport = int(os.environ.get('WINGDB_ATTACHPORT', kAttachPort)) + + # Check if running embedded script + embedded = int(os.environ.get('WINGDB_EMBEDDED', kEmbedded)) + + # Obtain debug password file search path + if has_key(os.environ, 'WINGDB_PWFILEPATH'): + pwfile_path = os.environ['WINGDB_PWFILEPATH'].split(os.pathsep) + else: + pwfile_path = kPWFilePath + + # Obtain debug password file name + if has_key(os.environ, 'WINGDB_PWFILENAME'): + pwfile_name = os.environ['WINGDB_PWFILENAME'] + else: + pwfile_name = kPWFileName + + # Load wingdb.py + wingdb = _ImportWingdb(WINGHOME, kUserSettingsDir) + if wingdb == None: + sys.stdout.write("*******************************************************************\n") + sys.stdout.write("Error: Cannot find wingdb.py in $(WINGHOME)/bin or $(WINGHOME)/src\n") + sys.stdout.write("Error: Please check the WINGHOME definition in wingdbstub.py\n") + sys.exit(2) + + # Find the netserver module and create an error stream + netserver = wingdb.FindNetServerModule(WINGHOME, kUserSettingsDir) + err = wingdb.CreateErrStream(netserver, logfile, very_verbose_log) + + # Start debugging + debugger = netserver.CNetworkServer(host, port, attachport, err, + pwfile_path=pwfile_path, + pwfile_name=pwfile_name, + autoquit=not embedded) + debugger.StartDebug(stophere=0) + os.environ['WINGDB_ACTIVE'] = "1" + if debugger.ChannelClosed(): + raise ValueError('Not connected') + + except: + if exit_on_fail: + raise + else: + pass + +def Ensure(require_connection=1, require_debugger=1): + """ Ensure the debugger is started and attempt to connect to the IDE if + not already connected. Will raise a ValueError if: + + * the require_connection arg is true and the debugger is unable to connect + * the require_debugger arg is true and the debugger cannot be loaded + + If SuspendDebug() has been called through the low-level API, calling + Ensure() resets the suspend count to zero and additional calls to + ResumeDebug() will be ignored until SuspendDebug() is called again. + + """ + + if debugger is None: + if require_debugger: + raise ValueError("No debugger") + return + + resumed = debugger.ResumeDebug() + while resumed > 0: + resumed = debugger.ResumeDebug() + + if not debugger.DebugActive(): + debugger.StartDebug() + elif debugger.ChannelClosed(): + debugger.ConnectToClient() + + if require_connection and debugger.ChannelClosed(): + raise ValueError('Not connected') + \ No newline at end of file diff --git a/Wing/wing/wingdbstub.pyc b/Wing/wing/wingdbstub.pyc new file mode 100644 index 0000000..f9ae9f8 Binary files /dev/null and b/Wing/wing/wingdbstub.pyc differ diff --git a/autorun.py b/autorun.py new file mode 100644 index 0000000..6d1e4ed --- /dev/null +++ b/autorun.py @@ -0,0 +1,614 @@ +#!/usr/bin/python +""" + + Description: osxautoruns.py - Lists applications set to auto-launch either during os startup or during login + Platform: Mac OS X, Python 2.x + Copywrite: (C)2010 Joel Yonts, Malicious Streams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + See companion file, Licenses.txt, for a copy of the GNU General Public License + or a copy is available at . + +""" +__version__ = '0.6a' + +import plistlib +import sys +import os +import operator +#import config +import Foundation + +# Section: CONFIGURATION ------------------------------------------- +PLIST_EXT=".plist" +USERDIR="/Users" + +STARTUP_PLISTS=["/System/Library/LaunchDaemons", + "/Library/LaunchDaemons", + "/System/Library/LaunchAgents", + "/Library/LaunchAgents", + "/Library/StartupItems", + "/Library/Preferences/loginwindow.plist", + "~/Library/LaunchAgents", + "~/Library/Preferences/loginitems.plist", + "~/Library/Preferences/loginwindow.plist"] + +LOGIN_PLIST="/Library/Preferences/loginwindow.plist" + +USER_CONFIG_DIR="/private/var/db/dslocal/nodes/Default/users" +USER_DIR_BLACKLIST=["/var/empty", "/dev/null"] +USER_SHELL_BLACKLIST=["/usr/bin/false"] + +# Section: PRIMARY CLASS DEFINITION ------------------------------------------- +class AutoRuns: + + def __init__(self, verbose, user, mntPoint=""): + + + self.target_user = user + + if mntPoint == "": + self.live_response = True + print "Analysis Type: Live Analysis" + else: + self.live_response = False + print "Analysis Type: Mounted System" + + if verbose == True: + self.verbose = True + else: + self.verbose = False + + # Build Comphrensive List of Startup Plist Paths + self.startup_plists =STARTUP_PLISTS + + # Retrieve Full User if using SUDO perms, otherwise just get current user + self.user_list=self._getUserList(mntPoint) + + self._expandUserPaths(mntPoint) + self._expandPlistDirs(mntPoint) + + self.startupItems=self._parsePlists() + + self.startup_plists.sort() + self.startupItems.sort(key=operator.itemgetter('User')) + + def _userExists(self, username): + """ Determines if a specified user exists on the system. + + @type raw_data: string + @param row_data: Username to be checked for existence + + @return: boolean + """ + userExists = False + + for user, homdir in self.user_list: + if username == user: + userExists =True + + return userExists + + def _getCurrentUser(self): + """ Return current user name and home directory + + @type raw_data: string + @param row_data: Mountpoint (if any) for OS X image. Default is / + + @return: list containing name,home directory for each valid user + """ + try: + logname = os.environ.get('LOGNAME') + homedir = os.environ.get('HOME') + return [[logname, homedir]] + except: + return [] + + def _getUserList(self, mntPoint): + """ Build a valid user list by reading configuration file(s). + Function also uses black lists to remove system accounts. + + @type raw_data: string + @param row_data: Mountpoint (if any) for OS X image. Default is / + + @return: list containing name,home directory for each valid user + """ + error_state = False + error_msgs =[] + + user_list =[] + if mntPoint!= "" and mntPoint.endswith('/')==False: + mntPoint=mntPoint+os.sep + + user_config_dir=mntPoint+USER_CONFIG_DIR + if os.access(user_config_dir, os.R_OK)==True: + for user_config in os.listdir(user_config_dir): + + user_config_plist=self._readRawPlist(user_config_dir+os.sep+user_config) + if user_config_plist != None: + #print user_config_plist + if user_config_plist.has_key("name") == True and user_config_plist.has_key("home") == True and user_config_plist.has_key("shell"): + username=user_config_plist['name'][0] + homedir=user_config_plist['home'][0] + shell=user_config_plist['shell'][0] + if USER_DIR_BLACKLIST.count(homedir)==0 and USER_SHELL_BLACKLIST.count(shell)==0: + user_list.append([username, homedir]) + else: + if self.verbose == True: + error_msgs.append("Error reading: %s%s%s" % (user_config_dir, os.sep, user_config)) + + error_state = True + + else: + if self.verbose == True: + error_msgs.append("Error accessing: "+str(user_config_dir)) + + error_state = True + + if error_state == True and self.live_response == True: + user_list =self._getCurrentUser() + if user_list[0][0] != self.target_user: + if self.verbose == True: + print "===> WARNING: Error accessing user configurations:" + for msg in error_msgs: + print "\t"+msg + else: + print "===> WARNING: Error accessing user configurations (Use -V option for details)." + + print "===> WARNING: Only global (SYSTEM Level) and current user startup items will be displayed." + print "===> WARNING: Use of elevated (SUDO) permissions may provide access to startup items for all users.\n\n" + + + + + if error_state == True and self.live_response == False: + if self.verbose == True: + print "===> WARNING: Error accessing user configurations:" + for msg in error_msgs: + print "\t"+msg + else: + print "===> WARNING: Error accessing user configurations (Use -V option for details)." + print "===> WARNING: Only global (SYSTEM Level) startup items will be displayed." + print "===> WARNING: Use of elevated (SUDO) permissions may provide access to user startup items.\n\n" + + return user_list + + # --- Section: Parse Plists and Build Startup Items List ------- + def _OLD_readPlist(self, filename): # Does not support binary plists + """ Read and Parse the specified plist. + This function is currently not used because the libraries inability to deal with all plist types (Binary Plists). + + @type raw_data: string + @param row_data: Full path to plist file + + @return: list containing plist data + """ + + plist=plistlib.readPlist(filename) + return plist + + def _readRawPlist(self, plistFile): + """ Read and Parse the specified plist file. + This function uses the Foundation class to achieve compatibility with all plist types (This does break cross-platform compatibility). + + @type raw_data: string + @param row_data: Full path to plist file + + @return: dictionary containing plist data + """ + # Use Foundation class to extract plist data (Foundation used instead of plistlib due to support for binary plists) + plistNSData = Foundation.NSData.dataWithContentsOfFile_(plistFile) + plistData, format, error = Foundation.NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(plistNSData, Foundation.NSPropertyListMutableContainersAndLeaves, None, None) + + return plistData + + def _parsePlists(self): + """ Extract relevant plist fields from the internally built list of startup plists. + This function calls _readRawPlist to load plist data. + + @type raw_data: None + @param row_data: None + + @return: dictionary containing startup items + """ + + startupItems =[] + for user, plistName in self.startup_plists: + pEntry ={} + pEntry['User']=user + pEntry['Fname']=plistName + plistData = self._readRawPlist(plistName) + #print plistData + + try: + # Begin Label String + if plistData.has_key("Label")==True: + pEntry["Label"]=plistData["Label"] + # End Label String + + # Begin Program String + progStr="" + if plistData.has_key("Program") == True: + progStr=str(plistData["Program"]) + + if plistData.has_key("ProgramArguments") == True: + # If program exists then eliminate the arg of ProgramArguments if it matches basename(Program) + for entry in plistData["ProgramArguments"]: + if os.path.basename(progStr) != entry: # Remove first program arg if already specified by "Program" + if len(progStr) > 0: + progStr=progStr+" " + + progStr=progStr+str(entry) + + # This section is designed to support the alternate formate of the loginwindow.plist format. + # This could have been handled more elegantly but would have forced a rewrite of several functions. + if plistName.endswith(LOGIN_PLIST): + if plistData.has_key("AutoLaunchedApplicationDictionary") == True: + for entry in plistData["AutoLaunchedApplicationDictionary"]: + pEntry["Program"]=entry["Path"] + pEntry["Disabled"]=False + startupItems.append(pEntry.copy()) + + pEntry=None + + else: + if len(progStr) >0: + pEntry["Program"]=progStr + # End Program String + + # Begin Disabled Status + if plistData.has_key("Disabled") == True: + pEntry["Disabled"]=plistData["Disabled"] + else: + pEntry["Disabled"]=False + # End Diabled Status + + except: + pEntry['Label'] = "ERROR" + pEntry['Disabled'] = False + pEntry['Program'] = "Error Parsing Plist(%s)" % (plistName) + + if pEntry != None: + startupItems.append(pEntry) + + return startupItems + + # --- Section: Building List of Plists ------- + def _expandPlistDirs(self, mntPoint): + """ Expand the plist entries to represent fullpaths to actual files. + Startup plist configuration entries may contain a directory where plists can be found (/Library/LaunchDaemons) not full paths. + This functions does the appropriate expansion to make the complete list. + + @type raw_data: string + @param row_data: MointPoint (Default=/) of the OS X image to analyze + + @return: None, Updates internal startupitem list + """ + expandedPaths=[] + + # Convert Plist Directories into Full Path of All Plists + for user, plistPath in self.startup_plists: + # Do not prepend only /, would result in a path starting with // + if mntPoint == "/": + mntPoint="" + + plistPath=mntPoint+plistPath + + try: + if os.path.isfile(plistPath) and plistPath.endswith(PLIST_EXT): + expandedPaths.append([user, plistPath]) + + elif os.path.isdir(plistPath): + for root, subFolders, files in os.walk(plistPath): + for file in files: + if file.endswith(PLIST_EXT): + fpath =os.path.join(root,file) + expandedPaths.append([user, fpath]) + + except IOError: + print "Error reading: "+ plistPath + sys.exit(1) + + self.startup_plists=expandedPaths + + def _expandUserPaths(self, mntPoint): + """ Expand the plist entries to represent fullpaths to actual files. + Startup plist configuration entries may use ~ short cuts to represent user directories. + This functions does the appropriate substitutions and expansion to make the complete list. + + @type raw_data: string + @param row_data: MointPoint (Default=/) of the OS X image to analyze + + @return: None, Updates internal startupitem list + """ + listOfPlists =[] + + if mntPoint == "/": + mntPoint="" + + userDirs = self.user_list + + for plistPath in self.startup_plists: + if plistPath.startswith("~") == True: + for user,homedir in userDirs: + listOfPlists.append([user, plistPath.replace("~", homedir)]) + else: + listOfPlists.append(["SYSTEM", plistPath]) + + self.startup_plists =listOfPlists + + # ---- Section: UI & Formatting ------- + def _FormatedStartupItemsText(self, targetUser): + """ Builds a text formatted list of startupitems for the specified user. + + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + + @return: List containing formatted output + """ + rtnList =[] + + currentUser ="" + for cmd in self.getStartupItemsRaw(targetUser): + if cmd.has_key('User') == True and cmd.has_key('Program') == True: + if cmd['User'] != currentUser: + rtnList.append(cmd['User']) + currentUser = cmd['User'] + + cmdStr="\t"+cmd['Program'] + if cmd['Disabled'] == True: + cmdStr=cmdStr+"(Disabled)" + else: + cmdStr="\tError Reading Plist -- Fields 'User' and/or 'Program' does not exist\nEntry:" +str(cmd) + + rtnList.append(cmdStr) + + return rtnList + + def _FormatedStartupListCSV(self, targetUser): + """ Builds a csv formatted list of startupitems for the specified user. + + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + + @return: List containing formatted output + """ + import csv + import StringIO + + rtnList=StringIO.StringIO() + startupItems = self.getStartupItemsRaw(targetUser) + + keys=startupItems[0].keys() + + csvWriter = csv.DictWriter(rtnList, keys, restval="NO VALUE", delimiter=',', skipinitialspace=True, quotechar='\"', lineterminator="", quoting=csv.QUOTE_ALL) + + csvWriter.writerow(dict((nHeader,nHeader) for nHeader in keys)) + + for entry in startupItems: + csvWriter.writerow(entry) + + return rtnList.buflist + + # ---- Section: Public Accessor Functions ----- + def getStartupPlists(self, targetUser="ALL"): + """ Return a text list of startup plists for the specified user. + This information is a subset of the information return from startupItems. + + @type raw_data: string + @param row_data: Only show startup plists related to specified user (Default is ALL) + + @return: List of text entries + """ + + rtnList =[] + + for owner, plist in self.startup_plists: + if targetUser=='ALL' or owner == 'SYSTEM' or owner == targetUser: + rtnList.append(plist) + + return rtnList + + + def getStartupCmds(self, user): + """ Return a text list of startup commands for the specified user. + This information is a subset of the information return from startupItems. + + @type raw_data: string + @param row_data: Only show startup commands related to specified user (Default is ALL) + + @return: List of text entries + """ + rtnList =[] + + for entry in self.getStartupItemsRaw(user): + if entry.has_key('Program') == True: + cmdStr=entry['Program'] + if entry.has_key('Disabled') == True and entry['Disabled'] == True: + cmdStr=cmdStr+" (Disabled)" + else: + cmdStr=str("Error Reading Plist -- Field 'Program' does not exist") + + rtnList.append(cmdStr) + + return rtnList + + def getStartupItemsRaw(self, user="ALL"): + """ Return a raw list of startupitems for the specified user. + + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + + @return: List containing dictionary entries + """ + rtnList = [] + + if user=="ALL": + return self.startupItems + else: + for entry in self.startupItems: + if entry.has_key('User') == True: + if entry['User'] == 'SYSTEM' or entry['User'] == user: + rtnList.append(entry) + else: + rtnList.append("Error Reading Plist -- Field 'User' does not exist") + + return rtnList + + def getStartupItemsText(self, targetUser="ALL"): + """ Return a text list of startupitems by called _FormatedStartupXXXX functions. + + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + + @return: List containing text entries + """ + itemList=self._FormatedStartupItemsText(targetUser) + return itemList + + def getStartupItemsCSV(self, targetUser="ALL"): + """ Return a csv list of startupitems by called _FormatedStartupXXXX functions. + + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + + @return: List containing text entries + """ + itemList =self._FormatedStartupListCSV(targetUser) + return itemList + + # --- Section: Output Methods + def outputStartupList (self, format, targetUser="ALL", filename=None): + """ Output the list of startup items. Formatting & Output destination specified. + + @type raw_data: string + @param row_data: Format to be used in generating output + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + @type raw_data: string + @param row_data: Output written to filename specified, None=stdout + + @return: None + """ + if targetUser != "ALL" and targetUser != "System": + if self._userExists(targetUser) == False: + print "User: %s does not exist or incorrect permissions to access user information!\nPlease validate username and/or use elevated privileges" % (targetUser) + sys.exit(1) + + if format == 'csv': + itemList=self.getStartupItemsCSV(targetUser) + elif format == 'raw': + itemList=self.getStartupItemsRaw(targetUser) + elif format == 'text': + itemList=self.getStartupItemsText(targetUser) + else: + print "Invalid output format specified -o "+str(format) + sys.exit(1) + + if filename == None: + for entry in itemList: + print entry + else: + try: + fOut=open(filename, "w") + for entry in itemList: + fOut.write(str(entry)+"\n") + fOut.close() + except: + print "Error: Unable to write output to file -- "+str(filename) + sys.exit(1) + + def outputList (self, listType, targetUser="ALL", filename=None): + """ Output the list of cmands or plists for a target user. Formatting & Output destination specified. + + @type raw_data: string + @param row_data: Format to be used in generating output + @type raw_data: string + @param row_data: Only show startup items related to specified user (Default is ALL) + @type raw_data: string + @param row_data: Output written to filename specified, None=stdout + + @return: None + """ + if targetUser != "ALL" and targetUser != "System": + if self._userExists(targetUser) == False: + print "User: %s does not exist or incorrect permissions to access user information!\nPlease validate username and/or use elevated privileges" % (targetUser) + sys.exit(1) + + if listType == 'plists': + itemList =self.getStartupPlists(options.user) + + elif listType == 'cmds': + itemList=self.getStartupCmds(options.user) + else: + print "Invalid List Type Specified: "+listType + sys.exit(1) + + if filename == None: + for entry in itemList: + print entry + else: + try: + fOut=open(filename, "w") + for entry in itemList: + fOut.write(entry+"\n") + fOut.close() + except: + print "Error: Unable to write output to file -- "+str(filename) + sys.exit(1) + +if __name__ == '__main__': + import optparse + + usage = "usage: %prog -o [options]" + + parser = optparse.OptionParser() + parser.set_usage(usage) + + parser.add_option("-o", "--output", dest="output", + help="Output format: csv, raw, text", metavar="", default=None) + + parser.add_option("-l", "--list", dest="list", + help="List supporting items: cmds, plists", metavar="", default=None) + + parser.add_option("-m", "--mountpoint", dest="mountpoint", + help="Mac OS X Mount Point, Default = \"/\"", metavar="", default="") + + parser.add_option("-f", "--file", dest="outfile", + help="Send output to specified file instead of stdout", metavar="", default=None) + + parser.add_option("-u", "--user", dest="user", + help="Only show startup items for specified user (default = ALL)", metavar="", default="ALL") + + parser.add_option("-V", "--verbose", action="store_true", dest="verbose", + help="Verbose Output", default=False) + + (options, args) = parser.parse_args() + + if options.mountpoint != "": + if os.path.exists(options.mountpoint) == False: + print "Error invalid mount point: " + str(options.mountpoint) + sys.exit(1) + + if options.list != None: + aruns=AutoRuns(options.verbose, options.user, options.mountpoint) + aruns.outputList(options.list, options.user, options.outfile) + + elif options.output != None: + aruns=AutoRuns(options.verbose, options.user, options.mountpoint) + aruns.outputStartupList(options.output, options.user, options.outfile) + + else: + parser.print_help() + print "" + print "Usage Error -- Must specify output type" + sys.exit(1) \ No newline at end of file diff --git a/backwards.py b/backwards.py new file mode 100644 index 0000000..6eeced5 --- /dev/null +++ b/backwards.py @@ -0,0 +1,21 @@ +# Count +# Demonstrates the range() function + +print(""" + Welcome to Chuck's backwards word program! + + You will start by entering a word, + then the program will spell it back to you + backwards! +""" +) +word = input("\nEnter a word you want spelled backwards: ") +backWord = "" + +while word: + position = len(word) - 1 + backWord += word[position] + word = word[:position] + word[(position + 1):] + +print("\nYour word spelled backwards is :", backWord) +input("\n\nPress the enter key to exit.\n") \ No newline at end of file diff --git a/behave/tutorial/tutorial.feature b/behave/tutorial/tutorial.feature new file mode 100644 index 0000000..ee570ab --- /dev/null +++ b/behave/tutorial/tutorial.feature @@ -0,0 +1,6 @@ +Feature: showing off behave + + Scenario: run a simple test + Given we have behave installed + when we implement a test + then behave will test it for us! \ No newline at end of file diff --git a/behave/tutorial/tutorial:steps/tutorial.py b/behave/tutorial/tutorial:steps/tutorial.py new file mode 100644 index 0000000..1e2341d --- /dev/null +++ b/behave/tutorial/tutorial:steps/tutorial.py @@ -0,0 +1,13 @@ +from behave import * + +@given('we have behave installed') +def step_impl(context): + pass + +@when('we implement a test') +def step_impl(context): + assert True is not False + +@then('behave will test it for us!') +def step_impl(context): + assert context.failed is False \ No newline at end of file diff --git a/charCreate.py b/charCreate.py new file mode 100644 index 0000000..7c4ce82 --- /dev/null +++ b/charCreate.py @@ -0,0 +1,239 @@ +# Character Creator + +# Write a Character Creator program for a role-playing game. The player should be given a pool of 30 points to spend +# on four attributes: Strength, Health, Wisdom, and Dexterity. The player should be able to spend points from the +# pool on any attribute and should also be able to take points from an attribute and put them back into the pool. + +# import statements + +import pickle + +# initialize variables + +choice = None +chars = {} + +# constants + +POOL = 30 + +# do the deed + +while choice != "0": + + print(""" + Character Creator + ================= + Written by Charles Wade in August of 2013 + + Please choose your option below: + + 0 - Quit + 1 - Create New Character + 2 - Edit Attributes of Existing Character + 3 - Delete Character + 4 - List All Characters and Attributes + 5 - Save All Characters + 6 - Load All Characters + 7 - Erase All Characters + """) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # create new char + elif choice == "1": + pool = POOL + name = input("What name do you want your character to have?: ") + while name == "": + name = input("What name do you want your character to have?: ") + cname = name.capitalize() + if cname not in chars: + print("\nYou have ", pool, " points to spend on Strength, Health, Wisdom, and Dexterity!") + strn = input("How much strength do you want them to have?: ") + try: + cstr = int(strn) + except ValueError: + print("\nThat is not a number, made strength 0 and moving on! You can edit after if it was a mistake.") + cstr = 0 + while cstr > pool: + print("Not enough points left in pool, you have ", pool, " points available, try again!") + cstr = int(input("How much strength do you want them to have?: ")) + else: + pool -= cstr + print("\nYou have ", pool, " points to spend!") + hlth = input("How much health do you want them to have?: ") + try: + chlth = int(hlth) + except ValueError: + print("\nThat is not a number, making health 0 and moving on! You can edit after if it was a mistake.") + chlth = 0 + while chlth > pool: + print("Not enough points left in pool, you have ", pool, " points available, try again!") + chlth = int(input("How much health do you want them to have?: ")) + else: + pool -= chlth + print("\nYou have ", pool, " points to spend!") + wis = input("How much wisdom do you want them to have?: ") + try: + cwis = int(wis) + except ValueError: + print("\nThat is not a number, making wisdom 0 and moving on! You can edit after if it was a mistake.") + cwis = 0 + while cwis > pool: + print("Not enough points left in pool, you have ", pool, " points available, try again!") + cwis = int(input("How much wisdom do you want them to have?: ")) + else: + pool -= cwis + print("\nYou have ", pool, " points to spend!") + dex = input("How much dexterity do you want them to have?: ") + try: + cdex = int(dex) + except ValueError: + print("\nThat is not a number, made dexterity 0 and moving on! You can edit after if it was a mistake" + ".") + cdex = 0 + while cdex > pool: + print("Not enough points left in pool, you have ", pool, " points available, try again!") + cdex = int(input("How much dexterity do you want them to have?: ")) + else: + pool -= cdex + cdic = {"Strength": cstr, "Health": chlth, "Wisdom": cwis, "Dexterity": cdex} + chars.update({cname: cdic}) + print("\n") + print(cname, "\n") + for key in cdic: + print(key) + print(cdic.get(key)) + else: + print("\nCharacter already exsists! Try editing it or deleting it!") + + # edit chars + elif choice == "2": + print("\nHere are all of your characters you can currently edit:\n") + for key in chars: + print(key) + print(chars.get(key), "\n") + editchar = input("Which character would you like to edit?: ") + editcharCap = editchar.capitalize() + if editcharCap in chars: + pool2 = POOL + print("\nYou have ", pool2, " points to spend on Strength, Health, Wisdom, and Dexterity!") + strn2 = input("How much strength do you want them to have?: ") + try: + cstr2 = int(strn2) + except ValueError: + print("\nThat is not a number, made strength 0 and moving on! You can edit after if it was a mistake.") + cstr2 = 0 + while cstr2 > pool2: + print("Not enough points left in pool, you have ", pool2, " points available, try again!") + cstr2 = int(input("How much strength do you want them to have?: ")) + else: + pool2 -= cstr2 + print("\nYou have ", pool2, " points to spend!") + hlth2 = input("How much health do you want them to have?: ") + try: + chlth2 = int(hlth2) + except ValueError: + print("\nThat is not a number, making health 0 and moving on! You can edit after if it was a mistake.") + chlth2 = 0 + while chlth2 > pool2: + print("Not enough points left in pool, you have ", pool2, " points available, try again!") + chlth2 = int(input("How much health do you want them to have?: ")) + else: + pool2 -= chlth2 + print("\nYou have ", pool2, " points to spend!") + wis2 = input("How much wisdom do you want them to have?: ") + try: + cwis2 = int(wis2) + except ValueError: + print("\nThat is not a number, making wisdom 0 and moving on! You can edit after if it was a mistake.") + cwis2 = 0 + while cwis2 > pool2: + print("Not enough points left in pool, you have ", pool2, " points available, try again!") + cwis2 = int(input("How much wisdom do you want them to have?: ")) + else: + pool2 -= cwis2 + print("\nYou have ", pool2, " points to spend!") + dex2 = input("How much dexterity do you want them to have?: ") + try: + cdex2 = int(dex2) + except ValueError: + print("\nThat is not a number, made dexterity 0 and moving on! You can edit after if it was a mistake" + ".") + cdex2 = 0 + while cdex2 > pool2: + print("Not enough points left in pool, you have ", pool2, " points available, try again!") + cdex2 = int(input("How much dexterity do you want them to have?: ")) + else: + pool2 -= cdex2 + cdic2 = {"Strength": cstr2, "Health": chlth2, "Wisdom": cwis2, "Dexterity": cdex2} + chars.update({editcharCap: cdic2}) + print("\n") + print(editcharCap, "\n") +# print(cdic.items()) + for key in cdic2: + print(key) + print(cdic2.get(key)) + else: + print("\nThat character doesnt exsist, try creating them!") + + # del char + elif choice == "3": + chardel = input("Which character would you like to delete?: ") + chardelCap = chardel.capitalize() + if chardelCap in chars: + areusure = input("\nAre you sure you want to delete this character? 'Y' for YES, 'N' for NO: ") + areusureCap = areusure.upper() + if areusureCap == "Y": + del chars[chardelCap] + print("\nCharacter WAS deleted!") + else: + print("\nCharacter NOT deleted!") + else: + print("\nThat character doesnt exist! Try adding it.") + + # list chars + elif choice == "4": + if chars: + for key in chars: + print(key) + print(chars.get(key)) + else: + print("\nYou have no characters! Try creating some!") + + # save chars + elif choice == "5": + pickle.dump(chars, open("save.p", "wb")) + print("\nAll your characters have been saved! Choose option 6 to reload them on next launch!") + + # load chars + elif choice == "6": + chars = pickle.load(open("save.p", "rb")) + print("\nAll the following characters have been loaded from your last save:\n") + if chars: + for key in chars: + print(key) + print(chars.get(key)) + else: + print("\nYou have no characters! Try creating some!") + + # wipe all chars + elif choice == "7": + areusure2 = input("\nAre you sure you want to erase ALL characters? 'Y' for YES, 'N' for NO: ") + areusureCap2 = areusure2.upper() + if areusureCap2 == "Y": + chars = {} + print("\nAll characters have been erased!") + else: + print("\nCharacters NOT erased!") + + # some unknown choice + else: + print("\nSorry, but", choice, "isn't a valid choice.") + +input("\n\nPress the enter key to exit.") \ No newline at end of file diff --git a/charCreate2.py b/charCreate2.py new file mode 100644 index 0000000..37e98e5 --- /dev/null +++ b/charCreate2.py @@ -0,0 +1,100 @@ +# Character Creator +# +# Write a Character Creator program for a role-playing game. The player should be given a pool of 30 points to spend +# on four attributes: Strength, Health, Wisdom, and Dexterity. The player should be able to spend points from the +# pool on any attribute and should also be able to take points from an attribute and put them back into the pool. + +# imports + +import pprint + +# initialize variables + +choice = None +strg = 0 +hlth = 0 +wis = 0 +dex = 0 +pused = 0 +chars = [] + +# constants + +POOL = 30 +ATRIB = {"Strength": strg, + "Health": hlth, + "Wisdom": wis, + "Dexterity": dex} +ATRIB2 = [("Wisdom", wis), ("Health", hlth), ("Dexterity", dex), ("Stregth", strg)] + +# do the deed + +while choice != "0": + + print( + """ + Character Creator + + 0 - Quit + 1 - Create New Character + 2 - Edit Attributes of Existing Character + 3 - Delete Character + 4 - List All Characters and Attributes + """ + ) + + choice = input("Choice: ") + print() + + # exit + if choice == "0": + print("Good-bye.") + + # create new char + elif choice == "1": + cname = input("What name do you want your char to have?: ") + if cname not in chars: + cstr = input("How much strength do you want them to have?: ") + chlth = input("How much health do you want them to have?: ") + cwis = input("How much wisdom do you want them to have?: ") + cdex = input("How much dexterity do you want them to have?: ") + cdic = [["Strength", cstr], ["Health", chlth], ["Wisdom", cwis], ["Dexterity", cdex]] + chars.append([cname, cdic]) + print(cname) + for key1, value1 in cdic: + print(" ", key1, value1) + else: + print("\nCharacter already exsists! Try editing it or deleting it!") + + # del char + elif choice == "3": + term = input("What term do you want me to redefine?: ") + if term in chars: + definition = input("What's the new definition?: ") + chars[term] = definition + print("\n", term, "has been redefined.") + else: + print("\nThat term doesn't exist! Try adding it.") + + # list chars + elif choice == "4": + # for key in chars[0]: + # print(key) + pprint.pprint(chars, width=5) + + elif choice == "5": + for key, value in ATRIB.items(): + print(key, value) + for key2, value2 in ATRIB2: + print(key2, value2) + +# [['bobby', [['Strength', '1'], ['Health', '2'], ['Wisdom', '3'], ['Dexterity', '4']]], ['stacy', [['Strength', '9'], ['Health', '8'], ['Wisdom', '7'], ['Dexterity', '6']]]] + + elif choice == "6": + print(chars) + + # some unknown choice + else: + print("\nSorry, but", choice, "isn't a valid choice.") + +input("\n\nPress the enter key to exit.") diff --git a/charCreateCHEAT.py b/charCreateCHEAT.py new file mode 100644 index 0000000..1555e03 --- /dev/null +++ b/charCreateCHEAT.py @@ -0,0 +1,66 @@ +# Character creator program + +skills = {"Strength":0, "Wisdom":0, "Health":0, "Dextarity":0} +points=int(30) +player="" + +while player != "e": + print("Welcome to your character\n") + print("You have ",points," avaliable.") + print("1. Add points.") + print("2. Remove Points") + print("3. Show current status") + print("Type exit to quit") + print("------------------") + selection = input(">") + if selection == "1": + pts2 = 0 + pts=int(input("how many points would you like to apply?")) + if pts > points: + pts = 0 + print("ha, nice try") + else: + pts2=input("To what attribute would you like to apply? 1. Strength 2. Health 3. Dextarty, or 4. Wisdom?") + if pts2 == "1": + skills["Strength"] += pts + points -= pts + if pts2 == "2": + skills["Health"] += pts + points -= pts + if pts2 == "3": + skills["Dextarity"] += pts + points -= pts + if pts2 == "4": + skills["Wisdom"] += pts + points -= pts + if selection == "2": + rmv = int(input("How many points would you like back?")) + rmv2 = input("From where? 1. Strength 2. Health 3. Dextarty, or 4. Wisdom?") + if rmv2 == "1": + if rmv < skills["Strength"]: + skills["Strength"] -= rmv + points += rmv + else: + print("no") + if rmv2 == "2": + if rmv < skills["Health"]: + skills["Health"] -= rmv + points += rmv + else: + print("no") + if rmv2 == "3": + if rmv < skills[Dextarity]: + skills["Dextarity"] -= rmv + points += rmv + else: + print("No") + if rmv2 == "4": + if rmv < skills[Wisdom]: + skills["Wisdom"] -= rmv + points += rmv + else: + print("No") + + + if selection == "3": + print (skills) \ No newline at end of file diff --git a/coinFlip.py b/coinFlip.py new file mode 100644 index 0000000..f4fedb4 --- /dev/null +++ b/coinFlip.py @@ -0,0 +1,24 @@ +# COIN FLIP +# Demonstrates the while clause + +import random + +count = 0 +while count < 100: + coin = random.randint(1, 2) + if coin == 1: + # HEADS + print("HEADS") + elif coin == 2: + # TAILS + print("TAILS") + else: + print("Illegal coin value! (You must have a fake coin).") + count += 1 + +input("\n\nPress the enter key to exit.") + + + + + diff --git a/count.py b/count.py new file mode 100644 index 0000000..00a899d --- /dev/null +++ b/count.py @@ -0,0 +1,22 @@ +#! /usr/bin/python +# Count +# Demonstrates the range() function + +print(""" + Welcome to Chuck's counting app! + + You will start by entering a number to start from, + then enter a number to finsh to, and last enter how + many you want to count by! +""" +) +start = int(input("\nYour starting number: ")) +end = int(input("\nYour ending number: ")) +howMany = int(input("\nHow many you want counted by: ")) + +print("\nCounting by", howMany, ":") +for i in range(start, end, howMany): + print(i, end=" ") + + +input("\n\nPress the enter key to exit.\n") \ No newline at end of file diff --git a/cwwordjumble.py b/cwwordjumble.py new file mode 100644 index 0000000..e4b12a8 --- /dev/null +++ b/cwwordjumble.py @@ -0,0 +1,75 @@ +# Word Jumble +# +# The computer picks a random word and then "jumbles" it +# The player has to guess the original word + +import random + +# create a sequence of words to choose from +WORDS = ("python", "jumble", "easy", "difficult", "answer", "xylophone") +HINT = ("Programming langauge this was written with?", + "What kind of game is this?", + "This game isnt?", + "This game is very?", + "What do you give to a question?", + "It is a musical instrament?") + +# pick one word randomly from the sequence +word = random.choice(WORDS) +# create a variable to use later to see if the guess is correct +correct = word + +# create a jumbled version of the word +jumble ="" +while word: + position = random.randrange(len(word)) + jumble += word[position] + word = word[:position] + word[(position + 1):] + +# start the game +print( +""" + Welcome to Word Jumble! + + Unscramble the letters to make a word. +If you guess wrong 5 times you will get a hint. +A perfect score is 1000, you lose 100 points for every guess. + +(Press the enter key at the prompt to quit.) +""" +) +print("The jumble is:", jumble) + +gCount = 0 +score = 1000 +guess = input("\nYour guess: ") +while guess != correct and guess != "": + print("Sorry, that's not it.") + gCount += 1 + guess = input("\nYour guess: ") + if gCount > 3: + if correct == "python": + print(HINT[0]) + elif correct == "jumble": + print(HINT[1]) + elif correct == "easy": + print(HINT[2]) + elif correct == "difficult": + print(HINT[3]) + elif correct == "answer": + print(HINT[4]) + elif correct == "xylophone": + print(HINT[5]) + if guess == correct: + print("\nThat's it! You guessed it!\n") + +#This is what calculates the score +score -= 100 * gCount +if gCount > 9: + score = '"YOU SUCK BAD"' + +print("\nYour score is", score, "based on your total guesses count of", gCount, "!") + +print("\nThanks for playing.") + +input("\n\nPress the enter key to exit.") diff --git a/foruneCookie.py b/foruneCookie.py new file mode 100644 index 0000000..e7b7b36 --- /dev/null +++ b/foruneCookie.py @@ -0,0 +1,39 @@ +# Fortune cookie +# Demonstrates the elif clause + +import random + +print("Give me a quater and I'll tell you your fortune!") +print("You are going to...") + +fortune = random.randint(1, 5) + +if fortune == 1: + # fortune1 + print("DIE") +elif fortune == 2: + # fortune 2 + print("turn GAY") +elif fortune == 3: + # fortune 3 + print("get ARRESTED") +elif fortune == 4: + # fortune 4 + print("commit SUICIDE") +elif fortune == 5: + # fortune 5 + print("suck a COCK") +else: + print("Illegal fortune value! (You must have a really bad fortune).") + +print("...today!") + +input("\n\nPress the enter key to exit.") + + + + + + + + diff --git a/guess_my_ number_cw.py b/guess_my_ number_cw.py new file mode 100644 index 0000000..9e01080 --- /dev/null +++ b/guess_my_ number_cw.py @@ -0,0 +1,39 @@ +# Guess My Number +# +# The computer picks a random number between 1 and 100 +# The player tries to guess it and the computer lets +# the player know if the guess is too high, too low +# or right on the money + +import random + +print("\tWelcome to 'Guess My Number'!") +print("\nI'm thinking of a number between 1 and 100.") +print("Try to guess it in as few attempts as possible.\n") + +# set the initial values +thenumber = random.randint(1, 5) +guess = int(input("Take a guess: ")) +tries = 1 + +# guessing loop +while guess != thenumber: + if guess > thenumber: + print("Lower...") + else: + print("Higher...") + + guess = int(input("Take a guess: ")) + tries += 1 + if tries >= 5: + print("YOU SUCK, cant even guess it within 5 tries :(") + break + if guess == thenumber: + print("You guessed it! The number was", thenumber) + print("And it only took you", tries, "tries!\n") + + + + + +input("\n\nPress the enter key to exit.") diff --git a/idlex-1.11.2/Changelog.txt b/idlex-1.11.2/Changelog.txt new file mode 100644 index 0000000..dde5360 --- /dev/null +++ b/idlex-1.11.2/Changelog.txt @@ -0,0 +1,354 @@ + +Changelog for IdleX + +Version 1.11.2 - 2012-11-02 + + * IPyIDLE.py + - BUGFIX: Space escaping on Windows 7 now works when running a script. + - BUGFIX: Unicode filename support for 2.x series when running a script + from the Editor with F5. + - BUGFIX: Call tip window close normally when switched back to regular shell. + - BUGFIX: Avoid shell lock-up race condition during restart. + - Added PNG support for IPython.display.Image + + * PastePyShell.py + - BUGFIX: More general ps2 prompt handling for IPython pasting. + - Workaround for issue1207589. + + * RunSelection.py + - Avoid issue16152 when determining the true range of active code with + the tokenizer. + - Workaround for issue1207589. + + * RightClickMenu.py + - Deprecated due to issue1207589. + +Version 1.11.1 - 2012-10-10 + + * IPyIDLE.py + - BUGFIX: Pressing F5 from the editor runs the code when the directory + contains spaces. The path is now space-escaped. + +Version 1.11 - 2012-10-03 + + * IPyIDLE.py + - BUGFIX: Using F5 to run a script containing "input()" no longer + locks the shell as being busy. + +Version 1.1 - 2012-10-02 + + * IPyIDLE.py + - Overhaul of runcode handler for interfacing IPython with + ScriptBinding, SubCode, and RunSelection. + - Removed need for a temporary directory for storing source code. + - BUGFIX: Running with F5 no longer causes file not found error + if kernel parameters are changed without a kernel restart. + - BUGFIX: Syntax errors in source code now return IDLE's original + error message, with highlighting of the error. + - BUGFIX: Fixed code pickling error caused by IPython's change + to the default handler for code objects. + - BUGFIX: Switching from IPython to Python shell now restores + virtual events. + + * RunSelection.py + - BUGFIX: Handle implicit line joining. + - BUGFIX: Handle try/except/finally blocks + - BUGFIX: Pressing F9 on a line with "return" no longer generates + an error. + - BUGFIX: Error message line numbers are now aligned with source. + + * SubCode.py + - Behavior change - timestamped filename no longer included in + the traceback for SubCode execution. + + * ClearWindow.py + - Undo now restores color tag information. + + * ZoomFont.py + - Consolidate rapid font change events caused by scroll wheel. + - BUGFIX: More Python3 workarounds for TCL list for font information. + + +Version 1.0 - 2012-09-11 + + * IPyIDLE.py + - BUGFIX: SubCodes now work with IPython 0.13 + - BUGFIX: PIL now works with inline plotting for Python3 (using PIL-py3k) + - BUGFIX: F5 to run now works with Python3. + + * ZoomFont.py + - BUGFIX: Python3 workaround for font information returned as TCL list + + * extensionManager.py + - Modified to work with new import machinery in Python3.3 + + * EventLoop.py + - Defaults to off + + * General + - created idlex2 and idlex3 for launching IdleX + +Version 0.9 - 2012-04-13 + + * setup.py (NEW) + - Optional use of distutils to install IdleX. + + * IPyIDLE.py + - BUGFIX: Ctrl+C on raw_input no longer breaks future raw_input calls. + + * ZoomFont.py (NEW) + - Change the font size with Ctrl+Scroll Wheel + - Menu items for changing font size. + + * EventLoop.py + - BUGFIX: IPyIDLE shell restart no longer triggers error in rare cases. + + * TabExtension.py + - BUGFIX: File->Close now closes the tab. + +Version 0.8 - 2012-03-10 + + * IPyIDLE.py (NEW) + - IPython 0.12 support in shell + - Inline figures with --pylab=inline + - ANSI Terminal Color Highlighting + - Toggle between IDLE and IPython shell + + * RightClickMenu.py (NEW) + - Adds "Cut", "Copy", "Paste", and "Select All" to right-click menu. + + * RunSelection.py + - Taggable Regions (NEW) + - Updated demos/RunSelection_demo.py + - BUGFIX: Better syntax error handling + + * ClearWindow.py + - Overhaul of "undo" capability + + * LineNumbers.py + - Cooperates with IDLE's CodeContext for text alignment. + + * SubCodeToolBar.py + - MacOSX display fixed by adjusting button widths. + - BUGFIX: Number processor now excludes isolated periods + + * CodeBrowser.py + - Includes line numbers in listing + - Right-click of "Code Browser" now shows class and defs with comments + + - Terminal.py + - No longer holds reference to history handler. (IPython support) + - BUGFIX: No longer cycles history when shell is executing + + - SearchBar.py + - BUGFIX: Removed "wrap" flag from Replace All logic. + + * Squeezer.py + - Removed right-click menu for "Squeeze current text" + + * PastePyShell.py + - Added "Paste from Shell" to right-click menu. + - Now handles sys.ps2 and IPython prompts + - BUGFIX: Paste Shell (only code) now excludes comments. + + * EventLoop.py + - Overhaul of threading logic + - BUGFIX: Tkinter shell initialization code now checks for "None" + - BUGFIX: IDLE now exits on Wine without error if Eventloop enabled. + +Version 0.7 - 2012-02-12 + + * MultiLineRun.py (NEW) + - Allows pasting of many statements into the shell for execution. + - Resolves Issue3559 + + * DocViewer.py (NEW) + - Shows doc string and help information for an object + - Under "Help" menu as "Documentation Viewer" + + * Miscellaneous + - Refactored extension loader to prevent import conflicts + - IdleX reorganized as a module in "idlexlib" + + * TabExtension.py + - Hovering over tab shifters now scrolls the tabs. + - Right-click context menu now shows all tabs directly. + - Tool-tip's right edge guaranteed to stay on screen. (long path problem) + + * Squeezer.py + - Button font now matches shell font + - Scroll-wheel buttons pass through to text widget + - Remove ANSI terminal color codes from squeezed text + + * SubCode.py + - Ctrl+C now interrupts shell from editor + - BUGFIX: Raised highlight coloring above subcode coloring + Highlighting of subcode markers now works properly + - BUGFIX: SubCode syntax error checking handles ValueError + + * SubCodeToolbar.py + - Now displays above the Code Context window + + * SearchBar.py + - Search parameters are now saved + - BUGFIX: Regular Expressions now work with incremental highlighting + - BUGFIX: Only wrap around when selected. + - BUGFIX: TAB focus cycling works on Windows and Linux + + * TabHighlight.py + - BUGFIX: tabs are now highlighted after "undo" operation. + + * PastePyShell.py + - BUGFIX: No more errors if clipboard is empty + + * IDLE2HTML.py + - Changed menu entry to "Export to HTML" + +Version 0.6 - 2011-12-19 + + * PersistentHistory.py (NEW) + - Saves and restores shell command history across sessions. + + * Miscellaneous + - IdleX refactored to start faster. + - Fixed IdleConf warnings about default configurations. + - "PyShell" references changed to "Shell" in the interface. + + * SubCode.py + - Consolidated menu items to a "SubCode" menu + - Colors of highlight region and subcode marker adapt to color scheme. + + * LineNumbers.py + - Width of line number bar only increases. + + * CodeBrowser.py + - Use highlight color scheme for nearest class/def + + * SubCodeToolbar.py + - BUGFIX: Toolbar now displays in newly opened tabs + + * TabExtension.py + - BUGFIX: no more errors when exiting IdleX + + * idlexManager.py + - BUGFIX: "name" not defined for config parser + +Version 0.5 - 2011-12-02 + + * EditWithIdleX.py (NEW) + - Helper Script for Windows + - Windows users can add 'Edit with IdleX' to the right-click menu + + * PastePyShell.py (NEW) + - Intelligently paste code from PyShell into the Editor (Issue11838) + + * TabHighlght.py (NEW) + - Colors \t in the editor to help fix tab/space issues in code. + + * LineNumbers.py + - Clicking no longer brings up Go To Line dialog + + * EventLoop.py + - BUGFIX: no more memory leak when the Event Loop is enabled + + * idlex.py + - Fixed several outstanding IDLE keyset errors with EditorWindow patching + (Issue 12387, 4765, 13071, 6739, 5707, 11437) + - Removed configuration for extension directory + - Restructured IdleX as a module with idlexManager.py + - BUGFIX: Use idlex-config-extensions.cfg for loading user settings + - BUGFIX: "Use Extension Defaults" now resets keybindings + + * SubCode.py + - Import Subcode now uses correct path for relative imports + - Workaround an IDLE bug on 3.x where ## markers did not highlight properly - Issue13495 + + * SearchBar.py + - BUGFIX: "Replace All" now works + + * Demos + - Added a TabHighlight demo. + + +Version 0.4 - 2011-11-22 + + * RunSelection.py (NEW) + - Runs highlighted code or a single line in the + editor with F9. + + * Horizontal.py (NEW) + - Provides a horizontal scroll bar for the editor. + - Toggle under "Windows" menu + + * EventLoop.py + - Added PySide and wxPython support + + * SubCode.py + - Auto-enable when keyboard commands are given for: + "Run Subcode", "Run Subcode and Proceed", + "Import Subcode", "Import Subcode and Proceed" + - BUGFIX: Import Subcode error on Python 3.x + - BUGFIX: Python 2.6 syntax error on Run Subcode + + * SearchBar.py + - BUGFIX: "Replace+Find" no longer skips next match + - BUGFIX: "Replace" sets selection and cursor to replaced text + + * idlex.py + - added About box to Help menu. + - Changed extension loading criteria. See source. + - Detect extension name collisions for import. + - BUGFIX: disable missing extensions to avoid EditorWindow.py errors + - BUGFIX: use extension directory for recent version + + * CythonScript.py + - Allow for Cython editing when Cython is not installed. + - Allow for earlier versions of Cython (without "reload_support") + - BUGFIX: Python 2.6 syntax error on Run Cython Script + + * Demos + - More GUI Demos (wx and PySide) + - Squeezer (NEW) + - SubCodeToolbar (NEW) + - RunSelection (NEW) + - cython_demo.py - fixed term-number offset + + * CodeBrowser.py + - BUGFIX: Scripts with no defs and classes no longer causes error + +Version 0.3 - 2011-11-15 + + * BUGFIX + - IDLE launching restored on Windows + - run fixIDLE.py + + * idlex.py + - saves configuration in own files in .idlerc + + * CenterDialogs.py removed + - bug in Python 3.2.2 preventing Open from working + +Version 0.2 - 2011-11-13 + + * EventLoop.py added + - a generic gui event loop driver + - replaces idleMPL.py + + * Demo script for GTK, QT4, and TK + - Includes SubCode markup for interactive demo + + * CythonScript.py + - Added .pyx to first entry of Open/Save dialogs + - minor menu changes + + * SearchBar.py + - Moved Find options to its own row + + * SubCode.py + - Bound Ctrl+F6 to restart shell from Editor + - Fixed a highlighting error when typing at subcode beginning + +Version 0.1 - 2011-11-08 + + * Initial release of IdleX + diff --git a/idlex-1.11.2/PKG-INFO b/idlex-1.11.2/PKG-INFO new file mode 100644 index 0000000..bc0bcdf --- /dev/null +++ b/idlex-1.11.2/PKG-INFO @@ -0,0 +1,19 @@ +Metadata-Version: 1.1 +Name: idlex +Version: 1.11.2 +Summary: IDLE Extensions for Python +Home-page: http://idlex.sourceforge.net +Author: Roger D. Serwy +Author-email: serwy@illinois.edu +License: NCSA License +Description: IdleX is a collection of over 20 extensions for the Python IDLE environment. +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Framework :: IDLE +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: University of Illinois/NCSA Open Source License +Classifier: License :: OSI Approved :: Python Software Foundation License +Classifier: Topic :: Text Editors :: Integrated Development Environments (IDE) +Classifier: Operating System :: OS Independent diff --git a/idlex-1.11.2/README.txt b/idlex-1.11.2/README.txt new file mode 100644 index 0000000..2ad69c5 --- /dev/null +++ b/idlex-1.11.2/README.txt @@ -0,0 +1,58 @@ + +IdleX - IDLE Extensions for Python +================================== + +Version 1.11.2 + +developer: Roger D. Serwy (RDS) +email: serwy@illinois.edu +web: http://idlex.sourceforge.net + +IdleX works with Python 2.6, 2.7, and 3.x. + + +Installing IdleX +================ + +This is optional. You may run idlex.py directly without installing. + + python setup.py install --user + + +Running IdleX +============= + +On Windows: + 1) Extract the contents of the idlex-x.x.zip file. + 2) Double-click "idlex.py" to launch IdleX. + 3) Run "scripts/EditWithIdleX.py" if you want 'Edit with IdleX' + in the right-click context menu. + +On Linux: + 1) Open a terminal. + 2) Change into the idlex directory. + 3) Run: + $ python idlex.py + + +Demos +===== + +The "demos" directory has several .py files that detail the +functionality of some of these extensions. + +Acknowledgements +================ + +Acknowledgements may be found in idlexlib/ACKS.txt + + +Adding Extensions to Standard IDLE +================================== + +If you want to include an extension in the standard IDLE, +copy the extension to "idlelib" and then modify +idlelib/config-extensions.def to include the contents +of "config_extension_def" found in the extension source code. + + diff --git a/idlex-1.11.2/demos/RunSelection_demo.py b/idlex-1.11.2/demos/RunSelection_demo.py new file mode 100644 index 0000000..06eaf40 --- /dev/null +++ b/idlex-1.11.2/demos/RunSelection_demo.py @@ -0,0 +1,37 @@ +""" + Run Selection Demo + + Run highlighted code or a single line in the editor with F9. + +""" + +### Demo 1 +print('Run a line') # Place cursor on this line and Press F9. + # The executed code highlights. + + + +### Demo 2 +for i in range(5): # Place cursor on this line and Press F9. + # A list of numbers will print. + + print(i) # Place cursor on this line and Press F9. + # The print function will still execute + + print(i**2) + +### Tag Region Demo + + +# Highlight the following lines and press Ctrl+J to Tag +# To untag a line, use Ctrl+K + +a = [1, 2, 3, 4, 5] +for i in a: + print(i) + +# Place the cursor in this region an press Ctrl+F9 to run it. + + + + diff --git a/idlex-1.11.2/demos/SubCodeToolbarDemo.py b/idlex-1.11.2/demos/SubCodeToolbarDemo.py new file mode 100644 index 0000000..d782257 --- /dev/null +++ b/idlex-1.11.2/demos/SubCodeToolbarDemo.py @@ -0,0 +1,66 @@ +""" + SubCode Toolbar Demo + + This file will show how to use the SubCodeToolbar extension in IDLE. + + Make sure that "Enable Subcode" and "Show Subcode Toolbar" + are checked under Options. + + About: + + [>] - list of subcode labels + [##] - insert a subcode marker + + [-] 1.0 [+] - add or subtract from number by cursor + [/] 1.1 [*] - divide or multiply number by cursor + + [RS] - Run SubCode + [RSP] - Run SubCode and Proceed + [RA] - Run All subcodes + +""" + +## Introduction + +# The [>] button will show a menu of all subcodes in the source code. +# Clicking one will jump to it + +# The [##] button will insert a subcode into the source near +# the cursor, like this one: + +## [subcode] + + + +## Numerical Adjustments (addition and subtraction) + +# Demo for [-] 1.0 [+] + +a = 50 # place the cursor on the number "50" + # and click [-] or [+] in the toolbar + +# You can change "1.0" to any number you want. + +print('Displaying Number: %i' % a) +for i in range(4): + print('-' * (i+2)) + + +## Numerical Adjustments (multiplication and division) + +# Demo for [/] 1.1 [*] + +b = 110.0 # place the curson on the number "110.0" + # and click [/] or [*] in the toolbar + +# You can change "1.1" to any number you want. + +print('Displaying Number: %f' % b) +for i in range(4): + print('-' * (i+2)) + + +## The Run Subcode Buttons + +# These are shortcuts to the menu items under the "Run" menu. + diff --git a/idlex-1.11.2/demos/SubCodeTutorial.py b/idlex-1.11.2/demos/SubCodeTutorial.py new file mode 100644 index 0000000..b112e08 --- /dev/null +++ b/idlex-1.11.2/demos/SubCodeTutorial.py @@ -0,0 +1,148 @@ +""" + + SubCode Tutorial File + --------------------- + + This file will show how to use the SubCode extension in IDLE. + + Make sure that "Enable Subcode" is checked under "SubCode". + + Follow the directions that are single-commented. + +""" + + +## This is a SubCode marker. A double comment denotes it. +# Place the text cursor here. +# Press CTRL+Return to run this subcode. +# The active subcode region is highlighted. +# Then press CTRL+Shift+Return to run this subcode and +# proceed to the next. + +print("Code being executed") +a = 0 + +print("Last line executed") +## This is another SubCode. +# Press CTRL+Return several times. +# You'll see the value of "a" increase. Go to the next subcode. + +print("Count: a = %i" % a) +a += 1 + +## SubCodes may be indented +# Press CTRL+Return. Then move to the indented subcode by pressing +# Ctrl+Down. + +print('Outer Subcode') +if True: + ## This is an indented SubCode + # Press CTRL+ENTER. Code will be dedented and executed + # automatically. + print("Indented Subcode") + + # The active subcode ends when encountering code at a + # shallower depth. + # Proceed to the next subcode by pressing Ctrl+Down + print("End of Indented Subcode") +print('Part of the outer subcode') + +## Uncommenting #: code +# Pressing CTRL+Return will display 0 through 4. +# Proceed to the indented subcode. + +for i in range(5): + ## Test the inner loop code + # Press Ctrl+Return + #:print('Uncommented code') + #:i = 55; # This is test value + print(i) + + # The #: at the subcode's indentaiton depth will be uncommented. + # This is useful for testing code. + +print('Done with subcode') # at depth=0 + +## Commenting out regions +# Using the "Comment Out Region" won't interfere with SubCode +# markers. Consecutive lines of "##" are treated as comments, with +# some caveats. SubCode markers may not be preceeded by unlabeled +# SubCode markers. + +## +##print (123) +##print (456) +##print (789) +## + +if True: + ## Indented SubCode + + print ("Indented") +# comments at shallow depths are ignored. This allows +# "Comment Out Region" to comment out part of a subcode +# without truncating it. +## print ("Blah") + +## Labeled SubCode markers can not have more than one space +## between ## and the label. + + print ("Still Indented") + # Press Ctrl+Return + + + +## Caveats about indented SubCodes +# The indented subcode markers indicate how much dedenting is +# needed. The active subcode spans until a line has non-whitespace +# characters shallower than the active subcode's depth. This means +# that line-continuations or multiline arguments may truncate a +# subcode too early and lead to a syntax error. To avoid this, +# indent multi-line statements appropriately. + +if 1: + ## indent depth = 4. Execution leads to a syntax error + print('depth=4') + + if 1: + ## indent depth = 8 - This subcode works + print('depth=8') + + print('code', # bad subcode truncation here for depth=4 +1) + if 1: # this line is considered to be in subcode at depth=0 + # because of "1)" + ## indent depth = 12 - This subcode works + + +# shallow comment + print('depth=12') + + print('code') # Ctrl+Return here will run the depth=0 subcode + # because of "1)" + ## indent depth = 16 + + # nothing will happen when pressing Ctrl+Return here. + +## Summary + +# SubCodes allow you to segment and execute parts of your script. +# Enjoy iterative development. + +## Other Notes +""" + +SubCode.py binds Ctrl+F6 in the editor so it restarts the shell. +The combination Ctrl+F6 and Ctrl+F5 are funcionally equivalent to F5. + +The "Import Subcode" will import (and reload) the subcode as a module. +If you Click on "Import All Subcodes", you will see all the printing +in this module. In the shell, you can type the following to see the value +of "a": + + >>> SubCodeTutorial.a + 1 + +""" + + diff --git a/idlex-1.11.2/demos/cython_demo.pyx b/idlex-1.11.2/demos/cython_demo.pyx new file mode 100644 index 0000000..2c5e482 --- /dev/null +++ b/idlex-1.11.2/demos/cython_demo.pyx @@ -0,0 +1,66 @@ +""" + +IDLE Cython Demo + +Be sure that your version of Cython has "reload_support" for pyximport. +This demo will still work, but "Import/Reload Cython Script" may fail. +Visit http://cython.org for more information. + +This demonstration shows Cython being used with IDLE. +It performs a simple benchmark on a naive implementation of +the Fibonacci sequence. + + +Pressing Ctrl+E restarts the shell and performs a + "from cython_demo import *" + +Pressing Ctrl+Shift+E effectively does + "import cython_demo" and, if needed, "reload(cython_demo)" +without restarting the shell. + +""" + + +from __future__ import division +import time + +reps = 1000 # Repetitions +term = 17 # Fibonacci Term + +cpdef int fib(int n): + if n <= 0: + return 0 + elif n == 1: + return 1 + else: + return fib(n-1) + fib(n-2) + + +tic = time.time() +for x in range(reps): + a = fib(term) +print('Fibonacci %ith term: %i' % (term, a)) +toc = time.time() + +print('Elapsed time for Cython with types: %f' % (toc - tic)) + + +import timeit +setup = """ +def fib(n): + if n <= 0: + return 0 + elif n == 1: + return 1 + else: + return fib(n-1) + fib(n-2) +""" + +ti = timeit.Timer('fib(%i)' % term, setup=setup) +pt = ti.timeit(reps) + +print('Elapsed time for Python: %f' % pt) +print('Speedup: %fx' % (pt / (toc - tic))) + + + diff --git a/idlex-1.11.2/demos/gui_demo_gtk.py b/idlex-1.11.2/demos/gui_demo_gtk.py new file mode 100644 index 0000000..36cd831 --- /dev/null +++ b/idlex-1.11.2/demos/gui_demo_gtk.py @@ -0,0 +1,33 @@ +""" + Demo for EventLoop.py - interactive GUI development + + This assumes you have PyGTK installed on your system. + + Make sure that you have enabled the Event Loop under "Shell" + and "Use GTK". + + The "GUI: ON/OFF" button in the status bar is clickable. + +""" + +## import +import pygtk +pygtk.require('2.0') +import gtk +# If you receive the following warning, this demo will still work: +# "RuntimeWarning: PyOS_InputHook is not available for interactive use of PyGTK" + +## make simple interface +def clicked(widget, data=None): + print('Click received.') + +w = gtk.Window(gtk.WINDOW_TOPLEVEL) +w.resize(300, 200) +w.set_title('IdleX GUI Event Loop Demo') +b = gtk.Button("Click Me") +b.connect("clicked", clicked, None) +w.add(b) +b.show() +w.show() + +#gtk.main() diff --git a/idlex-1.11.2/demos/gui_demo_pyside.py b/idlex-1.11.2/demos/gui_demo_pyside.py new file mode 100644 index 0000000..fe10f01 --- /dev/null +++ b/idlex-1.11.2/demos/gui_demo_pyside.py @@ -0,0 +1,31 @@ +""" + Demo for EventLoop.py - interactive GUI development + + This assumes you have PySide installed on your system. + + Make sure that you have enabled the Event Loop under "Shell" + and "Use PySide". + + The "GUI: ON/OFF" button in the status bar is clickable. + +""" + +## import once +import sys +import PySide.QtGui as Qt +app = Qt.QApplication(sys.argv) # twice may crash + +## +def clicked(): + print('Click received.') + +w = Qt.QWidget() +w.resize(300, 200) +w.setWindowTitle('IdleX GUI Event Loop Demo') + +b = Qt.QPushButton('Click Me', w) +b.clicked.connect(clicked) +w.show() + + +#app.exec_() diff --git a/idlex-1.11.2/demos/gui_demo_qt4.py b/idlex-1.11.2/demos/gui_demo_qt4.py new file mode 100644 index 0000000..6b8f873 --- /dev/null +++ b/idlex-1.11.2/demos/gui_demo_qt4.py @@ -0,0 +1,31 @@ +""" + Demo for EventLoop.py - interactive GUI development + + This assumes you have PyQt4 installed on your system. + + Make sure that you have enabled the Event Loop under "Shell" + and "Use Qt4". + + The "GUI: ON/OFF" button in the status bar is clickable. + +""" + +## import once +import sys +from PyQt4 import Qt +app = Qt.QApplication(sys.argv) # running this subcode twice may crash + +## +def clicked(): + print('Click received.') + +w = Qt.QWidget() +w.resize(300, 200) +w.setWindowTitle('IdleX GUI Event Loop Demo') + +b = Qt.QPushButton('Click Me', w) +b.clicked.connect(clicked) +w.show() + + +#app.exec_() diff --git a/idlex-1.11.2/demos/gui_demo_tk.py b/idlex-1.11.2/demos/gui_demo_tk.py new file mode 100644 index 0000000..59dd74f --- /dev/null +++ b/idlex-1.11.2/demos/gui_demo_tk.py @@ -0,0 +1,49 @@ +""" + Demo for EventLoop.py - interactive GUI development + + Make sure that you have enabled the Event Loop under "Shell" + and "Use Tkinter". + + The "GUI: ON/OFF" button in the status bar is clickable. + + Note: This file also makes use of SubCodes, but is not required. + +""" + +## import needed libraries +import sys +if sys.version < '3': + import Tkinter as tk +else: + import tkinter as tk + +root = None + +## Make a GUI + +try: + root.destroy() +except: + pass + +root = tk.Tk() +root.geometry('300x200') +root.title('Idlex GUI Event Loop Demo') + +def clicked(): + print('Click received.') + +B = tk.Button(root, text='Click me', command=clicked) +B.pack() + +# Clicking the "Click me" button will print to the shell. +# The shell is still interactive. + +## Add more widgets + +L = tk.Label(root, text='This is a label') +L.pack() + + +# Notice that calling mainloop is not needed. +#root.mainloop() diff --git a/idlex-1.11.2/demos/gui_demo_wx.py b/idlex-1.11.2/demos/gui_demo_wx.py new file mode 100644 index 0000000..39d188c --- /dev/null +++ b/idlex-1.11.2/demos/gui_demo_wx.py @@ -0,0 +1,25 @@ +""" + Demo for EventLoop.py - interactive GUI development + + This assumes you have "wx" installed on your system. + + Make sure that you have enabled the Event Loop under "Shell" + and "Use wx". + + The "GUI: ON/OFF" button in the status bar is clickable. + +""" +import wx + +app = wx.App(redirect=False) + +def clicked(ev=None): + print('Click received.') + +top = wx.Frame(None, title="IdleX GUI Event Loop Demo", size=(300,200)) +top.Show() +btn = wx.Button(top, label='Click Me') +btn.Bind(wx.EVT_BUTTON, clicked) + +#app.MainLoop() + diff --git a/idlex-1.11.2/demos/matplotlib_demo.py b/idlex-1.11.2/demos/matplotlib_demo.py new file mode 100644 index 0000000..881f9cc --- /dev/null +++ b/idlex-1.11.2/demos/matplotlib_demo.py @@ -0,0 +1,45 @@ +""" + +Demo of Matplotlib interaction +============================== + +Make sure that you have "Enable GUI Event Loop" checked +under the "Shell" menu, as well as the proper toolkit +selected. + +Run this code. You'll have an interactive figure. +Click on the figure to see feedback in the shell. + +The shell is still usable. If you are missing the ">>>" prompt, +press enter to create a new one. + +If you want, change the backend to different toolkits, and then +select the appropriate one under the "Shell" menu. + +""" + + +## code for interaction +from matplotlib import pyplot as plt +import numpy as np + +print('You are using the %s backend.' % plt.rcParams['backend']) + +plt.interactive(True) +fig = plt.figure() +ax = fig.add_subplot(111) +ax.plot(np.random.rand(10)) +plt.title('Click on the plot') + +def onclick(event): + try: + print ('button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%( + event.button, event.x, event.y, event.xdata, event.ydata)) + plt.plot(event.xdata, event.ydata, 'o') + + + except TypeError as e: + print('Click event received, but outside of plot.', e) + +cid = fig.canvas.mpl_connect('button_press_event', onclick) + diff --git a/idlex-1.11.2/demos/squeezer_demo.py b/idlex-1.11.2/demos/squeezer_demo.py new file mode 100644 index 0000000..4177235 --- /dev/null +++ b/idlex-1.11.2/demos/squeezer_demo.py @@ -0,0 +1,25 @@ +""" + Squeezer Demo + + This will generate a lot of text. With Squeezer, this text + will be captured and displayed as a box, similar to + + [ Squeezed text (about 188 lines). + Double-click to expand, middle-click to copy, right-click to preview ] + + This will prevent IDLE from locking up when displaying a lot of + text to the shell. + +""" + +## [subcode] + +import random +L = [] +for i in range(5000): + L.append(random.randint(0, 9)) + +print(L) # print a lot of text + + + diff --git a/idlex-1.11.2/demos/tabhighlight_demo.py b/idlex-1.11.2/demos/tabhighlight_demo.py new file mode 100644 index 0000000..2458a06 --- /dev/null +++ b/idlex-1.11.2/demos/tabhighlight_demo.py @@ -0,0 +1,25 @@ +""" + TabHighlight Demo + + Make sure "Highlight \t tabs" is checked in the Options menu. + + You will see tabs in this file highlighted as a checkered red rectangle. + + + TabNanny will complain if you press F5. + + +""" + +if True: + print('A tab is highlighted before this line') + # more tabs + # more tabs + + +for i in range(2): + print(i) + print(2*i) # tab/space issue on this line - clearly visible + print(3*i) + + diff --git a/idlex-1.11.2/idlex.py b/idlex-1.11.2/idlex.py new file mode 100755 index 0000000..4467ae6 --- /dev/null +++ b/idlex-1.11.2/idlex.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python + +# Launch IdleX +import sys + +def show_error(): + if sys.version < '3': + import Tkinter as tk + import tkMessageBox as messagebox + else: + import tkinter as tk + import tkinter.messagebox as messagebox + + root = tk.Tk() + root.withdraw() + messagebox.showerror(title='IdleX Error', + message=('Unable to located "idlexlib".\n' + + 'Make sure it is located in the same directory ' + + 'as "idlexlib" or run setup.py to install IdleX.\n' + + ' python setup.py install --user')) + +try: + import idlexlib +except ImportError: + show_error() + sys.exit(-1) + +from idlexlib.idlexMain import main +main() + diff --git a/idlex-1.11.2/idlexlib/ACKS.txt b/idlex-1.11.2/idlexlib/ACKS.txt new file mode 100644 index 0000000..6b932b9 --- /dev/null +++ b/idlex-1.11.2/idlexlib/ACKS.txt @@ -0,0 +1,9 @@ + +This file acknowledges those who have helped with solving IdleX issues. + +Monte Milanuk - identifed a bug with TabExtension.py (December 2011) +John Withers - diagnosed a bug with EventLoop.py (November 2011) +Erhan Kudeki - MacOSX toolbar fixes (February 2012) +Ken Howard - MacOSX "Options" menu issue (March 2012) +Konsta Tiihonen - TabExtension File->Close bug (April 2012) + diff --git a/idlex-1.11.2/idlexlib/__init__.py b/idlex-1.11.2/idlexlib/__init__.py new file mode 100644 index 0000000..d4d9c55 --- /dev/null +++ b/idlex-1.11.2/idlexlib/__init__.py @@ -0,0 +1,11 @@ + +import sys + +try: + import idlelib +except ImportError: + print("** IdleX can't import IDLE. Please install IDLE. **") + sys.exit(1) + +from .idlexMain import version as __version__ +from .extensionManager import extensionManager diff --git a/idlex-1.11.2/idlexlib/__pycache__/__init__.cpython-33.pyc b/idlex-1.11.2/idlexlib/__pycache__/__init__.cpython-33.pyc new file mode 100644 index 0000000..feb3566 Binary files /dev/null and b/idlex-1.11.2/idlexlib/__pycache__/__init__.cpython-33.pyc differ diff --git a/idlex-1.11.2/idlexlib/__pycache__/extensionManager.cpython-33.pyc b/idlex-1.11.2/idlexlib/__pycache__/extensionManager.cpython-33.pyc new file mode 100644 index 0000000..d13723c Binary files /dev/null and b/idlex-1.11.2/idlexlib/__pycache__/extensionManager.cpython-33.pyc differ diff --git a/idlex-1.11.2/idlexlib/__pycache__/idlexMain.cpython-33.pyc b/idlex-1.11.2/idlexlib/__pycache__/idlexMain.cpython-33.pyc new file mode 100644 index 0000000..a94f3ff Binary files /dev/null and b/idlex-1.11.2/idlexlib/__pycache__/idlexMain.cpython-33.pyc differ diff --git a/idlex-1.11.2/idlexlib/extensionManager.py b/idlex-1.11.2/idlexlib/extensionManager.py new file mode 100644 index 0000000..5c077f0 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensionManager.py @@ -0,0 +1,269 @@ +## """ +## Copyright(C) 2011-2012 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## + + +import sys + +if sys.version < '3': + from StringIO import StringIO + from Tkinter import * + import tkFileDialog + import tkMessageBox +else: + from io import StringIO + from tkinter import * + import tkinter.filedialog as tkFileDialog + import tkinter.messagebox as tkMessageBox + + + + + +import imp +try: + import importlib + HAS_IMPORTLIB = True +except ImportError: + HAS_IMPORTLIB = False + +from idlelib.configHandler import idleConf, IdleConfParser +import os + +def make_config_parser(cfg): + """ Stuff Configration String into a fake file and return an IDLE config parser """ + fp = StringIO() + fp.write(cfg) + fp.write('\n') + fp.seek(0) + + # parse the configuration from the fake file + confparse = IdleConfParser('') + try: + confparse.readfp(fp) + except BaseException as e: + print('\n Configuration Parse Error', e) + return None + return confparse + + +class ExtensionManager(object): + """ Manages extensions for IdleX + + """ + def __init__(self, path): + + head,tail = os.path.split(path) + self.extension_dir = head + + self.IDLEX_EXTENSIONS = self.get_idlex_extensions(head) + + IDLE_EXTENSIONS = [] # A list of default extensions in IDLE - those that come with the standard distribution + for i in idleConf.defaultCfg['extensions'].sections(): + if i.endswith('_cfgBindings') or i.endswith('_bindings'): + continue + IDLE_EXTENSIONS.append(i) + + self.IDLE_EXTENSIONS = IDLE_EXTENSIONS + + def get_idlex_extensions(self, directory): + """ Get a list of user extensions from 'directory' """ + contents = os.listdir(directory) + contents.sort() + + contents = [x for x in contents if not x.startswith('_')] + + user_extensions = [] + for i in contents: + fullpath = os.path.join(directory, i) + if fullpath.endswith('.py') \ + and os.path.isfile(fullpath): + try: + txt = open(fullpath, 'r').read(1000) + except IOError: + print(' IOError while loading extension: %r' % fullpath) + + if '# IDLEX EXTENSION' in txt: + name = i[:-3] # truncate .py + user_extensions.append(name) + else: + print(' Not an IdleX extension: %r' % fullpath) + + return user_extensions + + def load_extension(self, name): + """ Imports an extension by name and returns a reference to the module. + Invalid modules return None. + """ + fullname = 'extensions.%s' % name + try: + if HAS_IMPORTLIB: + mod = importlib.import_module('.' + fullname, package=__package__) + else: + mod = __import__(fullname, globals(), locals(), [''], 1) + except Exception as err: + import traceback + traceback.print_exc() + mod = None + return mod + + + def find_extension(self, name): + """ Locates an extension """ + path = self.extension_dir + info = imp.find_module(name, [path]) + + + def load_extension_cfg(self, extName): + """ Load the extension. get its default config string + from the "config_extension_def" variable.""" + mod = self.load_extension(extName) + if mod is None: + print("could not load %s" % extName) + return + + + if hasattr(mod, "config_extension_def"): + return mod.config_extension_def + else: + print("\n Missing 'config_extension_def' in %s. Not loading." % extName) + return None + + def copy_options(self, name, cfgdict, confparse, blank=False): + d = cfgdict["extensions"] + optionlist = confparse.GetOptionList(name) + for option in optionlist: + try: + value = confparse.get(name, option, raw=True) + except BaseException as e: + print(' Error during extension settings copy:\n', e) + return False + if not d.has_section(name): + d.add_section(name) + if not blank: + d.set(name, option, value) + else: + d.set(name, option, '') + return True + + + + + def transfer_cfg(self, extName, confparse, keys=True): + """ Transfer the configuration from the extension + into IDLE's configuration. Returns True if successful. """ + + + if confparse is None: + return False + + # copy the user extension configuration in IDLE + retval = self.copy_options(extName, idleConf.userCfg, confparse) + + if 0: # DEVELOPERS - this takes a long time to process + # Report Any keybinding conflicts the user extension may have + keyset = idleConf.GetCurrentKeySet() + name_cfg = extName+'_cfgBindings' + optionlist = confparse.GetOptionList(name_cfg) + for option in optionlist: + b = '<<%s>>' % option + value = confparse.get(name_cfg, option) + if value == '': continue # WORKAROUND: skip clear window binding + for event, binding in list(keyset.items()): + if value in binding and event != b and value: + print('\n Warning: [%s] has an event binding conflict with' % name_cfg) + print(' ', event, value) + + # idleConf.GetExtensionBindings pulls only from the default configuration. + # Must transfer bindings to defaultCfg dictionary instead. + if keys: + self.copy_options(extName+'_cfgBindings', idleConf.defaultCfg, + confparse) + + return retval + + + def load_idlex_extensions(self, userExt=None): + """ Load extensions. Returns number of extensions loaded. """ + + if userExt is None: + userExt = self.IDLEX_EXTENSIONS + + # get already-saved settings + d = idleConf.GetUserCfgDir() + usercfgfile = os.path.join(d, 'idlex-config-extensions.cfg') + if os.path.isfile(usercfgfile): + U = open(usercfgfile).read() + else: + U = '' + + count = 0 + userConfParser = make_config_parser(U) + + key_isdefault = idleConf.GetOption('main','Keys','default', type="bool") + for extName in userExt: + # get the default configuration for the individual extension + cfg = self.load_extension_cfg(extName) + if cfg is None: + continue + + # shove the conf string into a ConfigParse object + extConfParser = make_config_parser(cfg) + if extConfParser is None: + print('\n Unable to parse configuration for %s' % extName) + continue + + # transfer the configuration to IDLE + if not self.transfer_cfg(extName, extConfParser, keys=True): + print('\n Unable to transfer configuration for %s' % extName) + continue + + + count += 1 + # transfer already-saved settings, otherwise IDLE forgets them + # when idleConf.SaveUserCfgFiles is called from within IDLE. Bug? + self.transfer_cfg(extName, userConfParser, + keys=not key_isdefault) # Overwrite defaults with user config + + + idleConf.SaveUserCfgFiles() + return count + +try: + from . import extensions +except (ImportError, ValueError) as err: + import extensions + +path = extensions.__file__ + +extensionManager = ExtensionManager(path) diff --git a/idlex-1.11.2/idlexlib/extensions/ClearWindow.py b/idlex-1.11.2/idlexlib/extensions/ClearWindow.py new file mode 100644 index 0000000..29aa65c --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/ClearWindow.py @@ -0,0 +1,144 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011-2012 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## +## Clear Window Extension +## +## About: +## +## It provides "Clear Shell Window" under "Options" +## with ability to undo. +## +## Part of Issue 6143 +## +## """ + +config_extension_def = """ +[ClearWindow] +enable=1 +enable_editor=0 +enable_shell=1 +[ClearWindow_cfgBindings] +clear-window= +""" + + +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates +sp = lambda x: tuple(map(int, x.split('.'))) # convert tkinter Text coordinate to a line and column tuple + +import sys +import re +from idlelib.UndoDelegator import DeleteCommand + +ansi_re = re.compile(r'\x01?\x1b\[(.*?)m\x02?') +def strip_ansi(s): + return ansi_re.sub("", s) + +class ClearWindow: + menudefs = [ + ('options', [ + ('Clear Shell Window', '<>'), + ]),] + + def __init__(self, editwin): + self.editwin = editwin + self.text = self.editwin.text + self.text.bind("<>", self.clear_window) + + def clear_window_event(self, ev=None): + self.clear_window(ev) + return "break" + + def clear_window(self, event): + + per = self.editwin.per + text = per.bottom + + iomark_orig = text.index('iomark') + line_io, col_io = sp(iomark_orig) + + # if cursor is at the prompt, preserve the prompt (multiline) + prompt = strip_ansi(sys.ps1) + backlines = prompt.count('\n') + prompt_start = jn(line_io-backlines, 0) + maybe_prompt = text.get(prompt_start, prompt_start + '+%ic' % len(prompt)) + at_prompt = maybe_prompt == prompt + + if at_prompt: + endpos = text.index(prompt_start) + else: + endpos = text.index('iomark linestart') + + dump = text.dump('1.0', endpos, all=True) + + # Add a command to the undo delegator + undo = self.editwin.undo + if undo: + dc = ClearWindowDeleteCommand('1.0', endpos, dump) + undo.addcmd(dc) + + text.edit_reset() # clear out Tkinter's undo history + + +class ClearWindowDeleteCommand(DeleteCommand): + def __init__(self, index1, index2, dump): + DeleteCommand.__init__(self, index1, index2) + self.dump = dump + + def do(self, text): + text.delete(self.index1, self.index2) + text.see('insert') + + def redo(self, text): + text.delete(self.index1, self.index2) + text.see('insert') + + def undo(self, text): + # inspired by "Serializing a text widget" at http://wiki.tcl.tk/9167 + dump = self.dump + tag = {} # remember the index where a tag was activated + for key, value, index in dump: + if key == 'text': + text.insert(index, value, '') + elif key == 'tagon': + tag[value] = index + elif key == 'tagoff': + text.tag_add(value, tag[value], index) + del tag[value] + # extend existing tags to the end position + for value in tag: + text.tag_add(value, tag[value], self.index2) + text.see('insert') diff --git a/idlex-1.11.2/idlexlib/extensions/CodeBrowser.py b/idlex-1.11.2/idlexlib/extensions/CodeBrowser.py new file mode 100644 index 0000000..08d0af4 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/CodeBrowser.py @@ -0,0 +1,422 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## +## Code Browser Extension +## +## About: +## +## Provides a panel for browsing class and def of a file. +## A button in the status bar toggles it as well. +## +## +## """ + +config_extension_def = """ + +[CodeBrowser] +enable=1 +enable_shell=0 +[CodeBrowser_cfgBindings] +toggle-classdefbrowser= +toggle-keywordbrowser= + +""" + +import sys + +import sys +if sys.version < '3': + from Tkinter import * +else: + from tkinter import * + + +from idlelib.configHandler import idleConf +from idlelib.Delegator import Delegator +from idlelib.Percolator import Percolator +from idlelib.ColorDelegator import ColorDelegator +from idlelib.ToolTip import ToolTip +import re + + +FONTUPDATEINTERVAL = 1000 # milliseconds +CODECHECKINTERVAL = 1000 + +sp = lambda x: list(map(int, x.split('.'))) # convert tkinter Text coordinate to a line and column tuple +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates + +# TODO - provide multiline def strings + + +TEXTOFFSET = 1 +class CodeBrowser: + + menudefs = [ + ('edit', [None, + ('Toggle Code _Browser', '<>'), + ]),] + + def __init__(self, editwin): + self.editwin = editwin + self.text = self.editwin.text + self.text.bind('<>', self.toggle_classdef) + self.text.bind('<>', self.toggle_keyword) + #self.text.bind('', self.button_click, '+') + self.browser_text = [] + self.init_status_bar() + self.hidden = True + self.after_id = None + + def close(self): + if self.after_id is not None: + self.text.after_cancel(self.after_id) + + def init_status_bar(self): + sb = self.editwin.status_bar + sb.set_label('ClassDefBrowser', text="Code Browser") + L = sb.labels['ClassDefBrowser'] + L.bind('', self.toggle_classdef) + L.bind('', self.toggle_keyword) + ToolTip(L, "Click to Show Classes and Definitions in buffer") + + def font_timer_event(self): + if self.hidden: + return + # taken from CodeContext.py + newtextfont = self.editwin.text["font"] + if self.textln and newtextfont != self.textfont: + self.textfont = newtextfont + self.textln["font"] = self.textfont + self.after_id = self.text.after(FONTUPDATEINTERVAL, + self.font_timer_event) + + + def toggle_classdef(self, ev=None): + tag_filter = {'KEYWORD': ['def', 'class']} + self.text.after(1, lambda: self.toggle(tag_filter)) + + def toggle_keyword(self, ev=None): + tag_filter = {'KEYWORD': ['def', 'class'], + 'COMMENT': True} + + #self.toggle(tag_filter) + self.text.after(1, lambda: self.toggle(tag_filter)) + + def toggle(self, tag_filter=None): + if self.hidden: + self.show(tag_filter) + else: + self.hide() + + + def button_click(self, ev=None): + print('button_click') + # TODO - goto defintion on Ctrl+Click + + + def show(self, tag_filter=None): + + if self.hidden == False: + self.hide() + return + + self.hidden = False + + # add a text widget, left of code text widget + self.text_frame = text_frame = Frame(self.editwin.text) #_frame) + self.vbar = vbar = Scrollbar(text_frame, name='vbar') + vbar.pack(side=RIGHT, fill=Y) + + theme = idleConf.GetOption('main','Theme','name') + normal_colors = idleConf.GetHighlight(theme, 'normal') + text_options = { + 'padx': 5, + 'wrap': 'none', + 'cursor': 'arrow', + 'wrap': 'none', + 'foreground':normal_colors['foreground'], + 'background':normal_colors['background'], + } + + self.textln = textln = Text(text_frame, **text_options) + textln.pack(side='left',fill=BOTH, expand=YES) + vbar['command'] = textln.yview + textln['yscrollcommand'] = vbar.set + + # adjust font + + textln.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), + idleConf.GetOption('main', 'EditorWindow', 'font-size'))) + + + + textln.bind("", self.focus_in_event) + textln.bind("", self.enter_callback) + textln.bind("", self.escape_callback) + textln.bind('', self.focus_out, '+') + + # pass through keybindings for classdefbrowser + keydefs = idleConf.GetExtensionBindings('CodeBrowser') + for event, keylist in list(keydefs.items()): + for k in keylist: + def passthru(event, evName=event, text=self.text): + text.event_generate(evName) + try: + textln.bind(k, passthru) + except TclError as err: + print(err) + pass + + + + # start the line numbers + self.per = per = Percolator(textln) + self.color = ColorDelegator() + + self.per.insertfilter(self.color) + + self.line_delegator = LineDelegator() + per.insertfilter(self.line_delegator) + textln._insert = self.line_delegator.delegate.insert + textln._delete = self.line_delegator.delegate.delete + + self.update_display(tag_filter) + self.textfont = "" + self.font_timer_event() + + self.nearest() + + + text_frame.place(x=0, rely=1, relheight=1, relwidth=1, anchor=SW) + text_frame.lift() + + + def nearest(self): + """ Enter ClassDefBrowser """ + # scroll textln to the nearest keyword found in text + text = self.text + textln = self.textln + + text_insert, text_col = sp(text.index(INSERT)) + + text_end, col = sp(text.index(END)) + text_end -= 1 + text_top = (text.yview()[0] * text_end) + text_bot = (text.yview()[1] * text_end) + + if text_top <= text_insert <= text_bot: + pass + else: + text_insert = round((text_bot + text_top) / 2.0) + for i in reversed(self.taglines): + if i[0] <= text_insert: + text_insert = i[0] + break + + n = 0 + for n, i in enumerate(self.taglines): + if i[0] > text_insert: + target_line = n + break + else: + target_line = n + 1 + + + textln.tag_add("NEAREST", '%i.0' % target_line, '%i.0' % (target_line+1)) + theme = idleConf.GetOption('main','Theme','name') + hilite = idleConf.GetHighlight(theme, "hilite") + textln.tag_configure("NEAREST", **hilite) + textln.tag_raise('NEAREST') + + + # place cursor at beginning of line text + tline = min(target_line-1, len(self.taglines)-1) + if self.taglines: + origline, txt = self.taglines[tline] + text_col = txt.find(txt.strip()) + textln.mark_set(INSERT, '%i.%i' % (target_line, text_col)) + + + offset = text_insert - round(text_top) - 1 + textln.yview(target_line - offset) + + textln.focus_set() + + def focus_out(self, ev=None): + self.hide() + + + def hide(self, event=None): + if self.color: + self.per.removefilter(self.color) + self.text_frame.destroy() + self.browser_text = None + self.hidden = True + self.text.focus_set() + + + def enter_callback(self, ev=None): + self.focus_in_event() + + + def escape_callback(self, ev=None): + self.hide() + + + def focus_in_event(self, event=None): + """ Leaves ClassDefBrowser, returns to source code.""" + if self.hidden: + return + + # don't leave on scroll wheel events + if event and event.state != 256: # FIXME + return + + t = self.textln + line, col = list(map(int, t.index(INSERT).split('.'))) + + ind = line - TEXTOFFSET + if 0 <= ind < len(self.taglines): + L = self.taglines[ind][0] + + self.text.mark_set(INSERT, '%i.%i' % (L, col)) + self.editwin.set_line_and_column() + + line_end, col_end = sp(self.textln.index(END)) + + d = self.textln.yview()[0] * line_end + offset = L - line + round(d//1) + 1 + + text_end, col_end = sp(self.text.index(END)) + self.text.yview(offset) + + + self.hide() + + def nextrange(self, taglist, marker): + text = self.text + + L = [] + for tag in taglist: + n = text.tag_nextrange(tag, marker) + if n: + L.append((sp(n[0]), sp(n[1]), tag)) + + if L: + # find nearest range + L.sort() + line, col, tag = L[0] + + return (jn(*line), jn(*col)), tag + + + else: + return None, None + + + def update_display(self, tag_filter=None): + if self.hidden: + return + + if tag_filter is None: + tag_filter = {'KEYWORD': ['def', 'class'], + 'COMMENT': True} + + text = self.text + marker = "1.0" + taglines = [] + lastline = 0 + lasttag = None + while True: + c, tag = self.nextrange(list(tag_filter.keys()), marker) + + if not c: + break + + line, col = sp(c[0]) + + if line == lastline: + #if tag == lasttag: + marker = c[1] + continue + + lastline = line + lasttag == tag + + tagtxt = text.get(c[0], c[1]) + + filt = tag_filter[tag] + if filt == True or tagtxt in filt: + txt = text.get('%i.0' % line, '%i.0 lineend' % (line)) + taglines.append((line, txt)) + marker = c[1] + + + textln = self.textln + VIEW = textln.yview() + + text = self.editwin.text + + code_items = [] + + for n, i in enumerate(taglines): + line, t = i + code_items.append('%4i %s' % i) + + code_items.extend(['']*5) + code_text = '\n'.join(code_items) + + if not code_text.strip(): + code_text = '\nCode Browser found no classes or definitions.\nPress Escape to return to editing.' + + if self.browser_text != code_text: # check if I need to update the display, avoid flickering + textln._delete(1.0, END) + textln._insert(END, code_text) + self.color.recolorize_main() + self.browser_text = code_text + self.taglines = taglines + + self.nearest() + + +class LineDelegator(Delegator): + + def insert(self, *args, **kargs): + pass + + def delete(self, *args, **kargs): + pass diff --git a/idlex-1.11.2/idlexlib/extensions/CythonScript.py b/idlex-1.11.2/idlexlib/extensions/CythonScript.py new file mode 100644 index 0000000..bd15fe5 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/CythonScript.py @@ -0,0 +1,392 @@ +# IDLEX EXTENSION +from __future__ import print_function +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## +## Cython Extension - provides basic Cython support for IDLE +## +## This intercepts calls for running the module and routes it here. +## Also, it installs a colorizer and patches EditorWindow so it works. +## +## Run Cython Module: +## - restarts shell +## - does a "from MODULE import *" +## +## Import/Reload Cython Module +## - does "import MODULE" if not in sys.modules +## else does a "reload(MODULE)" +## - does not restart shell +## +## To properly patch the idlelib source tree, you'd need to modify +## EditorWindow.py's "ispythonsource" to include .pyx files as well as create +## an appropriate color delegator. Since this is not +## likely to be accepted into IDLE, I'll stick to small monkey-patching. +## +## +## """ + +config_extension_def = """ + +[CythonScript] +enable=1 + +[CythonScript_cfgBindings] +cython-run= +cython-import= +cython-reload= +cython-pyximport-install= + +""" + +import sys +import os +import re +import time +from idlelib import PyShell +from idlelib.ColorDelegator import ColorDelegator, make_pat +from idlelib.configHandler import idleConf +import idlelib.IOBinding +import imp + +DEBUG = False + +HAS_CYTHON = True +HAS_RELOAD = False + +try: + imp.find_module('cython') +except ImportError: + HAS_CYTHON = False + +try: + import pyximport +except ImportError: + HAS_CYTHON = False + + +if HAS_CYTHON: + if 'reload_support' in pyximport.install.__code__.co_varnames: + HAS_RELOAD = True + else: + HAS_RELOAD = False + + + +if sys.version < '3': + import tkMessageBox +else: + import tkinter.messagebox as tkMessageBox + + + +CYTHON_IMPORT = 'import pyximport; pyximport.install(reload_support=True)' + +def any_re(groupname, re_list): + return "(?P<%s>" % groupname + "|".join(re_list) + ")" + + +cythonlist = "cdef, cpdef, intern, struct, union, enum, ctypedef, void, double, public, inline, api, nogil, gil, except, cimport, NULL, char, DEF, IF, ELSEIF, ELSE, readonly, bint, by, include".split(', ') +cython_re = r"([^.'\"\\#]\b|^)" + any_re("cython", cythonlist) + r"\b" + + +CYTHON_BACKGROUND = '#EEDDFF' + +def get_cfg(cfg, type="bool", default=True): + return idleConf.GetOption("extensions", "CythonScript", + cfg, type=type, default=default) + +def set_cfg(cfg, b): + return idleConf.SetOption("extensions", "CythonScript", + cfg,'%s' % b) + +def dbprint(*args, **kwargs): + args = (' CythonScript:',) + tuple(args) + if DEBUG: + print(*args, **kwargs) + + +if HAS_CYTHON or True: + # add to filetypes in IOBinding + f = idlelib.IOBinding.IOBinding.filetypes + f.insert(1, ("Cython files", "*.pyx")) + f.insert(0, ("Python/Cython files", "*.py *.pyw *.pyx", "TEXT")) + + +class CythonScript(object): + menudefs = [ + ('run', [None, + ('Run Cython Module', '<>'), + ('Import/Reload Cython Module', '<>'), + ]), + ('shell', [('Install PyxImport', '<>')]), + ] + + def __init__(self, editwin): + + self.editwin = editwin + self.text = editwin.text + + if isinstance(editwin, PyShell.PyShell): + dbprint('running in shell', editwin) + self.inshell = True + return + else: + dbprint('running in editor', editwin) + self.inshell = False + self.ispythonsource_orig = editwin.ispythonsource + editwin.ispythonsource = self.ispythonsource + + self.ec_id = None + if self.iscython(): + self.text.after(10, self.editwin.ResetColorizer) + + self.text.after(10, self.ec_timer) + + + def close(self): # Extension is being unloaded + if not self.inshell: + self.editwin.ispythonsource = self.ispythonsource_orig + self.ispythonsource_orig = None + text = self.text + text.unbind(self.cython_run_event) + text.unbind(self.check_cython_event) + self.ec_cancel() + self.editwin = None + self.text = None + self.shell = None + + def _has_cython(func): # decorator function + def f(self, *args, **kwargs): + if HAS_CYTHON: + return func(self, *args, **kwargs) + else: + tkMessageBox.showerror("Cython not Found", + "Cython was not found by IDLE. Make sure it is installed or disable the CythonScript extension. " + "For more details about Cython, visit http://cython.org/", + parent=self.editwin.text) + return "break" + return f + + + + def ec_timer(self, ev=None): + """ Periodically try to install the colorizer. """ + self.ec_cancel() + self.extend_colorizer() + self.ec_id = self.text.after(1000, self.ec_timer) + + def ec_cancel(self): + if self.ec_id is not None: + self.text.after_cancel(self.ec_id) + self.ec_id = None + + def extend_colorizer(self): + """ Provides syntax highlighting for cython by patching the existing colorizer. """ + color = self.editwin.color + if color is None: + return + + p = color.prog.pattern + if 'cython' in p: + return # already extended + + if self.iscython() == False: + return + dbprint('extending colorizer') + + color.tagdefs['cython'] = color.tagdefs['KEYWORD'].copy() + #color.tagdefs['cython']['background'] = CYTHON_BACKGROUND + color.config_colors() + + rlist = [] + rlist.append(cython_re) + rlist.append(p) + + color.prog = re.compile('|'.join(rlist), re.S) + color.notify_range('1.0', 'end') + + @_has_cython + def cython_pyximport_install_event(self, ev=None): + dbprint('install_pyximport_event') + if self.inshell: + text = self.text + runit = self.editwin.runit + else: + shell = self.editwin.flist.open_shell() + text = shell.text + runit = shell.runit + + text.delete('iomark', 'end-1c') + text.insert('iomark', CYTHON_IMPORT) + text.mark_set('insert', 'end-1c') + + text.after(1, runit) # allow colorizer to kick in + + + def ispythonsource(self, filename): + """ Patch to EditorWindow's ispythonsource to detect .pyx files """ + # The ResetColorizer code calls into ispythonsource. Trick it. + if filename: + base, ext = os.path.splitext(os.path.basename(filename)) + if os.path.normcase(ext) in (".pyx",): + return True + return self.ispythonsource_orig(filename) + + + def iscython(self, filename=None): + if filename is None: + filename = self.editwin.io.filename # in case nothing specified + if filename is None: # still? this means the buffer is not saved + return False + base, ext = os.path.splitext(os.path.basename(filename)) + ext_list = (".pyx", ".pxd") + if os.path.normcase(ext) in ext_list: + return True + else: + return False + + @_has_cython + def check_cython_event(self, ev=None): + dbprint('check_cython_event') + return "break" + + @_has_cython + def add_cython_event(self, ev=None): + self.text.insert('insert', CYTHON_IMPORT) + + def getfilename(self): + if self.inshell: + return None + + filename = self.editwin.io.filename + if filename is None: + tkMessageBox.showerror("No File Name", + "Please save the buffer (with a .pyx extension).", + parent=self.editwin.text) + return filename + + @_has_cython + def cython_import_event(self, ev=None): + dbprint('cython_import_event') + self.cython_run_event(do_import=True) + + @_has_cython + def cython_run_event(self, ev=None, do_import=False): + self.text.tag_remove("ERROR", "1.0", "end") + + filename = self.getfilename() + if filename is None: + return + + if not self.iscython(filename): + dbprint('filename not cython') + self.not_cython_message() + return + + base, ext = os.path.splitext(os.path.basename(filename)) + modname = base + dirname = os.path.dirname(filename) + + try: + f = imp.find_module(modname) + if f[2][0] != '.pyx': + dbprint('Conflicting with .py', f) + self.pycy_conflict(f[1]) + return "break" + + except ImportError as err: + # good, .py or .pyc not found + pass + + + self.editwin.io.save(None) # save the cython module so it is reloaded properly + + self.shell = shell = self.editwin.flist.open_shell() + interp = shell.interp + if PyShell.use_subprocess and not do_import: + shell.restart_shell() + + interp.prepend_syspath(filename) + src = r"""if 1: + _filename = %(filename)r + import sys as _sys + from os.path import basename as _basename + if (not _sys.argv or + _basename(_sys.argv[0]) != _basename(_filename)): + _sys.argv = [_filename] + import os as _os + _os.chdir(%(dirname)r) + del _filename, _basename, _os + + if "pyximport" not in _sys.modules: + import pyximport + if %(has_reload)s: + pyximport.install(reload_support=True) + else: + pyximport.install() + try: + if not %(do_import)s: + from %(modname)s import * + else: + if "%(modname)s" not in _sys.modules: + #print('importing') + import %(modname)s + elif %(has_reload)s: + #print('reloading') + import %(modname)s + reload(%(modname)s) + else: + print('\nUnable to reload module. Your Cython version does not support module reloading.\n') + except ImportError as err: + print('\nThe Cython Module could not be built.\n') + + """ % {'filename':filename, + 'dirname':dirname, + 'modname':modname, + 'do_import':do_import, + 'has_reload':str(HAS_RELOAD)} + interp.runcode(src.strip()) # BUGFIX: .strip() for Python 2.6 + return "break" + + def not_cython_message(self): + tkMessageBox.showerror("Not a Cython Script", + "This buffer is not a Cython script.\n" + \ + "Save with a .pyx extension.", + parent=self.editwin.text) + def pycy_conflict(self, filename): + tkMessageBox.showerror("Cython Import Collision", + 'The file "%s"\nconflicts with importing this Cython script. Please relocate the file or rename this buffer.' % (filename), + parent=self.editwin.text) diff --git a/idlex-1.11.2/idlexlib/extensions/DocViewer.py b/idlex-1.11.2/idlexlib/extensions/DocViewer.py new file mode 100644 index 0000000..0675ad5 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/DocViewer.py @@ -0,0 +1,422 @@ +# IDLEX EXTENSION +""" + +Documentation Viewer Extension +Version: 0.2 + +Author: Roger D. Serwy + roger.serwy@gmail.com + +Date: 2009-05-29 +Date: 2011-12-26 - modified to work with IdleX and Python 3 + +It provides "Documentation Viewer" under "Help" + +Add these lines to config-extensions.def + +Parts of this code is based on a patch submitted to the +Python Software Foundation under to Apache 2 License, per +a contributor agreement. +See http://bugs.python.org/issue964437 for the patch + +""" +config_extension_def = """ +[DocViewer] +enable=1 +enable_editor=1 +enable_shell=1 +calltip=1 + +[DocViewer_cfgBindings] +docviewer-window= + + +""" + + +# TODO: +# - sanitize command input box + + +from idlelib.configHandler import idleConf +import idlelib.IOBinding as IOBinding +from idlelib.EditorWindow import EditorWindow +from idlelib.OutputWindow import OutputWindow +from idlelib.Delegator import Delegator +from idlelib.HyperParser import HyperParser +import idlelib.WindowList as WindowList +import idlelib.SearchDialog as SearchDialog +import time +import sys +if sys.version < '3': + from Tkinter import * +else: + from tkinter import * + + + + +def get_cfg(cfg, type="bool", default=True): + return idleConf.GetOption("extensions", "DocViewer", + cfg, type=type, default=default) + +def set_cfg(cfg, b): + return idleConf.SetOption("extensions", "DocViewer", + cfg,'%s' % b) + + + +class DocViewer: + + menudefs = [ + ('help', [None, + ('Documentation Viewer', '<>'), + ]),] + + + def __init__(self, editwin): + self.editwin = editwin + self.top = editwin.top + text = self.editwin.text + text.bind("<>", self.calltip_event) + text.event_add("<>", "") + text.bind("<>", self.do_calltip) + self.docWindow = docWindow + + def do_calltip(self, event=None): + docWindow.show(self.editwin) + self.calltip_event() + + def calltip_event(self, event=None): + window = self.docWindow.window + if window is None: + return + + if not window.update_calltip.get(): + # don't process calltip event + return + + # get calltip + # code borrows from CallTips.py::open_calltip + evalfuncs = False + hp = HyperParser(self.editwin, "insert") + sur_paren = hp.get_surrounding_brackets('(') + if not sur_paren: + return + hp.set_index(sur_paren[0]) + name = hp.get_expression() + if not name or (not evalfuncs and name.find('(') != -1): + return + + w = window + w.entry.delete("0", "end") + w.entry.insert("insert", name) + w.get_doc() + + def show_docviewer(self, event=None): + # create a window with two text boxes and a label + self.docWindow.show(self.editwin) + +class DocDelegator(Delegator): + """ Prevent modifications to the text widget that displays the documentation. + Text may only be inserted if the .enabled value is True. + """ + + def insert(self, index, chars, tags=None): + try: + self.entry.insert('insert', chars) + self.entry.focus() + except Exception as err: + print(' Internal DocDelegator Error:', err) + + def delete(self, index1, index2=None): + pass + + +class DocWindowHandler(object): + """ For handling a singleton instance of the DocViewer""" + def __init__(self): + self.window = None + WindowList.registry.register_callback(self.check_close) + def show(self, editwin, near=None): + if self.window is None: + shell = editwin.flist.open_shell() + interp = shell.interp + win = DocumentationWindow(flist=editwin.flist, + interp=interp, + editwin=shell) + self.window = win + win.top.bind('', self.destroy, '+') + self.nearwindow(editwin.top) + + def nearwindow(self, near): + w = self.window.top + w.withdraw() + geom = (near.winfo_rootx() + 10, near.winfo_rooty() + 10) + w.geometry('=+%d+%d' % geom) + w.deiconify() + w.lift() + + def check_close(self, event=None): + """ callback function to make sure the DocumentationWindow is + not the last instance. If so, then close it. + """ + if self.window is None: + return + + d = WindowList.registry.dict + t = str(self.window.top) + if len(d) == 1: + if t in d: + self.window.close() + else: + #Strange situation. DocViewer is open, but not it the dict. + #This should not happen. + pass + + def destroy(self, event=None): + self.window = None + +docWindow = DocWindowHandler() + +class DocumentationWindow(EditorWindow): + """ Create an editor window for the purpose of displaying documentation """ + + def __init__(self, flist=None, interp=None, editwin=None): + EditorWindow.__init__(self, flist) + self.interp = interp + self.editwin = editwin + + # TODO: figure out better way to eliminate menu bar + m = Menu(self.root) + self.top.config(menu=m) + + root = self.top + + self.doc_frame = doc_frame = Frame(root) + # make first line + f_top = Frame(doc_frame) + label = Label(f_top, text='Help on:') + + self.entry = entry = Entry(f_top) + entry.bind('', self.get_doc) + + self.update_calltip = IntVar(root) + check = Checkbutton(f_top, text='Update from Calltip', + variable=self.update_calltip) + check.var = self.update_calltip + if get_cfg('calltip'): + check.select() + + f_top.pack(side='top', fill=X, padx=5) + label.pack(side='left') + entry.pack(side='left', fill=X, expand=True, padx=5, ipadx=5) + check.pack(side='right') + + # make command buttons + f_cmd = Frame(doc_frame) + f_cmd.pack(side='top', fill=X, padx=3) + + + + self.button_showdoc = Button(f_cmd, text='Show Doc String', + default='active', + command=self.get_doc) + + self.button_showhelp = Button(f_cmd, text='Show help()', + command=self.get_help) + + button_search = Button(f_cmd, text='Search Text', + command=self.search) + button_close = Button(f_cmd, text='Close', + command=self.close) + + + button_close.pack(side='right') + self.button_showdoc.pack(side='left') + self.button_showhelp.pack(side='left') + button_search.pack(side='left') + + doc_frame.pack(side=TOP, before=self.text_frame, fill=X) + + # change focused widget to entry box + self.entry.focus_set() + self.top.focused_widget = self.entry + + # remove unneeded stuff + self.per.removefilter(self.undo) + self._rmcolorizer() + #self.status_bar.pack_forget() + + # add a delegator to prevent editing of text widget + self.doc_del = DocDelegator() + self.doc_del.entry = self.entry + + self.per.insertfilter(self.doc_del) + + self.text._insert = self.doc_del.delegate.insert + self.text._delete = self.doc_del.delegate.delete + self.text.configure(wrap='none') + + keySetName = idleConf.CurrentKeys() + find_bindings = idleConf.GetKeyBinding(keySetName, '<>') + for key_event in find_bindings: + #self.entry.event_add('<>', key_event) + self.entry.bind(key_event, lambda e: self.text.event_generate('<>')) + + def get_standard_extension_names(self): + # Only load SearchBar if needed + ret = [] + #a = idleConf.GetExtensions(editor_only=True) + #if 'SearchBar' in a: + # ret.append('SearchBar') + return ret + + + + def search(self, event=None): + self.text.focus_set() + self.text.update_idletasks() + self.text.event_generate('<>') + self.text.update_idletasks() + return "break" + + + def get_help(self, event=None): + #self.button_showhelp.configure(default='active') + #self.button_showdoc.configure(default='disabled') + b = self.entry.get().strip() + if not b: + return + + cmd = """if 1: + try: + help(%s) + except: + print("'%s' not found")""" % (b,b) + + self.process_request(cmd) + + def get_doc(self, event=None): + #self.button_showhelp.configure(default='disabled') + #self.button_showdoc.configure(default='active') + + b = self.entry.get().strip() + if not b: + return + + + cmd = """if 1: + try: + if hasattr(%s, '__doc__'): + print(%s.__doc__) + else: + print("%s doesn't have a doc string") + except: + print("'%s' not found in the shell's namespace.")""" % ((b,)*4) + + cmd2 = """if 1: + print "====Displaying %s.__doc__" + print + try: + if hasattr(%s, '__doc__'): + print(%s.__doc__) + else: + print("%s doesn't have a doc string") + except: + print("'%s' not found in the shell's namespace.") + print() + print() + print("====Displaying help(%s)") + print() + + try: + help(%s) + except: + print("'%s' not found in the shell's namespace.") """ % ((b,)*8) + + + + self.process_request(cmd) + + + + def process_request(self, cmd=None): + + if cmd is None: + return + + try: + test = compile(cmd, '', 'exec') + except Exception as err: + t = 'Unable to process your request.\nIs your given object in the namespace?' + self.text._delete('1.0', 'end') + self.text._insert('1.0', t) + return + + interp = self.interp + editwin = self.editwin + + self.count = 0 + + if editwin.executing: + self.text._insert(1.0, "The shell currently is executing a command.\n" \ + "Please try again when the shell is done executing.\n") + return + + editwin.text.mark_set("iomark2", "iomark") + self.text._delete("1.0", "end") + + # redirect output from PyShell to DocViewer + def insert_bridge(self, index, chars, tags=None): + #self.count += 1 + #if self.count < 50: + self.text.insert(index, chars, tags) + + + __insert = editwin.text.insert + editwin.text.insert = insert_bridge + + def mywrite(s, tags=()): + if tags in ('stdout', 'stderr'): + # send to me + self.text._insert('insert', s,tags) + + __write = editwin.write + editwin.write = mywrite + + + interp.runcommand(cmd) + + # go into a loop, until help has arrived :) + while editwin.executing: + editwin.text.update_idletasks() + time.sleep(0.05) + + # restore output to PyShell + editwin.text.insert = __insert + editwin.write = __write + + editwin.text.mark_set("iomark", "iomark2") + + def close(self): + set_cfg('calltip', self.update_calltip.get()) + # remove all references + if 0: + self.doc_frame.destroy() + self.editwin = None + self.interp = None + self.text._delete = None + self.text._insert = None + self.per.removefilter(self.doc_del) + self.undo = None + self.doc_del = None + #EditorWindow.close(self) + self._close() + self.top.destroy() + #print 'refcount: ', sys.getrefcount(DocViewer.WINDOW) + DocViewer.WINDOW = None + + + def short_title(self): + return "IDLE Documentation Viewer" diff --git a/idlex-1.11.2/idlexlib/extensions/EventLoop.py b/idlex-1.11.2/idlexlib/extensions/EventLoop.py new file mode 100644 index 0000000..9918d3f --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/EventLoop.py @@ -0,0 +1,483 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## EventLoop.py +## +## Drive a GUI event loop within the subprocess from the IDLE event loop. +## Allows for interactive GUI development. +## +## Supports the following toolkits: +## * Tkinter +## * GTK +## * Qt +## * Qt4 +## * PySide +## * wx +## +## A callback function gets called periodically from the IDLE gui when +## the shell is idle... (pun intended) +## +## +## """ + +config_extension_def = """ + +[EventLoop] +enable=1 +active=False +toolkit=TK + +[EventLoop_cfgBindings] +eventloop-toggle= + +""" + +INTERVAL = 100 # milliseconds +INSTALL_DELAY = 250 # milliseconds + +from idlelib.configHandler import idleConf + +import sys +if sys.version < '3': + from Tkinter import * +else: + from tkinter import * + + +import idlelib.PyShell as PyShell +import idlelib.ToolTip as ToolTip +import threading +import time + +# subprocess initialization code +gui_init = r""" + +def __IDLE_eventloop_set(kit='TK'): + " Sets up a callback function for a GUI toolkit. See EventLoop.py." + global __IDLE_eventloop, __IDLE_eventloop_counter + import sys + guiptr = None + + if kit == 'CUSTOM': + if '__IDLE_eventloop' not in globals() or hasattr(__IDLE_eventloop, 'default'): + def __IDLE_eventloop(): + if __IDLE_eventloop.message: + print(__IDLE_eventloop.message) + __IDLE_eventloop.message = None + msg = ['\n', + 'You have chosen to use a custom callback function for the event loop.', + 'Please define a callback function of the following form:', + '', + ' def __IDLE_eventloop():', + ' pass # Your GUI callback code here', + '', + "(Press Enter if you don't have a >>> prompt.)\n" + ] + + __IDLE_eventloop.message = '\n'.join(msg) + return + + elif kit == 'TK': + if sys.version < '3': + guiptr = sys.modules.get('Tkinter') + else: + guiptr = sys.modules.get('tkinter') + + if guiptr: + tcl = guiptr.Tcl() + def __IDLE_eventloop(guiptr=guiptr, tcl=tcl): + tcl.eval('update') + + elif kit == 'GTK': + guiptr = sys.modules.get('gtk') + def __IDLE_eventloop(guiptr=guiptr): + while guiptr.events_pending(): + guiptr.main_iteration() + + elif kit == 'QT4': + guiptr = sys.modules.get('PyQt4.QtGui') + def __IDLE_eventloop(guiptr=guiptr): + guiptr.qApp.processEvents() + + elif kit == 'QT': + guiptr = sys.modules.get('qt') + def __IDLE_eventloop(guiptr=guiptr): + guiptr.qApp.processEvents() + + elif kit == 'PYSIDE': + guiptr = sys.modules.get('PySide.QtGui') + def __IDLE_eventloop(guiptr=guiptr): + guiptr.qApp.processEvents() + + elif kit == 'WX': + guiptr = sys.modules.get('wx') + def __IDLE_eventloop(guiptr=guiptr): + guiptr.Yield() + + elif kit == 'FLTK': # TODO + pass + + else: # Well this is a problem. The "kit" argument is not valid. + print("EventLoop.py: %r is not a supported toolkit." % kit) + def __IDLE_eventloop(): + pass + + if guiptr is None: + # The toolkit has not been imported yet. + # Install a callback that will check periodically and + # do a proper install. + + __IDLE_eventloop_counter = 0 # only check periodically if Toolkit is not imported + def __IDLE_eventloop(): + global __IDLE_eventloop_counter + __IDLE_eventloop_counter = (__IDLE_eventloop_counter + 1) % 25 + if __IDLE_eventloop_counter == 0: + __IDLE_eventloop_set(kit=kit) + + if kit != 'CUSTOM' and '__IDLE_eventloop' in globals(): + __IDLE_eventloop.default = True + +def __IDLE_eventloop_error(err): + " For when the __IDLE_eventloop callback experiences an error. " + global __IDLE_eventloop + msg = [ '', + 'An error has occurred in the __IDLE_eventloop callback:', + '', + str(err), + '', + 'The callback function has been reset to do nothing.', + "(Press Enter if you don't have a >>> prompt.)", + ] + print('\n'.join(msg)) + def __IDLE_eventloop(): # avoid displaying errors repeatedly + pass + +""" + +# subprocess callback code +gui_ping_src = r""" +try: + __IDLE_eventloop() +except Exception as err: + try: + __IDLE_eventloop_error(err) + except: + pass +""" + +GUI_PING_CODE = gui_ping_src + +TOOLTIP_TEXT = 'Click to toggle Event Loop driver' + +# Methods and state for threading +EV_ACTIVE = False +DO_PING = False # Flag to allow for sending ping +INTERP = None # pointer to Interpreter object +PING_RUNNING = False # Flag for pinging thread + +def ping_eventloop(): + global DO_PING, PING_RUNNING + PING_RUNNING = True + request = ("QUEUE", ("exec", "runcode", (GUI_PING_CODE,), {})) + while EV_ACTIVE: + if INTERP is None: + time.sleep(0.050) + elif INTERP.tkconsole.executing: + # wait longer before trying again + time.sleep(0.050) + else: + # By waiting for the idleEV extension to set the DO_PING flag, + # the rpcclt.asyncqueue command will return. Otherwise, during + # a shell restart, this thread will be blocked on the + # asyncqueue command. + if DO_PING: + try: + # BUGFIX - 2011-11-30 + # Using .asyncqueue caused a massive memory "leak" because + # responses were not cleared. Putting message with sequence 0 + # into the RPC works and doesn't cause a leak. + #print('putting', request) + INTERP.rpcclt.putmessage((0, request)) + except: + pass + DO_PING = False + time.sleep(0.010) + PING_RUNNING = False + +# The IDLE EV Extension +class EventLoop(object): + + TOOLKITS = [('!Use Tkinter', '<>', 'TK'), + ('!Use GTK', '<>', 'GTK'), + ('!Use Qt', '<>', 'QT'), + ('!Use Qt4', '<>', 'QT4'), + ('!Use PySide', '<>', 'PYSIDE'), + ('!Use wx', '<>', 'WX'), + #('!Use FLTK', '<>', 'FLTK'), # TODO + ('!Use Custom Callback', '<>', 'CUSTOM'), + ] + + # Build the menudefs entry + _shell = [None, + ('!Enable GUI Event Loop', '<>'), + None] + + for _menu_item, _event, _kit_id in TOOLKITS: + _shell.append((_menu_item, _event)) + _shell.append(None) + + menudefs = [('shell', _shell)] # build menu entries + + + shell_instance = None # pointer to the PyShell instance + + def __init__(self, editwin): + self.is_shell = False + self.editwin = editwin + if isinstance(editwin, PyShell.PyShell): + self.editwin.top.after(1,lambda: self.init_pyshell()) + else: + self.init_editor() + + def init_editor(self): + # initialize in an Editor. Only do key binding. + def eventloop_toggle_editor(ev=None): + if EventLoop.shell_instance: + EventLoop.shell_instance.eventloop_toggle() + self.editwin.text.bind('<>', eventloop_toggle_editor) + + def init_pyshell(self): + if not PyShell.use_subprocess: + print('EventLoop.py only works with a subprocess.') + return + EventLoop.shell_instance = self # set class variable - it is singleton anyways + + self.is_shell = True + text = self.text = self.editwin.text + text.bind('<>', self.eventloop_toggle) + + self.after_id = None + self.delay_id = None + + self.install_socket = None + self.active = False + + self.active = idleConf.GetOption("extensions", "EventLoop", + "active", type="bool", default=True) + + self.kit = idleConf.GetOption("extensions", "EventLoop", + "toolkit", type="raw", default="TK") + + if self.kit == 'CUSTOM': + self.active = False # avoid needless startup message + + self.init_status_bar() + for menu, event, kit_id in EventLoop.TOOLKITS: + text.bind(event, lambda ev, kit=kit_id: self.use_kit(kit)) + + self.use_kit(self.kit, install=False) + self.eventloop_enable(self.active) + + + + def init_status_bar(self): # for PyShell instance + """ Place a status box into the status bar. It is also a clickable toggle.""" + sb = self.editwin.status_bar + sb.set_label('EventLoop', text="") + L = sb.labels['EventLoop'] + L.bind('', self.eventloop_toggle) + self.tooltip = ToolTip.ToolTip(L, TOOLTIP_TEXT) + self.display_state() + + def display_state(self): + """ Update the state of the extension. """ + sb = self.editwin.status_bar + if self.active: + s = 'ON' + else: + s = 'OFF' + + txt = 'GUI: %s (%s)' % (s, self.kit) + sb.set_label('EventLoop', text=txt) + + def close(self): + if self.is_shell: + self.close_pyshell() + self.editwin = None + self.text = None + + + def close_pyshell(self): + global INTERP, DO_PING + idleConf.SetOption("extensions", "EventLoop", "active", + '%s' % self.active) + idleConf.SetOption("extensions", "EventLoop", + "toolkit", self.kit) + idleConf.SaveUserCfgFiles() + + self.eventloop_enable(False) + + INTERP = None + DO_PING = False + + + def eventloop_enable(self, b=True): + global EV_ACTIVE + self.active = b + self.editwin.setvar("<>", self.active) + EV_ACTIVE = self.active + + if self.active: + self.use_kit(self.kit) + else: + if self.after_id: + self.text.after_cancel(self.after_id) + self.after_id = None + if self.delay_id: + self.text.after_cancel(self.delay_id) + self.delay_id = None + + self.display_state() + + def eventloop_toggle(self, event=None): + # Tk callback to the <> event + self.eventloop_enable(not self.active) + return "break" + + def use_kit(self, kit=None, install=True): + + toolkits = EventLoop.TOOLKITS + if kit not in [i[2] for i in toolkits]: + print('Need to specify a valid kit. %s not valid' % kit) + kit = 'TK' # do default + + editwin = self.editwin + for menu, event, kit_id in toolkits: + if kit == kit_id: + editwin.setvar(event, True) + else: + editwin.setvar(event, False) + + self.tooltip.text = TOOLTIP_TEXT + ' for %s' % kit + + if kit != self.kit: + self.kit = kit + self.install_socket = None # invalidate cache + install = True + + if install: + self.delay_install() + + self.display_state() + + ### Code for handling installing and running the GUI Callbacks in the Shell + + def start_threading(self): + global EV_ACTIVE + EV_ACTIVE = True + if not PING_RUNNING: + t = threading.Thread(target=ping_eventloop) + t.daemon = True + t.start() + self.do_update() + else: + raise Exception('Ping Thread still running when it should not be running') + + def stop_threading(self): + global EV_ACTIVE + EV_ACTIVE = False + if self.after_id: + self.text.after_cancel(self.after_id) + self.after_id = None + + def delay_install(self): + if self.delay_id: + self.text.after_cancel(self.delay_id) + self.delay_id = self.text.after(INSTALL_DELAY, self._do_install) + + def _do_install(self, count=200): + global INTERP + if not self.active: + return + if count == 0: + self.eventloop_enable(False) + return + + self.stop_threading() # make sure PING loop is disabled while installing the event handler + tryagain = True + interp = self.editwin.interp + if interp: + if not interp.tkconsole.executing: + if interp.rpcclt: + if not interp.debugger: + try: + cmd = '%s\n%s' % (gui_init, + "__IDLE_eventloop_set(%r)" % self.kit) + interp.runcommand(cmd) + self.install_socket = interp.rpcclt.sock + self.compile_gui_ping() + tryagain = False + INTERP = interp + self.text.after(1, lambda: self.start_threading()) + except Exception as err: + print(err) + pass + + if tryagain: + self.text.after(INSTALL_DELAY, lambda: self._do_install()) + else: + self.delay_id = None + + def compile_gui_ping(self): + global GUI_PING_CODE + GUI_PING_CODE = self.editwin.interp.compile(gui_ping_src, '', 'exec') + + def do_update(self, event=None): + """ periodic callback to set flags for pinging eventloop """ + global DO_PING, INTERP + if not self.active: return + self.after_id = self.text.after(INTERVAL, self.do_update) + interp = self.editwin.interp + if interp: + if interp.rpcclt and interp.rpcclt.sock is self.install_socket: + # same subprocess + if not interp.tkconsole.executing and interp.debugger is None \ + and interp.rpcclt: + DO_PING = True + else: + # different subprocess + self.stop_threading() + if self.delay_id is None: + self.delay_install() diff --git a/idlex-1.11.2/idlexlib/extensions/Horizontal.py b/idlex-1.11.2/idlexlib/extensions/Horizontal.py new file mode 100644 index 0000000..4bacf54 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/Horizontal.py @@ -0,0 +1,96 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## Developed by: Roger D. Serwy +## University of Illinois +## License: See LICENSE.txt +## """ + + +# +# Horizontal Scroll Bar Extension +# Provides a horizontal scroll bar on the Editor window. +# +# This extension is meant to be an introduction +# to writing IDLE extensions. +# + + +# config_extension_def is for IdleX to configure this extension. +# This would normally be placed in config-extensions.def +# in the idlelib directory. +config_extension_def = """ +[Horizontal] +enable=1 +enable_editor=1 +enable_shell=0 +visible=True +""" + + +# Python 2/3 compatibility +import sys +if sys.version < '3': + import Tkinter as tk +else: + import tkinter as tk + + +# get the IDLE configuration handler +from idlelib.configHandler import idleConf + +class Horizontal: # must be the same name as the file for EditorWindow.py + # to load it. + + menudefs = [ + ('windows', [ + ('!Show Horizontal Scrollbar', '<>'), + ]),] + + def __init__(self, editwin): + self.editwin = editwin # reference to the editor window + self.text = text = self.editwin.text + self.text.bind("<>", self.show_toggle) + + # See __init__ in EditorWindow.py to understand + # the widget layout + self.xbar = xbar = tk.Scrollbar(editwin.text_frame, + orient=tk.HORIZONTAL) # create the scroll bar + + xbar['command'] = text.xview # connect it to the text widget + + text['xscrollcommand'] = xbar.set # connext text widget to scroll bar + + + self.visible = idleConf.GetOption("extensions", "Horizontal", + "visible", type='bool', default=True) + + if self.visible: + self._show_bar() + + def show_toggle(self, ev=None): + self.visible = not self.visible + if self.visible: + self._show_bar() + else: + self._hide_bar() + + # save the option + idleConf.SetOption("extensions", "Horizontal", + "visible", '%s' % self.visible) + + def _show_bar(self): + # pack the bar so it is visible + widgets = self.editwin.text_frame.pack_slaves() + widgets = list(widgets) # list for Python 3 support + self.xbar.pack(side=tk.BOTTOM, + fill=tk.BOTH, + expand=0, + before=widgets[0]) # pack before everything + self.editwin.setvar("<>", True) + + def _hide_bar(self): + # forget the packing so it is not visible + self.xbar.pack_forget() + self.editwin.setvar("<>", False) diff --git a/idlex-1.11.2/idlexlib/extensions/IDLE2HTML.py b/idlex-1.11.2/idlexlib/extensions/IDLE2HTML.py new file mode 100644 index 0000000..e701abe --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/IDLE2HTML.py @@ -0,0 +1,153 @@ +# IDLEX EXTENSION +## """ +## IDLE2HTML - IDLE extension +## saves the contents of the editwindow (file or shell) +## to a html file using css styles +## +## creator: d2m +## 0.1/2006-07-22: initial revision +## 0.2/2007-06-14: added styles for BODY +## thanks to Tal Einat who pointed out a problem +## with non Black-on-White color schemes +## removed Selection highlightning +## added ERROR highlightning +## +## todo: check for valid css color values +## use elementtree for html generation +## enable Selection highlightning +## +## PSF LICENSE AGREEMENT FOR PYTHON 2.7.2 +## +## 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and +## the Individual or Organization ("Licensee") accessing and otherwise using Python 2.7.2 +## software in source or binary form and its associated documentation. +## 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants +## Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, +## perform and/or display publicly, prepare derivative works, distribute, and otherwise use +## Python 2.7.2 alone or in any derivative version, provided, however, that PSF's License +## Agreement and PSF's notice of copyright, i.e., "Copyright (C) 2001-2010 Python Software +## Foundation; All Rights Reserved" are retained in Python 2.7.2 alone or in any derivative +## version prepared by Licensee. +## 3. In the event Licensee prepares a derivative work that is based on or incorporates +## Python 2.7.2 or any part thereof, and wants to make the derivative work available to +## others as provided herein, then Licensee hereby agrees to include in any such work a brief +## summary of the changes made to Python 2.7.2. +## 4. PSF is making Python 2.7.2 available to Licensee on an "AS IS" basis. PSF MAKES NO +## REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, +## PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +## FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.7.2 WILL NOT INFRINGE ANY THIRD +## PARTY RIGHTS. +## 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.2 FOR ANY +## INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, +## DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.2, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED +## OF THE POSSIBILITY THEREOF. +## 6. This License Agreement will automatically terminate upon a material breach of its terms +## and conditions. +## 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, +## partnership, or joint venture between PSF and Licensee. This License Agreement does not +## grant permission to use PSF trademarks or trade name in a trademark sense to endorse or +## promote products or services of Licensee, or any third party. +## 8. By copying, installing or otherwise using Python 2.7.2, Licensee agrees to be bound by +## the terms and conditions of this License Agreement. +## +## +## +## License: Python Software Foundation License +## +## Modified to work with idlex by Roger D. Serwy +## October 2011 +## +## Some minor bug fixes and feature enhancements: +## - Check if user clicked "Cancel" on saveas dialog. +## - Save to .html by default instead of .htm (a relic from 8.3 filename days) +## - Save dialog has a proper title +## - Modified to work with Python 3 +## +## """ + +config_extension_def = """ +[IDLE2HTML] +enable=1 + +[IDLE2HTML_cfgBindings] +idle2html= + +""" + +__version__ = '0.2' + +import sys + +if sys.version < '3': + import Tkinter + import tkFileDialog +else: + import tkinter as Tkinter + import tkinter.filedialog as tkFileDialog + +import cgi + +class IDLE2HTML(object): + menudefs=[('options',[('Export to HTML', '<>')])] + + def __init__(self,editwin): + self.editwin=editwin + self.text=editwin.text + + def idle2html_event(self, event=None): + filetypes = [ + ("All HTML files", "*.html *.htm", "TEXT"), + ("All files", "*"), + ] + filename=tkFileDialog.SaveAs(master=self.text, + filetypes=filetypes, + title="Export to HTML", + ).show() + if filename: + f=open(filename,'w') + f.write(self.idle2html()) + f.close() + + def idle2html(self): + """format tags 2 html + """ + out=['\n'] + out.append('\n\nIDLE2HTML\n') + out.append('\n' % __version__) + out.append('\n') + out.append('\n\n
')
+        inside_error=0
+        for tagname,content,dummy in self.text.dump('1.0',self.text.index('end')):
+            if tagname=='tagon' and not (content.upper() in ('SYNC','TODO','SEL')):
+                if not inside_error:
+                    out.append('' % content)
+                if content.upper() == 'ERROR':
+                    inside_error=1
+            if tagname=='text':
+                out.append(cgi.escape(content))
+            if tagname=='tagoff' and not (content.upper() in ('SYNC','TODO','SEL')):
+                if content.upper() == 'ERROR':
+                    inside_error=0
+                if not inside_error:
+                    out.append('')
+        out.append('
\n\n') + return ''.join(out) + diff --git a/idlex-1.11.2/idlexlib/extensions/IPyIDLE.py b/idlex-1.11.2/idlexlib/extensions/IPyIDLE.py new file mode 100644 index 0000000..802d736 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/IPyIDLE.py @@ -0,0 +1,2034 @@ +# IDLEX EXTENSION +from __future__ import print_function +## +## Copyright(C) 2012 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## An extension to allow IPython to work with IDLE's shell environment. +## The latest 0.12 version uses a two-process model with ZMQ. +## + + +config_extension_def = """ +[IPyIDLE] +enable = 1 +enable_shell = 1 +enable_editor = 1 + +ipython = True +allow_restart = True + +""" + + +# This extension relies on Terminal.py's behavior +# for the command line. +# +# This also expects that AutoComplete Calltips extensions +# are enabled (which is the default in IDLE) +# +# + +#----------------------------------------------------------------------------- +# Parts of this code derives from IPython 0.12 which has the following notice: +# +# Copyright (C) 2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +# Kernel manager launch code based on VIM integration +# by Paul Ivanov (http://pirsquared.org) + +#---------------------------------------------- +# Imports +#---------------------------------------------- + +HAS_PIL = True +try: + from PIL import ImageTk +except: + HAS_PIL = False + + +# python stdlib +import bdb +import signal +import sys +import time +import re +import marshal +import types +import traceback +from pprint import pprint +import time +from threading import Event, Lock, Thread +import string +import threading +import os +from base64 import decodestring +import tempfile +import shutil + +if sys.version < '3': + import Tkinter as tk + import tkSimpleDialog + import tkMessageBox + import tkFileDialog + from Queue import Queue, Empty + from io import BytesIO + import copy_reg as copyreg + +else: + import tkinter as tk + import tkinter.simpledialog as tkSimpleDialog + import tkinter.messagebox as tkMessageBox + import tkinter.filedialog as tkFileDialog + from queue import Queue, Empty + from io import BytesIO + import copyreg + unicode = str # runcode needs "unicode" for Python2 + + +# IPython breaks code pickling in IDLE, must correct it... +try: + _code_pickle_original = copyreg.dispatch_table.get(types.CodeType, None) +except: + _code_pickle_original = None + +HAS_IPYTHON = True + +try: + import IPython + if IPython.__version__ < '0.12': + HAS_IPYTHON = False +except: + HAS_IPYTHON = False + +if HAS_IPYTHON: + # IPython + from IPython.core import page + from IPython.utils.warn import warn, error, fatal + from IPython.utils import io as ipython_io + from IPython.core.oinspect import call_tip + from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell + from IPython.utils.traitlets import Type + from IPython.zmq.kernelmanager import (KernelManager, SubSocketChannel, HBSocketChannel, + ShellSocketChannel, StdInSocketChannel) + + #from IPython.frontend.consoleapp import IPythonConsoleApp + from IPython.config.loader import KeyValueConfigLoader + from IPython.zmq.kernelapp import kernel_aliases + from IPython.lib.kernel import find_connection_file + +else: + # pacify later code from errors + TerminalInteractiveShell = object + KernelManager = SubSocketChannel = HBSocketChannel = object + ShellSocketChannel = StdInSocketChannel = object + Type = lambda x: None + +if _code_pickle_original: + # FIX THE PICKLE DISPATCH TABLE + copyreg.dispatch_table[types.CodeType] = _code_pickle_original + + +# IDLE +from idlelib.configHandler import idleConf +from idlelib.Delegator import Delegator +from idlelib.IdleHistory import History +import idlelib.AutoComplete # circular import avoidance for AutoCompleteWindow +import idlelib.AutoCompleteWindow as AutoCompleteWindow +import idlelib.CallTipWindow as CallTipWindow +from idlelib.UndoDelegator import UndoDelegator +import idlelib.PyShell as PyShell +from idlelib.PyShell import ModifiedInterpreter +from idlelib.HyperParser import HyperParser +from idlelib.Percolator import Percolator +from idlelib import SearchDialog +from idlelib import Bindings +from idlelib.MultiCall import MultiCallCreator + +# IdleX +from idlexlib import idlexMain +from idlexlib.extensions.SearchBar import SearchBar + +#------------------------------------ +# basic functions +#------------------------------------ + +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates +sp = lambda x: list(map(int, x.split('.'))) # convert tkinter Text coordinate to a li + + +def background(f): # from IPython project + """call a function in a simple thread, to prevent blocking""" + t = Thread(target=f) + t.start() + return t + +def debug(*args, **kw): + print(*args, **kw) +debug = lambda *args, **kw: None + +orig_ps1 = getattr(sys, 'ps1', None) +orig_ps2 = getattr(sys, 'ps2', None) + +#----------------------------------------------------------------------------- +# IdleX IPython Kernel Channels +#----------------------------------------------------------------------------- + +# AFAIK, Tkinter can't handle placing events on its event queue from a thread +# other than MainThread. IPython's 0MQ channels provides events in different +# threads, so these messages are placed into a Queue for Tkinter to handle +# from its MainThread. + +##DISPLAY_MESSAGES = True +##GET_HISTORY = not DISPLAY_MESSAGES + +DISPLAY_MESSAGES = False +GET_HISTORY = True + + +ChannelLock = Lock() +def channel_lock(func): # lock decorator + def f(*arg, **kw): + with ChannelLock: + return func(*arg,**kw) + return f + +MessageQueue = Queue() # place all messages into here + +class SimpleChannel(object): + @channel_lock + def call_handlers(self, msg): + if DISPLAY_MESSAGES: + print('[[%s]]' % self.channel_name, '-'*50) + print('Thread: %r' % threading.currentThread().getName()) + if isinstance(msg, dict): + msg2 = msg.copy() + del msg2['parent_header'] + else: + msg2 = msg + pprint(msg2) + # Place all messaged into a Queue so that + # the Tkinter event loop in MainThread can process them. + MessageQueue.put((self.channel_name, msg)) + + +class IdleXSubSocketChannel(SimpleChannel, SubSocketChannel): + channel_name = 'sub' + +class IdleXShellSocketChannel(SimpleChannel, ShellSocketChannel): + channel_name = 'shell' + +class IdleXStdInSocketChannel(SimpleChannel, StdInSocketChannel): + channel_name = 'stdin' + +class IdleXHBSocketChannel(SimpleChannel, HBSocketChannel): + channel_name = 'hb' + time_to_dead = 1.0 + +class IdleXKernelManager(KernelManager): + shell_channel_class = Type(IdleXShellSocketChannel) + sub_channel_class = Type(IdleXSubSocketChannel) + stdin_channel_class = Type(IdleXStdInSocketChannel) + hb_channel_class = Type(IdleXHBSocketChannel) + + +class IPyProxy(object): + + shell_images = [] + socket = 0 + + def __init__(self): + + self.active = None # reference to IPyIDLE instance + self.active_pyshell = None # reference to active PyShell editor instance + self.switching = False # flag for switching shell + self.allow_restart = True # flag for ScriptBinding.py behavior + self._onetime_kernel_restart_message = False + self.kernel_manager = None + self.argv_changed = False + self._shell = None + + self._saved_history = None + self._reset() + + def _reset(self): + self._output = Queue() # for output to .text widget of PyShell + self._output_pyerr = Queue() + self._execute_queue = Queue() + self.execution_state = None + self._do_input = None + self._got_pyin = False + self._expected_execute_replies = [] + self.calltipwindow = None + self.readline_canceled = False + + + def get_fake_socket(self): + self.socket += 1 + return self.socket + + @property + def connection_file(self): + return idlexMain.ipython_connection_file + + def start_km(self): + self.stop_km() # make sure old was stopped + self._expected_execute_replies = [] + kwargs = {} + + found_cf = '' + + cf = self.connection_file + if cf: + found_cf = self.get_connection_file(cf) + + if idlexMain.ipython_argv: + kwargs['extra_arguments'] = idlexMain.ipython_argv + + + km = IdleXKernelManager() + self.kernel_manager = km + + if found_cf: + msg = '\n IPython using connection file:\n %s\n' % found_cf + try: + km.connection_file = found_cf + km.load_connection_file() + except IOError: + connection_file = '' + msg = '\n Unable to load connection file:\n %s\n' % found_cf + km.connection_file = '' + self.kernel_manager = km + + e = self.active_pyshell + print(msg, file=sys.stderr) + if e: + self._output.put((True, 'stderr', msg)) + #e.text.insert('end', msg, tags='stderr') + + if not found_cf: + km.start_kernel(**kwargs) + + km.start_channels() + if GET_HISTORY: + msg_id = km.shell_channel.history(hist_access_type='tail', n=1000) + #km.shell_channel.execute('None', silent=True) + self.switching = False + km.hb_channel.unpause() + self.active_pyshell.executing = 0 + + def _windows_error(self, err): + print('A Windows-specific error occurred:', file=sys.stderr) + print(err,file=sys.stderr) + + def stop_km(self): + km = self.kernel_manager + if km: + if not self.connection_file: + try: + km.shutdown_kernel(restart=False) + except WindowsError as err: + self._windows_error(err) + except RuntimeError as err: + pass + km.cleanup_connection_file() + background(km.stop_channels) + self.shell.execution_count = 1 + self.kernel_manager = None + + self._execute_queue = Queue() + self._expected_execute_replies = [] + + self._saved_history = None + self.switching = False + + def interrupt(self): + self.readline_canceled = True + self.kernel_manager.interrupt_kernel() + + def request_kernel_restart(self): + if self.switching: + self.switching = False + return False + + if not self.allow_restart: + e = self.active_pyshell + if not self._onetime_kernel_restart_message: + self._onetime_kernel_restart_message = True + msg = ('\n Kernel restart is not allowed.\n ' + + 'To allow it, check "Allow IPython Kernel Restart" under "Shell".') + self._output.put((True, 'stderr', msg)) + return False + else: + self._onetime_kernel_restart_message = False + + if self.argv_changed: + self.argv_changed = False + self.stop_km() + self.start_km() + else: + if self.connection_file: + e = self.active_pyshell + msg = ('\n Will not restart kernel when connected ' + + 'to a remote kernel:\n %s\n' % self.connection_file) + self._output.put((True, 'stderr', msg)) + return False + self._expected_execute_replies = [] + self.kernel_manager.restart_kernel() + if GET_HISTORY: + self.kernel_manager.shell_channel.history(hist_access_type='tail', n=1000) + self.kernel_manager.shell_channel.execute('None', silent=True) + self.shell.execution_count = 1 + self.regenerate_prompt_string() + self.execution_state = 'idle' + self.readline_canceled = True + return True + + def cleanup_connection_file(self): + if not self.connection_file: + km = self.kernel_manager + if km and km.has_kernel: + km.cleanup_connection_file() + + @property + def autocompletewindow(self): + return self.active.autocompletewindow + + @property + def executing(self): + return ((self._execute_queue.qsize() > 0) or + (len(self._expected_execute_replies) > 0)) + + def enqueue_execute(self, source, *args, **kw): + self._execute_queue.put((source, args, kw)) + + def _exec_if_idle(self): + # called from poll_subprocess + #print('what', self.execution_state, len(self._expected_execute_replies)) + if (self.execution_state == 'idle' and + len(self._expected_execute_replies) == 0): + try: + m = self._execute_queue.get_nowait() + except Empty: + return + self.active_pyshell.executing = 0 # in case Ctrl+C aborted execution + source, args, kw = m + exec_callback = kw.pop('exec_callback') + finish_callback = kw.pop('finish_callback') + km = self.kernel_manager + if exec_callback: + try: + exec_callback() + except Exception as err: + print('Exception in callback: %r' % err) + msg_id = km.shell_channel.execute(source, *args, **kw) + #print('sending: %s: %r' % (msg_id, source)) + self._expected_execute_replies.append((msg_id, finish_callback)) + + if self.execution_state is None: + self.execution_state = 'idle' # never got initialized + + if self.execution_state not in ['busy', 'idle']: + print('Unknown execution state: %r' % self.execution_state) + + # TODO: failsafe if execute reply never gets received.... + #print('_exec_if_idle', self.execution_state, self._expected_execute_replies) + + def _get_console_output(self): + # combine all the output for the console + _output = self._output + _output_pyerr = self._output_pyerr + retval = [] + while 1: # flush the stdout/stderr queue + try: + stuff = _output.get(block=False) + retval.append(stuff) + except Empty: + break + while 1: # flush the pyerr queue + try: + stuff = _output_pyerr.get(block=False) + retval.append(stuff) + except Empty: + break + return retval + + def write_console_output(self): + # handle console output, write before the prompt + tkconsole = self.active_pyshell + out = self._get_console_output() + if out: + text = tkconsole.text + for before, name, data in out: + if before: + # remember the location of iomark + text.mark_set('iomark2', 'iomark') + text.mark_gravity('iomark2', text.mark_gravity('iomark')) + + + text.mark_set('iomark', 'before_prompt') # Undo Delegator + text.mark_gravity("before_prompt", "right") + if name in ['stdout', 'stderr']: + #print('%r' % data) + text.insert('before_prompt', data, tags=name) + + elif name == 'image/png': + if HAS_PIL: + prior_root = tk._default_root # Work around a bug in IDLE + tk._default_root=tkconsole.top + + si = ShellImage(text, data, format='image/png') + text.window_create('before_prompt', window=si) + self.shell_images.append(si) + + tk._default_root = prior_root + + else: + text.insert('before_prompt', + ("\nMissing 'ImageTk' from PIL to display image/png.\n" + + 'Please install Python Imaging Library with Tkinter support.\n' + + 'http://www.pythonware.com/products/pil/' + )) + else: + print('Unsupported image type: %r' % name) + + text.mark_gravity("before_prompt", "left") + if tkconsole.text.compare('iomark2', '<', 'before_prompt'): + tkconsole.text.mark_set('iomark2', 'before_prompt') + + # restore iomark + text.mark_set('iomark', 'iomark2') + text.mark_unset('iomark2') + + else: # after prompt + if name in ['stdout', 'stderr']: + text.insert('end', data, tags=name) + + tkconsole.text.see('insert') + + def clear_shell_images(self): + for i in self.shell_images: + i.destroy() + self.shell_images = [] + + def regenerate_prompt_string(self): + shell = self.shell + shell.hooks.pre_prompt_hook() + try: + prompt = shell.separate_in + shell.prompt_manager.render('in') + except Exception: + traceback.print_exc() + shell.showtraceback() + + sys.ps1 = prompt + + try: + prompt = shell.prompt_manager.render('in2') + except Exception: + traceback.print_exc() + shell.showtraceback() + + sys.ps2 = prompt + + @property + def shell(self): + if self._shell is None: + e = self.active_pyshell + idle_io = (e, e.stdout, e.stderr) + km = ipyProxy.kernel_manager + self._shell = IPyIDLEShell(user_ns={}, + kernel_manager=km, + io=idle_io) + return self._shell + + def call_message_handlers(self, m): + channel_name, msg = m + if channel_name != 'hb': + msg_type = msg['header']['msg_type'] + h = getattr(self, '_handle_%s_%s' % (channel_name, msg_type), None) + if h: + h(msg) + else: + print('Not Implemented: %s on %s channel' % (msg_type, channel_name)) + pprint(msg) + else: + print('heartbeat:', msg) + + def get_the_calltip(self, obj): + # called from FakeSubProcess + #print('get_the_calltip: %r' % obj) + msg_id = self.kernel_manager.shell_channel.object_info(obj) + + + def restore_IDLE_history(self): + # forget the IPython command history and restore + # what the normal IDLE shell had + if self.active_pyshell and self._saved_history: + h = History(active_pyshell.text) + h.history = self._saved_history + h.history_prefix = None + h.history_pointer = None + self._saved_history = None + active_pyshell.history = h + + def set_IPython_history(self, items): + #print('set_ipython_history', items) + e = self.active_pyshell + if e: + history = IPythonHistory(e.text) + history.history = items + e.history = history + else: + print('Unable to set history - not active pyshell', file=sys.stderr) + + + def _from_this_session(self, msg): + km_session = ipyProxy.kernel_manager.session.session + parent = msg['parent_header'] + if not parent: + return True + else: + return parent.get('session') == km_session + + def get_connection_file(self, connection_file): + try: + connection_file = find_connection_file(connection_file) + except IOError as err: + s = 'Unable to find: %r' % connection_file + print(s, file=sys.stderr) + if self.active: + tkMessageBox.showerror('IPython Connection File', message=s, + parent=self.active.editwin.text) + return '' + return connection_file + + #------------------------------- + # Channel Message Handlers + #------------------------------- + def check_session(func): + def f(self, msg, *arg, **kw): + if not self._from_this_session(msg): + #print('not from session, call to %s' % func) + return + else: + #print('is from session, call to %s' % func) + return func(self, msg, *arg, **kw) + return f + + + @check_session + def _handle_sub_stream(self, msg): + datetime = msg['header']['date'] + before = True + content = msg['content'] + name = content['name'] # stout or stderr + data = content['data'] + self._output.put((before, name, data)) + + @check_session + def _handle_sub_status(self, msg): + content = msg['content'] + ec = content['execution_state'] + self.execution_state = ec + png_data = content.get('image/png') + self._output_png_data(png_data) + + def _handle_sub_pyerr(self, msg): + pass + + @check_session + def _handle_sub_pyout(self, msg): + content = msg['content'] + execution_count = int(content["execution_count"]) + self.shell.execution_count = execution_count + format_dict = msg["content"]["data"] + # from displayhook.py + outprompt = self.shell.prompt_manager.render('out') #write_output_prompt + result_repr = format_dict['text/plain'] + if '\n' in result_repr: + prompt_template = self.shell.prompt_manager.out_template + if prompt_template and not prompt_template.endswith('\n'): + result_repr = '\n' + result_repr + last = self.shell.separate_out2 + + out = outprompt + result_repr + last + out += '\n' + self._output.put((True, 'stdout', out)) + self._output_png_data(format_dict.get('image/png', None)) + + + def _handle_sub_pyin(self, msg): + # acknowledgment of sent command + pass + + def _handle_sub_shutdown_reply(self, msg): + self._expected_execute_replies = [] + + def _handle_shell_shutdown_reply(self, msg): + pass + + @check_session + def _handle_shell_execute_reply(self, msg): + self.kernel_manager.sub_channel.flush() + + parent_id = msg['parent_header']['msg_id'] + + content = msg['content'] + status = content['status'] + + expecting = self._expected_execute_replies + finish_callback = None + + expected_msg_id = [msg_id for msg_id,callback in expecting] + + a = [n for n, i in enumerate(expecting) if i[0] == parent_id] + if a: + if a[0] == 0: + #print('in order') + pass + else: + #print('out of order') + pass + + emsgid, finish_callback = expecting.pop(a[0]) + else: + #print('not expecting') + pass + + ec = content.get("execution_count", None) + if ec: + if int(ec + 1) != self.shell.execution_count: + self.shell.execution_count = int(ec + 1) + self.regenerate_prompt_string() + if status == 'error': + name = 'stderr' + self._output_pyerr.put((True, 'stderr', '\n')) + for frame in content["traceback"]: + self._output_pyerr.put((True, name, frame)) + self._output_pyerr.put((True, 'stderr', '\n')) + + if 'payload' in content: + for item in content["payload"]: + text = item.get('text', None) + if text: + Pager(self.active.editwin.top, + 'IDLE IPython Pager', + text) + + filename = item.get('filename', None) + if filename: + flist = self.active_pyshell.flist + flist.open(filename) + # TODO: handle going to a line number + + if finish_callback: + finish_callback() + + def _handle_shell_history_reply(self, msg): + content = msg['content'] + items = content['history'] + + # TODO: handle case if history request failed + history_items = content['history'] + items = [] + last_cell = "" + for _, _, cell in history_items: + cell = cell.rstrip() + if cell != last_cell: + items.append(cell) + last_cell = cell + + self.set_IPython_history(items) + + def _handle_shell_object_info_reply(self, msg): + """ Display a call tip """ + # show call tip + if self.calltipwindow: + self.calltipwindow.hidetip() + + editwin = self.active.editwin + self.calltipwindow = ctw = CallTipWindow.CallTip(editwin.text) + + content = msg['content'] + call_info, doc = call_tip(content, format_call=True) + maxlines = 15 + # code from Qt console call_tip_widget.py + if doc: + match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc) + if match: + doc = doc[:match.end()] + '\n[Documentation continues...]' + else: + doc = '' + + if call_info: + doc = '\n\n'.join([call_info, doc]) + + hp = HyperParser(editwin, "insert") + sur_paren = hp.get_surrounding_brackets('(') + try: + if sur_paren: + p1, p2 = sur_paren + MARK_RIGHT = "calltipwindowregion_right" + editwin.text.mark_set(MARK_RIGHT, p2) + ctw.showtip(doc, p1, p2) + except: + print('CALLTIP ERROR', '-'*50) + traceback.print_exc() + + + def _handle_shell_complete_reply(self, msg): + """ Display an AutoCompleteWindow with this reply """ + self.autocompletewindow.hide_window() + m = msg['content'].get('matches', None) + if not m: + return + + comp_start = msg['content']['matched_text'] + + # remove leading matched_text from the results + # up to the last "." + p = re.split("[./]", comp_start) + + if len(p) > 1: + ignore = '.'.join(p[:-1]) + remain = p[-1] + offset = len(ignore) + 1 # "+ 1" for the last . + m = [i[offset:] for i in m] + comp_start=remain + + # Take the result and make it compatible + # with IDLE's autocomplete extension + comp_lists = (m,m) + mode = AutoCompleteWindow.COMPLETE_ATTRIBUTES + userWantsWin = True + complete = True + self.autocompletewindow.show_window(comp_lists, + "insert-%dc" % len(comp_start), + complete, + mode, + userWantsWin) + + def _handle_stdin_input_request(self, msg): + self.kernel_manager.sub_channel.flush() + + prompt = msg["content"]["prompt"] + if not isinstance(prompt, (str, unicode)): + prompt = str(prompt) + + self._do_input = prompt + # _do_input is a flag used in poll_subprocess + # to initialize readline. + tkconsole = self.active_pyshell + + text = tkconsole.text + text.mark_set('before_prompt', 'iomark linestart') + text.mark_gravity("before_prompt", "left") + text.insert('before_prompt', prompt) + text.mark_set('iomark', 'end-1c') + + self.readline_canceled = False + debug('ENTER READLINE') + raw_data = tkconsole.readline() + debug('EXIT READLINE: %r' % raw_data) + if tkconsole.closing: + # Note: tkconsole.closing must be tested in case + # PyShell is closed during readline + return + + if self.readline_canceled: + # Ctrl+C or restart of kernel + self.readline_canceled = False + return + + raw_data = raw_data[:-1] # eliminate \n + debug('SENDING RAW_INPUT: %r' % raw_data) + ipyProxy.kernel_manager.stdin_channel.input(raw_data) + text.mark_set('before_prompt', 'insert') + if self.active_pyshell: + self.active_pyshell.resetoutput() + + def _handle_sub_display_data(self, msg): + data = msg['content']['data'] + png_data = data.get('image/png', None) + self._output_png_data(png_data) + + def _output_png_data(self, png_data): + if png_data: + png = decodestring(png_data.encode('ascii')) + self._output.put((True, 'image/png', png)) + + +ipyProxy = IPyProxy() # Launch a singleton for IPyProxy + + +class ShellImage(tk.Label): + def __init__(self, text, data, format=None): + # format - TODO allow for SVG in future + # For now, only handle .png + + fid = BytesIO(data) + im = ImageTk.Image.open(fid) + try: + tkim = ImageTk.PhotoImage(im) + except ImportError: + tkim = None + pass + tk.Label.__init__(self, text, image=tkim, takefocus=0, borderwidth=0) + + self.text = text + self.tkim = tkim + self.im = im + self.bind('<4>', lambda e: text.event_generate('<4>')) + self.bind('<5>', lambda e: text.event_generate('<5>')) + self.bind('<3>', self.rmenu) + + def rmenu(self, event=None): + # Do "save image" + # Tkinter can not place an image on the clipboard. + rmenu = tk.Menu(self.text, tearoff=0) + + rmenu.add_command(label='Save Image As...', command=self.saveas) + rmenu.tk_popup(event.x_root, event.y_root) + + def saveas(self): + s = tkFileDialog.SaveAs(parent=self.text, + filetypes=[('Portable Network Graphic', '*.png')]) + initial = str(self.tkim) + '.png' + filename = s.show(initialfile=initial) + if filename: + try: + self.im.save(filename, format='png') + except Exception as err: + traceback.print_exc() + + +#---------------------- +# Delegators for Text +#---------------------- +# Based on code from: http://wiki.ipython.org/Old_Embedding/Tkinter +ansi_colors = {'0;30': '#000000', # black + '0;31': '#880000', # red + '0;32': '#008800', # green + '0;33': '#664400', # brown + '0;34': '#000088', # blue + '0;35': '#880088', # purple + '0;36': '#008888', # cyan + '0;37': '#AAAAAA', # white + + '1;30': '#555555', # light black + '1;31': '#AA0000', # light red + '1;32': '#00AA00', # light green + '1;33': '#888800', # light brown + '1;34': '#0000AA', # light blue + '1;35': '#AA00AA', # light purple + '1;36': '#00AAAA', # light cyan + '1;37': '#000000', # light white + + '01;30': '#555555', # light black + '01;31': '#AA0000', # light red + '01;32': '#00AA00', # light green + '01;33': '#888800', # light brown + '01;34': '#0000AA', # light blue + '01;35': '#AA00AA', # light purple + '01;36': '#00AAAA', # light cyan + '01;37': '#000000', # light white + } + + +ansi_re = re.compile(r'\x01?\x1b\[(.*?)m\x02?') +def strip_ansi(s): + return ansi_re.sub("", s) + +class AnsiColorDelegator(Delegator): + # Colorize IPython ansi color codes + def __init__(self, *args, **kw): + Delegator.__init__(self, *args, **kw) + + def insert(self, index, chars, tags=None): + chars = chars.replace('\r', '') + delegate = self.delegate + + index = delegate.index(index) + if tags: + tags = tags.replace('stdout', '') + + m = ansi_re.search(chars) + if not m: + delegate.insert(index, chars, tags) + else: + if tags is None: + tags = '' + active_tag = '' + prior = 0 + ic = 0 + while m: + s = m.start() + e = m.end() + b = chars[prior:s] + if len(b): + delegate.insert(index + '+%ic' % ic, b, tags + ' ' + active_tag) + ic += len(b) + + active_tag = ansi_re.split(chars[s:e])[1] + prior = e + m = ansi_re.search(chars, prior) + + delegate.insert(index + '+%ic' % ic, chars[prior:], tags + ' ' + active_tag) + + +class IPythonHistory(History): + + def history_store(self, source): + debug('storing: ', source, self) + source = source.rstrip() # preserve leading whitespace + if len(source) > 0: # avoid duplicates + try: + if source in self.history[-10:]: + self.history.remove(source) + except ValueError: + pass + self.history.append(source) + self.history_pointer = None + self.history_prefix = None + + +#----------------------------------------- +# PyShell Subprocess-to-IPython Interface +#----------------------------------------- +eloop = """try: + __IDLE_eventloop() +except NameError: + pass +except Exception as err: + __IDLE_eventloop_error(err) +""" # EventLoop.py support + +class FakeSubProcess(object): + # used by BridgeInterpreter + restarting = False + def __init__(self, interp): + self.interp = interp # pointer to idleIPython instance + self.sock = ipyProxy.get_fake_socket() + + def __getattr__(self, name): + print('__getattr sub__', name) + def f(*args, **kwargs): + return None + return f + + def putmessage(self, message): + seq, resq = message + how = resq[0] + if how in ("CALL", "QUEUE"): + cmd, method, code, d = resq[1] + if cmd == 'exec' and method =='runcode': + #self.interp.runcommand(code[0]) # for EventLoop.py + self.interp.runcommand(eloop) # eventloop.py support only + + def remotecall(self, oid, methodname, args, kwargs): + # pacify IDLE's default autocomplete and calltip extensions + #print('remotecall', oid, methodname, args, kwargs) + if methodname == "get_the_completion_list": + # the event handles autocomplete in class IPyIDLE + return [], [] + elif methodname == "get_the_calltip": + obj = args[0] # get tip for this object string + ipyProxy.get_the_calltip(obj) + return None + + def close(self): + pass + + +class BridgeInterpreter(ModifiedInterpreter): + # IDLE creates a new instance of ModifiedInterpreter on each restart. + # This relies on ipyProxy to do the actual interface to the IPython kernel + + def __init__(self, tkconsole): + ModifiedInterpreter.__init__(self, tkconsole) + ipyProxy.interp = self + self.top = self.tkconsole.top + self.poll_id = None # poll_subprocess .after id + self.debugger = None + + self.IMAGES = [] + + def set_before_prompt(self): + # mark set the "before_prompt" in the console window + backlines = sys.ps1.count('\n') + text = self.tkconsole.text + text.mark_set('before_prompt', 'iomark linestart') + if backlines: + text.mark_set('before_prompt', 'before_prompt -%i lines' % backlines) + text.mark_gravity("before_prompt", "left") + + def beginexecuting(self): + debug('beginexecuting') + tkconsole = self.tkconsole + text = getattr(tkconsole, 'text', None) + if text is None: + # text can be None if the shell is closed after + # the execution has been queued, but before it gets + # executed. For example, press F5 and then quickly close the shell. + print('.text not found during beginexecuting') + return + + a = text.get('iomark', 'end') + + # remove excess whitespace due to newline and indent + i1 = a.rfind('\n',0, len(a)-1) + 1 + text.delete('iomark+%ic' % i1, 'end-1c') + + tkconsole.resetoutput() + text.mark_set('before_prompt', 'iomark linestart') + text.mark_gravity("before_prompt", "left") + tkconsole.executing = 1 + text.see('insert') + + def endexecuting(self): + debug('endexecuting') + tkconsole = self.tkconsole + text = getattr(tkconsole, 'text', None) + if text is None: + print('.text not found during endexecuting') + return + ipyProxy.regenerate_prompt_string() + tkconsole.resetoutput() + tkconsole.showprompt() + + if not ipyProxy.executing: + tkconsole.executing = 0 + tkconsole.canceled = 0 + + + def _finalize_init(self): + # For an unknown reason (possibly a race condition), IPython + # sometimes fails to send an execute reply over the 0MQ channels + # for this no-op "None" command. For now, ignore it. + # FIXME: Determine why IPython fails to send execute reply sometimes. + return # skip the execution of this command. + def fcb(): + self.tkconsole.executing = 0 + ipyProxy.enqueue_execute('None', + silent=True, + exec_callback=None, + finish_callback=fcb) + + def start_subprocess(self): + #print('start_subprocess') + welcome = ' Welcome to the IdleX IPython shell. ' + B = ['-'*len(welcome), + welcome, + '-'*len(welcome), + ipyProxy.shell.banner] + + PyShell.PyShell.COPYRIGHT = '\n'.join(B) + ipyProxy.regenerate_prompt_string() + self._poll_cancel(reschedule=True) + self.rpcclt = FakeSubProcess(self) + self.transfer_path(with_cwd=True) + self._finalize_init() + self.restarting = False + text = self.tkconsole.text + return self.rpcclt + + def restart_subprocess(self, with_cwd=False): + if self.restarting: + return self.rpcclt + self.rpcclt.restarting = True + self.restarting = True + + if ipyProxy.request_kernel_restart(): + pass + else: + print('unable to restart kernel', file=sys.stderr) + self.restarting = False + self.rpcclt.restarting = False + return + + console = self.tkconsole + was_executing = console.executing + + if console.reading and ipyProxy.readline_canceled == True: + console.reading = False + console.top.quit() # IDLE workaround + + # annotate restart in shell window and mark it + console.text.delete("iomark", "end-1c") + console.write('\n') + if was_executing: + console.write('\n') + halfbar = ((int(console.width) - 16) // 2) * '=' + console.write(halfbar + ' RESTART ' + halfbar) + console.text.mark_set("restart", "end-1c") + console.text.mark_gravity("restart", "left") + + ipyProxy.regenerate_prompt_string() + console.showprompt() + self.set_before_prompt() + self.transfer_path(with_cwd=with_cwd) + self._finalize_init() + self._poll_cancel(reschedule=True) + self.rpcclt = FakeSubProcess(self) # fake out IDLE + self.restarting = False + return self.rpcclt + + def poll_subprocess(self): + tkconsole = self.tkconsole + km = ipyProxy.kernel_manager + + if (not self.restarting and not ipyProxy.kernel_manager.is_alive + and not tkconsole.closing): + self.tkconsole.write("\n\n Kernel Died.\n Returing to IDLE's shell...\n") + self.tkconsole.text.event_generate('<>') + return + + if not tkconsole.closing: + self._poll_cancel(reschedule=True) + + while 1: + try: + m = MessageQueue.get_nowait() + except Empty: + break + ipyProxy.call_message_handlers(m) + + ipyProxy.write_console_output() + ipyProxy._exec_if_idle() + + + def _poll_cancel(self, reschedule=False): + """ For managing the poll_subprocess timer """ + if self.poll_id: + self.top.after_cancel(self.poll_id) + if reschedule: + self.poll_id = self.top.after(25, self.poll_subprocess) + + def interrupt_subprocess(self): + ipyProxy.interrupt() + c = self.tkconsole + + def kill_subprocess(self): + ipyProxy.restore_IDLE_history() + self._poll_cancel() + self.poll_id = None + #self.compile = None + + def runsource(self, source): + # Used by PyShell for the interactive prompt + # Returns True if the prompt can accept more lines. + #print('runsource %r' % source) + shell = ipyProxy.shell + r = shell.input_splitter.source_raw_reset() + shell.input_splitter.push(source + '\n') + more = shell.input_splitter.push_accepts_more() + if not more: + self.runcode(source, store_history=True, do_exec=True) + return more + + def runcommand(self, source): + # Used to run commands silently in IDLE, + # like path initialization, etc... + source = source.rstrip() # Python 2.6 compatibility + exec_callback = finish_callback = None + ipyProxy.enqueue_execute(source, silent=True, + exec_callback=exec_callback, + finish_callback=finish_callback) + + def runcode(self, source, store_history=False, do_exec=True): + # Called from runsource usually. ScriptBinding, SubCode, and RunSelection + # call this as well. Used to run code objects originally. + #print('runcode: %r' % source) + if isinstance(source, types.CodeType): + store_history = False + f = source.co_filename + if os.path.isfile(f): + if sys.version < '3': + # Python3 really did fix the unicode problem of Python2. + try: + f = unicode(f.decode('utf8')) + except Exception as err: + print('Unable to decode filename: %r' % f) + pass + # ScriptBinding's Run Module should invoke this code path... + #source = "%%run %s" % f # Only works with IPython 0.13 + # Using get_ipython() is compatible with 0.12 and 0.13 + f = f.replace('"', '\\"') + f = f.replace("'", "\\'") + + runmagic = '%%run "%s"' % f + source = """if 1: + __iP__=get_ipython(); + __iP__.run_cell(%r); del(__iP__)""" % runmagic + # run_line_magic only available in 0.13 + else: + # regular code object - RunSelection.py should invoke this code path... + source = """if 1: # Running a Code Object from IPyIDLE + import marshal as __marshal + exec(__marshal.loads(%r)); del __marshal""" % marshal.dumps(source) + + elif isinstance(source, (str, unicode)): + if not store_history: + # SubCode should invoke this codepath... + source = """__iP__=get_ipython();__iP__.run_cell(%r); del(__iP__)""" % source + else: + # A string of the command from the interactive shell invokes this path... + pass + + def do_exec_cb(): + self.beginexecuting() + + if do_exec: + exec_callback = do_exec_cb + finish_callback = self.endexecuting + else: + exec_callback = None + def fcb(): + self.tkconsole.executing = 0 + finish_callback = fcb + + self.tkconsole.executing = 1 # console is executing + ipyProxy.enqueue_execute(source, silent=not store_history, + exec_callback=exec_callback, + finish_callback=finish_callback) + + if not store_history: + ipyProxy.enqueue_execute("""if 1: + try: # if pylab inline + from IPython.zmq.pylab.backend_inline import flush_figures as __flush + __flush(); del __flush + except: pass""", + silent=True, + exec_callback=None, + finish_callback=None) + + def getdebugger(self): + pass + + def transfer_path(self, with_cwd=False): + if with_cwd: # Issue 13506 + path = [''] # include Current Working Directory + path.extend(sys.path) + else: + path = sys.path + + self.runcommand("""if 1: + import sys as _sys + _sys.path = %r + del _sys + \n""" % (path,)) + + +class IPyIDLEShell(TerminalInteractiveShell): + """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" + _executing = False + + # The only reason this class exists is to access its input_splitter + # and its prompt generating routines... + + def __init__(self, *args, **kwargs): + self.km = kwargs.pop('kernel_manager') + self.io = kwargs.pop('io') + self.session_id = self.km.session.session + super(IPyIDLEShell, self).__init__(*args, **kwargs) + + + def init_io(self): + stdin, stdout, stderr = self.io + io = ipython_io + io.stdout = stdout + io.stdin = stdin + io.stderr = stderr + + def init_completer(self): + from IPython.core.completerlib import (module_completer, + magic_run_completer, cd_completer) + + self.set_hook('complete_command', module_completer, str_key = 'import') + self.set_hook('complete_command', module_completer, str_key = 'from') + self.set_hook('complete_command', magic_run_completer, str_key = '%run') + self.set_hook('complete_command', cd_completer, str_key = '%cd') + + +class Pager(tk.Toplevel): #textView.py modified + """A simple text viewer dialog for IDLE + + """ + def __init__(self, parent, title, raw_text): + + tk.Toplevel.__init__(self, parent) + self.configure(borderwidth=5) + + self.geometry("=%dx%d+%d+%d" % ( + parent.winfo_width(), + max([parent.winfo_height() - 40, 200]), + parent.winfo_rootx() + 10, + parent.winfo_rooty() + 10)) + + self.bg = '#ffffff' + self.fg = '#000000' + + self.CreateWidgets() + self.title(title) + self.protocol("WM_DELETE_WINDOW", self.Ok) + self.parent = parent + self.text.focus_set() + self.bind('',self.Ok) #dismiss dialog + self.bind('',self.Ok) #dismiss dialog + self.per = Percolator(self.text) + self.ansi = AnsiColorDelegator() + self.doc = PyShell.ModifiedUndoDelegator() + + self.per.insertfilter(self.ansi) + self.per.insertfilter(self.doc) + + for code in ansi_colors: + self.text.tag_config(code, + foreground=ansi_colors[code]) + + self.text.insert(0.0, raw_text) + self.apply_bindings() + + # get SearchBar working with this + self.top = self + self.sb = SearchBar(self) + + self.text.mark_set('iomark', 'end') + self.text.mark_set('before_prompt', 'end') + self.text.mark_set('insert', '1.0') + + + + def search(self): + self.text.event_generate("<>") + + + def CreateWidgets(self): + + frameText = tk.Frame(self, relief=tk.SUNKEN, height=700) + frameButtons = tk.Frame(self) + self.buttonOk = tk.Button(frameButtons, text='Close', + command=self.Ok, takefocus=tk.FALSE) + self.buttonSearch = tk.Button(frameButtons, text='Search /', + command=self.search, takefocus=tk.FALSE) + + self.scrollbarView = tk.Scrollbar(frameText, orient=tk.VERTICAL, + takefocus=tk.FALSE, highlightthickness=0) + width = idleConf.GetOption('main','EditorWindow','width') + height = idleConf.GetOption('main', 'EditorWindow', 'height') + + text_options = { + 'name': 'text', + 'padx': 5, + 'wrap':tk.WORD, + 'highlightthickness':0, + 'fg':self.fg, + 'bg':self.bg, + 'width': width, + 'height': height} + + + self.text = text = MultiCallCreator(tk.Text)(frameText, **text_options) + fontWeight='normal' + if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'): + fontWeight='bold' + self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), + idleConf.GetOption('main','EditorWindow','font-size'), + fontWeight)) + + + self.scrollbarView.config(command=self.text.yview) + self.text.config(yscrollcommand=self.scrollbarView.set) + self.buttonSearch.pack(side=tk.LEFT) + self.buttonOk.pack(side=tk.LEFT) + self.scrollbarView.pack(side=tk.RIGHT,fill=tk.Y) + self.text.pack(side=tk.LEFT,expand=tk.TRUE,fill=tk.BOTH) + frameButtons.pack(side=tk.BOTTOM,fill=tk.X) + frameText.pack(side=tk.TOP,expand=tk.TRUE,fill=tk.BOTH) + + # editwin mock (so SearchBar works) + self.status_bar = frameButtons + + def Ok(self, event=None): + self.destroy() + + def apply_bindings(self, keydefs=None): + if keydefs is None: + keydefs = Bindings.default_keydefs + text = self.text + text.keydefs = keydefs + + invalid = [] + for event, keylist in keydefs.items(): + if keylist: + try: + text.event_add(event, *keylist) + except tk.TclError as err: + invalid.append((event, keylist)) + text.event_add('<>', '/') # VIM key binding for search + +#------------------------------------- +# The IDLE Extension +#------------------------------------- + +EXT_NAME = 'IPyIDLE' + +def get_cfg(cfg, type="bool", default=True): + return idleConf.GetOption("extensions", EXT_NAME, + cfg, type=type, default=default) + +def set_cfg(cfg, b): + return idleConf.SetOption("extensions", EXT_NAME, + cfg,'%s' % b) + + + + +COMPLETE_CHARS = string.ascii_letters + string.digits + "_" + '.' + '/' + +class OverBinding(object): + """ class for replacing bindings of MultiCall-wrapped Widget """ + # Tkinter has no facility to recover the original Python function + # bound to a Tk event. This is fundamentally a broken design. + # Now, add on top of that broken design the MultiCall handler... + + def __init__(self, tkobj, basewidget=None): + self.tkobj = tkobj + if basewidget is None: + raise Exception('Must specifiy a base widget class') + self.basewidget = basewidget + self.old = {} + + + _tk_callback = r"""(?<=if {"\[)[\d]*[\S]*""" + def _get_cb(self, cb): + """ Parse out the TCL proc """ + # Tkinter .bind returns the TCL command that calls the internal CallWrapper + # instance in the tkinter code. It needs to be parsed + # to remove the actual callback... + + # For example, .bind may return + # if {"[139690432888560callback %# %b %f %h %k %s %t %w %x %y %A %E %K %N %W %T %X %Y %D]" == "break"} break + # when it ought to return 139690432888560callback + + m = re.search(self._tk_callback, cb) + if m: + cb = m.group() + return cb + + + def bind(self, event, newbind): + cb = self.tkobj.bind(event) + self.old[event] = cb + self.tkobj.bind(event, newbind) + + def call_original(self, event, tkev): + """ Pass the event to the originally bound function. """ + + tkobj = self.tkobj + if event in self.old: + if event.startswith('<<'): + e = self._get_cb(self.old[event]) + # Assuming that the virtual event takes no arguments... + tkobj.tk.call(e) + else: + func = tkobj.bind(event) + tkobj.bind(event, self.old[event]) + tkobj.event_generate(event) + tkobj.bind(event, func) + else: + tkobj.event_generate(event) + + def restore(self): + # need to use basewidget because + # Tkinter uses old-style classes in 2.x series, + # so "super" doesn't work... + # The purpose is to bypass MultiCall + tkobj = self.tkobj + old = self.old + for virtual in old: + cb = old[virtual] + if virtual.startswith('<<'): + cb = self._get_cb(cb) + #print('Restoring: %r : %r' % (virtual, cb)) + self.basewidget.bind(tkobj, virtual, cb) + +ipyList = [] # list of IPyIDLE instances + +class IPyIDLE(object): + menudefs = [('shell', [('!IPython Shell', '<>'), + ('!Allow IPython Kernel Restart', '<>'), + ('Adjust Kernel Arguments', '<>'), + ])] + + def check_ipython(func): + def f(self, *arg, **kw): + if HAS_IPYTHON: + return func(self, *arg, **kw) + else: + t = self.editwin.top + tkMessageBox.showinfo('IPython Missing', + 'IPython not available', + parent=t) + self.editwin.setvar('<>', 0) + self.editwin.setvar('<>', 0) + return "break" + return f + + + def __init__(self, editwin): + ipyList.append(self) + # If IPython gets installed into the shell, then all open editors need + # to be modified as well, hence the saving of IPyIDLE instances. + + self.text_binding = OverBinding(editwin.text, tk.Text) + self._save_ps1 = None + self._save_ps2 = None + self._save_shell_title = None + self._save_copyright = None + self._save_squeezer = None + + self.first_load = True # flag if shell is first starting and ipython needs to be loaded + self.editwin = editwin + self.console = editwin + self.text = editwin.text + + if isinstance(editwin, PyShell.PyShell): + self.is_shell = True + self.text.bind('<>', self.toggle_IPython) + self.text.bind('<>', self.toggle_restart) + self.text.bind('<>', self.adjust_command_line) + else: + self.is_shell = False + + if not HAS_IPYTHON: + return + + self.last_codeline = '' + + # each editorwindow object gets an autocomplete window + self.autocompletewindow = AutoCompleteWindow.AutoCompleteWindow(self.text) + editwin.text.bind('', self.focus_event, '+') + + + enable = get_cfg('ipython', type="bool", default=True) + if idlexMain.ipython_argv or idlexMain.ipython_connection_file: + enable = True + + self.allow_restart = get_cfg('allow_restart', type="bool", default=True) + if idlexMain.ipython_argv: + enable = True + self.use_ipython = enable + + if self.is_shell: + if self.use_ipython: + self.set_ipython_state(True) + else: + self.first_load = False # not loading ipython on pyshell init + self.set_restart_state(self.allow_restart) + else: + if self.use_ipython: + for i in ipyList: + if i.is_shell: + self.install() + break + + def close(self): + if self.is_shell: + PyShell.ModifiedInterpreter = ModifiedInterpreter + + # restore IDLE's default event handlers + # for the editors if shell is close + self._uninstall_editors() + + ipyProxy.cleanup_connection_file() + ipyProxy.stop_km() + + self.uninstall() + + if self in ipyList: + ipyList.remove(self) + else: + print('IPY:', self) + + def focus_event(self, event=None): + ipyProxy.active = self + + @check_ipython + def toggle_IPython(self, ev=None): + self.set_ipython_state(not self.use_ipython) + + def set_ipython_state(self, b=False): + self.use_ipython = b + set_cfg('ipython', '%s' % b) + if b: + self.install() + else: + self.uninstall() + + if self.is_shell: + editwin = self.editwin + try: + editwin.setvar('<>', b) + except Exception: + print('Exception should not occur', file=sys.stderr) + traceback.print_exc() + + @check_ipython + def toggle_restart(self, ev=None): + self.set_restart_state(not self.allow_restart) + + def set_restart_state(self, b=False): + self.allow_restart = b + set_cfg('allow_restart', '%s' % b) + self.editwin.setvar('<>', b) + ipyProxy.allow_restart = b + + @check_ipython + def adjust_command_line(self, event=None): + current_argv = idlexMain.ipython_argv + cf = idlexMain.ipython_connection_file + args = ' '.join(current_argv) + if cf: + args += ' --existing %s' % cf + + while True: + new_args = tkSimpleDialog.askstring('IPython Kernel Arguments', + 'Adjust the kernel start arguments:', + parent=self.editwin.text, + initialvalue=args + ) + if new_args is None: + idlexMain.ipython_argv = current_argv + break + else: + new_argv = new_args.split() + ipy_argv = idlexMain.handle_ipython_argv(new_argv) + if new_argv: + # there should not be anything left in this: + tkMessageBox.showwarning('IDLE IPython', + ('IDLE IPython does not support:\n' + + ' '.join(new_argv)), + parent=self.editwin.top) + args = new_args # allow for repeat + else: + if current_argv != ipy_argv: + ipyProxy.argv_changed = True + else: + ipyProxy.argv_changed = False + break + + if ipyProxy.argv_changed: + restart = tkMessageBox.askyesno('IDLE IPython', + ('Changes to the kernel will be applied on (re)start of kernel.\n\n' + + 'Do you want to start a new kernel?'), + default=tkMessageBox.NO, + parent=self.editwin.top) + print('restart: %r %s' % (restart, type(restart))) + if restart == True: + if self.use_ipython: + self.editwin.interp.restart_subprocess() + else: + self.set_ipython_state(True) + + + def _install_editors(self): + for i in ipyList: + if not i.is_shell: + i.install() + + def _uninstall_editors(self): + for i in ipyList: + if not i.is_shell: + i.uninstall() + + def switch_message(self): + if self.first_load: + return + hb = '=' * 10 + msg = '\n %(halfbar)s SWITCHING SHELL %(halfbar)s \n\n' % {'halfbar':hb} + self.text.insert('end', msg) + self.text.mark_set('iomark', 'end') + + + def reset_shell(self): + #print('reset_shell') + e = self.editwin + if e.reading: + e.top.quit() + e.reading = False + if e.interp.rpcclt: + e.interp.kill_subprocess() + + e.executing = False + + + #------------------------------ + # UnInstall this instance + #----------------------------- + + def uninstall(self): # uninstall ipython and return to IDLE + e = self.editwin + + self.text_binding.restore() + + s = self.editwin.extensions.get('CallTips', None) + if s: + try: + self.text.bind('<>', s.refresh_calltip_event) + except Exception as err: + print('Error restoring calltip binding: %r' % err) + pass + + # Restore values + if self.is_shell: + closing = e.closing + if not closing: # switching + self.reset_shell() + self.switch_message() + + ipyProxy.stop_km() + ipyProxy.restore_IDLE_history() + self._uninstall_editors() + ipyProxy.active_pyshell = None + ipyProxy.calltipwindow = None + + if self._save_shell_title: + PyShell.PyShell.shell_title = self._save_shell_title + if self._save_copyright: + PyShell.PyShell.COPYRIGHT = self._save_copyright + if self._save_ps1 is not None: + sys.ps1 = self._save_ps1 + if self._save_ps2 is not None: + sys.ps2 = self._save_ps2 + PyShell.ModifiedInterpreter = ModifiedInterpreter + + s = self.editwin.extensions.get('Squeezer', None) + if s and self._save_squeezer: + s._MAX_NUM_OF_LINES = self._save_squeezer + + if not closing: # then switching + e.interp._poll_cancel() + e.interp = ModifiedInterpreter(e) + e.console.write('\n') + e.per.removefilter(self.ansi) + e.history = self._save_history + e.executing = False + e.begin() + + + #--------------------------- + # Install into this instance + #--------------------------- + + def install(self): + + e = self.editwin + + # Save existing values + if self.is_shell: + self.text.mark_set('before_prompt', '1.0') + self.reset_shell() + ipyProxy._reset() + self.switch_message() + ipyProxy.active_pyshell = self.editwin + ipyProxy.active = self + ipyProxy.start_km() + e.executing = 0 + self._save_shell_title = PyShell.PyShell.shell_title + self._save_copyright = PyShell.PyShell.COPYRIGHT + + if self._save_ps1 is None: + self._save_ps1 = getattr(sys, 'ps1', None) + self._save_ps2 = getattr(sys, 'ps2', None) + + + sys.ps1 = '>>> ' # temporary + sys.ps2 = '... ' # temporary + + self._save_undo = e.undo + + e.interp = BridgeInterpreter(e) # replace the existing interpreter + PyShell.PyShell.shell_title = "IPython Shell" + PyShell.ModifiedInterpreter = BridgeInterpreter + + def delay_install(): + # the purpose of delay_install is if "install_shell" is called + # as part of the init routine for pyshell + + if self.is_shell: + e.closing = False + self._install_editors() + self._save_history = e.history + + # Add Ansi Color Delegator + self.ansi = AnsiColorDelegator() + e.per.insertfilter(self.ansi) + self.config_ansi_colors() + + s = self.editwin.extensions.get('Squeezer', None) + if s: + self._save_squeezer = s._MAX_NUM_OF_LINES + s._MAX_NUM_OF_LINES = 10000 + + e.console.write('\n') + + + if not self.first_load: + ipyProxy.switching = True # flag to avoid double launching kernels + e.begin() + ipyProxy.switching = False # flag to avoid double launching kernels + e.executing = False + self.first_load = False # no longer the case that the shell opened and needs ipython + e.text.mark_set('before_prompt', 'iomark linestart') + e.text.mark_gravity("before_prompt", "left") + + #self.text.configure(wrap="word") # Uncomment to enable word-wrapping like QtConsole + self.text_binding.bind('', self.tab_event) + if self.is_shell: + self.text_binding.bind('', self.control_enter_callback) + self.text_binding.bind('<>', self.clear_window) + self.text_binding.bind('<>', self.toggle_debugger) + self.text_binding.bind('<>', self.refresh_calltip_event) + + self.text.after(1, delay_install) + + def config_ansi_colors(self): + """ Configure the ansi color tags for the .text widget """ + text = self.text + for code in ansi_colors: + text.tag_config(code, + foreground=ansi_colors[code]) + + def refresh_calltip_event(self, event=None): + # this is a hack to make calltip hiding work with + # CallTips.py refresh_calltip_event + ctw = ipyProxy.calltipwindow + if ctw: + ctw.hidetip() + self.text.event_generate('<>') + + def tab_event(self, event=None): + # AutoCompleteWindow event + text = self.text + + lineno, col = sp(text.index('insert')) # where the cursor is + + # Determine the start of the code for context + if self.is_shell: + IOMARK = 'iomark' + else: + IOMARK = 'insert linestart' + + ioline, iocol = sp(text.index(IOMARK)) # where the start of code is + if lineno > ioline: + scol = 0 + codecol = col + else: + scol = iocol + codecol = col - iocol + + buf = text.get(IOMARK, 'insert') # full cell for context + codeline = text.get(jn(lineno, scol), 'insert') # the actual line of code + + + # Assume that we will do a completion, unless it is not needed + do_complete = True + + if not codeline.strip(): + if self.is_shell: + if text.compare('insert', '>', 'iomark'): + do_complete = False # So TAB works in multiline input + else: + # To mimic the behavior of the IPython shell, + # do the completion when the prompt is blank + pass + else: + do_complete = False # blank code line, not needed + + elif not self.is_shell: + if codeline[-1] not in COMPLETE_CHARS: + do_complete = False + + # Check if the window is already open + if self.autocompletewindow and self.autocompletewindow.is_active(): + if codeline == self.last_codeline: + # send twice to select it - logic of IDLE's ACW + self.autocompletewindow.keypress_event(event) + self.autocompletewindow.keypress_event(event) + return "break" + + if do_complete: + msg_id = ipyProxy.kernel_manager.shell_channel.complete( + '', # text + codeline, # line + codecol, # cursor_pos + buf, # block + ) + self.last_codeline = codeline + else: + # call original binding + self.text_binding.call_original('', event) + return "break" + + def toggle_debugger(self, event=None): + tkMessageBox.showinfo('IDLE IPython', + 'IDLE Debugger is not compatible with IPython (yet). Try using %pdb', + parent=self.editwin.top) + self.editwin.setvar('<>', 0) + return "break" + + def control_enter_callback(self, event=None): + # only in shell + e = self.editwin + if e.executing or e.reading: + self.text_binding.call_original('', event) + return "break" + + if self.text.compare('insert', '>=', 'iomark'): + e.newline_and_indent_event(event) + return "break" + + self.text_binding.call_original('', event) + return "break" + + def clear_window(self, event): + # forget all images + ipyProxy.clear_shell_images() + self.text_binding.call_original('<>', event) diff --git a/idlex-1.11.2/idlexlib/extensions/LineNumbers.py b/idlex-1.11.2/idlexlib/extensions/LineNumbers.py new file mode 100644 index 0000000..e0dcc82 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/LineNumbers.py @@ -0,0 +1,353 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## LineNumbers Extension +## +## Provides line numbers to the left of the source code. +## +## The width of the line numbers adapts. Limit of 99,999 lines (for proper display). +## +## """ + +config_extension_def = """ + +[LineNumbers] +enable=1 +enable_shell=0 +visible=True + +[LineNumbers_cfgBindings] +linenumbers-show= + +""" + +import sys +if sys.version < '3': + from Tkinter import END, Text, LEFT, Y, NONE, RIGHT, NORMAL, DISABLED, Label, TOP, Frame, X +else: + from tkinter import END, Text, LEFT, Y, NONE, RIGHT, NORMAL, DISABLED, Label, TOP, Frame, X + +from idlelib.configHandler import idleConf +from idlelib.Delegator import Delegator +from idlelib.Percolator import Percolator + + +FONTUPDATEINTERVAL = 1000 # milliseconds + +_AFTER_UNDO = True # Flag to have the LineNumberDelegator inserted after the undo delegator + +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates +sp = lambda x: map(int, x.split('.')) # convert tkinter Text coordinate to a line and column tuple + + +def dbprint(func): # A decorator for debugging + def f(*args, **kwargs): + print(func, args, kwargs) + return func(*args, **kwargs) + return f + +class LineNumbers(object): + + menudefs = [('options', [('!Show Line Numbers', '<>')])] + + def __init__(self, editwin): + self.editwin = editwin + self.text = self.editwin.text + self.textfont = None + self.width = 2 + self.after_id = None + + self.create_linenumbers() + + e = idleConf.GetOption("extensions", "LineNumbers", + "visible", type="bool", default=True) + self.set_visible(e) + + self.code_context_fix() + + def close(self): + if self.after_id: + self.text.after_cancel(self.after_id) + self.visible = False + + def adjust_font(self): + try: + # taken from CodeContext.py + newtextfont = self.editwin.text["font"] + if self.textln and newtextfont != self.textfont: + self.textfont = newtextfont + self.textln["font"] = self.textfont + if self._cc_text: + self._cc_text["font"] = self.textfont + self.update_numbers() + except Exception as err: + import traceback; traceback.print_exc() + + def font_timer(self): + if not self.visible: + return + + self.adjust_font() + + if self.after_id: + self.text.after_cancel(self.after_id) + self.after_id = self.text.after(FONTUPDATEINTERVAL, self.font_timer) + if not _AFTER_UNDO: + self.update_numbers() # fixes a bug due to this percolator being ahead of undo percolator. + + def set_visible(self, b=True): + self.visible = b + + if self.visible: + self.text.after(1, self.font_timer) # avoid a start-up bug + self.show() + # use .after to avoid a start-up error caused by update_idletasks in update_numbers + self.text.after(1, self.update_numbers) + else: + self.hide() + + idleConf.SetOption("extensions", "LineNumbers", + "visible", '%s' % self.visible) + + self.editwin.setvar("<>", self.visible) + + def linenumbers_show_event(self, ev=None): + self.set_visible(not self.visible) + self._code_context_toggle() + + def create_linenumbers(self): + """ Create the widget for displaying line numbers. """ + editwin = self.editwin + text = self.text + text_frame = editwin.text_frame + self.textln = textln = Text(text_frame, width=self.width, + height=1, wrap=NONE) + + # adjust font + textln.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), + idleConf.GetOption('main', 'EditorWindow', 'font-size'))) + + textln.bind("", self.focus_in_event) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + textln.bind('', self.button_ignore) + + textln.bind("", self.button4) + textln.bind("", self.button5) + + textln.tag_config('LINE', justify=RIGHT) + textln.insert(END, '1') + textln.tag_add('LINE', '1.0', END) + + # start the line numbers + self.per = per = Percolator(textln) + self.line_delegator = LineDelegator() + per.insertfilter(self.line_delegator) + textln._insert = self.line_delegator.delegate.insert + textln._delete = self.line_delegator.delegate.delete + + + lines = LineNumberDelegator(self) + if _AFTER_UNDO: + # Percolator.py's .insertfilter should have an "after=" argument + lines.setdelegate(editwin.undo.delegate) + editwin.undo.setdelegate(lines) + else: + editwin.per.insertfilter(lines) + + editwin.vbar['command'] = self.vbar_split + editwin.text['yscrollcommand'] = self.yscroll_split + + def button4(self, ev=None): + self.text.event_generate("") + return "break" + + def button5(self, ev=None): + self.text.event_generate("") + return "break" + + def button_ignore(self, ev=None): + return "break" + + def show(self): + self.textln.pack(side=LEFT, fill=Y, before=self.editwin.text) + + def hide(self): + self.textln.pack_forget() + + def focus_in_event(self, event=None): + self.editwin.text.focus_set() + self.textln.tag_remove('sel', '1.0', 'end') + #self.editwin.text.event_generate("<>") + + def generate_goto_event(self, event=None): + self.editwin.text.event_generate("<>") + return "break" + + def vbar_split(self, *args, **kwargs): + """ split scrollbar commands to the editor text widget and the line number widget """ + self.textln.yview(*args, **kwargs) + self.text.yview(*args, **kwargs) + + def yscroll_split(self, *args, **kwargs): + """ send yview commands to both the scroll bar and line number listing """ + #import traceback; traceback.print_stack() + self.editwin.vbar.set(*args) + self.textln.yview_moveto(args[0]) + + def update_numbers(self, add=None, remove=None): + if not self.visible: return + + textln = self.textln + text = self.editwin.text + + + endline1, col1 = sp(text.index(END)) + endline2, col2 = sp(textln.index(END)) + + + if endline1 < endline2: + # delete numbers + textln._delete('%i.0' % endline1, END) + elif endline1 > endline2: + # add numbers + q = range(endline2, endline1) + r = map(lambda x: '%i' % x, q) + s = '\n' + '\n'.join(r) + textln._insert(END, s) + textln.tag_add('LINE', '1.0', END) + + # adjust width of textln, if needed. (counts from 1, not zero) + if endline1 <= 100: + width = 2 + elif endline1 <= 1000: + width = 3 + elif endline1 <= 10000: + width = 4 + else: + width = 5 # more than 9999 lines in IDLE? Really? + + # XXX: If your code requires width>5, i.e > 100,000 lines of code, + # you probably should not be using IDLE. + + if width > self.width: # 2011-12-18 - only grow, not shrink + self.width = width + textln.configure(width=width) + if self._cc_text: # adjust CC width + self._cc_text.configure(width=width) + + self.textln.update_idletasks() + a = self.text.yview() + self.textln.yview_moveto(a[0]) + + def code_context_fix(self): + self._cc_text = None + self._cc_frame = None + def f(): + self.text.bind('<>', self._code_context_toggle, '+') + self._code_context_toggle() + self.text.after(10, f) + + def _code_context_toggle(self, event=None): + cc = self.editwin.extensions.get('CodeContext', None) + if cc is None: + return + + if not self.visible: + if self._cc_frame: + L = cc.label + L.pack_forget() + self._cc_frame.destroy() + L.pack(side=TOP, fill=X, expand=False, + before=self.editwin.text_frame) + return + + + editwin = self.editwin + text = self.text + text_frame = editwin.text_frame + + # repack the Label in a frame + if cc.label: + cc.label.pack_forget() + F = Frame(self.editwin.top) + F.lower() # fix Z-order + t = Text(F, width=self.width, height=1, + takefocus=0) + t.bind("", lambda x: self.text.focus()) + t["font"] = self.textln.cget('font') + t.pack(side=LEFT, fill=Y) + cc.label.pack(in_=F, fill=X, expand=False) + + F.pack(side=TOP, before=text_frame, fill=X, expand=False) + self._cc_frame = F + self._cc_text = t + else: + if self._cc_frame: + self._cc_frame.destroy() + self._cc_frame = None + self._cc_text = None + + + + +class LineNumberDelegator(Delegator): + # for editwin.text + def __init__(self, line_number_instance): + Delegator.__init__(self) + self.ext = line_number_instance + + def insert(self, index, chars, tags=None): + self.delegate.insert(index, chars, tags) + if '\n' in chars: + self.ext.update_numbers()#add=chars.count('\n')) + def delete(self, index1, index2=None): + t = self.get(index1, index2) + self.delegate.delete(index1, index2) + if '\n' in t: + self.ext.update_numbers()#remove=t.count('\n')) + + +class LineDelegator(Delegator): + # for textln + def insert(self, *args, **kargs): + pass + + def delete(self, *args, **kargs): + pass diff --git a/idlex-1.11.2/idlexlib/extensions/MultiLineRun.py b/idlex-1.11.2/idlexlib/extensions/MultiLineRun.py new file mode 100644 index 0000000..4fc5814 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/MultiLineRun.py @@ -0,0 +1,170 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2012 The Board of Trustees of the University of Illinois. +## All rights reserved. +## Developed by: Roger D. Serwy +## University of Illinois +## License: See LICENSE.txt +## """ + + + +## +## This extension allows for pasting of multiple lines of code into the shell +## for execution. This addresses http://bugs.python.org/issue3559 +## + + +from __future__ import print_function + +config_extension_def = """ +[MultiLineRun] +enable=1 +enable_editor=0 +enable_shell=1 +""" + +from idlelib.configHandler import idleConf +from idlelib.Delegator import Delegator +import time +import re +import sys +import traceback + +class MultiLineDelegator(Delegator): + def __init__(self, callback): + Delegator.__init__(self) + self.callback = callback + self.paste = False + + def insert(self, index, chars, tags=None): + do_insert = True + if self.paste: + self.paste = False + try: + do_insert = self.callback(chars) + except Exception as err: + # Must catch exception else IDLE closes + print(' MultiLineRun Internal Error', file=sys.stderr) + traceback.print_exc() + + if do_insert: + self.delegate.insert(index, chars, tags) + + def delete(self, index1, index2=None): + self.delegate.delete(index1, index2) + + +class MultiLineRun: + + # eol code from IOBinding.py + eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac) + eol_re = re.compile(eol) + + def __init__(self, editwin): + self.editwin = editwin # reference to the editor window + self.text = text = self.editwin.text + + self.mld = MultiLineDelegator(self.paste_intercept) + self.editwin.per.insertfilter(self.mld) + + self.text.bind('<>', self.paste, '+') + + wsys = text.tk.call('tk', 'windowingsystem') + if wsys == 'x11': + self.text.bind('', self.paste, '+') # For X11 middle click + + self.playback_list = [] + + def paste(self, event=None): + self.mld.paste = True + + def paste_intercept(self, chars): + self.mld.paste = False + if self.editwin.executing: + return True + + self.play(chars) + + def play(self, chars): + chars = self.eol_re.sub(r"\n", chars) + + L = [] # list of entries to play into the shell + index = 0 + while True: + next_index = chars.find('\n', index) + if next_index > -1: + line = chars[index:next_index] + L.append((line, True)) + else: + line = chars[index:] + L.append((line, False)) + break + index = next_index + 1 + + L = self.dedent(L) + self.playback_list = L + self._pre_playback() + self.do_playback() + + def dedent(self, L): + return L + + # TODO: make this work + # Multiline strings may make code appear less indented than it is... + Lcode = [line for line, ret in L if line.rstrip() and not line.startswith('#')] + + dedent_tab =0 + while all(map(Lcode.startswith('\t'))): + Lcode = [x[1:] for x in Lcode] + dedent_tab += 1 + + if not dedent_tab: + dedent_space = 0 + while all(map(Lcode.startswith(' '))): + Lcode = [x[1:] for x in Lcode] + dedent_space += 1 + depth = dedent_space + src = '\n'.join(L) + src = re.sub(r"(?') + t.see('insert') + + t.after(10, self.do_playback) + + diff --git a/idlex-1.11.2/idlexlib/extensions/PastePyShell.py b/idlex-1.11.2/idlexlib/extensions/PastePyShell.py new file mode 100644 index 0000000..3dd0198 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/PastePyShell.py @@ -0,0 +1,257 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## PastePyShell.py +## +## Handle code formatting of PyShell text, per issue11838. +## Also addresses "issue7676 - IDLE shell shouldn't use TABs" +## where TABs are a problem due to pasting into the editor from PyShell. +## +## +## """ + +config_extension_def = """ +[PastePyShell] +enable=1 +enable_shell=0 +enable_editor=1 + +[PastePyShell_cfgBindings] +paste-code= +paste-code-only= +""" +# NOTE: the "Key" in is needed for it to work. + + +# get the IDLE configuration handler +from idlelib.configHandler import idleConf +from idlelib.EditorWindow import classifyws +import re + +class PastePyShell: + + # A more proper place is to add the menu entry in "Bindings.py" + # just after "Paste" under "edit". + menudefs = [ + ('edit', [ + ('Paste from S_hell', '<>'), + ('Paste from Shell (only code)', '<>') + ]),] + + + def __init__(self, editwin): + self.editwin = editwin # reference to the editor window + self.text = text = self.editwin.text + self.text.bind("<>", self.paste_code_event) + + r = editwin.rmenu_specs + # See Issue1207589 patch + if len(r) > 0: + if len(r[0]) == 2: + specs = [('Paste from Shell', '<>'), + ('Paste from Shell (only code)', '<>')] + elif len(r[0]) == 3: + specs = [('Paste from Shell', '<>', None), + ('Paste from Shell (only code)', '<>', None)] + else: + specs = [] + + for m in specs: + if m not in editwin.rmenu_specs: + editwin.rmenu_specs.append(m) + + def paste_code_only_event(self, ev=None): + self.paste_code(comments=False) + return "break" + + def paste_code_event(self, ev=None): + self.paste_code() + return "break" + + def paste_code(self, comments=True): + editwin = self.editwin + text = self.text + per = self.editwin.per + + try: + chars = editwin.top.selection_get(selection="CLIPBOARD") + except Exception: + return "break" + + pc = PastePyShellProcessor(comments=comments) + res = pc._paste_process(chars) + text.insert('insert', res) + return "break" + +class PastePyShellProcessor: + def __init__(self, tabwidth=8, usetabs=False, comments=True): + self.tabwidth = tabwidth + self.usetabs = usetabs + self.comments = comments + + # TODO - allow "email" and "diff" pasting + self.prompts = [r'^>>> ', # standard Python ps1 + r'^\.\.\. ', # standard Python ps2 + r'^[ ]*In \[[\d]*\]: ', # IPython ps1 + r'^[ ]*[\.]*: '] # IPython ps2 + + def starts_with_prompt(self, L): + for p in self.prompts: + m = re.match(p, L) + if m: + return True + else: + return False + + def remove_prompt(self, L): + for p in self.prompts: + m = re.match(p, L) + if m: + L = re.sub(p, '', L, count=1) + break + return L + + def _paste_process(self, chars): + """ Handle code formatting of PyShell text, per issue11838. """ + + # This code implements a two-state machine, where + # "iscode" indicates the state. + + insert_blankline = False # flag to insert blank lines around code + include_comments = self.comments + + lines = chars.split('\n') + iscode = False # state indicator for >>> + + has_prompt = False + + INDENT_LIST = [' ', '\t'] + + NL = [] + for L in lines: + # handle state machine transition + if iscode: + # Leave "iscode" state if the line is not indented + # and the line is not another prompt. + if L: + if L[0] not in INDENT_LIST: + + if not self.starts_with_prompt(L): + iscode = False + if insert_blankline: NL.append('') + else: + #pass + continue # skip double blank line at end of multi-line + else: + # Enter "iscode" state is the line begins with a prompt. + if self.starts_with_prompt(L): + if insert_blankline: NL.append('') + iscode = True + has_prompt = True + + + # handle output of state machine + if not iscode: + if include_comments: + NL.append('#%s' % L) # comment output + else: + # remove >>> and tabs (if necessary) + if self.starts_with_prompt(L): + L = self.remove_prompt(L) + + # convert to spaces + raw, effective = classifyws(L, self.tabwidth) + L = self._make_blanks(effective) + L[raw:] + + # handle shell RESTART + if "= RESTART =" in L: + L = '#%s' % L + + NL.append(L) + + return '\n'.join(NL) + +## # Code to fall back to normal Paste if no prompts detected in buffer +## if has_prompt: +## return '\n'.join(NL) +## else: +## return chars + + def _make_blanks(self, n): # from EditorWindow.py + # copied over in case EditorWindow's implementation changes. + if self.usetabs: + ntabs, nspaces = divmod(n, self.tabwidth) + return '\t' * ntabs + ' ' * nspaces + else: + return ' ' * n + + + +test_code = r"""Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) +[GCC 4.5.2] on linux2 +Type "copyright", "credits" or "license()" for more information. +>>> ================================ RESTART ================================ +>>> if 1: + print(123) + print(456) + + +123 +456 +>>> import sys +>>> print sys.version +2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) +[GCC 4.5.2] +>>> for x in range(3): + print(x**2) + + +0 +1 +4 +>>> print('>>> ') # This output will be treated as a prompt... +>>> +>>> +>>> +>>> print('\tThis line will be considered code.') + This line will be considered code. +>>> +""" + +if __name__ == '__main__': + # test + pc = PastePyShellProcessor() + res = pc._paste_process(test_code) + for i in res.split('\n'): # work-around squeezer + print(i) diff --git a/idlex-1.11.2/idlexlib/extensions/PersistentHistory.py b/idlex-1.11.2/idlexlib/extensions/PersistentHistory.py new file mode 100644 index 0000000..8b4f69b --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/PersistentHistory.py @@ -0,0 +1,116 @@ +# IDLEX EXTENSION + +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## Developed by: Roger D. Serwy +## University of Illinois +## License: See LICENSE.txt +## """ + +config_extension_def = """ +[PersistentHistory] +enable=1 +enable_editor=0 +enable_shell=1 + +keep = 500 + +""" + +from idlelib.configHandler import idleConf +import pickle +import os +import sys +import idlelib.PyShell as PyShell + +# TODO: decide if having history tied to the python version is a good thing. + +class PersistentHistory: + + cfg_dir = idleConf.GetUserCfgDir() + history_file = os.path.join(cfg_dir, + 'shell-history.dat') + #'shell-history-%X.dat' % sys.hexversion) + + menudefs = [ + ('options', [ + ('Clear History', '<>'), + ]),] + + def __init__(self, editwin): + if not isinstance(editwin, PyShell.PyShell): + print('Not a PyShell instance - not running Persistent History') + self.close = lambda *args, **kwargs: None + return + + self.editwin = editwin + self.text = text = editwin.text + self.history = None + + + self.text.bind('<>', self.history_clear_event) + self.keep = idleConf.GetOption('extensions', + 'PersistentHistory', + 'keep', + type='int', + default=500) + self.delay_init() + + def close(self): + try: + self.save_history() + except Exception as err: + print(' An error occurred during the close of PersistentHistory.py: %s' % str(err)) + + def delay_init(self): + if hasattr(self.editwin, 'history'): + self.history = self.editwin.history + self.load_history() + #self._showit() # for testing + return + self.text.after(100, self.delay_init) + + def save_history(self): + hist = self.history.history[:] + if len(hist) > self.keep: # limit the amount kept + hist = hist[len(hist)-self.keep:] + + f = open(self.history_file, 'wb') + pickle.dump(hist, f, 1) # protocol to be compatible with 2/3 + f.close() + + + def load_history(self): + if not os.path.exists(self.history_file): + return + + f = open(self.history_file, 'rb') + try: + data = pickle.load(f) + except Exception as err: + print('Unable to load history: %s ' % str(err)) + data = [] + f.close() + + + data.extend(self.history.history) # Just in case a history already had + # contents before being loaded. (Not supposed to happen) + self.history.history = data + + + def history_clear_event(self, ev=None): + h = self.history + h.history_prefix = None + h.history_pointer = None + h.history = [] + return "break" + + + def _showit(self): # for testing + h = self.history + print('-'*80) + print(h.history) + print(h.history_prefix) + print(h.history_pointer) + self.text.after(250, self.showit) diff --git a/idlex-1.11.2/idlexlib/extensions/ReindentExtension.py b/idlex-1.11.2/idlexlib/extensions/ReindentExtension.py new file mode 100644 index 0000000..1af9939 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/ReindentExtension.py @@ -0,0 +1,372 @@ +# IDLEX EXTENSION +## +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## Reindent Extension: +## +## Relies on ScriptBinding.py extension +## +## Provides "Re-indent" on the Format menu. +## Based on "reindent.py" code released to the public domain, by Tim Peters, 03 October 2000. +## +## Part of Python Bug, issue 5150 Editing. +## +## Modified to use IdleX October 2011 +## +## +## This code includes a patch from http://bugs.python.org/issue12930 +## to disallow strings from being modified. That code is under the PSF license, +## based on the contributor's agreement with the PSF. +## + +config_extension_def = """ + +[ReindentExtension] +enable=1 +enable_shell=0 +enable_editor=1 + +[ReindentExtension_cfgBindings] +reindent-apply= + +""" + +import sys +import tokenize + +if sys.version < '3': + from StringIO import StringIO +else: + from io import StringIO + xrange = range + + +change_strings = False # flag for reindent code patch + +def _rstrip(line, JUNK='\n \t'): + """Return line stripped of trailing spaces, tabs, newlines. + + Note that line.rstrip() instead also strips sundry control characters, + but at least one known Emacs user expects to keep junk like that, not + mentioning Barry by name or anything . + """ + + i = len(line) + while i > 0 and line[i-1] in JUNK: + i -= 1 + return line[:i] + +class Reindenter: + + def __init__(self, f): + self.find_stmt = 1 # next token begins a fresh stmt? + self.level = 0 # current indent level + + # Raw file lines. + self.raw = f.readlines() + + # File lines, rstripped & tab-expanded. Dummy at start is so + # that we can use tokenize's 1-based line numbering easily. + # Note that a line is all-blank iff it's "\n". + self.lines = [_rstrip(line).expandtabs() + "\n" + for line in self.raw] + self.lines.insert(0, None) + self.index = 1 # index into self.lines of next line + + # List of (lineno, indentlevel) pairs, one for each stmt and + # comment line. indentlevel is -1 for comment lines, as a + # signal that tokenize doesn't know what to do about them; + # indeed, they're our headache! + self.stats = [] + + # Save the newlines found in the file so they can be used to + # create output without mutating the newlines. + self.newlines = '\n' # RDS - default in IDLE + + def rstrip_and_expand_tabs(self): + self.lines = [line for line in self.raw] + self.lines.insert(0, None) + # Only apply rstrip if the line is not part of a multiline string + # and expand tabs only if not within in a string. + tokens = tokenize.generate_tokens(self.getline) + + strmask = {} + def addmask(line, scol, ecol): + # Keep track of start/end columns for a string on a particular + # line. Each element is used to toggle the string state on a line. + if line in strmask: + strmask[line].extend([scol, ecol]) + else: + strmask[line] = [scol, ecol] + + lines = self.lines + for _token in tokens: + _type, string, slinecol, elinecol, line = _token + if _type == tokenize.STRING: + sl, sc = slinecol + el, ec = elinecol + if sl == el: + # Single line string + addmask(sl, sc, ec) + else: + # Multi-line string + addmask(sl, sc, len(lines[sl])) + strmask[sl].append(-1) # Start multi-line + strmask[el] = [-1] # Stop multi-line + addmask(el, 0, ec) + + self.index = 1 # reset index for self.getline + + n = 1 + multi = False + while n < len(lines): + line = lines[n] + strtoggle = strmask.get(n, None) + if strtoggle is None: + if not multi: + lines[n] = _rstrip(line).expandtabs() + '\n' + else: + # Process strings on a single line + isstr = False + scol = 0 + processed = [] + while strtoggle: + ecol = strtoggle.pop(0) + if ecol == -1: # toggle multiline mode + if not multi: + ecol = len(line) + multi = not multi + isstr = multi + continue + if isstr: + processed.append(line[scol:ecol]) + else: + processed.append(line[scol:ecol].expandtabs()) + + scol = ecol + isstr = not isstr + + if not multi: + processed.append(_rstrip(line[ecol:]).expandtabs() + '\n') + else: + processed.append(line[ecol:-1]) + + lines[n] = ''.join(processed) + n += 1 + + def run(self): + if not change_strings: + self.rstrip_and_expand_tabs() + tokens = tokenize.generate_tokens(self.getline) + for _token in tokens: + self.tokeneater(*_token) + # Remove trailing empty lines. + lines = self.lines + while lines and lines[-1] == "\n": + lines.pop() + # Sentinel. + stats = self.stats + stats.append((len(lines), 0)) + # Map count of leading spaces to # we want. + have2want = {} + # Program after transformation. + after = self.after = [] + # Copy over initial empty lines -- there's nothing to do until + # we see a line with *something* on it. + i = stats[0][0] + after.extend(lines[1:i]) + for i in range(len(stats) - 1): + thisstmt, thislevel = stats[i] + nextstmt = stats[i + 1][0] + have = getlspace(lines[thisstmt]) + want = thislevel * 4 + if want < 0: + # A comment line. + if have: + # An indented comment line. If we saw the same + # indentation before, reuse what it most recently + # mapped to. + want = have2want.get(have, -1) + if want < 0: + # Then it probably belongs to the next real stmt. + for j in range(i + 1, len(stats) - 1): + jline, jlevel = stats[j] + if jlevel >= 0: + if have == getlspace(lines[jline]): + want = jlevel * 4 + break + if want < 0: # Maybe it's a hanging + # comment like this one, + # in which case we should shift it like its base + # line got shifted. + for j in range(i - 1, -1, -1): + jline, jlevel = stats[j] + if jlevel >= 0: + want = have + (getlspace(after[jline - 1]) - + getlspace(lines[jline])) + break + if want < 0: + # Still no luck -- leave it alone. + want = have + else: + want = 0 + assert want >= 0 + have2want[have] = want + diff = want - have + if diff == 0 or have == 0: + after.extend(lines[thisstmt:nextstmt]) + else: + for line in lines[thisstmt:nextstmt]: + if diff > 0: + if line == "\n": + after.append(line) + else: + after.append(" " * diff + line) + else: + remove = min(getlspace(line), -diff) + after.append(line[remove:]) + return self.raw != self.after + + def write(self, f): + f.writelines(self.after) + + # Line-getter for tokenize. + def getline(self): + if self.index >= len(self.lines): + line = "" + else: + line = self.lines[self.index] + self.index += 1 + return line + + # Line-eater for tokenize. + def tokeneater(self, type, token, slinecol, end, line, + INDENT=tokenize.INDENT, + DEDENT=tokenize.DEDENT, + NEWLINE=tokenize.NEWLINE, + COMMENT=tokenize.COMMENT, + NL=tokenize.NL): + + if type == NEWLINE: + # A program statement, or ENDMARKER, will eventually follow, + # after some (possibly empty) run of tokens of the form + # (NL | COMMENT)* (INDENT | DEDENT+)? + self.find_stmt = 1 + + elif type == INDENT: + self.find_stmt = 1 + self.level += 1 + + elif type == DEDENT: + self.find_stmt = 1 + self.level -= 1 + + elif type == COMMENT: + if self.find_stmt: + self.stats.append((slinecol[0], -1)) + # but we're still looking for a new stmt, so leave + # find_stmt alone + + elif type == NL: + pass + + elif self.find_stmt: + # This is the first "real token" following a NEWLINE, so it + # must be the first token of the next program statement, or an + # ENDMARKER. + self.find_stmt = 0 + if line: # not endmarker + self.stats.append((slinecol[0], self.level)) + + +# Count number of leading blanks. +def getlspace(line): + i, n = 0, len(line) + while i < n and line[i] == " ": + i += 1 + return i + + +class ReindentExtension: + + menudefs = [('format', [('Apply Reindent', '<>')])] + + def __init__(self, editwin): + self.editwin = editwin + + + def reindent_apply_event(self, event=None): + + text = self.editwin.text + undo = self.editwin.undo + + f_in = StringIO() + source = text.get('0.0', 'end -1 char') # -1 char to avoid trailing \n, + # otherwise the file is always + # marked as "changed" + f_in.write(source) + f_in.seek(0) + + r = Reindenter(f_in) + try: + changed = r.run() + except (IndentationError, SyntaxError) as err: + msg, (errorfilename, lineno, offset, line) = err + + sb = self.editwin.extensions['ScriptBinding'] + sb.colorize_syntax_error(msg, lineno, offset) + sb.errorbox("Syntax error", + "There's an error in your program:\n" + msg) + return 'break' + + if not changed: + return 'break' + + f_out = StringIO() + r.write(f_out) + f_out.seek(0) + + CUR = text.index('insert') # save cursor index + loc = text.yview()[0] + + undo.undo_block_start() + text.delete('0.0', text.index('end')) + text.insert('0.0', f_out.read()) + undo.undo_block_stop() + + text.mark_set('insert', CUR) # restore cursor index + text.yview_moveto(loc) + + return 'break' diff --git a/idlex-1.11.2/idlexlib/extensions/RightClickMenu.py b/idlex-1.11.2/idlexlib/extensions/RightClickMenu.py new file mode 100644 index 0000000..1d31bb1 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/RightClickMenu.py @@ -0,0 +1,128 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2012 The Board of Trustees of the University of Illinois. +## All rights reserved. +## Developed by: Roger D. Serwy +## University of Illinois +## License: See LICENSE.txt +## """ + + +config_extension_def = """ +[RightClickMenu] +enable=1 +enable_editor=1 +enable_shell=1 +visible=True +""" + + +# Python 2/3 compatibility +import sys +if sys.version < '3': + import Tkinter as tk +else: + import tkinter as tk + +from pprint import pprint + +# get the IDLE configuration handler +from idlelib.configHandler import idleConf +from idlelib import macosxSupport +from idlelib.PyShell import PyShell + + +# A right-click menu has been added. See http://bugs.python.org/issue1207589 +# from 2012-11-01. As a result, this extension is no longer necessary. + + +class RightClickMenu: # must be the same name as the file for EditorWindow.py + # to load it. + + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + self.is_shell = isinstance(editwin, PyShell) + + m = editwin.rmenu_specs + if len(m) > 0: + if len(m[0]) == 3: + # RightClickMenu.py no longer needed. See issue1207589 + return + + self.rmenu_specs = [None, + ('Cut', '<>'), + ('Copy', '<>'), + ('Paste', '<>'), + None, + ('Select All', '<>'), + ] + + editwin.text.after(1, self.delay) + + def delay(self): + e = self.editwin + m = e.rmenu_specs + try: + # This functionality in OutputWindow.py + # requires the cursor to leave the input area in the shell. + # IDLE should not do that. + m.remove(('Go to file/line', '<>')) + + except: + pass + + text = self.text + if macosxSupport.runningAsOSXApp(): + # Some OS X systems have only one mouse button, + # so use control-click for pulldown menus there. + # (Note, AquaTk defines <2> as the right button if + # present and the Tk Text widget already binds <2>.) + text.bind("",self.right_menu_event) + else: + # Elsewhere, use right-click for pulldown menus. + text.bind("<3>",self.right_menu_event) + + + bmenu = [None] # breakpoint options + for i in m: + if 'breakpoint' in i[0].lower(): + bmenu.append(i) + else: + self.rmenu_specs.append(i) + + self.rmenu_specs.extend(bmenu) + + + self.make_rmenu() + + def make_rmenu(self): + rmenu = tk.Menu(self.text, tearoff=0) + for entry in self.rmenu_specs: + if not entry: + rmenu.add_separator() + else: + label, eventname = entry + def command(text=self.text, eventname=eventname): + text.event_generate(eventname) + rmenu.add_command(label=label, command=command) + + self.editwin.rmenu = rmenu + + def right_menu_event(self, event): + + sel_first = self.text.index('sel.first') + if not sel_first and not self.is_shell: + self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) + + e = self.editwin + if not e.rmenu: + e.make_rmenu() + rmenu = e.rmenu + iswin = sys.platform[:3] == 'win' + if iswin: + e.text.config(cursor="arrow") + rmenu.tk_popup(event.x_root, event.y_root) + if iswin: + e.text.config(cursor="ibeam") diff --git a/idlex-1.11.2/idlexlib/extensions/RunSelection.py b/idlex-1.11.2/idlexlib/extensions/RunSelection.py new file mode 100644 index 0000000..0367bc9 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/RunSelection.py @@ -0,0 +1,679 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## Run Selection Extension - run parts of source code in IDLE +## +## About: +## +## This code executes the currently highlighted text +## from the editor in the shell window, or a single line. +## +## How to Use: +## +## Run Selection or Line: +## +## * To run a single statement (including indented code) +## move cursor to the line and press F9. +## +## * To run a selection of statements, select the statements +## to run and press F9 +## +## +## Run Region +## +## A Run Region behaves like a selection, but it is tagged +## with a blue background. A group of commands can be tagged +## as a Run Region and executed using Ctrl+F9. +## +## Tag/Join Run Region +## - Tags a line or selection of code as a Run Region +## - Can join separate Run Regions +## +## Untag/Split Run Region +## - Untags a line or selection of code +## - Can split a Run Region into two separate Run Regions +## +## """ +config_extension_def = """ + +[RunSelection] +enable=1 +enable_shell=0 +enable_editor=1 +[RunSelection_cfgBindings] +run-region= +""" + +TAG_REGIONS = False # allow for selections to be tagged (EXPERIMENTAL) +TAG_REGIONS = True + +if TAG_REGIONS: + config_extension_def += """ +run-tagged= +region-tagged-join= +region-tagged-split= +""" + +RUNREGION_BACKGROUND = '#BBE0FF' # background color for a runregion + +import sys + +if sys.version < '3': + from Tkinter import * + import tkMessageBox +else: + from tkinter import * + import tkinter.messagebox as tkMessageBox + xrange = range + + +from idlelib.EditorWindow import classifyws +from idlelib.configHandler import idleConf + +import ast +import tokenize +import re + +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates +sp = lambda x: list(map(int, x.split('.'))) # convert tkinter Text coordinate to a line and column tuple + +def index(text, marker='insert', lineoffset=0): + """ helper function to split a marker location of a text object """ + line, col = sp(text.index(marker)) + return (line + lineoffset, col) + + +#--------------------- +# AST helper functions +#--------------------- + +def find_sel_range(y, sline, eline=None, first=None, last=None, EF=None): + """ Returns the first and last nodes in AST for given line range. + + y: module in the AST + sline: start line number + eline: end line number + first: a tuple of the first module and line + last: a tuple of the last module and line + EF: an optional callback for using EndFinder to find the true endline of a module. + + """ + if EF is None: + EF = lambda x: x + + if eline is None: + eline = sline + + def helper(M, first=None, last=None): + for n, stmt in enumerate(M): + if sline <= EF(stmt.lineno): + if stmt.lineno <= eline: + last = (M, n) # last found statement in range + if first is None: + first = (M, n) # first found statement in rangef + first, last = find_sel_range(stmt, sline, eline, first, last, EF) + return first, last + + for elt in ['body', 'orelse', 'handlers', 'finalbody']: + M = getattr(y, elt, None) + if M is not None: + first, last = helper(M, first, last) + + return first, last + + +def get_module_endline(y): + if not hasattr(y, 'body'): + return y.lineno + lastnode = y.body[-1] + + for elt in ['orelse', 'handlers', 'finalbody']: + M = getattr(lastnode, elt, None) + if M is not None: + if M: + lastnode = M[-1] + + v = get_module_endline(lastnode) + if v is None: + return lastnode.lineno + else: + return v + + +class EndFinder(object): + + def __init__(self, src): + self.lines = src.split('\n') + self.length = len(self.lines) + self.offset = 0 + + def _make_readline(self, offset): + self.offset = offset - 1 + def readline(): + if self.offset < self.length: + # Strip trailing whitespace to avoid issue16152 + ret = self.lines[self.offset].rstrip() + '\n' + self.offset += 1 + else: + ret = '' + return ret + return readline + + def __call__(self, lastline): + endreader = self._make_readline(lastline) + opener = ['(', '{', '['] + closer = [')', '}', ']'] + op_stack = [] + lastline_offset = 1 # default value for no effective offset + try: + for tok in tokenize.generate_tokens(endreader): + t_type, t_string, t_srow_scol, t_erow_ecol, t_line = tok + if t_type == tokenize.OP: + if t_string in closer: + if op_stack: + c = op_stack.pop() + if t_string in opener: + op_stack.append(t_string) + + if t_type == tokenize.NEWLINE and not op_stack: + lastline_offset = t_erow_ecol[0] + break + except tokenize.TokenError: + pass + + lastline += (lastline_offset - 1) + + return lastline + + +#--------------- +# The extension +#---------------- + +class RunSelection(object): + + _menu = [None, + ('Run Selection or Line', '<>')] + + if TAG_REGIONS: + _menu.extend([ + ('Run Tagged Region', '<>'), + ('Tag/Join Region', '<>'), + ('Untag/Split Region', '<>')]) + menudefs = [ + ('run', _menu), + ] + + def __init__(self, editwin): + self.editwin = editwin + text = self.text = editwin.text + + text.tag_configure('RUNREGION', **{'background':RUNREGION_BACKGROUND, + #'borderwidth':2, + #'relief':GROOVE, + }) + text.tag_raise('sel') + text.tag_raise('ERROR') + + r = editwin.rmenu_specs + # See Issue1207589 patch + if len(r) > 0: + if len(r[0]) == 2: + specs = [('Run Selection or Line', '<>')] + elif len(r[0]) == 3: + specs = [('Run Selection or Line', '<>', None)] + + for m in specs: + if m not in editwin.rmenu_specs: + editwin.rmenu_specs.append(m) + + + #--------------------------------- + # Event Handlers + #--------------------------------- + + def run_region_event(self, event=None): + # <> was triggered + + sel = self.get_sel_range() + if sel: + # Run the selected code. + code_region = self.send_code(*sel) + else: + # Run the code on current line + lineno, col = sp(self.text.index('insert')) + code_region = self.send_code(lineno, lineno) + + self.text.tag_remove('sel', '1.0', END) + if code_region: + self.shrink_region(*code_region, tag='sel') + + + def run_tagged_event(self, event=None): + # <> was triggered + lineno, col = sp(self.text.index('insert')) + r = self.get_tagged_region(lineno) + if r: + # Run the code contained in the tagged run region. + self.untag_entire_region(r[0]) + self.tag_run_region(*r) + self.adjust_cursor(*r) + code_region = self.send_code(*r) + if code_region: + self.shrink_region(*code_region, tag='RUNREGION') + + def region_tagged_join_event(self, event=None): + sel = self.get_sel_range() + if sel: + self.tag_run_region(*sel) + self.clear_sel() + else: + lineno, col = sp(self.text.index('insert')) + self.tag_run_region(lineno, lineno) + return "break" + + def region_tagged_split_event(self, event=None): + sel = self.get_sel_range() + if sel: + self.untag_run_region(*sel) + self.clear_sel() + else: + lineno, col = sp(self.text.index('insert')) + self.untag_run_region(lineno, lineno) + return "break" + + + #-------------------------- + # Tag/Untag Run Region Code + #-------------------------- + + def _tag_region(self, first, last, tag): + self.text.tag_add(tag, '%i.0' % (first), + '%i.0' % (last+1)) + + def _untag_region(self, first, last, tag): + self.text.tag_remove(tag, '%i.0' % (first), + '%i.0' % (last+1)) + + def get_tagged_region(self, line): + """ Return the Region at line number. + + None If no region + (first, last) Line numbers + + """ + if line < 0: + return None + + text = self.text + loc = '%i.0+1c' % line + p = text.tag_prevrange("RUNREGION", loc) + if p: + if text.compare(p[0], '<=', loc) and \ + text.compare(p[1], '>=', loc): + first, col = sp(p[0]) + last, col = sp(p[1]) + return first, last-1 + return None + + + def tag_run_region(self, first, last): + """ Tag a given span of lines as a Run Region """ + if first > last: + first, last = last, first + + tree = self.get_tree() + active = self.get_active_statements(first, last, tree=tree) + + if active: + # Active statements are in the given range. + # Tag this range. + mod, firstline, lastline, offset = active + self._tag_region(firstline, lastline, 'RUNREGION') + + # Check if joining regions. This may happen + # if the user expanded an existing region + # but the expansion contains no active code. + firstregion = self.get_tagged_region(first) + lastregion = self.get_tagged_region(last) + + if firstregion: + if lastregion: + # join the separated run regions + r1 = self.get_active_statements(*firstregion, tree=tree) + r2 = self.get_active_statements(*lastregion, tree=tree) + # make sure both regions at same indentation + if r1 and r2: + if r1[3] == r2[3]: # make sure offsets are same + firstline = firstregion[0] + lastline = lastregion[0] + self._tag_region(firstline, lastline, 'RUNREGION') + else: + #print('wrong offsets for join') + msg = 'Could not join regions due to indentation mismatch.' + self.show_error('Join region error', msg) + pass + else: + # expand downward + self.tag_run_region(firstregion[0], last) + + def untag_run_region(self, first, last): + """ Untags a run region over the given range of lines. """ + if first > last: + first, last = last, first + + tag = 'RUNREGION' + + r1 = self.get_tagged_region(first) + r2 = self.get_tagged_region(last) + + self._untag_region(first, last, tag) # untag the given range + + T = self.get_tree() + + # shrink the surrounding run regions if they exist + firstregion = self.get_tagged_region(first-1) + lastregion = self.get_tagged_region(last+2) + + def retag(region): + if region is None: + return + F, L = region + self._untag_region(F, L, tag) + active = self.get_active_statements(F, L, tree=T) + if active: + mod, F, L, offset = active + self._tag_region(F, L, tag) + + retag(firstregion) + retag(lastregion) + + # If RUNREGION tag still exists within [first, last] + # then restore prior tags + t1 = self.text.tag_names('%i.0' % first) + t2 = self.text.tag_names('%i.0-1c' % last) + + restore = False + if first != last: + if tag in t1 or tag in t2: + #print('could not untag') + restore = True + else: + if tag in t1: + #print('could not untag2') + restore = True + if restore: + msg = 'Could not untag because line %i is tagged.' % firstregion[0] + self._untag_region(first, last, tag) + retag(r1) + retag(r2) + self.show_error('Split region error', msg) + + def untag_entire_region(self, line): + """ Untags an entire region containing the given line. """ + r = self.get_tagged_region(line) + if r: + self.untag_run_region(*r) + + + def shrink_region(self, first, last, tag): + text = self.text + if first == last: + endsel = '%i.0 lineend' % first + else: + endsel = '%i.0' % (last + 1) + text.tag_add(tag, '%i.0' % first, endsel) + + + #--------------------- + # Editor-handling code + #--------------------- + + def adjust_cursor(self, start, end): + """ Move the cursor in case run region shrinks """ + text = self.text + if text.compare('insert', '>', '%i.0' % end): + text.mark_set('insert', '%i.0' % end + ' lineend') + elif text.compare('insert', '<', '%i.0' % start): + text.mark_set('insert','%i.0' % start) + + def get_sel_range(self): + """ Return the first and last line containing the selection """ + text = self.text + sel_first = text.index('sel.first') + if sel_first: + firstline, firstcol = sp(sel_first) + lastline, lastcol = sp(text.index('sel.last')) + if lastcol == 0: + lastline -= 1 + return firstline, lastline + else: + return None + + def clear_sel(self): + self.text.tag_remove('sel', '1.0', 'end') + + def focus_editor(self, ev=None): + self.editwin.text.focus_set() + self.editwin.top.lift() + + + def show_error(self, title, msg): + tkMessageBox.showerror(title=title, + message=msg, + parent = self.text) + pass + + #------------- + # Code-parsing + #------------- + + def send_code(self, first, last): + """ Sends the code contained in the selection """ + text = self.text + active = self.get_active_statements(first, last) + if active is not None: + mod, firstline, lastline, offset = active + # return the code that can be executed + if firstline != lastline: + msg = '# Run Region [%i-%i]' % (firstline, lastline) + else: + msg = '# Run Region [line %i]' % firstline + + src = text.get('%i.0' % firstline, + '%i.0' % (lastline + 1)) + + if offset: # dedent indented code + src = re.sub(r"(? 5 ] +123 + +""" + e = EndFinder(src) + print(e(1)) + + + root = Tk() + class ignore: + def __getattr__(self, name): + print('ignoring %s' % name) + return lambda *args, **kwargs: None + + class EditorWindow(ignore): + text = Text(root) + text.tag_raise = lambda *args, **kw: None + text.insert('1.0', src) + rmenu_specs = [] + + + editwin = EditorWindow() + rs = RunSelection(editwin) + tree = rs.get_tree() + s = rs.get_active_statements(1.0, 1.0, tree=tree) + + print(s) + diff --git a/idlex-1.11.2/idlexlib/extensions/SearchBar.py b/idlex-1.11.2/idlexlib/extensions/SearchBar.py new file mode 100644 index 0000000..581c81d --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/SearchBar.py @@ -0,0 +1,944 @@ +# IDLEX EXTENSION + +## """SearchBar.py - An IDLE extension for searching for text in windows. +## +## Copyright (c) 2011 Tal Einat +## All rights reserved. +## +## Developed by: Tal Einat +## +## Permission is hereby granted, free of charge, to any person obtaining a +## copy of this software and associated documentation files (the "Software"), +## to deal with the Software without restriction, including without limitation +## the rights to use, copy, modify, merge, publish, distribute, sublicense, +## and/or sell copies of the Software, and to permit persons to whom the +## Software is furnished to do so, subject to the following conditions: +## +## Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimers. +## +## Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimers in the documentation +## and/or other materials provided with the distribution. +## +## Neither the name of Tal Einat, nor the names of its contributors may be +## used to endorse or promote products derived from this Software without +## specific prior written permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +## TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +## OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## The interface is a small bar which appears on the bottom of the window, +## and dissapears when the user stops searching. +## +## This extension implements the usual search options, as well as regular +## expressions. +## +## Another nice feature is that while searching all matches are highlighted. +## +## +## Original Author: Tal Einat +## +## Modified by Roger D. Serwy to work with idlex and Python 3, +## as well as some bugfixes and improvements. +## +## +## +## """ + +config_extension_def = """ +[SearchBar] +enable=1 +is_incremental=1 +reg_exp=0 +match_case=0 +whole_word=0 +wrap_around=0 + +[Searchbar_cfgBindings] +toggle-search-bar= + +""" + +import time +import string +import re +import sys + +if sys.version < '3': + import Tkinter + from Tkconstants import TOP, BOTTOM, LEFT, RIGHT, X, NONE +else: + import tkinter as Tkinter + from tkinter.constants import TOP, BOTTOM, LEFT, RIGHT, X, NONE + +EXTNAME = 'SearchBar' + +from idlelib.configHandler import idleConf +from idlelib.SearchEngine import SearchEngine + + +class SearchBarSearchEngine(SearchEngine): + """ Silence regex errors. + Incremental highlighting doesn't play well with mal-formed regex. + + """ + + def __init__(self, *args, **kw): + SearchEngine.__init__(self, *args, **kw) + self._error_callback_ptr = None + self._incrementalSearch = False + self.varlist = [self.revar, self.casevar, self.wordvar, self.wrapvar] + self.tracelist = [] + + def report_error(self, pat, msg, col=-1): + #print('report_error', pat, msg, col,self._incrementalSearch) + if self._incrementalSearch: + if self._error_callback_ptr: + return self._error_callback_ptr(pat, msg, col) + else: + return None + else: + return SearchEngine.report_error(self, pat, msg, col) + + def error_callback(self, ptr): + # This is set by FindBar and ReplaceBar instances so that it + # calls the correct callback + self._error_callback_ptr = ptr + + def load_cfg(self): + # Load settings from configuration handler - RDS 2012-02-03 + self.revar.set(get_cfg('reg_exp', default=False)) + self.casevar.set(get_cfg('match_case', default=False)) + self.wordvar.set(get_cfg('whole_word', default=False)) + self.wrapvar.set(get_cfg('wrap_around', default=False)) + + def save_cfg(self): + set_cfg('reg_exp', '%s' % self.revar.get()) + set_cfg('match_case', '%s' % self.casevar.get()) + set_cfg('whole_word', '%s' % self.wordvar.get()) + set_cfg('wrap_around', '%s' % self.wrapvar.get()) + + def set_var_trace(self, ptr): + obs = [] + for v in self.varlist: + obs.append(v.trace("w", ptr)) + self.tracelist = zip(obs, self.varlist) + + def remove_var_trace(self): + for obs, v in self.tracelist: + v.trace_vdelete('w', obs) + self.tracelist = [] + + + +def get_cfg(cfg, type="bool", default=True): + return idleConf.GetOption("extensions", EXTNAME, + cfg, type=type, default=default) + +def set_cfg(cfg, b): + return idleConf.SetOption("extensions", EXTNAME, + cfg,'%s' % b) + +class SearchBar: + menudefs = [] + def __init__(self, editwin): + text = editwin.text + self.engine = engine = SearchBarSearchEngine(text) + self.fb = find_bar = FindBar(editwin, editwin.status_bar, engine) + self.rb = replace_bar = ReplaceBar(editwin, editwin.status_bar, engine) + + def find_event(event): + replace_bar.hide_findbar_event(event, focus=False) + find_bar.show_findbar_event(event) + return "break" + text.bind("<>", find_event) + + def find_again_event(event): + find_bar.search_again_event(event) + return "break" + text.bind("<>", find_again_event) + + def find_selection_event(event): + find_bar.search_selection_event(event) + return "break" + text.bind("<>", find_selection_event) + + def replace_event(event): + find_bar.hide_findbar_event(event, focus=False) + replace_bar.show_findbar_event(event) + return "break" + text.bind("<>", replace_event) + + def close(self): + self.engine.save_cfg() + + + + +def FindBar(editwin, pack_after, engine): + return SearchBarWidget(editwin, pack_after, engine, is_replace=False) +def ReplaceBar(editwin, pack_after, engine): + return SearchBarWidget(editwin, pack_after, engine, is_replace=True) + +class SearchBarWidget: + def __init__(self, editwin, pack_after, engine, is_replace=False): + self.text = editwin.text + self.root = self.text._root() + self.engine = engine + self.window_engine = get_window_engine(editwin) + self.is_replace = is_replace + + self.top = editwin.top + self.pack_after = pack_after + + self.widgets_built = False + self.shown = False + + self.find_var = Tkinter.StringVar(self.root) + + # The text widget's selection isn't shown when it doesn't have the + # focus. Let's replicate it so it will be seen while searching as well. + self.text.tag_configure("findsel", + background=self.text.tag_cget("sel","background"), + foreground=self.text.tag_cget("sel","foreground")) + + self._is_incremental = None + self._expand_state = None + + self.text.bind('', self.text_focusin_event, '+') + + def toggle_search_bar_event(self, event=None): # RDS - 2011-10-18 + self.text.event_generate('<>') + return "break" + + def _show(self): + if not self.widgets_built: + self._build_widgets() + + if not self.shown: + self.bar_frame.pack(side=BOTTOM, fill=X, expand=0, pady=1, + after=self.pack_after) + self.window_engine.show_find_marks() + self.shown = True # must be _before_ reset_selection()! + # Add the "findsel" tag, which looks like the selection + self._reset_selection() + + self._is_incremental = self.is_incremental() + self._expand_state = None + self.engine.error_callback(self._error_callback) + self.engine.load_cfg() + self.engine.set_var_trace(self._incremental_callback) + + def _hide(self, setcursor=False): + if self.widgets_built and self.shown: + v = self.text.yview() + self.bar_frame.pack_forget() + self.text.update_idletasks() + try: + self.text.yview_moveto(v[0]) # Tkinter work-around + except Exception as err: # This should never happen + print('SearchBar._hide', err) + + self.window_engine.reset() + self.window_engine.hide_find_marks() + + sel = self._get_selection() + self.shown = False # must be _after_ get_selection()! + if setcursor: + if sel: + self._set_selection(sel[0], sel[1]) + self.text.mark_set("insert", sel[0]) + else: + self._reset_selection() + self.text.see("insert") + + self.text.tag_remove("findsel","1.0","end") + self._is_incremental = None + self._expand_state = None + self.engine.error_callback(None) + self.engine.save_cfg() + self.engine.remove_var_trace() + + def _error_callback(self, pat, msg, col=-1): + # A callback for the SearchBarSearchEngine .report_error method + self.window_engine.reset() + pass + + def is_incremental(self): + if self._is_incremental is None: + return get_cfg("is_incremental", default=False) + else: + return self._is_incremental + + def _incremental_callback(self, *args): + self.engine._incrementalSearch = True + + if self.shown and self.is_incremental(): + if self.find_var.get(): + self._safe_search(start=self.text.index("insert")) + else: + self.window_engine.reset() + self._clear_selection() + self.text.see("insert") + + self.engine._incrementalSearch = False + + + def _build_widgets(self): + if not self.widgets_built: + def _make_entry(parent, label, var): + l = Tkinter.Label(parent, text=label) + l.pack(side=LEFT, fill=NONE, expand=0) + e = Tkinter.Entry(parent, textvariable=var, exportselection=0, + width=30, border=1) + e.pack(side=LEFT, fill=NONE, expand=0) + e.bind("", self.hide_findbar_event) + return e + + def _make_checkbutton(parent, label, var): + btn = Tkinter.Checkbutton(parent, anchor="w", + text=label, variable=var) + btn.pack(side=LEFT, fill=NONE, expand=0) + btn.bind("", self.hide_findbar_event) + return btn + + def _make_button(parent, label, command): + btn = Tkinter.Button(parent, text=label, command=command) + btn.pack(side=LEFT, fill=NONE, expand=0) + btn.bind("", self.hide_findbar_event) + return btn + + # Frame for the entire bar + self.bar_frame = Tkinter.Frame(self.top, border=1, relief="flat") + + # Frame for the 'Find:' / 'Replace:' entry and direction + self.find_frame = Tkinter.Frame(self.bar_frame, border=0) + + # Frame for the 'Find:' options + self.find_frame_options = Tkinter.Frame(self.bar_frame, border=0) # RDS - 2011-11-12 + + + tabstop_top = Tkinter.Label(self.find_frame, takefocus=1, text='', + highlightthickness=0) + tabstop_top.pack(side=LEFT) + + + # 'Find:' / 'Replace:' entry + if not self.is_replace: tmp = "Find:" + else: tmp = "Replace:" + + + self.find_ent = _make_entry(self.find_frame, + tmp, self.find_var) + + + # Regular expression checkbutton + btn = _make_checkbutton(self.find_frame_options, + "Reg-Exp", self.engine.revar) + if self.engine.isre(): + btn.select() + self.reg_btn = btn + + # Match case checkbutton + btn = _make_checkbutton(self.find_frame_options, + "Match case", self.engine.casevar) + if self.engine.iscase(): + btn.select() + self.case_btn = btn + + # Whole word checkbutton + btn = _make_checkbutton(self.find_frame_options, + "Whole word", self.engine.wordvar) + if self.engine.isword(): + btn.select() + self.word_btn = btn + + # Wrap checkbutton + btn = _make_checkbutton(self.find_frame_options, + "Wrap around", self.engine.wrapvar) + if self.engine.iswrap(): + btn.select() + self.wrap_btn = btn + + + # Direction checkbutton + Tkinter.Label(self.find_frame, text="Direction:").pack(side=LEFT, + fill=NONE, + expand=0,padx=6) + + self.direction_txt_var = Tkinter.StringVar(self.root) + btn = Tkinter.Checkbutton(self.find_frame, + textvariable=self.direction_txt_var, + variable=self.engine.backvar, + command=self._update_direction_button, + indicatoron=0, + width=5, + ) + btn.config(selectcolor=btn.cget("bg")) + btn.pack(side=LEFT, fill=NONE, expand=0) + + if self.engine.isback(): + btn.select() + self.direction_txt_var.set("Up") + else: + btn.deselect() + self.direction_txt_var.set("Down") + btn.bind("",self.hide_findbar_event) + self.direction_btn = btn + + self.find_frame.pack(side=TOP, fill=X, expand=1) + self.find_frame_options.pack(side=TOP, fill=X, expand=1) + + + if self.is_replace: + # Frame for the 'With:' entry + replace options + self.replace_frame = Tkinter.Frame(self.bar_frame, border=0) + self.replace_frame_buttons = Tkinter.Frame(self.bar_frame, border=0) + + tmp = Tkinter.Label(self.replace_frame, takefocus=0, text='', + highlightthickness=0) + tmp.pack(side=LEFT) + + + self.replace_with_var = Tkinter.StringVar(self.root) + self.replace_ent = _make_entry(self.replace_frame,"With:", + self.replace_with_var) + + self.find_btn = _make_button(self.replace_frame_buttons, "Find", + self._search) + self.replace_btn = _make_button(self.replace_frame_buttons, "Replace", + self._replace_event) + self.replace_find_btn = _make_button(self.replace_frame_buttons, "Replace+Find", + self._replace_find_event) + self.replace_all_btn = _make_button(self.replace_frame_buttons, "Replace All", + self._replace_all_event) + + self.replace_frame.pack(side=TOP, fill=X, expand=0) + self.replace_frame_buttons.pack(side=TOP, fill=X, expand=0) + + self.widgets_built = True + + # Key bindings for the 'Find:' / 'Replace:' Entry widget + self.find_ent.bind("", self._safe_search) + self.find_ent.bind("", self._safe_search) + self.find_ent.bind("", self._toggle_reg_event) + self.find_ent.bind("", self._toggle_case_event) + self.find_ent.bind("", self._toggle_wrap_event) + self.find_ent.bind("", self._toggle_direction_event) + self.find_ent_expander = EntryExpander(self.find_ent, self.text) + self.find_ent_expander.bind("") + + callback = self.find_ent._register(self._incremental_callback) + self.find_ent.tk.call("trace", "variable", self.find_var, "w", + callback) + + keySetName = idleConf.CurrentKeys() + find_bindings = idleConf.GetKeyBinding(keySetName, '<>') + for key_event in find_bindings: + self.find_ent.bind(key_event, self._search) # RDS - 2011-11-03 + + if not self.is_replace: + # Key bindings for the 'Find:' Entry widget + self.find_ent.bind("", self._safe_search) + + + def tab_fix1(ev): + if ev.state & 1 == 0: # Windows Fix + self.find_ent.focus() + return "break" + + self.wrap_btn.bind('', tab_fix1) + + def tab_fix2(ev): + self.wrap_btn.focus() + return "break" + tabstop_top.bind('', tab_fix2) + + else: + # Key bindings for the 'Replace:' Entry widget + self.find_ent.bind("", self._replace_bar_find_entry_return_event) + + # Key bindings for the 'With:' Entry widget + self.replace_ent.bind("", self._replace_event) + self.replace_ent.bind("", self._safe_search) + self.replace_ent.bind("", self._safe_search) + self.replace_ent.bind("", self._safe_search) + self.replace_ent.bind("", self._toggle_reg_event) + self.replace_ent.bind("", self._toggle_case_event) + self.replace_ent.bind("", self._toggle_wrap_event) + self.replace_ent.bind("", self._toggle_direction_event) + self.replace_ent_expander = EntryExpander(self.replace_ent, + self.text) + self.replace_ent_expander.bind("") + for key_event in find_bindings: + self.replace_ent.bind(key_event, self._search) # RDS - 2011-11-19 + + def tab_fix1(ev): + if ev.state & 1 == 0: # Windows Fix + self.find_ent.focus() + return "break" + self.replace_all_btn.bind('', tab_fix1) + + def tab_fix2(x): + self.replace_all_btn.focus() + return "break" + tabstop_top.bind('', tab_fix2) + + + + def _destroy_widgets(self): + if self.widgets_built: + self.bar_frame.destroy() + + def show_findbar_event(self, event): + self.text.tag_raise('findmark') + self.text.tag_raise('findsel') + self.text.tag_raise('sel') + + # Get the current selection + sel = self._get_selection() + if sel: + # Put the current selection in the "Find:" entry + # FIXME: don't overwrite regexp if it matches the selection + self.find_var.set(self.text.get(sel[0],sel[1])) + self._clear_selection() + + # Now show the FindBar in all it's glory! + self._show() + + # Set the focus to the "Find:"/"Replace:" entry + self.find_ent.focus() + + # Select all of the text in the "Find:"/"Replace:" entry + self.find_ent.selection_range(0,"end") + + # Hide the findbar if the focus is lost + #self.bar_frame.bind("", self.hide_findbar_event) + # RDS - 2012-02-02 - Don't hide on focus_out, since regex error messages + # trigger this. + + # Focus traversal (Tab or Shift-Tab) shouldn't return focus to + # the text widget + self.prev_text_takefocus_value = self.text.cget("takefocus") + self.text.config(takefocus=0) + self._incremental_callback() + return "break" + + def text_focusin_event(self, event=None): # RDS - 2012-02-02 + if not self.shown: + return + else: + self.hide_findbar_event(setcursor=False) + + def hide_findbar_event(self, event=None, setcursor=True, focus=True): + if not self.shown: + return "break" + + self._hide(setcursor=setcursor) + if focus: + self.text.focus() + + return "break" + + + def search_again_event(self, event): + if self.engine.getpat(): + return self._search(event) + else: + return self.show_findbar_event(event) + + def search_selection_event(self, event): + # Get the current selection + sel = self._get_selection() + if not sel: + # No selection - beep and leave + self.text.bell() + return "break" + + # Set the window's search engine's pattern to the current selection + self.find_var.set(self.text.get(sel[0],sel[1])) + + return self._search(event) + + def _toggle_reg_event(self, event): + self.reg_btn.invoke() + return "break" + + def _toggle_case_event(self, event): + self.case_btn.invoke() + return "break" + + def _toggle_wrap_event(self, event): + self.wrap_btn.invoke() + return "break" + + def _toggle_direction_event(self, event): + self.direction_btn.invoke() + return "break" + + def _update_direction_button(self): + if self.engine.backvar.get(): + self.direction_txt_var.set("Up") + else: + self.direction_txt_var.set("Down") + + def _replace_bar_find_entry_return_event(self, event=None): + # Set the focus to the "With:" entry + self.replace_ent.focus() + # Select all of the text in the "With:" entry + self.replace_ent.selection_range(0,"end") + return "break" + + def _search_text(self, start, is_safe): + self.engine.patvar.set(self.find_var.get()) + regexp = self.engine.getprog() + + if not regexp: + # an error occurred. + return None + + direction = not self.engine.isback() + wrap = self.engine.iswrap() + sel = self._get_selection() + + if start is None: + if sel: + start = sel[0] + else: + start = self.text.index("insert") + if ( direction and sel and start == sel[0] and + regexp.match(self.text.get(sel[0],sel[1])) ): + _start = start + "+1c" + else: + _start = start + res = self.window_engine.findnext(regexp, + _start, direction, wrap, is_safe) + + # ring the bell if the selection was found again + if sel and start == sel[0] and res == sel: + self.text.bell() + + return res + + def _search(self, event=None, start=None, is_safe=False): + t = time.time() + res = self._search_text(start, is_safe) + if res: + first, last = res + self._clear_selection() + self._set_selection(first, last) + self.text.see(first) + if not self.shown: + self.text.mark_set("insert", first) + else: + self._clear_selection() + self.text.bell() + return "break" + + def _safe_search(self, event=None, start=None): + return self._search(event=event, start=start, is_safe=True) + + def _replace_event(self, event=None): + self.engine.patvar.set(self.find_var.get()) + regexp = self.engine.getprog() + if not regexp: + return "break" + + # Replace if appropriate + sel = self._get_selection() + if sel and regexp.match(self.text.get(sel[0], sel[1])): + replace_with = self.replace_with_var.get() + + self.text.undo_block_start() + if sel[0] != sel[1]: + self.text.delete(sel[0], sel[1]) + if replace_with: + self.text.insert(sel[0], replace_with) + self.text.undo_block_stop() + + self._clear_selection() + self._set_selection(sel[0], sel[0] + '+%ic' % len(replace_with)) + self.text.mark_set("insert", sel[0] + '+%ic' % len(replace_with)) + + return "break" + + def _replace_find_event(self, event=None): # RDS - 2011-10-18 + self._replace_event(event) + return self._search(event, is_safe=False) + + def _replace_all_event(self, event=None): + self.engine.patvar.set(self.find_var.get()) + regexp = self.engine.getprog() + if not regexp: + return "break" + + direction = not self.engine.isback() + wrap = self.engine.iswrap() + self.window_engine.replace_all(regexp, self.replace_with_var.get()) + return "break" + + + ### Selection related methods + def _clear_selection(self): + tagname = self.shown and "findsel" or "sel" + self.text.tag_remove(tagname, "1.0", "end") + + def _set_selection(self, start, end): + self._clear_selection() + tagname = self.shown and "findsel" or "sel" + self.text.tag_add(tagname, start, end) + + def _get_selection(self): + tagname = self.shown and "findsel" or "sel" + return self.text.tag_nextrange(tagname, '1.0', 'end') + + def _reset_selection(self): + if self.shown: + sel = self.text.tag_nextrange("sel", '1.0', 'end') + if sel: + self._set_selection(sel[0], sel[1]) + else: + self._clear_selection() + + +class EntryExpander(object): + """Expand words in an entry, taking possible words from a text widget.""" + def __init__(self, entry, text): + self.text = text + self.entry = entry + self.reset() + + self.entry.bind('', self.reset) + + def reset(self, event=None): + self._state = None + + def bind(self, event_string): + self.entry.bind(event_string, self._expand_word_event) + + def _expand_word_event(self, event=None): + curinsert = self.entry.index("insert") + curline = self.entry.get() + if not self._state: + words = self._get_expand_words() + index = 0 + else: + words, index, insert, line = self._state + if insert != curinsert or line != curline: + words = self._get_expand_words() + index = 0 + if not words: + self.text.bell() + return "break" + + curword = self._get_curr_word() + newword = words[index] + index = (index + 1) % len(words) + if index == 0: + self.text.bell() # Warn the user that we cycled around + + idx = int(self.entry.index("insert")) + self.entry.delete(str(idx - len(curword)), str(idx)) + self.entry.insert("insert", newword) + + curinsert = self.entry.index("insert") + curline = self.entry.get() + self._state = words, index, curinsert, curline + return "break" + + def _get_expand_words(self): + curword = self._get_curr_word() + if not curword: + return [] + + regexp = re.compile(r"\b" + curword + r"\w+\b") + # Start at 'insert wordend' so current word is first + beforewords = regexp.findall(self.text.get("1.0", "insert wordend")) + beforewords.reverse() + afterwords = regexp.findall(self.text.get("insert wordend", "end")) + # Interleave the lists of words + # (This is the next best thing to sorting by distance) + allwords = [] + for a,b in zip(beforewords, afterwords): + allwords += [a,b] + minlen = len(allwords)/2 + allwords += beforewords[minlen:] + afterwords[minlen:] + + words_list = [] + words_dict = {} + for w in allwords: + if w not in words_dict: + words_dict[w] = w + words_list.append(w) + words_list.append(curword) + return words_list + + _wordchars = string.ascii_letters + string.digits + "_" + def _get_curr_word(self): + line = self.entry.get() + i = j = self.entry.index("insert") + while i > 0 and line[i-1] in self._wordchars: + i = i-1 + return line[i:j] + + +def get_window_engine(editwin): + if not hasattr(editwin, "_window_search_engine"): + editwin._window_search_engine = WindowSearchEngine(editwin.text) + return editwin._window_search_engine + +class WindowSearchEngine: + def __init__(self, text): + self.text = text + + # Initialize 'findmark' tag + self.hide_find_marks() + + self.reset() + + def __del__(self): + self.text.tag_delete("findmark") + + def show_find_marks(self): + # Get the highlight colors for 'hit' + # Do this here (and not in __init__) for color config changes to take + # effect immediately + currentTheme = idleConf.CurrentTheme() + mark_fg = idleConf.GetHighlight(currentTheme, 'hit', fgBg='fg') + mark_bg = idleConf.GetHighlight(currentTheme, 'hit', fgBg='bg') + + self.text.tag_configure("findmark", + foreground=mark_fg, + background=mark_bg) + + def hide_find_marks(self): + self.text.tag_configure("findmark", + foreground='', + background='') + + def reset(self): + self.text.tag_remove("findmark", "1.0", "end") + self.regexp = None + + def _pos2idx(self, pos): + "Convert a position in the text string to a Text widget index" + return self.text.index("1.0+%dc"%pos) + + def _set_regexp(self, regexp): + "Set the current regexp; search for and mark all matches in the text" + ## When searching for an extension of the previous search, + ## i.e. regexp.startswith(self.regexp), update hits instead of starting from + ## scratch + self.reset() + self.regexp = regexp + + txt = self.text.get("1.0", "end-1c") + prev = 0 + line = 1 + rfind = txt.rfind + tag_add = self.text.tag_add + for res in regexp.finditer(txt): + start, end = res.span() + line += txt[prev:start].count('\n') + prev = start + start_idx = "%d.%d" % (line, + start - (rfind('\n', 0, start) + 1)) + end_idx = start_idx + '+%dc'%(end-start) + tag_add("findmark", start_idx, end_idx) + + def findnext(self, regexp, start, direction=1, wrap=True, is_safe=False, + last=False): + """Find the next text sequence which matches the given regexp. + + The 'next' sequence is the one after the selection or the insert + cursor, or before if the direction is up instead of down. + + The 'is_safe' argument tells whether it is safe to assume that the text + being searched has not been changed since the previous search; if the + text hasn't been changed then the search is almost trivial (due to + pre-processing). + + """ + if regexp != self.regexp or not is_safe: + self._set_regexp(regexp) + + # Search! + if direction: + next = self.text.tag_nextrange("findmark", start) + if not next: + if wrap: + # TODO: give message about wrap + next = self.text.tag_nextrange("findmark", '1.0', start) + else: + # TODO: no more matches message + pass + else: + next = self.text.tag_prevrange("findmark", start) + if not next: + if wrap: + # TODO: give message about wrap + next = self.text.tag_prevrange("findmark", 'end', start) + else: + # TODO: no more matches message + pass + + if not last and not next: + if direction==1: + delta='-1c' + else: + delta='+1c' + q1 = self.text.index(start+delta) + next = self.findnext(regexp, q1, direction=direction, + wrap=wrap, is_safe=is_safe, last=True) + # the "last=True" flag is to prevent infinite recursion if something + # should go wrong with tag_nextrange or prevrange. + + return next + + def replace_all(self, regexp, replace_with): + + oldhit = None + searchfrom = '1.0' + self.text.undo_block_start() + while True: + hit = self.findnext(regexp, searchfrom, + direction=1, wrap=False, is_safe=False) + if not hit or hit == oldhit: + break + oldhit = hit # avoid infinite loop due to ModifiedUndoDelegator in PyShell + first, last = hit + if first != last: + self.text.delete(first, last) + if replace_with: + self.text.insert(first, replace_with) + + searchfrom = last + + self.text.undo_block_stop() + + +def get_selection(text): + "Get the selection range in a text widget" + tmp = text.tag_nextrange("sel","1.0","end") + if tmp: + first, last = tmp + else: + first = last = text.index("insert") + return first, last + +##def idx2ints(idx): +## "Convert a Text widget index to a (line, col) pair" +## line, col = map(int,idx.split(".")) # Fails on invalid index +## return line, col + +##def ints2idx(ints): +## "Convert a (line, col) pair to Tk's Text widget's format." +## return "%d.%d" % ints # Fails on invalid index diff --git a/idlex-1.11.2/idlexlib/extensions/Squeezer.py b/idlex-1.11.2/idlexlib/extensions/Squeezer.py new file mode 100644 index 0000000..350a0c8 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/Squeezer.py @@ -0,0 +1,351 @@ +# IDLEX EXTENSION +## """ +## Squeezer - using this extension will make long texts become a small button. +## +## Copyright (c) 2011 Tal Einat +## All rights reserved. +## +## Developed by: Tal Einat +## +## Permission is hereby granted, free of charge, to any person obtaining a +## copy of this software and associated documentation files (the "Software"), +## to deal with the Software without restriction, including without limitation +## the rights to use, copy, modify, merge, publish, distribute, sublicense, +## and/or sell copies of the Software, and to permit persons to whom the +## Software is furnished to do so, subject to the following conditions: +## +## Redistributions of source code must retain the above copyright notice, +## this list of conditions and the following disclaimers. +## +## Redistributions in binary form must reproduce the above copyright notice, +## this list of conditions and the following disclaimers in the documentation +## and/or other materials provided with the distribution. +## +## Neither the name of Tal Einat, nor the names of its contributors may be +## used to endorse or promote products derived from this Software without +## specific prior written permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +## TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +## OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## Original Author: Noam Raphael +## Maintained by: Tal Einat +## +## Modified by Roger D. Serwy to work with idleX and Python 3. +## Some minor tweaks to the code were made. +## +## """ + +config_extension_def = """ +[Squeezer] +enable=1 +enable_shell=1 +enable_editor=0 +max-num-of-lines=150 +preview-command-posix=(xterm -e less %(fn)s; rm -f %(fn)s) & +preview-command-win=notepad %(fn)s +preview-command-mac= +[Squeezer_cfgBindings] +squeeze-last-output= +expand-last-squeezed= +preview-last-squeezed= +""" + +import re +from idlelib.PyShell import PyShell +from idlelib.configHandler import idleConf + +import sys + +if sys.version < '3': + import Tkinter + import tkFont +else: + import tkinter as Tkinter + import tkinter.font as tkFont + xrange = range + +import os + + +# define IDLE-infrastructure specific functions + +def _get_base_text(editwin): + "Return the base Text widget of an editwin, which can be changed before "\ + "the iomark." + return editwin.per.bottom + + +# define a function to count the number of lines in a given string +_TABWIDTH = 8 +_LINEWIDTH = 80 +_tab_newline_re = re.compile(r"[\t\n]") +_tab_table_cache = {} + +def _countlines(s, linewidth=_LINEWIDTH, tabwidth=_TABWIDTH): + if (tabwidth, linewidth) not in _tab_table_cache: + _tab_table_cache[(tabwidth, linewidth)] = \ + [ncols+tabwidth-(ncols%tabwidth) for ncols in xrange(linewidth)] + tab_table = _tab_table_cache[(tabwidth, linewidth)] + + pos = 0 + linecount = 1 + current_column = 0 + + for m in _tab_newline_re.finditer(s): + # process the normal chars up to tab or newline + numchars = m.start() - pos + if numchars > 0: # must special-case, otherwise divmod(-1, linewidth) + # If the length was exactly linewidth, divmod would give + # (1,0), even though a new line hadn't yet been started. + # Therefore subtract 1 before doing divmod, and later add + # 1 to the column to compensate. + lines, column = divmod(current_column + numchars - 1, linewidth) + linecount += lines + current_column = column + 1 + pos += numchars + + # deal with tab or newline + if s[pos] == '\n': + linecount += 1 + current_column = 0 + else: + assert s[pos] == '\t' + current_column = tab_table[current_column] + + pos += 1 # after the tab or newline + + # process remaining chars (no more tabs or newlines) + numchars = len(s) - pos + if numchars > 0: # must special-case, otherwise divmod(-1, linewidth) + linecount += (current_column + numchars - 1) // linewidth + return linecount + + +# define the extension's classes + +class ExpandingButton(Tkinter.Button): + + color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') + + def __init__(self, s, tags, numoflines, squeezer): + self.s = self.strip_ansi_colors(s) + self.tags = tags + self.squeezer = squeezer + self.editwin = editwin = squeezer.editwin + self.text = text = editwin.text + + caption = "Squeezed text (about %d lines).\n"\ + "Double-click to expand, middle-click to copy" % numoflines + if squeezer._PREVIEW_COMMAND: + caption += ", right-click to preview." + else: + caption += "." + Tkinter.Button.__init__(self, text, + text=caption, + background="#FFFFC0", + activebackground="#FFFFE0", + justify='left', + font=self.editwin.text["font"]) + self.bind("", self.expand) + self.bind("", self.copy) + if squeezer._PREVIEW_COMMAND: + self.bind("", self.preview) + self.selection_handle(lambda offset,length: s[int(offset):int(offset)+int(length)]) + + self.bind("", lambda x: self.text.event_generate('')) + self.bind("", lambda x: self.text.event_generate('')) + + def strip_ansi_colors(self, s): # For IPython compatibility + return self.color_pat.sub("", s) + + def expand(self, event): + # We must use the original insert and delete methods of the Text widget, + # to be able to change text before the iomark. + basetext = _get_base_text(self.editwin) + basetext.insert(self.text.index(self), self.s, self.tags) + basetext.delete(self) + self.squeezer.expandingbuttons.remove(self) + basetext.see('insert') + + def copy(self, event): + self.clipboard_clear() + self.clipboard_append(self.s, type='STRING') + self.selection_own() + + def preview(self, event): + from tempfile import mktemp + fn = mktemp("longidletext") + f = open(fn, "w") + f.write(self.s) + f.close() + os.system(self.squeezer._PREVIEW_COMMAND % {"fn":fn}) + + +class Squeezer: + + menudefs = [ + ('edit', [ + None, # Separator + ("Expand last squeezed text", "<>"), + ]), + #('options', [('!Enable Squeezer', '<>')]), + ] + #if _PREVIEW_COMMAND: + if True: + menudefs[0][1].append(("Preview last squeezed text", + "<>")) + + + def __init__(self, editwin): + + self._MAX_NUM_OF_LINES = idleConf.GetOption("extensions", "Squeezer", + "max-num-of-lines", type="int", + default=30) + + self._PREVIEW_COMMAND = idleConf.GetOption( + "extensions", "Squeezer", + "preview-command-"+{"nt":"win"}.get(os.name, os.name), + + default="", raw=True) + self.editwin = editwin + self.text = text = editwin.text + self.expandingbuttons = [] + if isinstance(editwin, PyShell): + # If we get a PyShell instance, replace its write method with a + # wrapper, which inserts an ExpandingButton instead of a long text. + def mywrite(s, tags=(), write=editwin.write): + if tags != "stdout": + return write(s, tags) + else: + numoflines = self.count_lines(s) + if numoflines < self._MAX_NUM_OF_LINES: + return write(s, tags) + else: + expandingbutton = ExpandingButton(s, tags, numoflines, + self) + text.mark_gravity("iomark", Tkinter.RIGHT) + text.window_create("iomark",window=expandingbutton, + padx=2, pady=5) + text.see("iomark") + text.update() + text.mark_gravity("iomark", Tkinter.LEFT) + self.expandingbuttons.append(expandingbutton) + editwin.write = mywrite + + #text.bind('<>', self.squeezer_enable_event) + + def squeezer_enable_event(self, event=None): + + # TODO - configure dialog + pass + + def count_lines(self, s): + "Calculate number of lines in given text.\n\n" \ + "Before calculation, the tab width and line length of the text are" \ + "fetched, so that up-to-date values are used." + # Tab width is configurable + tabwidth = self.editwin.tabwidth + + text = self.editwin.text + # Get the Text widget's size + linewidth = text.winfo_width() + # Deduct the border and padding + linewidth -= 2*sum([int(text.cget(opt)) + for opt in ('border','padx')]) + + # Get the Text widget's font + font = tkFont.Font(text, name=text.cget('font')) + + # Divide the size of the Text widget by the font's width. + # According to Tk8.4 docs, the Text widget's width is set + # according to the width of its font's '0' (zero) character, + # so we will use this as an approximation. + linewidth //= font.measure('0') + + try: + result = _countlines(s, linewidth, tabwidth) + except TypeError: + result = 0 + return result + + def expand_last_squeezed_event(self, event): + if self.expandingbuttons: + self.expandingbuttons[-1].expand(event) + else: + self.text.bell() + return "break" + + def preview_last_squeezed_event(self, event): + if self._PREVIEW_COMMAND and self.expandingbuttons: + self.expandingbuttons[-1].preview(event) + else: + self.text.bell() + return "break" + + def squeeze_last_output_event(self, event): + last_console = self.text.tag_prevrange("console",Tkinter.END) + if not last_console: + return "break" + + prev_ranges = [] + for tag_name in ("stdout","stderr"): + rng = last_console + while rng: + rng = self.text.tag_prevrange(tag_name, rng[0]) + if rng and self.text.get(*rng).strip(): + prev_ranges.append((rng, tag_name)) + break + if not prev_ranges: + return "break" + + if not self.squeeze_range(*max(prev_ranges)): + self.text.bell() + return "break" + + def squeeze_current_text_event(self, event): + insert_tag_names = self.text.tag_names(Tkinter.INSERT) + for tag_name in ("stdout","stderr"): + if tag_name in insert_tag_names: + break + else: # no tag associated with the index + self.text.bell() + return "break" + + # find the range to squeeze + rng = self.text.tag_prevrange(tag_name, Tkinter.INSERT+"+1c") + if not self.squeeze_range(rng, tag_name): + self.text.bell() + return "break" + + def squeeze_range(self, rng, tag_name): + if not rng or rng[0]==rng[1]: + return False + start, end = rng + + s = self.text.get(start, end) + # if the last char is a newline, remove it from the range + if s and s[-1] == '\n': + end = self.text.index("%s-1c" % end) + s = s[:-1] + # delete the text + _get_base_text(self.editwin).delete(start, end) + # prepare an ExpandingButton + numoflines = self.count_lines(s) + expandingbutton = ExpandingButton(s, tag_name, numoflines, self) + # insert the ExpandingButton to the Text + self.text.window_create(start, window=expandingbutton, + padx=3, pady=5) + # insert the ExpandingButton to the list of ExpandingButtons + i = len(self.expandingbuttons) + while i > 0 and self.text.compare(self.expandingbuttons[i-1], + ">", expandingbutton): + i -= 1 + self.expandingbuttons.insert(i, expandingbutton) + return True diff --git a/idlex-1.11.2/idlexlib/extensions/SubCode.py b/idlex-1.11.2/idlexlib/extensions/SubCode.py new file mode 100644 index 0000000..fb66049 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/SubCode.py @@ -0,0 +1,1320 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## +## SubCode Extension - provides segmented code regions for IDLE +## similar to Cell Mode in MATLAB and Cells as found in Sagemath. +## +## Usage: +## +## Subcode Regions are separated by "##" comment at the start of a line. +## Subcode markers may be indented to run indented code. +## +## The active subcode is a region where your cursor is. +## * Ctrl+Return runs the active subcode region without +## restarting the shell. +## * Ctrl+Shift+Return runs the active subcode region, +## and proceeds to the next subcode region. +## * Ctrl+Up moves to the previous subcode region. +## * Ctrl+Down moves to the next subcode region. +## +## Using "Import Subcode" will import the subcode as a module +## using the current file name. +## +## Any lines starting with "#:" at the active subcode depth will +## have the "#:" removed, effectively uncommenting commented code. +## This is useful for developing logic for indented code by setting +## test conditions. +## +## For indented subcode markers, placing the cursor one character +## to the left will enter the shallower subcode region. +## +## NOTE: +## +## This extension only works with spaces. Tabs won't work. +## +## """ + +config_extension_def = """ + +[SubCode] +enable=1 +enable_shell=0 +enable_editor=1 + +active=True +indented=True +uncomment=True +highlighting=True + +[SubCode_cfgBindings] +subcode-run= +subcode-run-proceed= +subcode-run-all= +subcode-import= +subcode-import-proceed= +subcode-import-all= +subcode-goto-previous= +subcode-goto-next= +subcode-goto-previous-same= +subcode-goto-next-same= +subcode-insert-marker= +subcode-enable-toggle= +subcode-indented-toggle= +subcode-uncomment-toggle= +subcode-highlighting-toggle= + +""" + + + + + +# Colors +COLOR_ADAPT = True # Flag for adapting subcode colors to the theme + # If FALSE, then use the following "constants": +SUBCODE_FOREGROUND = '#DD0000' # Foreground color for a subcode marker +SUBCODE_BACKGROUND = '#D0D0D0' # Background color for a subcode marker +SUBCODE_HIGHLIGHT = '#DDFFDD' # Active Region Highlighting +SUBCODE_HIGHLIGHT_OUT = '#EEFFEE' # Active Region Unfocused Window + + + +SUBCODE_INSERT = '## [subcode]\n' # Label inserted when "Insert Subcode Marker" + +HIGHLIGHT_INTERVAL = 250 # milliseconds + +import sys +import os +import re +import time +import __future__ +import tempfile +import shutil + +from idlelib import PyShell +from idlelib.ColorDelegator import ColorDelegator, make_pat +from idlelib.configHandler import idleConf + +if sys.version < '3': + from Tkinter import END, SUNKEN, RAISED, GROOVE, RIDGE, FLAT, INSERT, Menu + import tkMessageBox +else: + from tkinter import END, SUNKEN, RAISED, GROOVE, RIDGE, FLAT, INSERT, Menu + import tkinter.messagebox as tkMessageBox + + +jn = lambda x,y: '%i.%i' % (x,y) # join integers to text coordinates +sp = lambda x: list(map(int, x.split('.'))) # convert tkinter Text coordinate to a line and column tuple + +def index(text, marker='insert', lineoffset=0): + """ helper function to split a marker location of a text object """ + line, col = sp(text.index(marker)) + return (line + lineoffset, col) + + +def get_cfg(cfg, type="bool", default=True): + return idleConf.GetOption("extensions", "SubCode", + cfg, type=type, default=default) + +def set_cfg(cfg, b): + return idleConf.SetOption("extensions", "SubCode", + cfg,'%s' % b) + +def dbprint(func): # A decorator for debugging + def f(*args, **kwargs): + print(func, args, kwargs) + return func(*args, **kwargs) + return f + +# regex for startup_enable code. Only detect depth=0 markers +auto_re = '|'.join([r"(?P(?((?>'), + ]), + + ('run', [None, + ('Run _SubCode', '<>'), + ('Run SubCode and _Proceed', '<>'), + ('Run _All SubCodes', '<>'), + None, + ('Import SubCode', '<>'), + ('Import SubCode and Proceed', '<>'), + ('Import All SubCodes', '<>'), + ]), + ('format', [None, + ('_Insert SubCode Marker', '<>')]), + ('edit', [None, + ('Goto _Previous SubCode Marker', '<>'), + ('Goto _Next SubCode Marker', '<>'), + None]), + ('options', + [ + ('!Allow Indented SubCodes', '<>'), + ('!Uncomment #: at depth', '<>'), + ('!Highlight Active SubCode', '<>'), + None, + ]), + ] + + + # Menu items for a dedicated "SubCode" menu + # Collapse the normal menu to a single menu + _L = [] + menudefs_dedicated = [('subcode', _L)] + for menu, items in menudefs_normal: + _L.extend(items) + if _L[0] is None: + _L.pop(0) + if _L[-1] is None: + _L.pop() + del _L + + if SUBCODE_MENU: + menudefs = menudefs_dedicated + else: + menudefs = menudefs_normal + + + whitespace_re = {} # cache for white space regex matchers + + SC_INSTANCES = [] # list of all subcode instances + + def __init__(self, editwin): + ## Install the SubCode Color Delegator + def getColorDelegator(): + """ Returns a SubCodeColorDelegator instance, properly initialized. """ + def f(*args, **kwargs): + a = {'subcode_enable':self.enable, + 'subcode_indented':self.indented} + kwargs.update(a) + return SubcodeColorDelegator(*args, **kwargs) + return f + + self.ColorDelegatorOrig = editwin.ColorDelegator + editwin.ColorDelegator = getColorDelegator() + + if SUBCODE_MENU: + mbar = editwin.menubar + menudict = editwin.menudict + label='SubCode' + underline=0 + menudict['subcode'] = menu = Menu(mbar, name='subcode') + index = 5 # magic number - place SubCode after Run menu + mbar.insert_cascade(index, label=label, menu=menu, underline=underline) + keydefs = idleConf.GetExtensionBindings('SubCode') + editwin.fill_menus(self.menudefs, keydefs) + + + self.editwin = editwin + self.text = editwin.text + self.reset_id = None + + self.runmanager = RunManager(editwin) + self.enable = False + self.indented = False + self.uncomment = False + self.highlighting = False + self.highlighter_init() + self.subcode_indented(get_cfg("indented"), init=True) + self.subcode_uncomment(get_cfg("uncomment")) + self.subcode_highlighting(get_cfg("highlighting")) + self.startup_enable() + text = self.text + text.bind('', self.focus_out, '+') + text.bind('', self.focus_in, '+') + + text.bind('<>', self.focus_editor) + text.bind('<>', self.using_tabs, '+') + + SubCode.SC_INSTANCES.append(self) + + + text.bind("<>", self.restart_shell_event, '+') + text.bind("<>", self.cancel_callback) + + self._BUG_FIX() + + if self.enable: + self.ResetColorizer() + + + def close(self): # Extension is being unloaded + self.runmanager.close() + if self.hl_id: + self.text.after_cancel(self.hl_id) + idleConf.SaveUserCfgFiles() + SubCode.SC_INSTANCES.remove(self) + try: + self.editwin.ColorDelegator = self.ColorDelegatorOrig + finally: + self.ColorDelegatorOrig = None + + def _BUG_FIX(self): + # Two ColorDelegators get loaded on IDLE (only apparent on Windows). + # This is a bug in IDLE itself. See Issue13495. + # Must eliminate the ColorDelegator that is not supposed to be there. + + CD = [] # list of color delegators in the percolator chain + per = self.editwin.per + top = per.top + while True: + if isinstance(top, ColorDelegator): + CD.append(top) + top = top.delegate + if top == per.bottom: + break + + rcd = [i for i in CD if i is not self.editwin.color] + for filt in rcd: + print(' Removing a rogue color delegator instance.', filt) + print(' See http://bugs.python.org/issue13495') + per.removefilter(filt) + + def startup_enable(self): + """ If the text buffer is empty, then use saved value. + If text buffer has code, check if it has subcode markers. + """ + src = self.text.get('1.0', 'end') + if src.strip(): + m = auto_detect.search(src) + while m: + value = m.groupdict()['SUBCODE'] + if value: # found depth=0 subcode marker, enable + if self.ispython(init=True): + self.subcode_enable(True, init=True) + break + m = auto_detect.search(src, m.end()) + else: + if self.ispython(init=True): + #self.subcode_enable(False) + self.subcode_enable(get_cfg("active"), init=True) + + def ResetColorizer(self): + editwin = self.editwin + editwin.ResetColorizer() + if not isinstance(editwin.color, SubcodeColorDelegator): + # Subcode Color Delegator is not being used. + # Subcodes won't work. Disable subcode. + print(' Subcode Internal Error: SubCodeColorDelegator not installed. ', + editwin.color) + if self.enable: + self.subcode_enable(b=False) + + def ispython(self, init=False): + e = self.editwin + filename = e.io.filename + if filename is None: + return True # benefit of the doubt + else: + if not e.ispythonsource(filename): + if not init and not self.enable: + # Show warning only if not initializing the extension. + # This avoids a message box when first opening + # non-python files. + self.text.after_idle(lambda:\ + tkMessageBox.showwarning("SubCodes", + "SubCodes work only with valid Python files.", + parent=self.text)) + return False # definitely not python + else: + return True + + def subcode_enable_toggle_event(self, ev=None): + self.subcode_enable(not self.enable) + return "break" + + def subcode_indented_toggle_event(self, ev=None): + self.subcode_indented(not self.indented) + return "break" + + def subcode_uncomment_toggle_event(self, ev=None): + self.subcode_uncomment(not self.uncomment) + return "break" + + def subcode_highlighting_toggle_event(self, ev=None): + self.subcode_highlighting(not self.highlighting) + return "break" + + + def subcode_enable(self, b=True, init=False): + if self.ispython(init=init): + self.enable = b + set_cfg("active", self.enable) + else: + self.enable = False + + if self.enable: + self.text.after(1, lambda: self.text.event_generate("<>")) + self.highlighter_schedule() + else: + self.text.after(1, lambda: self.text.event_generate("<>")) + self.highlighter_schedule(cancel=True) + self.text.tag_remove("ACTIVE", "1.0", END) + + self.editwin.setvar("<>", self.enable) + if not init: + self.ResetColorizer() + + def subcode_indented(self, b=True, init=False): + self.indented = b + set_cfg("indented", self.indented) + self.editwin.setvar("<>", self.indented) + if not init: + self.ResetColorizer() + + def subcode_uncomment(self, b=True): + self.uncomment = b + set_cfg("uncomment", self.uncomment) + self.editwin.setvar("<>", self.uncomment) + + def subcode_highlighting(self, b=True): + self.highlighting = b + set_cfg("highlighting", self.highlighting) + self.editwin.setvar("<>", self.highlighting) + if not b: + self.text.tag_remove("ACTIVE", "1.0", END) + self.hl_AC = None + else: + if self.enable: + self.highlighter_schedule() + + def using_tabs(self, ev=None): + if self.editwin.usetabs: + tkMessageBox.showwarning("SubCode Tabs", + "SubCodes do not work with tabs.\nPlease convert tabs to spaces and then re-enable SubCodes.", + parent=self.editwin.text) + self.subcode_enable(False) + return "break" + + def _check_enable(auto=False): + """ Decorator with arguments to intercept Tkinter event callbacks.""" + def f(func): + def decfunc(self, ev=None): + if self.enable: + if self.editwin.usetabs: + self.using_tabs() + return "break" + else: + return func(self, ev) + else: + # FIXME: find more robust method to differentiate + # menu item events from key events. + if ev: + if ev.keysym_num == '??': # menu event + tkMessageBox.showinfo("SubCodes Disabled", + "Please enable SubCodes to use this command.", + parent=self.editwin.text) + elif auto: # keyboard event and auto enabling + tkMessageBox.showinfo("SubCodes Enabled", + "SubCodes have just been enabled in response to your SubCode key command. Please repeat your command.", + parent=self.text) + self.subcode_enable() + return "break" + + return decfunc # return the decorated function + return f # return the decorator + + @_check_enable() + def subcode_insert_marker_event(self, event): + # if cursor at beginning of line, insert before else insert after + text = self.text + sel = text.tag_ranges('sel') + if sel: return + offset = SUBCODE_INSERT.count('\n') + line, column = index(text, INSERT) + if column == 0: # insert at the start of the line + text.insert('insert', '%s\n' % SUBCODE_INSERT); + text.mark_set('insert', '%i.0' % (line + 1 + offset)) + else: # insert at the start of the next line + text.insert('insert lineend', '\n%s' % SUBCODE_INSERT) + text.mark_set('insert', '%i.0' % (line + 2 + offset)) + + @_check_enable(auto=True) + def subcode_run_event(self, ev=None): + self.run_code() + return "break" + + @_check_enable(auto=True) + def subcode_run_proceed_event(self, ev=None): + if self.run_code(): self.subcode_goto('nextsame') + return "break" + + @_check_enable() + def subcode_run_all_event(self, ev=None): + self.run_code(entire=True) + return "break" + + @_check_enable(auto=True) + def subcode_import_event(self, ev=None): + self.run_code(as_import=True) + return "break" + + @_check_enable(auto=True) + def subcode_import_proceed_event(self, ev=None): + if self.run_code(as_import=True): + self.subcode_goto('nextsame') + return "break" + + @_check_enable() + def subcode_import_all_event(self, ev=None): + self.run_code(entire=True, as_import=True) + return "break" + + def run_code(self, entire=False, as_import=False): + """ Runs a subcode. Returns True if successful. """ + text = self.text + fn = self.editwin.io.filename + if fn is None: + fn = 'untitled.py' + + file_head, file_tail = os.path.split(fn) + + if entire: + end = text.index(END) + sline = 1 + eline, endcol = sp(end) + code = text.get('1.0', end) + message = "All SubCodes [%s:1-%i]" % (file_tail, eline-1) + depth = 0 + else: + i = self.text.index('insert') + sline, eline, depth = self.get_active_subcode(i) + code = self.get_code(sline, eline, depth, + header=True, uncomment=self.uncomment) + subcodename = text.get('%i.0' % sline, '%i.0 lineend' % sline).strip() + if sline == 1 and not subcodename.startswith('##'): + subcodename = 'beginning of file' + + message = "SubCode [%s:%i-%i] '%s'" % \ + (file_tail, sline, eline, subcodename) + + + linestr = '%i-%i' % (sline, eline) + filename = '%s:::%s at %s' % (fn, + linestr, + time.strftime("%H:%M:%S")) + + try: # check for errors + text.tag_remove("ERROR", '1.0', END) + if not as_import: + # TODO: optional syntax check for subcode + # 2012-09-25 RDS + # Code object compiling has been disabled so that the IPyIDLE + # extension can return useful tracebacks with source code + # instead of a marshal-encoded string of the code object. + # The tracebacks no longer include filenames with a timestamp. + + #code = self.runmanager.compile(code, filename, mode='exec') + status = self.runmanager.run(code, message) + else: + status = self.runmanager.run_as_import(code, message) + + except (SyntaxError, OverflowError, ValueError) as err: + self.handle_error(err, depth) + status = False + except Exception as err: + # why? + raise + + self.focus_editor() + + return status + + + def handle_error(self, e, depth=0): + """ Highlight the error and display it on the shell prompt """ + text = self.editwin.text + try: + msg, lineno, offset = e.msg, e.lineno, e.offset + message = '\n There is an error (%s) at line %i, column %i.\n' % \ + (msg, lineno, offset) + self._highlight_error(lineno, offset, depth) + except Exception as err: + msg = e + lineno = 0 + offset = 0 + message = '\n There is an error: %s' % e + + # send error feedback to shell + shell = self.editwin.flist.open_shell() + shell.interp.tkconsole.stderr.write(message) + shell.showprompt() + self.focus_editor() + + def _highlight_error(self, lineno, offset, depth): + text = self.text + if offset is None: + offset = 0 + offset += depth # for dedented code + + pos = '0.0 + %i lines + %i chars' % (lineno - 1, offset - 1) + pos = '%i.%i' % (lineno, offset-1) + text.tag_add("ERROR", pos) + + if text.get(pos) != '\n': + pos += '+1c' + + text.mark_set("insert", pos) + text.see(pos) + + + @_check_enable() + def restart_shell_event(self, ev=None): + self.runmanager.restart_shell() + self.focus_editor() + + @_check_enable() + def cancel_callback(self, ev=None): + try: + if self.text.compare("sel.first", "!=", "sel.last"): + return # Active selection -- always use default binding + except: + pass + + # place message + message = '# Sending KeyboardInterrupt from SubCode...\n' + shell = self.editwin.flist.open_shell() + if hasattr(shell, 'interp'): + #shell.interp.tkconsole.stderr.write(message) + shell.cancel_callback() + self.text.update_idletasks() + self.focus_editor() + + return "break" + + def focus_editor(self, ev=None): + self.editwin.text.focus_set() + self.editwin.top.lift() + + def get_prev_subcode(self, i): + pr = self.text.tag_prevrange("SUBCODE", i) + if pr: + sline, depth = sp(pr[0]) + else: + sline, depth = 1, 0 + return sline, depth + + def get_next_subcode(self, i, end=True): + nr = self.text.tag_nextrange("SUBCODE", i+'+1c') + if nr: + eline, depth = sp(nr[0]) + else: + if end: + e = self.text.index('end') # EOF is next subcode + eline, depth = sp(e) + else: + eline, depth = sp(i) # stay at location + + return eline, depth + + + @_check_enable() + def subcode_goto_previous_event(self, ev=None): + return self.subcode_goto('prev') + @_check_enable() + def subcode_goto_next_event(self, ev=None): + return self.subcode_goto('next') + @_check_enable() + def subcode_goto_previous_same_event(self, ev=None): + return self.subcode_goto('prevsame') + @_check_enable() + def subcode_goto_next_same_event(self, ev=None): + return self.subcode_goto('nextsame') + + def subcode_goto(self, w=None): + """ Move cursor to another subcode. """ + text = self.text + i = text.index('insert') + goto_line, goto_col = sp(i) + + if w == 'prev': + goto_line, goto_col = self.get_prev_subcode(i) + elif w == 'next': + goto_line, goto_col = self.get_next_subcode(i, end=False) + + elif w in ['nextsame', 'prevsame']: + astart, astop, adepth = self.get_active_subcode(i) + while True: + if w == 'nextsame': + nline, ndepth = self.get_next_subcode(i, end=False) + test = (nline == astop + 1) + + elif w == 'prevsame': + nline, ndepth = self.get_prev_subcode(i+'-1c') + nstop = self.get_subcode_stop(jn(nline, ndepth)) + test = (astart == nstop + 1) \ + or i != jn(nline, ndepth) + + next_i = jn(nline, ndepth) + if i == next_i: break # don't loop forever + i = next_i + + if ndepth == adepth and test: + goto_line, goto_col = nline, ndepth + break + + # make sure subcode is visible + text.mark_set('insert', '%i.%i' % (goto_line, goto_col)) + top, bot = self.editwin.getwindowlines() + if goto_line - 3 < top: + text.yview(goto_line - 3) + elif goto_line + 4 > bot: + height = bot - top + text.yview(goto_line - height + 4) + + return "break" + + + def _get_depth_re(self, depth): + """ Cache regular expression engines for subcode depths. + Helper function for get_subcode_stop. + """ + w = SubCode.whitespace_re + if depth not in w: + r = [r"((?= sdepth + while m: + d = m.groupdict() + if d['DEPTH']: + a,b = m.span('DEPTH') + if a == lastb: + span += 1 + else: + break # truncated by shallower code + lastb = b + m = q.search(chars, m.end()) + + eline += span + if span != chars.count('\n'): + break + + sline += grablines + if sline > hardstop: + eline = hardstop + break + + grablines *= 4 # grab even more lines + + return eline - 1 + + def get_active_subcode(self, i): + """ Returns a tuple of the start line, end line, and subcode depth """ + lineno, col = sp(i) + while True: + sline, sdepth = self.get_prev_subcode(i+'+1c') + eline = self.get_subcode_stop(jn(sline, sdepth)) + + next_i = jn(sline-1, 0) + if next_i == i: break + i = next_i + + if eline >= lineno: + break + + return sline, eline, sdepth + + + def get_code(self, sline, eline, depth=0, header=True, uncomment=True): + """ Returns the code to be executed """ + src = self.text.get('%i.0' % sline, '%i.0' % (eline+1)) + + if uncomment: # uncomment any #: comments at depth + src = re.sub(r"(? prev_eline: + self.text.tag_add("ACTIVE", '%i.0' % (prev_eline), + '%i.0' % (eline+1)) + else: + self.text.tag_remove('ACTIVE', '%i.0' % (eline+1), + '%i.0' % (prev_eline+1)) + else: + # same active subcode - make sure highlighting is present. + r = self.text.tag_prevrange("ACTIVE", 'insert+1c') + if not r: + self.hl_AC = None # invalidate cache + else: + sl, sc = sp(r[0]) # catch loss of highlighting at start of subcode + if sc != 0: + self.hl_AC = None + + + self.highlighter_schedule() + + def highlighter_schedule(self, cancel=False): + if self.hl_id is not None: + self.text.after_cancel(self.hl_id) + + if not cancel: + self.hl_id = self.text.after(HIGHLIGHT_INTERVAL, + self.highlighter_callback) + else: + self.hl_id = None + + @_highlighter + def focus_in(self, ev=None): + self.text.tag_config('ACTIVE', background=SUBCODE_HIGHLIGHT) + self.highlighter_schedule() + + @_highlighter + def focus_out(self, ev=None): + self.text.tag_config('ACTIVE', background=SUBCODE_HIGHLIGHT_OUT) + self.highlighter_schedule(cancel=True) + + +def any_re(groupname, re_list): + return "(?P<%s>" % groupname + "|".join(re_list) + ")" + + +class SubcodeColorDelegator(ColorDelegator): + """ Performs a two-pass highlighting of subcode markers, to work around Python's + RE having a fixed-width lookbehind. Subcode markers should be preceeded + by white space only, but this whitespace should not be highlighted. + + The first pass identifies possible subcodes using IDLE's original + "recolorize_main". This pass matches the preceeding whitespace as well. + + The second pass colorizes properly indented subcode markers, while + ignoring consecutive double-comment lines, which can be caused by + "Comment Out Region". + + """ + + # Require subcode markers be unlabeled or strictly labeled, + # i.e. only one space between ## and the label or carriage return + subcode = [r"(?(?(? '#RRGGBB' + c = [min((255, int(x))) for x in c] + c = [max((0, int(x))) for x in c] + return '#%02X%02X%02X' % tuple(c) + + def colorhack(rgb, target): + # apply some DC offset and "gamma" correction + R, G, B = map(float, rgb) + mR, mG, mB = target + m = lambda x, y: (x + y[0])**y[1] if x < 128 else (x - y[0]) ** (1/y[1]) + R = m(R, mR) + G = m(G, mG) + B = m(B, mB) + return (R, G, B) + + def average(a, b): + return [(x[0]+x[1])/2.0 for x in zip(a,b)] + + BACK = rgb_h2d(background) + + a = (10, 1.03) + SubCode.SUBCODE_BACKGROUND = rgb_d2h(colorhack(BACK, (a,a,a))) + + a = (10, 1.019) + b = (10, .98) + c = colorhack(BACK, (a,b,a)) + HL = rgb_d2h(c) + SubCode.SUBCODE_HIGHLIGHT = HL + + d = average(BACK, c) + SubCode.SUBCODE_HIGHLIGHT_OUT = rgb_d2h(d) + + self.tag_config('ACTIVE', background=SUBCODE_HIGHLIGHT) diff --git a/idlex-1.11.2/idlexlib/extensions/SubCodeToolbar.py b/idlex-1.11.2/idlexlib/extensions/SubCodeToolbar.py new file mode 100644 index 0000000..53db816 --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/SubCodeToolbar.py @@ -0,0 +1,357 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## +## SubCode Toolbar - provides a toolbar for SubCode.py +## +## About: +## +## [>] - list of subcode labels +## [##] - insert a subcode marker +## +## [-] 1.0 [+] - add or subtract from number by cursor +## [/] 1.1 [*] - divide or multiply number by cursor +## +## [RS] - Run SubCode +## [RSP] - Run SubCode and Proceed +## [RA] - Run All subcodes +## +## +## """ + +config_extension_def = """ + +[SubCodeToolbar] +enable=1 +enable_shell=0 +enable_editor=1 + +visible=True + +[SubCodeToolbar_cfgBindings] +subcode-toolbar= +""" + +import re +from idlelib.configHandler import idleConf +from idlelib import macosxSupport + +import sys +if sys.version < '3': + from Tkinter import * +else: + from tkinter import * + +import idlelib.ToolTip as ToolTip +from . import SubCode + +class SubCodeToolbar(object): + + + menudefs = [ + ('options' if not SubCode.SUBCODE_MENU else 'subcode', + [ + ('!Show SubCode Toolbar', '<>'), + ]), + ] + + def __init__(self, editwin): + self.editwin = editwin + self.text = editwin.text + + self.TB = None # pointer to toolbar tkinter object + + self.visible = idleConf.GetOption("extensions", "SubCodeToolbar", + "visible", type="bool", default=True) + + self.setvars() + + self.text.bind('<>', self.subcode_enable_event, '+') + self.text.bind('<>', self.subcode_disable_event, '+') + sc = self.editwin.extensions.get('SubCode') + + if sc and sc.enable: + self.subcode_enable_event() + + + def close(self): + idleConf.SetOption("extensions", "SubCodeToolbar", + "visible", '%s' % self.visible) + idleConf.SaveUserCfgFiles() + + + def subcode_enable_event(self, ev=None): + if self.visible: + self.toolbar_enable(True) + + def subcode_disable_event(self, ev=None): + self.toolbar_enable(False) + + + def subcode_toolbar_event(self, ev=None): + self.visible = not self.visible + self.toolbar_enable(self.visible) + self.setvars() + + + def setvars(self): + try: + self.editwin.setvar("<>", self.visible) + except Exception as err: + pass + + def toolbar_enable(self, b=True): + sc = self.editwin.extensions.get('SubCode', None) + if b: + if sc and sc.enable: + self._make_toolbar() + else: + self._destroy_toolbar() + + def _destroy_toolbar(self): + self.TITLES = None + if self.TB is not None: + self.TB.destroy() + self.TB = None + + + def _make_toolbar(self): + if self.TB is not None: + return # toolbar exists + + top = self.editwin.top + + f = Frame(top) + widgets = self.editwin.top.pack_slaves() + widgets = list(widgets) # list for Python 3 support + f.pack(side='top', fill=X, before=widgets[0]) # make toolbar play nicely with CodeContext + + + f.config(height=8) + mvar = [StringVar(top), StringVar(top)] + Separator = Label + + try: + osx = (macosxSupport.runningAsOSXApp() or + sys.platform == 'darwin') + except: + osx = False + + toolbar = [(Button(f, command=lambda: self.toolbar('titles'), text='>', + width=1 if not osx else 2), + None, 'Show SubCode Labels'), + + (Button(f, command=lambda: self.toolbar('ins'), text='##', + width=2 if not osx else 3), + None, 'Insert SubCode Marker'), + + (Separator(f), {'fill':Y, 'pady':0, 'padx':4}, None), + + (Button(f, command=lambda: self.toolbar('minus'), text='-', + width=1 if not osx else 2), + None, 'Subtract from number by cursor then run subcode'), + + (Entry(f, width=6, justify='center', textvar=mvar[0]), {'fill':Y}, + '+ - value'), + + (Button(f, command=lambda: self.toolbar('plus'), text='+', + width=1 if not osx else 2), + None, 'Add to number by cursor then run subcode'), + + (Separator(f), {'fill':Y, 'pady':0, 'padx':4}, None), + + (Button(f, command=lambda: self.toolbar('div'), text='/', + width=1 if not osx else 2), + None, 'Divide number by cursor then run subcode'), + + (Entry(f, width=6, justify='center', + textvar=mvar[1]), {'fill':Y}, + '* / value'), + + (Button(f, command=lambda: self.toolbar('mult'), text='*', + width=1 if not osx else 2), + None, 'Multiply number by cursor then run subcode'), + + (Separator(f), {'fill':Y, 'pady':0, 'padx':4}, None), + + (Button(f, command=lambda: self.toolbar('run_subcode'), text='RS', + width=2 if not osx else 4), + None, 'Run SubCode'), + + (Button(f, command=lambda: self.toolbar('run_subcode_proceed'), text='RSP', + width=3 if not osx else 4), + None, 'Run SubCode and Proceed'), + + (Button(f, command=lambda: self.toolbar('run_all'), text='RA', + width=2 if not osx else 4), + None, 'Run All SubCodes'), + + (Separator(f), {'fill':Y, 'pady':0, 'padx':4}, None), + + ] + + mvar[0].set('1.0') + mvar[1].set('1.1') + self.mvar = mvar + + for i, cfg, tooltip in toolbar: + if cfg is None: + cfg = {} + try: + i.configure(pady=0, padx=7) + i.configure(wraplength=0) + i.configure(borderwidth=1) + except: # catch ALL THE ERRORS + #print 'error',i, cfg, tooltip + pass + i.pack(side='left', **cfg) + if tooltip is not None: + ToolTip.ToolTip(i, ' %s ' % tooltip) + + self.TB = f + self.TITLES = toolbar[0][0] # pointer to the titles button + + + def toolbar(self, cmd): + text = self.editwin.text + text.focus() + if cmd in ['plus', 'minus', 'mult', 'div']: + self.process_number(cmd) + if cmd == 'run_subcode': + text.event_generate("<>") + if cmd == 'run_subcode_proceed': + text.event_generate("<>") + if cmd == 'run_all': + text.event_generate("<>") + if cmd == 'ins': + text.event_generate("<>") + if cmd == 'titles': + self.titles() + + + def titles(self): + # make a popup menu of all subcodes in file + text = self.editwin.text + c = text.tag_ranges('SUBCODE') + + L = [] + if len(c) == 0 or c[0].string != '1.0': + L.append(('1.0', '## beginning of file')) + + for tr in c[::2]: # only grab start of ranges + line, col = map(float, tr.string.split('.')) + L.append((tr.string, text.get(line, line+1))) + + B = self.TITLES + + rmenu = Menu(B, tearoff=0) + for line, label in L: + def jump(lineno=line): + self.text.mark_set('insert', lineno) + self.text.event_generate('<>') + self.text.yview(lineno) + + #m1 = label[0:min([50,len(label)])].strip()[2:] + m1 = label.strip()[2:] + lineno, col = map(int, line.split('.')) + + m2 = '%s[%6s] %-50s' % (' '*col, line, m1) + rmenu.add_command(label=m2, command=jump) + + x = B.winfo_rootx() + y = B.winfo_rooty() + B.winfo_height() + rmenu.tk_popup(x,y) + + def shell_busy(self): + shell = self.editwin.flist.open_shell() + self.editwin.text.event_generate('<>') + try: + if shell.interp.tkconsole.executing: + return True # shell is busy + except: + return True # shell is not in a valid state + + return False + + def process_number(self, cmd): + + text = self.editwin.text + undo = self.editwin.undo + + lineno, col = map(float, text.index(INSERT).split('.')) + txt = text.get(lineno, lineno + 1) + # get the number + pattern = r"([-]?[\d]+[\.]?[\d]*|\.[\d]+)" + p = re.compile(pattern) + h = p.finditer(txt) + j = [i for i in h if i.start()<=col and i.end()>=col] + if j: + if j[0].group() == '': + return + s,e,t = j[0].start(), j[0].end(), j[0].group() + else: + return + + # at this point, a number has been found + if self.shell_busy(): + return + + a = float(self.mvar[0].get()) + m = float(self.mvar[1].get()) + + if cmd == 'plus': + r = float(t) + a + elif cmd == 'minus': + r = float(t) - a + elif cmd == 'mult': + r = float(t) * m + elif cmd == 'div': + r = float(t) / m + + if cmd in ['plus', 'minus']: + if '.' not in t: # keep integers as integers + r = int(round(r)) + + new_t = str(r) + + undo.undo_block_start() + text.delete('%i.%i' % (lineno, s), '%i.%i' % (lineno, e)) + text.insert('%i.%i' % (lineno, s), new_t) + text.mark_set(INSERT, '%i.%i' % (lineno, s + len(new_t))) + undo.undo_block_stop() + + text.event_generate("<>") + diff --git a/idlex-1.11.2/idlexlib/extensions/TabExtension.py b/idlex-1.11.2/idlexlib/extensions/TabExtension.py new file mode 100644 index 0000000..3b7720d --- /dev/null +++ b/idlex-1.11.2/idlexlib/extensions/TabExtension.py @@ -0,0 +1,987 @@ +# IDLEX EXTENSION +## """ +## Copyright(C) 2011 The Board of Trustees of the University of Illinois. +## All rights reserved. +## +## Developed by: Roger D. Serwy +## University of Illinois +## +## Permission is hereby granted, free of charge, to any person obtaining +## a copy of this software and associated documentation files (the +## "Software"), to deal with the Software without restriction, including +## without limitation the rights to use, copy, modify, merge, publish, +## distribute, sublicense, and/or sell copies of the Software, and to +## permit persons to whom the Software is furnished to do so, subject to +## the following conditions: +## +## + Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimers. +## + Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following disclaimers in the +## documentation and/or other materials provided with the distribution. +## + Neither the names of Roger D. Serwy, the University of Illinois, nor +## the names of its contributors may be used to endorse or promote +## products derived from this Software without specific prior written +## permission. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +## IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +## ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +## THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +## +## +## +## Tabbed Editor Window Extension - provide tabs in IDLE's editor +## +## About: +## +## This extenion is a gross hack on the object system of IDLE. +## The first EditorWindow instance gets configured as a TabManager +## and subsequent EditorWindow instances use a duck-typed Frame instead +## of a toplevel Tk object. +## +## The tab bar itself works best under Linux. Under MacOSX, the buttons +## are misplaced. Under Windows, the scroll wheel doesn't move the tabs. +## +## """ + +config_extension_def = """ +[TabExtension] +enable=1 +enable_shell = 0 +always_show = False +[TabExtension_cfgBindings] +tab-new-event= + +""" +import sys + +if sys.version < '3': + from Tkinter import * + import tkMessageBox +else: + from tkinter import * + import tkinter.messagebox as tkMessageBox + + + +import idlelib.EditorWindow as EditorWindow +import idlelib.WindowList as WindowList +from idlelib.ToolTip import ToolTipBase +import idlelib.ToolTip as ToolTip +import idlelib.FileList as FileList +import idlelib.Bindings as Bindings +from idlelib.configHandler import idleConf + +TAB_BAR_SIDE = 'top' # 'bottom' +WARN_MULTIPLE_TAB_CLOSING = True + + +def get_cfg(cfg, type="bool", default=True): + return idleConf.GetOption("extensions", "TabExtension", + cfg, type=type, default=default) + +def set_cfg(cfg, b): + return idleConf.SetOption("extensions", "TabExtension", + cfg,'%s' % b) + + + +class TabExtension(object): + + menudefs = [ + ('options', [ + ('!Always Show Tabs', '<>'), + ]),] + + def __init__(self, editwin): + # This is called from a unique "EditorWindow" instance, with its own set of menu/text widgets + self.editwin = editwin + + # add "New Tab to file menu + self.add_menu_entry() + + # monkey-patching the call backs to get updates to filename into tab bar + editwin.undo.set_saved_change_hook(self.saved_change_hook) + def updaterecentfileslist(x): + editwin.update_recent_files_list(x) + self.saved_change_hook() # to reflect opened file names in tab bar + editwin.io.updaterecentfileslist = updaterecentfileslist + + text = self.editwin.text + text.bind('<>', self.tab_new_event) + text.bind('<>', self.close_window_event) + + self.editwin.setvar("<>", get_cfg("always_show")) + + if 'TAB_MANAGER' in dir(editwin.top): + # clone the tab master pointers + self.TAB_FRAME = editwin.top # containing widget + self.tabmanager = editwin.top.TAB_MANAGER + self.button = self.add_tab_button() + editwin.top.TAB_MANAGER = None # break reference, no longer needed + editwin.top.wakeup = self.wakeup + self.button.select() + return + + # INITIALIZE THE FIRST TAB MANAGER + + text.bind('<>', self.toggle_show) + flist = self.editwin.flist + self.tabmanager = tabmanager = TabManager(top=self.editwin.top, tab=self, flist=flist) + tabmanager.ACTIVE = self + + # REPACK the EditorWindow widget contents into a Frame + TOPLEVEL = self.editwin.top + F = tabmanager.create_frame() + F.wakeup = self.wakeup + + for elt in TOPLEVEL.pack_slaves(): + p = elt.pack_info() + p['in'] = F + elt.pack(**p) + + F.pack(side='top', fill=BOTH, expand=YES) + F._lower() # fix Z-order + + # TODO: repack all grid and place widgets + + self.TAB_FRAME = F # reference to container frame + editwin.top = F + + self.button = self.add_tab_button() # populate tab bar + TOPLEVEL.after_idle(self.editwin.postwindowsmenu) # need to change menu + + def add_menu_entry(self): + # patch "New Tab" into the File Menu + e = self.editwin + f = e.menudict['file'] + text = e.text + eventname = '<>' + def command(text=text, eventname=eventname): + text.event_generate(eventname) + + keydefs = Bindings.default_keydefs + accelerator = EditorWindow.get_accelerator(keydefs, eventname) + + f.insert_command(2, label="New Tab", command=command, + accelerator=accelerator) + + def toggle_show(self, ev=None): + self.always_show = not get_cfg("always_show") + set_cfg("always_show", self.always_show) + self.editwin.setvar("<>", self.always_show) + self.tabmanager.visible_bar() + + def wakeup(self): + return self.button.select() + + def select(self, event=None): + return self.tabmanager.tab_wakeup(tabframe=self) + + def closetab(self, event=None): + return self.tabmanager.close_tab(tabframe=self) + + def add_tab_button(self): + b = self.tabmanager.addtab(tabframe=self) + #self.tooltip = ToolTip.ToolTip(b, self.get_filepath()) + self.tooltip = TabToolTip(b, self.get_filepath) + return b + + def tab_new_event(self, event=None): + self.tabmanager.newtab() + return "break" + + def saved_change_hook(self): + self.editwin.saved_change_hook() + self.button.set_text(self.get_title()) + self.tooltip.text = self.get_filepath() + + def save_stars(self, txt): + """ wrap strings with ** if it refers to a window that's not saved""" + if not self.editwin.get_saved(): + txt = "*%s*" % txt + return txt + + def get_filepath(self, event=None): + f = self.editwin.long_title() + if not f: + f = 'Untitled' + return self.save_stars(f) + + def get_title(self, event=None): + short = self.editwin.short_title() + if not short: + short = "Untitled" + return self.save_stars(short) + + def close(self): + #print 'unloading tabextension.py' + self.editwin = None + self.TAB_FRAME = None + self.tooltip = None + + def close_window_event(self, event=None): + """ Redirect to close the current tab """ + self.button.remove() + return "break" + +class TabToolTip(ToolTipBase): + def __init__(self, button, text_callback): + ToolTipBase.__init__(self, button) + self.text_callback = text_callback + + def showcontents(self): + try: + text = self.text_callback() + except: + text = '' + ToolTipBase.showcontents(self, text) + + def schedule(self): + self.unschedule() + self.id = self.button.after(500, self.showtip) + + def showtip(self): + # make sure tip is on the screen + + ToolTipBase.showtip(self) + tipwindow = self.tipwindow + tipwindow.update_idletasks() + + sw = tipwindow.winfo_screenwidth() + tw = tipwindow.winfo_width() + tx = tipwindow.winfo_x() + ty = tipwindow.winfo_y() + + delta = tw + tx - sw + if delta > 0: + # must shift the tipwindow to the left by delta + dx = tx - delta + tipwindow.wm_geometry('+%d+%d' % (dx, ty)) + + +class TabManagerList(object): # for window list + def __init__(self): + self.clients = [] + self.ACTIVE = None + self.orig_LTL = WindowList.ListedToplevel # save original + + def get_frame(self): + if self.ACTIVE is not None: + F = self.ACTIVE.create_frame() + else: + if self.clients: + F = self.clients[0].create_frame() + else: + F = None # should not happen + return F + + def set_active(self, t): + if t in self.clients: + self.ACTIVE = t + self.postwindowsmenu() + else: + pass + + def postwindowsmenu(self, event=None): + # FIXME: what does this do again? + for t in self.clients: + if t.active_frame.editwin is not None: + t.active_frame.editwin.postwindowsmenu() + else: + print('null editwin:', t, t.active_frame) + + + def add(self, m): + TOPLEVEL = m.TOPLEVEL + def change(event=None, m=m): + tabmanagerlist.set_active(m) + TOPLEVEL.bind('', change, '+') + + self.clients.append(m) + + def change_manager(self, event=None): + self.set_active(self) + + def remove(self, m): + if m is self.ACTIVE: + self.ACTIVE = None + self.clients.remove(m) + + +tabmanagerlist = TabManagerList() # This is a stand-in object for ListedTopLevel in WindowList + +# MONKEY PATCH - temporarily replace the ListedTopLevel with a Frame +# object in the current TabManager window +def patch(func): + def n(*arg, **kw): + if tabmanagerlist.ACTIVE is not None: # are there any toplevel windows? + orig = WindowList.ListedToplevel # save original + def open_patch(*arg, **kw): + return tabmanagerlist.get_frame() + WindowList.ListedToplevel = open_patch # patch it + retval = func(*arg, **kw) # call function + WindowList.ListedToplevel = orig # restore it + return retval + else: + return func(*arg, **kw) # call original function + return n + +FileList.FileList.open = patch(FileList.FileList.open) + + +class TabManager(object): # for handling an instance of ListedTopLevel + + def __init__(self, top=None, tab=None, flist=None): + self.flist = flist + TOPLEVEL = self.TOPLEVEL = top + self.TABS = [] + self.CLOSE_FRAME = None + self.active_frame = tab + TOPLEVEL.protocol("WM_DELETE_WINDOW", self.closetoplevel) + TOPLEVEL.bind('<>', self.visible_bar) + # create a tab bar widget + tab_bar = self.tab_bar = TabWidget(self.TOPLEVEL) + tab_bar.config(height=7, relief=GROOVE, bd=1) + tab_bar.bind('', lambda x: self.tabmenu(event=x)) + + tabmanagerlist.add(self) + + def create_frame(self): + # make a FRAME for holding the editors, + # duck-typing to mimic a Toplevel object + + TOPLEVEL = self.TOPLEVEL + F = Frame(TOPLEVEL) + F.state = lambda: "normal" + F.wm_geometry = TOPLEVEL.wm_geometry + F.protocol = lambda *args, **kwargs: True # override protocol requests + F.wakeup = None # will be overwritten by TabExtension + F.wm_title = TOPLEVEL.wm_title # pass-thru + F.wm_iconname = TOPLEVEL.wm_iconname # pass-thru + F.TAB_MANAGER = self # INDICATOR + F._lower = F.lower + F._lift = F.lift + + F.lift = TOPLEVEL.lift + F.lower = TOPLEVEL.lower + + F.instance_dict = TOPLEVEL.instance_dict + F.update_windowlist_registry = TOPLEVEL.update_windowlist_registry + F.iconbitmap = TOPLEVEL.iconbitmap + return F + + def newtab(self): + patch(self.flist.new)() + + def addtab(self, tabframe=None): + tab_bar = self.tab_bar + b = tab_bar.add(text=tabframe.get_title(), + select_callback=tabframe.select, + remove_callback=tabframe.closetab) + + def mb(event=None, tabframe=tabframe): + self.tabmenu(event=event, tabframe=tabframe) + b.totalbind('', mb) + + self.TABS.append(tabframe) + self.visible_bar() + return b + + + def tabmenu(self, event=None, tabframe=None): + + rmenu = Menu(self.TOPLEVEL, tearoff=0) + + if tabframe is not None: + rmenu.add_command(label='Close tab', command=tabframe.button.remove) + rmenu.add_separator() + rmenu.add_command(label='New tab', command=tabframe.tab_new_event) + rmenu.add_separator() + + for t in self.TABS: + label = t.get_title() + rmenu.add_command(label=label, command=t.button.select) + + rmenu.tk_popup(event.x_root, event.y_root) + + def visible_bar(self, ev=None): + a = get_cfg("always_show") + if len(self.TABS) > 1 or a: #TAB_SHOW_SINGLE: + if TAB_BAR_SIDE == 'top': + self.tab_bar.pack(side='top', fill=X, expand=NO, + before=self.active_frame.TAB_FRAME) + else: + self.tab_bar.pack(side='bottom', fill=X, expand=NO) + else: + self.tab_bar.pack_forget() + + def tab_wakeup(self, tabframe=None): + #print 'tab_wakeup', tabframe.get_title() + + if self.active_frame is tabframe: + return # already awake + + if self.active_frame: + self.active_frame.TAB_FRAME.pack_forget() + + tabframe.TAB_FRAME.pack(fill=BOTH, expand=YES) + self.active_frame = tabframe + + # switch toplevel menu + TOPLEVEL = self.TOPLEVEL + TOPLEVEL.config(menu=None) # restore menubar + + def later(TOPLEVEL=TOPLEVEL, tabframe=tabframe): + TOPLEVEL.config(menu=tabframe.editwin.menubar) # restore menubar + TOPLEVEL.update_idletasks() # critical for avoiding flicker (on Linux at least) + TOPLEVEL.lift() # fix a bug caused in Compiz? where a maximized window loses the menu bar + TOPLEVEL.focused_widget=tabframe.editwin.text # for windowlist wakeup + tabframe.editwin.text.focus_set() + + TOPLEVEL.after_idle(later) + TOPLEVEL.after_idle(self.visible_bar) + TOPLEVEL.after_idle(tabframe.saved_change_hook) + TOPLEVEL.after_idle(tabmanagerlist.postwindowsmenu) # need to change only the menus of active tabs + TOPLEVEL.after_idle(tabframe.button.ensure_visible) + + if self.CLOSE_FRAME is not None: # to prevent flicker when the recently closed tab was active + self.delayed_close() + + + def _close(self): + self.TOPLEVEL.destroy() + tabmanagerlist.remove(self) + + def close_tab(self, tabframe=None): + reply = tabframe.editwin.maybesave() + if str(reply) == "cancel": + return "cancel" + + #self.tab_bar._remove(tabframe.button) # 2012-04-05 bug fix - File->Close now works + + self.CLOSE_FRAME=tabframe + if self.active_frame is not tabframe or len(self.TABS) == 1: + self.delayed_close() + + return "break" + + + def delayed_close(self): + # for when closing the active tab, + # to prevent flicker of the GUI when closing the active frame + tabframe = self.CLOSE_FRAME + if tabframe is not None: + tabframe.editwin._close() + self.TABS.remove(tabframe) + + if self.TABS: # some tabs still exist + self.visible_bar() + else: # last tab closed + self._close() + self.CLOSE_FRAME = None + + + def closetoplevel(self, event=None): + if self.closewarning() == False: + return "break" + + for tabframe in self.TABS: + if not tabframe.editwin.get_saved(): + tabframe.button.select() + reply = tabframe.editwin.maybesave() + if str(reply) == "cancel": + return "break" + + # close all tabs + for tabframe in self.TABS: + tabframe.editwin._close() + + self._close() + return "break" + + def closewarning(self, event=None): + if not WARN_MULTIPLE_TAB_CLOSING: + return True + + L = len(self.TABS) + if L > 1: + res = tkMessageBox.askyesno( + "Close Multiple Tabs?", + "Do you want to close %i tabs?" % L, + default="no", + parent=self.TOPLEVEL) + else: + res = True + return res + + +# Tab Widget code + + +class TabWidget(Frame): + def __init__(self, *args, **kw): + Frame.__init__(self, *args, **kw) + self.scrollbind(self) # enable scroll-wheel + self.bind('', self._config) + + # add scroll buttons + self._BL = BL = Button(self, text="<", padx=0, bd=2, pady=0, + command=lambda: self._shift('left'), + relief=FLAT) + BL.pack(side='left', fill=Y) + + self.scrollbind(BL) + self._BR = BR = Button(self, text=">", padx=0, bd=2, pady=0, + command=lambda: self._shift('right'), + relief=FLAT) + BR.place(relx=1, y=-1, rely=0.5, anchor=E, relheight=1) + self.scrollbind(BR) + + # internal variables to track TABS + self.tablist = [] # list of tabs + self._offset = 0 # offset of leftmost tab + self._active = None + self.drag_pos = None # for drag'n'drop support + + self.POINT = Label(self, bg="#FF0000", width=2) # for drag'n'drop + self.VALID_OFFSET = True # boolean flag for tab coordinates + + + self.hover_scroll(BL, 'left') + self.hover_scroll(BR, 'right') + + + def hover_scroll(self, btn, cmd): + class Shifter: + def __init__(self, btn, cmd, tw): + self.tw = tw + self.btn = btn + self.cmd = cmd + self.timer = None + + def start(self, ev=None): + self.timer = self.btn.after(500, self.doit) + + def doit(self, ev=None): + state = self.btn.cget('state') + if state != 'disabled': + self.tw._shift(cmd, magdx=5) + self.tw.enable_shifters() + self.stop() + self.timer = self.btn.after(50, self.doit) + + def stop(self, ev=None): + if self.timer is not None: + self.btn.after_cancel(self.timer) + + a = Shifter(btn, cmd, self) + btn.bind('', a.start) + btn.bind('', a.stop) + btn.bind('