【问题标题】:Stop text from overlapping in Tkinter canvas阻止文本在 Tkinter 画布中重叠
【发布时间】:2020-09-09 04:41:24
【问题描述】:

我正在 tkinter 画布中创建一个游戏,其中涉及生成文本(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 是画布的尺寸。我尝试使用this 来阻止文本重叠,并尝试使用this 在创建文本之前找到文本的像素大小。尽管如此,文本仍然像这样重叠:

我不知道如何解决这个问题,或者为什么它不能按原样工作。任何帮助表示赞赏。

【问题讨论】:

  • 总是创建一个新的文本项,即使您已达到最大尝试次数。允许超过 5 次尝试可能会有所帮助。
  • 你做了什么来调试这个?您是否已验证 pxSize 是您的瘦身套件应有的样子? find_overlapping 是否返回您认为应该返回的内容? coord 是否设置为您认为的设置?
  • 我测试过的 @BryanOakley pxSize 似乎是正确的。我不确定 find_overlapping 是否返回了正确的东西,我只是从问题中提到的另一个答案中得到它。 Coord 设置为画布内的随机坐标。我看不出哪里出了问题。
  • @jasonharper 我不知道我是否理解正确,但我刚刚尝试在创建文本之前添加if not maxAttempt,因此只有在有效位置为找到了,但这似乎不能解决问题
  • edit您的问题包含完整的minimal reproducible example

标签: python tkinter tkinter-canvas


【解决方案1】:

这里有一个解决方案:

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,它应该仍然可以工作。

【讨论】:

    【解决方案2】:

    我认为问题在于:

    1. 你放弃得太早了,而且
    2. 当您达到尝试次数限制时,无论是否重叠,您都可以添加文本

    您应该大幅增加尝试次数(可能是几百次),然后如果次数超过最大值,则不应绘制文本。

    我认为更好的策略可能是先绘制文本项,然后使用该方法的bbox 计算该项实际占用的空间量。然后,使用它来查找重叠的项目。刚刚创建的item总是会重叠,但是如果重叠的次数大于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)
    

    当可用空间不多时,任何一种算法都会很慢。当我创建一个包含 100 个数字的 1000x1000 画布时,它在一秒钟内将它们布置成零重叠。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 2022-01-06
      • 1970-01-01
      • 2019-06-07
      • 2012-05-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多