如何在Python Tkinter中根据需要容纳的文本量调整多边形的高度?

huangapple go评论63阅读模式
英文:

How to make a polygon change it's height depending on the amount of text that needs to fit in there in Python Tkinter?

问题

我一直在尝试使用Tkinter在Python中创建类似聊天界面的UI。我取得了一些进展,但由于某种原因,我的多边形的高度没有足够变化以容纳整个文本。在几行后,文本会溢出多边形的外部,通常在3或4行后发生。我尝试了各种方法来使其工作,但仍然不起作用。我希望多边形能够根据内部文本的数量而改变大小,宽度方面可以,但高度方面不行。有谁能帮我找出原因并解决这个问题吗?对于如何改进和提高代码效率,欢迎提供建议。

我尝试使用len()和splitlines()来计算行数以调整高度,但似乎没有起作用。

英文:

I've been trying to make a chat like UI in python using Tkinter. I've made some progress but for some reason, the height of my polygon isn't changing enough to fit the whole text. After a few lines the text overspills outside of the polygon, usually after 3 or 4. I've tried various things to get it to work, but it still doesn't? I would like the polygon to be able to change size depending on the amount of text that is inside, width wise it works, but height wise it doesn't? Could anyone help me figure out why and how to fix it? Any suggestions is helpful? Also? Feel free to give me any advice/help on how to make my code better and more efficient?

from tkinter import *

root = Tk()
root.config(bg="lightblue")

canvas = Canvas(root, width=600, height=600,bg="grey")
canvas.grid(row=0,column=0,columnspan=2)

bubbles = []

def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs):
        points = [x1+radius, y1,
                  x1+radius, y1,
                  x2-radius, y1,
                  x2-radius, y1,
                  x2, y1,
                  x2, y1+radius,
                  x2, y1+radius,
                  x2, y2-radius,
                  x2, y2-radius,
                  x2, y2,
                  x2-radius, y2,
                  x2-radius, y2,
                  1+radius, y2,
                  x1+radius, y2,
                  x1, y2,
                  x1, y2-radius,
                  x1, y2-radius,
                  x1, y1+radius,
                  x1, y1+radius,
                  x1, y1]

        return canvas.create_polygon(points, **kwargs, smooth=True)
    

class RoundBubble:
    def __init__(self,master,message=""):
        self.master = master
        self.canvas = canvas
        resize = canvas.create_text(15, 15, text=message, width=300, anchor="w")
        x, y, w, h = canvas.bbox(resize)
        self.chonk = round_rectangle(x-5, y-5, x+w, y+h, radius=25, fill="light blue")
        canvas.move(self.chonk, 0, 530)
        self.canvas.tag_lower(self.chonk)
        canvas.move(resize, 0, 530)
        root.update_idletasks()

def send_message():
    
    if bubbles:
        canvas.move(ALL, 0, -50)
    a = RoundBubble(canvas,message=entry.get())
    bubbles.append(a)

entry = Entry(root,width=26)
entry.grid(row=1,column=0)
Button(root,text="Send",command=send_message).grid(row=1,column=1)
root.mainloop()

I've tried using len() and splitlines() to count the number of lines to adjust for the height but that didn't seem to work?

答案1

得分: 0

这里是一个部分解决方案,它向画布添加了一个“Scrollbar”和一个“scrollregion”,然后跟踪每个气泡的位置,然后使用这些位置来计算下一个消息的位置。我使用了“tags”来同时移动文本和气泡。滚动消息通过向“canvas.yview_scroll”提供位置数据来执行。

from tkinter import *

root = Tk()
root.config(bg="lightblue")

canvas = Canvas(
    root, width=600, height=600,
    xscrollincrement=1, yscrollincrement=1,
    bg="grey", scrollregion="0 0 600 4000")
canvas.grid(columnspan=2, sticky="nsew")

vert = Scrollbar(root, orient="vertical", command=canvas.yview)
vert.grid(row=0, column=3, sticky="ns")
canvas["yscrollcommand"] = vert.set

bubbles = []
location = [15]

