TkInter python — создание точек на холсте для получения треугольника Серпинского

Я хочу сделать программу, которая строит треугольник Серпинского (любого модуля). Для этого я использовал TkInter. Программа генерирует фрактал, перемещая точку случайным образом, всегда оставляя ее в стороне. После многократного повторения процесса появляется фрактал.

Однако есть проблема. Я не знаю, как рисовать точки на холсте в TkInter. В остальном программа в порядке, но мне пришлось «схитрить», чтобы нанести точки, рисуя вместо точек маленькие линии. Он работает более или менее, но у него не такое разрешение, как могло бы быть.

Есть ли функция для построения точек на холсте или другой инструмент для этого (с использованием Python)? Также приветствуются идеи по улучшению остальной части программы.

Спасибо. Вот что у меня есть:

from tkinter import *
import random
import math
def plotpoint(x, y):
    global canvas
    point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
    #Procedure for placing the points
    while True:
        #First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
        a = random.randint(0,mod-1)
        b = random.randint(0,mod-1)
        if a + b < mod:
            break
    #The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
    x = x / mod + a / mod + b / 2 / mod
    y = y / mod + b / mod
    #Coordinates [0,1] converted to pixels, for plotting in the canvas.
    X = math.floor(x * 808)
    Y = math.floor((1-y) * 700)
    plotpoint(X, Y)
tkengine.mainloop()

person Martín Gómez    schedule 29.08.2017    source источник


Ответы (3)


Если вы хотите отображать пиксели, холст, вероятно, будет неправильным выбором. Вы можете создать PhotoImage и изменить отдельные пиксели. Это немного медленно, если вы рисуете каждый отдельный пиксель, но вы можете получить существенное ускорение, если вызовете метод put только один раз для каждой строки изображения.

Вот полный пример:

from tkinter import *
import random
import math

def plotpoint(x, y):
    global the_image
    the_image.put(('#000000',), to=(x,y))

x = 0
y = 0
mod = 3
points = 100000
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
the_image = PhotoImage(width=809, height=700)
label = Label(window, image=the_image, borderwidth=2, relief="raised")
label.pack(fill="both", expand=True)

for t in range(points):
    while True:
        a = random.randint(0,mod-1)
        b = random.randint(0,mod-1)
        if a + b < mod:
            break
    x = x / mod + a / mod + b / 2 / mod
    y = y / mod + b / mod

    X = math.floor(x * 808)
    Y = math.floor((1-y) * 700)
    plotpoint(X, Y)

tkengine.mainloop()
person Bryan Oakley    schedule 30.08.2017
comment
Что вы имеете в виду, когда говорите: вы можете получить резкое ускорение, если вызовете метод put только один раз для каждой строки изображения? - person Martín Gómez; 31.08.2017
comment
@MartínGómez Я имею в виду, что вызов put 100 раз с одним пикселем данных значительно медленнее, чем вызов put 1 раз с 100 пикселями данных. - person Bryan Oakley; 31.08.2017

Вы можете использовать canvas.create_oval с одинаковыми координатами для двух углов ограничивающей рамки:

from tkinter import *
import random
import math
def plotpoint(x, y):
    global canvas
#     point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
    point = canvas.create_oval(x, y, x, y, fill="#000000", outline="#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
    #Procedure for placing the points
    while True:
        #First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
        a = random.randint(0,mod-1)
        b = random.randint(0,mod-1)
        if a + b < mod:
            break
    #The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
    x = x / mod + a / mod + b / 2 / mod
    y = y / mod + b / mod
    #Coordinates [0,1] converted to pixels, for plotting in the canvas.
    X = math.floor(x * 808)
    Y = math.floor((1-y) * 700)
    plotpoint(X, Y)
tkengine.mainloop()

при глубине 3 и 100 000 точек это дает:

введите здесь описание изображения

person Reblochon Masque    schedule 30.08.2017
comment
У этого решения есть еще одна проблема: овалы, которые создаются при этом, на самом деле являются квадратами 2 х 2 пикселя. Кажется, нет никакого способа сделать пиксели. - person Martín Gómez; 30.08.2017
comment
IDK, круги в ограничительной рамке в (x, y, x, y) выглядели 1x1 для меня на ЖК-дисплее Mac (не Retina)... тогда как круги в (x, y, x+1, y+ 1) ББ выглядел крупнее... Может от системы зависит? - person Reblochon Masque; 30.08.2017

Наконец нашел решение: если точка 1x1 должна быть помещена в пиксель (x,y), команда, которая делает это точно:

point = canvas.create_line(x, y, x+1, y+1, fill = "colour")

Овал — хорошая идея для точек 2x2.

Что примечательно в оригинальной программе, так это то, что она использует много оперативной памяти, если каждая точка обрабатывается как отдельный объект.

person Martín Gómez    schedule 30.08.2017