def onclick(btn, ev): "Event handler for button clicks" # Remove images from previous round sk = btn.sketch try: sk -= sk["Player1"], sk["Player2"] except: pass # Nothing to remove in first round! # Add images for current round p1, p2 = btn.layer, randint(0, 2) w, h = sk.size x, y, h = w / 4, h / 2, 0.6 * h sk["Player1"] = Image(sk.imgs[p1]).config(height=h, pos=(x, y)) sk["Player2"] = Image(sk.imgs[p2]).config(height=h, pos=(3 * x, y)) # Determine the winner and update scores/status if p1 == p2: win = "It's a draw!" elif p1 - p2 in (1, -2): win = "You win!" sk["Score1"] += 1 else: win = "You lose!" sk["Score2"] += 1 sk["Status"].config(data=win, layer=-1)
def _joinLines(self, srfs): "Join the lines of text into a single surface" # Calculate total size wMax, hMax = 0, 0 n = len(srfs) for s in srfs: w, h = s.get_size() if w > wMax: wMax = w if h > hMax: hMax = h y = self.padding w = wMax + 2 * y h = n * hMax + (n - 1) * self.spacing + 2 * y # Blit lines to final image wt = self.weight dx = 2 * wt y += wt srf = Image((w + dx, h + dx), self.bg).image a = self.color.a for s in srfs: if a < 255: setAlpha(s, a) x = self.padding + wt if self.align != LEFT: dx = wMax - s.get_size()[0] x += dx if self.align == RIGHT else dx // 2 srf.blit(s, (x, y)) y += hMax + self.spacing if wt: drawBorder(srf, self.border, wt) return srf
def setup(sk): # Load image originals fldr = resolvePath("img", __file__) sk.imgs = [ Image("{}/{}.png".format(fldr, f)) for f in ("target", "paper", "scissors") ] # Create button controls and add images to buttons w, h = sk.size x = w / 4 - 44 for img in sk.imgs: btn = Button((40, 40), 2).config(anchor=BOTTOM, pos=(x, h - 4)) btn += Image(img).config(pos=btn.center, height=32) sk += btn.bind(onclick) # Bind event handlers x += 44 # Initialize scores to zero font = {"font": Font.mono(), "fontSize": 40, "fontStyle": BOLD} sk["Score1"] = Text(0).config(color="red", anchor=TOPLEFT, pos=(4, 4), **font) sk["Score2"] = Text(0).config(color="blue", anchor=TOPRIGHT, pos=(w - 4, 4), **font) # Create status text sk["Status"] = Text("Make a choice!").config(pos=(0.75 * w, h - 24), font=Font.sans(), fontSize=32, color="red")
def setup(self): # Draw star field self.bg = img = Image(self.size, "black") img = img.image for i in range(150): img.set_at(randPixel(self.size), rgba(False)) # Load high scores try: with open(JSON) as f: self.highScores = json.load(f) except: self.highScores = [] # Load images Missile.original = Image(self.imgFldr + "/missile.png") Asteroid.original = Image(self.imgFldr + "/target.png") self.player = Ship(self.imgFldr + "/ship.png").config(wrap=BOTH) # Start the game self.playerName = None self.score = Score() self.collisions = Collisions(self) self.start()
def field(self): "Draw red and yellow goal boxes on green turf" w, h = self.size y = self.center[1] sz = boxSize(w, h) cv = Canvas((w, h), "#20b050") cv += Image(sz, "red").config(pos=(0, y), anchor=LEFT) cv += Image(sz, "yellow").config(pos=(w-1, y), anchor=RIGHT) return cv.snapshot()
def setup(self): "Add random-color rectangles to the sketch to represent the disks" h = min(14, max(1, self.height // self.disks - 1)) self.height = max(self.height, self.disks * (h + 1)) width = lambda i: self.width * (0.07 + 0.24 * i / (self.disks - 1)) for i in range(self.disks): w = width(i) img = Image((2 * w, 2 * h), False) self += img.snapshot(weight=2).config(anchor=BOTTOM, size=(w, h)) self.setDiskPositions()
def setup(self): "Add random-color rectangles to the sketch to represent the disks" h = min(14, max(1, self.height // self.disks - 1)) self.height = max(self.height, self.disks * (h + 1)) width = lambda i: self.width * (0.07 + 0.24 * i / (self.disks - 1)) for i in range(self.disks): w = width(i) img = Image((2*w, 2*h), False) self += img.snapshot(weight=2).config(anchor=BOTTOM, size=(w,h)) self.setDiskPositions()
def __getitem__(self, n): "Return a frame as an Image instance" if n != self._current[0]: self._current = n, self._costumes[n].img img = self._current[1] srf = img.original if self.alpha and not hasAlpha(srf): img = Image(srf.convert_alpha()) self._current = n, img return img.config(size=self.size, angle=self.angle)
def __init__(self, image, cols=1, rows=1, flip=0, padding=0): # Clone costumes images from existing list if type(image) in (list, tuple): tiles = [Image(s if type(s) is str else s.image) for s in image] # Slice spritesheet into individual costumes and make flipped copies else: tiles = Image(image).tiles(cols, rows, flip, padding) # Initialize self._costumes = self.costumeList = tiles self._size = tiles[0].size
def __init__(self, size=(128,16), knob="grey", lower=0, upper=1, steps=0): super().__init__(size) self.steps = steps if not isinstance(knob, Graphic): knob = Image(self._knobSize(), knob) self += knob.bind(ondrag=_knobDrag, onrelease=_knobRelease) self.knob = knob.config(_dragRel=None) if upper < lower: self._flip = True upper, lower = lower, upper else: self._flip = False self.lower = lower self.upper = upper self.val = lower
def __getitem__(self, n): "Return a frame as an Image instance, or a slice as a new Video instance" if type(n) is slice: r = range(n.start, n.stop, n.step) if n.step else range(n.start, n.stop) return self.clip(r) if n != self._current[0]: self._current = n, self._costumes[n].img img = self._current[1] srf = img.original if self.alpha and not hasAlpha(srf): img = Image(srf.convert_alpha()) self._current = n, img return img.config(size=self.size, angle=self.angle)
def _tick(param, marker=9, y=False, **kwargs): if type(marker) is int: marker = ((9,1) if y else (1,9), "black") label = type(marker) is str if not (label or isinstance(marker, Graphic)): marker = Image(*marker) s = list(Series._lattice(0, param) if y else Series._lattice(param, 0)) if label: isZero = (lambda x: _isZero(x, marker)) if kwargs.get("omitZero")\ else (lambda x: False) i = 1 if y else 0 text = list(marker.format(x[i]) for x in s) marker = [Text("" if isZero(x) else x).config(**kwargs) for x in text] return Series(s).config(marker=marker)
def __init__(self, size=(128,16), knob="grey", lower=0, upper=1, steps=0): super().__init__(size) if not isinstance(knob, Graphic): knob = Image(bg=knob) w, h = size kwargs = {"width":w} if h > w else {"height":h} self += knob.bind(ondrag=_knobDrag).config(**kwargs) self.knob = knob self.steps = steps if upper < lower: self._flip = True upper, lower = lower, upper else: self._flip = False self.lower = lower self.upper = upper self.val = lower
def _tick(param, marker=9, y=False, **kwargs): if type(marker) is int: marker = ((9, 1) if y else (1, 9), "black") label = type(marker) is str if not (label or isinstance(marker, Graphic)): marker = Image(*marker) s = list(Series._lattice(0, param) if y else Series._lattice(param, 0)) if label: isZero = (lambda x: _isZero(x, marker)) if kwargs.get("omitZero")\ else (lambda x: False) i = 1 if y else 0 text = list(marker.format(x[i]) for x in s) marker = [ Text("" if isZero(x) else x).config(**kwargs) for x in text ] return Series(s).config(marker=marker)
def setup(sk): "Add all content to the sketch" # Add text to the sketch font = {"font": Font.mono(), "fontStyle": BOLD} sk["Score"] = Text(0).config(anchor=TOPLEFT, color="red", **font) text = "Pummel the Chimp, and Win $$$" sk += Text(text).config(pos=(sk.width - 1, 0), anchor=TOPRIGHT, **font).config(width=0.75 * sk.width) # Add fist folder = resolvePath("examples/data", pygame.__file__) + "/" img = loadImage(folder + "fist.bmp") sk += Image(img).config(pos=sk.center, anchor=TOP).bind(ondraw) # Add chimp sprite img = loadImage(folder + "chimp.bmp") sk["Chimp"] = Sprite(img).config(pos=(48, 48), vel=(10, 0), bounce=BOTH).bind(ondraw=chimpDraw) # Load audio files audio = "punch.wav", "whiff.wav" sk.sounds = [pygame.mixer.Sound(folder + f) for f in audio] # Bind click event handler; hide cursor sk.bind(onmousedown) sk.cursor = False
def setup(self): try: img = self.pattern try: self.bg = img(self) except: self.bg = Image(img) except: self.bg = Image(self.size, "white") self.weight = 1 robo = Robot(["#ff5050", "#ffd428"]) self["Red"] = self.bindBrain(robo).config(width=64, pos=self.center, angle=270, bounce=BOTH) robo.gyro = robo.angle
def render(self): "Render the vector as an arrow on a pygame.Surface" l = self.length() * self.canvas.unit if l < 2: return Image((1, 1), self.stroke).image shape = self.arrowShape if type(shape) is dict: if shape["fixed"]: shape = shape.copy() shape["width"] /= l shape["head"] /= l del shape["fixed"] a = Arrow(l, **shape).config(fill=self.fill, stroke=self.stroke, weight=self.weight) else: dx, dy = shape cv = Canvas((l, 2 * dy)) y = cv.center[1] cv += Line((0, y), (l, y)).config(stroke=self.stroke, weight=self.weight) cv += Line((l - dx, y - dy), (l, y)).config(stroke=self.stroke, weight=self.weight) cv += Line((l - dx, y + dy), (l, y)).config(stroke=self.stroke, weight=self.weight) a = cv.snapshot() return a.image
def setup(game): "Create Tic-Tac-Toe board with 100 pixel squares and 20 pixel margins" # Load costumes for X and O sprites, and logo img = Image(resolvePath("img/xo.png", __file__)).tiles(3) game.alien = Image.fromBytes(sc8prData("alien")).config(height=36) # Create and position sprites, one per square for s in range(9): pos = 70 + 100 * (s % 3), 70 + 100 * (s // 3) game += Sprite(img).bind(contains=Graphic.contains, onclick=clickSquare).config(pos=pos, width=100) # Draw the board and start game for pts in [((20,120), (320,120)), ((20,220), (320,220)), ((120,20), (120,320)), ((220,20), (220,320))]: game += Line(*pts) startGame(game)
def __init__(self, size=(128, 16), color="grey", lower=0, upper=1): super().__init__(size) cfg = dict(anchor=BOTTOMLEFT, pos=(0, size[1] - 1)) if tall( *size) else dict(anchor=TOPLEFT) self += Image(bg=color).config(**cfg) self.lower = lower self.upper = upper self.value = lower
def buttonClick(gr, ev): "Event handler for buttons" path = ev.target.path # [..., button, button box, dialog, sketch] btn, dlg = path[-4], path[-2] print(btn, btn.selected) if btn.name == "Popup": btn.status = "normal" dlg["Cover"] = Image(dlg.size, "#ffffffc0").config(anchor=TOPLEFT) dlg += dlg.menu
def _title(cv, title, font, folders): "Add graph or axis titles" if "text" in title: gr = Text(title["text"]).config(**font) else: # TODO: Pass image rather than text gr = Image(title["image"].format(**folders)) if gr: gr.setCanvas(cv).config(role="title", **title.get("config", {})) return gr
def render(self): "Render the plot as a surface" srf = Image(self._size, self.bg).image if self._xgrid: self._drawGrid(srf, 0, self._xgrid) if self._ygrid: self._drawGrid(srf, 1, self._ygrid) if self._xaxis: self._drawAxis(srf, 0, *self._xaxis) if self._yaxis: self._drawAxis(srf, 1, *self._yaxis) for k in self._keys: self._series[k].draw(srf, self.pixelCoords) return srf
def recordButton(self, y): "Compose the record button" sz = 21, 21 btn = Button(sz, ["#ffffff00", "#ffc0c0"]).config(anchor=LEFT, pos=(12, y), weight=0).bind(onclick) img = Circle(76).config(fill="red", weight=6).snapshot(), Image( (19, 19)) btn += Sprite(img).config(pos=btn.center, height=19) return btn
def setup(sk): # Create a Canvas as a GUI dialog cv = Canvas((384, 256)).config(bg="#f0f0ff", weight=1) # Vertical positioning 16 pixels below last item added down = lambda cv: 16 + cv[-1].height text = dict(color=BLUE, font=FONT, fontStyle=BOLD, padding=4) if TextInputCanvas: # v2.2.dev ti = TextInputCanvas(336, "", "Type Some Text...", **text) else: # v2.0-2.1 ti = TextInput("", "Type Some Text...").config(**text) x, y = cv.center[0] - 8, 16 cv["Input"] = ti.bind(onaction, onchange=onaction).config(anchor=TOP, pos=(x, y), bg="white", weight=1) # Add a Radio box y += down(cv) cfg = {"font": FONT, "fontSize": 14} text = "Option A", "Option B", "Option C" box = Radio(text, txtConfig=cfg).bind(onchange=radioChange) cv["ABC"] = box.config(pos=(x, y), anchor=TOP, selected=1) # Add an Options box y += down(cv) text = "Option X", "Option Y", "Option Z" box = Options(text, txtConfig=cfg).config(pos=(x, y), anchor=TOP) cv["XYZ"] = box.bind(onaction=optionsChange) # Add Buttons y += down(cv) cv["Button Box"] = buttons(cfg).config(anchor=TOP, pos=(x, y)) # Modify canvas and sketch size based on content cv.resize((cv.width, y + down(cv)), False) w, h = cv.size sk.size = w + 48, h + 48 # Add a Slider slider = Slider((16, cv.height), [BLUE], 100, 0, 100) slider.config(pos=(cv.width, 0), anchor=TOPRIGHT, bg=GREY, weight=1) cv["Slider"] = slider.bind(onchange=sliderChange) # Create a popup menu img = Image.fromBytes(sc8prData("alien")) items = [("Action", img, None), ("Back", None, R_TRIANGLE)] cv.menu = Menu(items, txtConfig=cfg).config(pos=cv.center) cv.menu.bind(onaction=menuAction) # Add the dialog to the sketch sk["Dialog"] = cv.bind(resize=nothing).config(pos=sk.center)
def __init__(self, colors=None): img = Image.fromBytes(sc8prData("robot")) if colors: # Replace body and nose colors px = pygame.PixelArray(img.image) body0, nose0, body, nose = rgba("red", "blue", *colors) orig = body0, nose0 if body in orig or nose in orig: colors = body0, nose0, body, nose body0 = _tempColor(px, body0, *colors) nose0 = _tempColor(px, nose0, *colors) if nose != nose0: px.replace(nose0, nose) if body != body0: px.replace(body0, body) img = img.tiles(2) super().__init__(img)
def textIcon(self, text, icon=None, padding=6): "Add text and icon to button" if type(text) is str: text = Text(text) if type(icon) is bool: icon = Image(self._yesNoImg(icon)) if icon: w = self._icon(icon, padding) x = (w + padding) / 2 else: x = w = 0 cx, cy = self.center if cx <= 0: self._size = (w + text.width + (3 if w else 2) * padding), self._size[1] cx = self.center[0] self += text.config(pos=(x + cx, cy)) return self
def setup(sk): # Create a Canvas as a GUI dialog cv = Canvas((384,256)).config(bg="#f0f0ff", weight=1) # Vertical positioning 12 pixels below last item added down = lambda cv: 16 + cv[-1].height # Add a TextInput x, y = cv.center[0], 16 cv["Input"] = TextInput("", "Type Some Text...").config(anchor=TOP, font=FONT, fontStyle=BOLD, pos=(x,y), color=BLUE, bg="white", padding=4).bind(onaction) # Add a Radio box y += down(cv) cfg = {"font":FONT, "fontSize":14} text = "Option A", "Option B", "Option C" radio = Radio(text, txtConfig=cfg).bind(onchange=radioChange) cv["ABC"] = radio.config(pos=(x,y), anchor=TOP, selected=1) # Add an Options box y += down(cv) text = "Option X", "Option Y", "Option Z" radio = Options(text, txtConfig=cfg).config(pos=(x,y), anchor=TOP) cv["XYZ"] = radio.bind(onaction=optionsChange) # Add Buttons y += down(cv) cv["Button Box"] = buttons(cfg).config(anchor=TOP, pos=(x,y)) # Modify canvas and sketch size based on content cv.resize((cv.width, y + down(cv)), False) w, h = cv.size sk.size = w + 48, h + 48 # Add a Slider slider = Slider((16, cv.height), BLUE, 100, 0, 100) slider.config(pos=(cv.width, 0), anchor=TOPRIGHT, bg=GREY, weight=1) cv += slider.bind(onchange=sliderChange) # Create a popup menu img = Image.fromBytes(sc8prData("alien")) items = [("Action", img, None), ("Back", None, R_TRIANGLE)] cv.menu = Menu(items, txtConfig=cfg).config(pos=cv.center) cv.menu.bind(resize=nothing, onaction=menuAction) # Add the dialog to the sketch cv.cover = sk.cover() sk["Dialog"] = cv.bind(resize=nothing).config(pos=sk.center)
def setup(self): self.config(border="blue", weight=1, bg=self.field()) # Display the score attr = {"font":self.font, "fontSize":48} self += Text(0).config(pos=(8,8), anchor=TOPLEFT, color="red", **attr) self += Text(0).config(pos=(631,8), anchor=TOPRIGHT, color="yellow", **attr) self.score = list(self)[-2:] # Paint the back of the nets so the robots can see them w, h = self.size d = h / 8 r = 0.65 * boxSize(w, h)[1] h = (h - 1) / 2 self += Sprite(Image((2,2), "red")).config(pos=(-d, h), wrap=0) self += Sprite(Image((2,2), "yellow")).config(pos=(w+d, h), wrap=0) for s in self[-2:]: s.radiusFactor *= r / s.radius # Get a soccer ball self += SoccerBall().config(pos=self.center) # Start the simulation if hasattr(self, "brains"): self.start() else: self += Dialog(self).config(pos=self.center)
def grid(*args, cols=None, size=None, fit=True): n = len(args) if cols is None: cols = n rows = (n - 1) // cols + 1 args = [(a if isinstance(a, Graphic) else Image(a)) for a in args] w, h = size if size else args[0].size cv = Table(cols * [w], rows * [h]) r = c = 0 for a in args: cv += a.config(pos=cv.cell(c, r).center, anchor=CENTER) if fit: f = min(w / a.width, h / a.height) if f < 1: a.scale(f) c += 1 if c == cols: c = 0 r += 1 return cv
def makeSlider(self, cv, dim, ch, sh, cw, other=False): "Create a vertical or horizontal scroll bar" w = min(self.sliderWidth, ch - 1, cw // 12) h = ch + 1 - (w if other and dim == 0 else 0) knob = round(ch * (h - 1) / sh) knob = Image((w, knob) if dim else (knob, w), self.knobColor) size = (w, h) if dim else (h, w) a = TOPRIGHT if dim else BOTTOMLEFT u = ch - sh - (w if other else 0) gr = Slider(size=size, knob=knob, upper=u).bind(onchange=self.sliderChange) gr.config(bg=self.sliderBg, anchor=a, weight=0, val=-cv._scroll[dim], dim=dim, _scrollAdjust=False) return gr
def quilt(sk): "Draw some colors on the floor in a quilt pattern" w, h = sk.size w = (w - 64) // 6 h = (h - 64) // 4 cv = Canvas(sk.size, "grey") c = [ "pink", "darkgreen", "mintcream", "gold", "white", "cyan", "yellow", "blue", "brown", "tan2", "royalblue", "steelblue", "violet", "orange", "skyblue", "black", "tomato", "seashell", "salmon", "turquoise", "red", "magenta", "purple", "green", ] shuffle(c) for i in range(4): for j in range(6): color = c[j + 6 * i] cv += Image( (w, h), color).config(pos=(32 + (j + 0.5) * w, 32 + (i + 0.5) * h)) return cv.snapshot()
def __init__(self, scale=10, size=50, step=5, unit=("cm", 2), **cfg): if "bg" not in cfg: cfg["bg"] = Image(bg="#e0e0f0a0") if "weight" not in cfg: cfg["weight"] = 1 coord = lambda x: (x + 1) * scale h = 3.5 * scale super().__init__((coord(size + 1), h)) self.config(**cfg) x = 0 cfg = dict(anchor=BOTTOM, font=MONO, fontStyle=BOLD, color="#000000a0") while x <= size: self += Text(x).config(pos=(coord(x), h - 1), **cfg) x += step if unit: self += Text(unit[0]).config(pos=(coord(unit[1]), h - 1), **cfg) x = 0 while x <= size: s = coord(x) self += Line((s, 0), (s, scale / (2 if x % step else 1))) x += 1 for t in self: if isinstance(t, Text): t.config(height=h / 2) self.bind(ondrag)
def _series(cv, series, font, folders): "Add markers, data labels, or bars" config = {} marker = series.get("marker", None) if marker is None: imgs = series["image"] if type(imgs) is str: imgs = [imgs] cfg = series.get("image_config", {}) marker = [Image(img.format(**folders)).config(**cfg) for img in imgs] if len(marker) == 1: marker = marker[0] elif type(marker) is str: config = font.copy() elif type(marker) is list: r = marker[0] marker = Circle(10 * r).config(**marker[1]).snapshot().config(height=2 * r) config.update(series.get("config", {})) data = series.get("data") sList = cv.series(data, marker, series.get("shift", (0, 0)), **config) cv.seriesList.append(sList) cv += sList for gr in sList: gr.role = "marker" return data
def _checkTiles(): "Images for creating check boxes" if Button._check is None: Button._check = Image.fromBytes(sc8prData("checkbox")) return Button._check.tiles(5)
def loadImage(filename): "Load image file and change background to transparent" img = Image(filename) color = img.original.get_at((0, 0)) return ReplaceColor(color).apply(img)
def _yesNoImg(n): if Button._yesNo is None: Button._yesNo = Image.fromBytes(sc8prData("yesNo")).tiles(2) return Button._yesNo[0 if n else 1]
def _radioTiles(): "Images for creating radio check boxes" if Button._radio is None: Button._radio = Image.fromBytes(sc8prData("radio")) return Button._radio.tiles(5)
def checkbox(imgs=None): "Return a check box Button" if imgs is None: imgs = Button._checkTiles() return Button(None, (Image(x) for x in imgs)).config(weight=0)