阻止文本在 Tkinter 中重叠 canvas
Stop text from overlapping in Tkinter canvas
我正在 tkinter canvas 中创建一个游戏,其中涉及生成文本(1 位或 2 位数字)并且我已经让它工作了,但我不知道如何显示它们他们不重叠。目前我有这个:
import tkinter as tk
from tkinter import font
import random
BOX_SIZE = 300
class Game(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.config(bg = "white")
self.numBox = tk.Canvas(self, height = BOX_SIZE, width = BOX_SIZE , bg = "white", highlightthickness = 0)
self.numBox.pack(expand = True)
self.score = 0
self.numberSpawn()
def placeNumber(self, value):
validSpawn = False
attempts = 0
maxAttempt = False
while not validSpawn and not maxAttempt:
attempts += 1
if attempts > 20:
maxAttempt = True
attempts = 0
size = random.choice([24,36,48,72])
coord = [random.randint(40,BOX_SIZE - 40) for x in range(2)]
self.numBox.update()
pxSize = tk.font.Font(size = size, family = "Times New Roman").measure(value)
if len(str(value)) == 1:
secondCoords = [coord[0] + pxSize *2.5 , coord[1] + pxSize]
else:
secondCoords = [x + pxSize for x in coord]
if not self.numBox.find_overlapping(*coord, *secondCoords):
validSpawn = True
if not maxAttempt:
newTxt = self.numBox.create_text(*coord, font = ("Times New Roman",size), text = value)
def numberSpawn(self):
self.maxNum = random.randint(3,19)
self.placeNumber(self.maxNum)
for i in range(random.randint(4, 16)):
num = random.randint(0, self.maxNum-1)
self.placeNumber(num)
app = Game()
app.mainloop()
value
是要显示的数字,BOX_SIZE
是canvas的维度。在创建文本之前,我尝试使用 this to stop the text overlapping and 来查找文本的像素大小。尽管如此,文本仍然像这样重叠:
我不确定如何解决这个问题,或者为什么它不能正常工作。感谢任何帮助。
我认为问题在于:
- 你放弃得太早了,而且
- 当您达到尝试次数上限时,无论是否重叠,您都可以添加文本
您应该大大增加尝试次数(可能是几百次),然后如果次数超过最大值,则不应绘制文本。
我认为更好的策略可能是先绘制文本项,然后使用该方法的 bbox
计算该项实际占用的 space 量。然后,使用它来查找重叠的项目。 just-created项会一直重叠,但如果重叠数大于1,则选择新的随机坐标。
例如,可能是这样的:
def placeNumber(self, value):
size = random.choice([24,36,48,72])
coord = [random.randint(40,BOX_SIZE - 40) for x in range(2)]
newTxt = self.numBox.create_text(*coord, font = ("Times New Roman",size), text = value)
for i in range(1000): # 1000 is the maximum number of tries to make
bbox = self.numBox.bbox(newTxt)
overlapping = self.numBox.find_overlapping(*bbox)
if len(overlapping) == 1:
return
# compute new coordinate
coord = [random.randint(40,BOX_SIZE - 40) for x in range(2)]
self.numBox.coords(newTxt, *coord)
# delete the text since we couldn't find a space for it.
self.numBox.delete(newTxt)
当没有太多可用空间时,这两种算法都会变慢 space。当我创建一个包含 100 个数字的 1000x1000 canvas 时,它在不到一秒的时间内以零重叠排列它们。
这里有一个解决方案:
import tkinter as tk
from tkinter import font
import random
def checkOverlap(R1, R2):
if (R1[0]>=R2[2]) or (R1[2]<=R2[0]) or (R1[3]<=R2[1]) or (R1[1]>=R2[3]):
return False
else:
return True
def go():
validSpawn = False
while not validSpawn:
value = random.randint(1,99)
size = random.choice([24,36,48,72])
coord = [random.randint(40,500 - 40) for x in range(2)]
new_number = canvas.create_text(*coord, font = ("Times New Roman",size),text=value)
new_box = canvas.bbox(new_number)
canvas.itemconfigure(new_number, state='hidden')
validSpawn = True
for i in canvas.items:
this_box = canvas.bbox(i)
if checkOverlap(this_box, new_box):
validSpawn = False
break
canvas.itemconfigure(new_number, state='normal')
canvas.items.append(new_number)
root = tk.Tk()
canvas = tk.Canvas(root, width = 500, height = 500, bg='white')
canvas.items = []
canvas.pack()
btn = tk.Button(root, text="Go", command=go)
btn.pack()
root.mainloop()
我没有让它尝试弄清楚项目有多大,而是让它绘制它、测量它、隐藏它,然后寻找重叠部分,然后删除它或根据结果。您将需要在其中添加最大尝试次数,否则屏幕上的数字越多,它就会开始变慢。它不应该在函数中间绘制一个框架,这样用户在进行测量时永远不会在那里看到它。
我还让它保存了一个数组,其中包含保存到屏幕上的所有数字,这样我就可以遍历它们和 运行 我自己的重叠函数。这正是我喜欢的方式,您可以返回使用 find_overlapping 它应该仍然有效。
我正在 tkinter canvas 中创建一个游戏,其中涉及生成文本(1 位或 2 位数字)并且我已经让它工作了,但我不知道如何显示它们他们不重叠。目前我有这个:
import tkinter as tk
from tkinter import font
import random
BOX_SIZE = 300
class Game(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.config(bg = "white")
self.numBox = tk.Canvas(self, height = BOX_SIZE, width = BOX_SIZE , bg = "white", highlightthickness = 0)
self.numBox.pack(expand = True)
self.score = 0
self.numberSpawn()
def placeNumber(self, value):
validSpawn = False
attempts = 0
maxAttempt = False
while not validSpawn and not maxAttempt:
attempts += 1
if attempts > 20:
maxAttempt = True
attempts = 0
size = random.choice([24,36,48,72])
coord = [random.randint(40,BOX_SIZE - 40) for x in range(2)]
self.numBox.update()
pxSize = tk.font.Font(size = size, family = "Times New Roman").measure(value)
if len(str(value)) == 1:
secondCoords = [coord[0] + pxSize *2.5 , coord[1] + pxSize]
else:
secondCoords = [x + pxSize for x in coord]
if not self.numBox.find_overlapping(*coord, *secondCoords):
validSpawn = True
if not maxAttempt:
newTxt = self.numBox.create_text(*coord, font = ("Times New Roman",size), text = value)
def numberSpawn(self):
self.maxNum = random.randint(3,19)
self.placeNumber(self.maxNum)
for i in range(random.randint(4, 16)):
num = random.randint(0, self.maxNum-1)
self.placeNumber(num)
app = Game()
app.mainloop()
value
是要显示的数字,BOX_SIZE
是canvas的维度。在创建文本之前,我尝试使用 this to stop the text overlapping and
我不确定如何解决这个问题,或者为什么它不能正常工作。感谢任何帮助。
我认为问题在于:
- 你放弃得太早了,而且
- 当您达到尝试次数上限时,无论是否重叠,您都可以添加文本
您应该大大增加尝试次数(可能是几百次),然后如果次数超过最大值,则不应绘制文本。
我认为更好的策略可能是先绘制文本项,然后使用该方法的 bbox
计算该项实际占用的 space 量。然后,使用它来查找重叠的项目。 just-created项会一直重叠,但如果重叠数大于1,则选择新的随机坐标。
例如,可能是这样的:
def placeNumber(self, value):
size = random.choice([24,36,48,72])
coord = [random.randint(40,BOX_SIZE - 40) for x in range(2)]
newTxt = self.numBox.create_text(*coord, font = ("Times New Roman",size), text = value)
for i in range(1000): # 1000 is the maximum number of tries to make
bbox = self.numBox.bbox(newTxt)
overlapping = self.numBox.find_overlapping(*bbox)
if len(overlapping) == 1:
return
# compute new coordinate
coord = [random.randint(40,BOX_SIZE - 40) for x in range(2)]
self.numBox.coords(newTxt, *coord)
# delete the text since we couldn't find a space for it.
self.numBox.delete(newTxt)
当没有太多可用空间时,这两种算法都会变慢 space。当我创建一个包含 100 个数字的 1000x1000 canvas 时,它在不到一秒的时间内以零重叠排列它们。
这里有一个解决方案:
import tkinter as tk
from tkinter import font
import random
def checkOverlap(R1, R2):
if (R1[0]>=R2[2]) or (R1[2]<=R2[0]) or (R1[3]<=R2[1]) or (R1[1]>=R2[3]):
return False
else:
return True
def go():
validSpawn = False
while not validSpawn:
value = random.randint(1,99)
size = random.choice([24,36,48,72])
coord = [random.randint(40,500 - 40) for x in range(2)]
new_number = canvas.create_text(*coord, font = ("Times New Roman",size),text=value)
new_box = canvas.bbox(new_number)
canvas.itemconfigure(new_number, state='hidden')
validSpawn = True
for i in canvas.items:
this_box = canvas.bbox(i)
if checkOverlap(this_box, new_box):
validSpawn = False
break
canvas.itemconfigure(new_number, state='normal')
canvas.items.append(new_number)
root = tk.Tk()
canvas = tk.Canvas(root, width = 500, height = 500, bg='white')
canvas.items = []
canvas.pack()
btn = tk.Button(root, text="Go", command=go)
btn.pack()
root.mainloop()
我没有让它尝试弄清楚项目有多大,而是让它绘制它、测量它、隐藏它,然后寻找重叠部分,然后删除它或根据结果。您将需要在其中添加最大尝试次数,否则屏幕上的数字越多,它就会开始变慢。它不应该在函数中间绘制一个框架,这样用户在进行测量时永远不会在那里看到它。
我还让它保存了一个数组,其中包含保存到屏幕上的所有数字,这样我就可以遍历它们和 运行 我自己的重叠函数。这正是我喜欢的方式,您可以返回使用 find_overlapping 它应该仍然有效。