def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs):
    points = [x1+radius, y1,
          x1+radius, y1,
          x2-radius, y1,
          x2-radius, y1,
          x2, y1,
          x2, y1+radius,
          x2, y1+radius,
          x2, y2-radius,
          x2, y2-radius,
          x2, y2,
          x2-radius, y2,
          x2-radius, y2,
          1+radius, y2,
          x1+radius, y2,
          x1, y2,
          x1, y2-radius,
          x1, y2-radius,
          x1, y1+radius,
          x1, y1+radius,
          x1, y1]

    return canvas.create_polygon(points, **kwargs, smooth=True)

class RoundBubble:

  def __init__(self, master, message=""):
    self.master = master
    self.canvas = canvas
    
    text = canvas.create_text(
        15, location[-1], text=message, width=300, anchor="nw", tags="A")
    x, y, w, h = canvas.bbox(text)
    chonk = round_rectangle(
        x-5, y-5, w+5, h+5, radius=25, fill="light blue", tags="A")

    x1, y1, w1, h1 = canvas.bbox(chonk)

    location.append(h1 + 5)

    self.canvas.tag_lower(chonk, text)
    self.canvas.dtag("A")

def send_message(e=None):

    if len(location) > 1:
        canvas.yview_scroll(location[1] - location[0], "units")

    a = RoundBubble(canvas, message=entry.get())
    bubbles.append(a)

entry = Entry(root, width=26)
entry.grid(row=1, column=0)
entry.bind("<Return>", send_message)
Button(root, text="Send", command=send_message).grid(row=1, column=1)
root.mainloop()
英文:

Here is a partial solution that adds a Scrollbar and a scrollregion to canvas then keeps track of the location of each bubble and uses that to calculate the next message position. I've used tags to move both text and bubble together. Scrolling messages is performed by supplying canvas.yview_scroll with location data.


root = Tk()
root.config(bg=&quot;lightblue&quot;)
canvas = Canvas(
root, width = 600, height = 600,
xscrollincrement = 1, yscrollincrement = 1,
bg = &quot;grey&quot;, scrollregion = &quot;0 0 600 4000&quot;)
canvas.grid(columnspan=2, sticky = &quot;nsew&quot;)
vert = Scrollbar(root, orient = &quot;vertical&quot;, command = canvas.yview)
vert.grid(row = 0, column = 3, sticky = &quot;ns&quot;)
canvas[&quot;yscrollcommand&quot;] = vert.set
bubbles = []
location = [15]
def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs):
points = [x1+radius, y1,
x1+radius, y1,
x2-radius, y1,
x2-radius, y1,
x2, y1,
x2, y1+radius,
x2, y1+radius,
x2, y2-radius,
x2, y2-radius,
x2, y2,
x2-radius, y2,
x2-radius, y2,
1+radius, y2,
x1+radius, y2,
x1, y2,
x1, y2-radius,
x1, y2-radius,
x1, y1+radius,
x1, y1+radius,
x1, y1]
return canvas.create_polygon(points, **kwargs, smooth=True)
class RoundBubble:
def __init__(self,master,message=&quot;&quot;):
self.master = master
self.canvas = canvas
text = canvas.create_text(
15, location[~0], text=message,width=300, anchor=&quot;nw&quot;, tags = &quot;A&quot;)
x, y, w, h = canvas.bbox(text)
chonk = round_rectangle(
x-5, y-5, w+5, h+5, radius=25, fill=&quot;light blue&quot;, tags = &quot;A&quot;)
x1, y1, w1, h1 = canvas.bbox(chonk)
location.append(h1 + 5)
self.canvas.tag_lower(chonk, text)
self.canvas.dtag(&quot;A&quot;)
def send_message(e = None):
if len(location) &gt; 1:
canvas.yview_scroll(location[1]- location[0], &quot;units&quot;)
a = RoundBubble(canvas,message=entry.get())
bubbles.append(a)
entry = Entry(root,width=26)
entry.grid(row=1,column=0)
entry.bind(&quot;&lt;Return&gt;&quot;, send_message)
Button(root,text=&quot;Send&quot;,command=send_message).grid(row=1,column=1)
root.mainloop()

答案2

得分: 0

请注意,.bbox() 返回边界框左上角和右下角的坐标,因此该行代码

