1

What I want to do is colour in a single pixel in the centre of the screen, then at random choose an adjacent pixel and colour it in, and then repeat until some condition is met - anything such as time, or the screen is full, or after a certain number of pixels are full. This ending isn't too important, I haven't got that far yet, and I think I could manage to work that out myself.

I have no experience with tkinter, but I decided it was the best way to display this, since I don't really no any other way. Some of this code (mainly the tkinter functions like Canvas, PhotoImage etc) is therefore copy-pasted (and slightly edited) from examples I found here.

What my code does when run is hard to tell - it uses the CPU as much as it can seemingly indefinitely, and slowly increases its memory usage, but doesn't appear to do anything. No window opens, and the IDLE interpreter goes to a blank line, as usual when calculating something. When killed, the window opens, and displays a white page with a little black blob in the bottom right corner - as if the program had done what it was meant to, but without showing it happening, and starting in the wrong place.

So:

  • Why does it do this?
  • What should I do to make my program work?
  • What would be a better way of coding this, changing as many things as you like (ie. no tkinter, a different algorithm etc)?

    from tkinter import Tk, Canvas, PhotoImage, mainloop from random import randrange from time import sleep

    def choose_pixel(pixel_list):
    
        possible_pixels = []
    
        for x in pixel_list:
    
            #adjacent pixels to existing ones
    
            a = [x[0] + 1, x[1]]
    
            b = [x[0] - 1, x[1]]
    
            c = [x[0], x[1] + 1]
    
            d = [x[0], x[1] - 1]
    
            #if a not in pixel_list:
    
            possible_pixels.append(a)
    
            #if b not in pixel_list:
    
            possible_pixels.append(b)
    
            #if c not in pixel_list:
    
            possible_pixels.append(c)
    
            #if d not in pixel_list:
    
            possible_pixels.append(d)
    
        pixel_choosing = randrange(len(possible_pixels))
    
        final_choice = possible_pixels[pixel_choosing]
    
        return final_choice
    
    def update_image(img_name, pixel):
    
        img.put("#000000", (pixel[0], pixel[1]))
    
    WIDTH, HEIGHT = 320, 240
    
    window = Tk()
    
    #create white background image
    
    canvas = Canvas(window, width=WIDTH, height=HEIGHT, bg="#ffffff")
    
    canvas.pack()
    
    img = PhotoImage(width=WIDTH, height=HEIGHT)
    
    canvas.create_image((WIDTH, HEIGHT), image=img, state="normal")
    
    first_pixel = [int(WIDTH/2), int(HEIGHT/2)]
    
    pixel_list = [first_pixel]
    
    img.put("#000000", (first_pixel[0], first_pixel[1]))
    
    canvas.pack()
    
    runs = 0
    
    while True:
    
        next_pixel = choose_pixel(pixel_list)
    
        pixel_list.append(next_pixel)
    
        window.after(0, update_image, img, next_pixel)
    
        canvas.pack()
    
        runs+=1
    
    
    window.mainloop()
    
2
  • 1
    You should really consider posting a Minimal, Complete, and Verifiable example; it's very unlikely that anyone can help you otherwise. Commented Nov 27, 2014 at 23:53
  • Oh...I wrote all of this, but forgot to add the code. Thanks. Now I can't format it nicely though, any tips? Commented Nov 28, 2014 at 17:38

1 Answer 1

2

The pattern for running something periodically in tkinter is to write a function that does whatever you want it to do, and then the last thing it does is use after to call itself again in the future. It looks something like this:

import tkinter as tk
...

class Example(...):
    def __init__(self, ...):
        ...
        self.canvas = tk.Canvas(...)
        self.delay = 100 # 100ms equals ten times a second
        ...
        # draw the first thing
        self.draw_something()

    def draw_something(self):
        <put your code to draw one thing here>
        self.canvas.after(self.delay, self.draw_something)

After the function draws something, it schedules itself to run again in the future. The delay defines approximately how long to wait before the next call. The smaller the number, the faster it runs but the more CPU it uses. This works, because between the time after is called and the time elapses, the event loop (mainloop) is free to handle other events such as screen redraws.

While you may think this looks like recursion, it isn't since it's not making a recursive call. It's merely adding a job to a queue that the mainloop periodically checks.

Sign up to request clarification or add additional context in comments.

1 Comment

What about variables created or used by the function, how do I get that to work? Also, I have no idea how to use classes, I've not been doing this stuff for long.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.