self.chonk = round_rectangle(x-5, y-5, x+w, y+h, radius=25, fill="light blue")

应该改为以下方式:

self.chonk = round_rectangle(x-5, y-5, w+5, h+5, radius=25, fill="light blue")

请注意,您可以简单地在画布底部创建气泡,然后通过新创建的气泡的高度(以及一些垂直空间)将所有消息上移。还建议将round_rectangle()函数放在RoundBubble类内部:

...
class RoundBubble:
    def __init__(self, master, message=""):
        self.canvas = master
        # 在可视区域下方的位置创建文本和气泡
        resize = self.canvas.create_text(15, self.canvas.winfo_height()+5, text=message, width=300, anchor="nw")
        x1, y1, x2, y2 = self.canvas.bbox(resize)
        self.chonk = self.round_rectangle(x1-5, y1-5, x2+5, y2+5, radius=25, fill="light blue")
        self.canvas.tag_lower(self.chonk)
        self.canvas.update_idletasks()
        # 将所有消息上移以查看刚添加的消息
        self.canvas.move(ALL, 0, -(y2-y1+10)-10)

    def round_rectangle(self, x1, y1, x2, y2, radius=25, **kwargs):
        points = [x1+radius, y1,
                  x1+radius, y1,
                  x2-radius, y1,
                  x2-radius, y1,
                  x2, y1,
                  x2, y1+radius,
                  x2, y1+radius,
                  x2, y2-radius,
                  x2, y2-radius,
                  x2, y2,
                  x2-radius, y2,
                  x2-radius, y2,
                  1+radius, y2,
                  x1+radius, y2,
                  x1, y2,
                  x1, y2-radius,
                  x1, y2-radius,
                  x1, y1+radius,
                  x1, y1+radius,
                  x1, y1]

        return self.canvas.create_polygon(points, **kwargs, smooth=True)


def send_message():
    a = RoundBubble(canvas, message=entry.get())
    bubbles.append(a)
...
英文:

Note that .bbox() returns the coordinates of the top-left and bottom-right corners of the bounding box, so the line

self.chonk = round_rectangle(x-5, y-5, x+w, y+h, radius=25, fill=&quot;light blue&quot;)

should be as below instead:

self.chonk = round_rectangle(x-5, y-5, w+5, h+5, radius=25, fill=&quot;light blue&quot;)

Note that you can simply create the bubble just below the bottom of the canvas and then move all messages up by the height (and some vertical space) of the newly created bubble. Also suggest to put the function round_rectangle() inside the RoundBubble class:

...
class RoundBubble:
    def __init__(self, master, message=&quot;&quot;):
        self.canvas = master
        # create the text and bubble at the position just below the viewable area
        resize = self.canvas.create_text(15, self.canvas.winfo_height()+5, text=message, width=300, anchor=&quot;nw&quot;)
        x1, y1, x2, y2 = self.canvas.bbox(resize)
        self.chonk = self.round_rectangle(x1-5, y1-5, x2+5, y2+5, radius=25, fill=&quot;light blue&quot;)
        self.canvas.tag_lower(self.chonk)
        self.canvas.update_idletasks()
        # move all messages up to see the just added message
        self.canvas.move(ALL, 0, -(y2-y1+10)-10)

    def round_rectangle(self, x1, y1, x2, y2, radius=25, **kwargs):
        points = [x1+radius, y1,
                  x1+radius, y1,
                  x2-radius, y1,
                  x2-radius, y1,
                  x2, y1,
                  x2, y1+radius,
                  x2, y1+radius,
                  x2, y2-radius,
                  x2, y2-radius,
                  x2, y2,
                  x2-radius, y2,
                  x2-radius, y2,
                  1+radius, y2,
                  x1+radius, y2,
                  x1, y2,
                  x1, y2-radius,
                  x1, y2-radius,
                  x1, y1+radius,
                  x1, y1+radius,
                  x1, y1]

        return self.canvas.create_polygon(points, **kwargs, smooth=True)


def send_message():
    a = RoundBubble(canvas, message=entry.get())
    bubbles.append(a)
...

huangapple
  • 本文由 发表于 2023年7月17日 13:56:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76701810.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定