【问题标题】:How to color a substring in Tkinter canvas如何在 Tkinter 画布中为子字符串着色
【发布时间】:2015-03-31 12:30:43
【问题描述】:

我正在编写一个程序,该程序涉及在循环中在 Tkinter 画布上的 create_text() 框中显示一些文本。显示每个单词,然后替换为下一个单词。有点像闪存卡。

我需要给每个单词的一个字母上色,靠近单词的中间,这样当用户阅读单词时,他们的眼睛就会集中在单词的中间。所以if len(i)=1, color i[0], if len(i)>= 2 and <= 5, color i[1],等等。它需要使用 Canvas 完成,并使用

canvas.create_text(text = i[focus_index],fill = 'red') 

结果应该是这样打印出来的

exaMple

(但显然“m”会被涂成红色,而不是大写)

【问题讨论】:

  • 您必须创建自己的方法来执行此操作,因为 create_text 仅将单一颜色作为参数,我建议您查看测量画布上文本大小的方法,并且然后编写一个方法来分解字符串并用自己的颜色分别创建每个字符串
  • 如果这只是为了告诉用户注视的位置,为什么不在文本后面放置一个彩色点呢?
  • 所以它是一个速读程序,有点模仿 Spritz 小部件。速读策略包括关注单词的中间部分,然后将整个单词与外围信息一起处理到你的大脑中。我需要用这个“焦点”作为单词中间的一个字母来呈现这个单词,颜色与文本的其余部分不同。我避免使用高亮这个词,因为 Tkinter 中有一个 highlight() 方法可以做其他事情
  • 您是否有理由必须使用画布?您可以使用标签或文本小部件吗?
  • 色彩焦点是我的导师提出的挑战。我的计划运行良好,但我真的很想实施这个挑战,这就是他希望我们这样做的方式

标签: python tkinter tkinter-canvas


【解决方案1】:

我假设你想要this 之类的东西?

这是我目前能得到的最接近的。它创建三个文本框并使用anchor 属性将它们保持在正确的位置。不过,对于真正宽或窄的字母来说,它并不是那么好。这并不完美,但它可能是一个开始。

import Tkinter as tk

root = tk.Tk()

c = tk.Canvas(root)
c.pack(expand=1, fill=tk.BOTH)

words = '''I am writing a program that involves displaying some text in a create_text() box on a Tkinter canvas, within a loop. Each word is displayed, then replaced by the next. Sort of like flash cards. I need to color one letter of each word, close to the middle of the word, so that when the user is reading the words their eyes focus on the middle of the word. So if len(i)=1, color i[0], if len(i)>= 2 and <= 5, color i[1], and so on. It needs to be done using the Canvas, and using canvas.create_text(text = i[focus_index],fill = 'red') The result should print like this exaMple (but obviously "m" would be colored red, not be uppercase)'''
words = words.split()

def new_word(i):
    if i == len(words):
        i = 0

    word = words[i]
    middle = (len(word)+1)//2
    c.itemconfigure(t1, text=word[:middle-1]+' ')
    c.itemconfigure(t2, text=word[middle-1:middle])
    c.itemconfigure(t3, text=word[middle:])

    root.after(100, lambda: new_word(i+1))


t1 = c.create_text(200,100,text='', anchor='e', font=("Courier", 25))
t2 = c.create_text(200,100,text='', anchor='e', font=("Courier", 25), fill='red')
t3 = c.create_text(200,100,text='', anchor='w', font=("Courier", 25))
new_word(0)

root.geometry('400x200+200+200')
root.mainloop()

好的,使用来自Bryan Oakley's comment 的链接,我进一步改进了代码,使其适用于任何字体,而不仅仅是等宽字体。代码将彩色字母的中心保持在同一位置,并将单词的前后放置在正确的距离周围。

import Tkinter as tk
import tkFont

root = tk.Tk()
c = tk.Canvas(root)
c.pack(expand=1, fill=tk.BOTH)

fn = "Helvetica"
fs = 24
font = tkFont.Font(family=fn, size=fs)

words = '''I am writing a program that involves displaying some text in a create_text() box on a Tkinter canvas, within a loop. Each word is displayed, then replaced by the next. Sort of like flash cards. I need to color one letter of each word, close to the middle of the word, so that when the user is reading the words their eyes focus on the middle of the word. So if len(i)=1, color i[0], if len(i)>= 2 and <= 5, color i[1], and so on. It needs to be done using the Canvas, and using canvas.create_text(text = i[focus_index],fill = 'red') The result should print like this exaMple (but obviously "m" would be colored red, not be uppercase)'''
words = words.split()

def new_word(i):
    if i == len(words):
        i = 0

    word = words[i]
    middle = (len(word)+1)//2

    front = word[:middle-1]
    letter = word[middle-1:middle]
    back = word[middle:]

    c.itemconfigure(t1, text=front)
    c.itemconfigure(t2, text=letter)
    c.itemconfigure(t3, text=back)
    c.coords(t1, 200-font.measure(letter)/2, 100)
    c.coords(t3, 200+font.measure(letter)/2, 100)

    root.after(100, lambda: new_word(i+1))


t1 = c.create_text(200,100,text='', anchor='e', font=font)
t2 = c.create_text(200,100,text='', anchor='c', font=font, fill='red')
t3 = c.create_text(200,100,text='', anchor='w', font=font)
new_word(0)

root.geometry('400x200+200+200')
root.mainloop()

【讨论】:

  • 是的,这适用于一些修改,但现在我无法在呈现下一个单词之前删除 t1,t2,t3。它只是将它们堆叠在一起,这是我在程序的早期阶段遇到的一个问题,但使用 delete() 方法解决得很好
  • 这就是我创建它们一次然后使用itemconfigure 替换文本而不是创建新文本框的原因。
  • 对!!说得通。现在效果很好。最后一个问题我保证.. 字体是 Courier,所以它是等宽字体,但是红色字母的两侧似乎有一个额外的空间区域,但是当我删除每个子字符串的 ' ' 中的空间时,它以中间字母重叠开头和结尾。如何解决此问题以使整个单词的间距相等?
  • 啊,等宽字体很有帮助。我已经编辑了答案中文本框的位置。我相信这很有效。
  • FWIW,tkinter 有一种方法可以测量给定字体中字符串的呈现宽度,这样您就不必依赖固定宽度的字体。例如,请参阅stackoverflow.com/a/7210681/7432
【解决方案2】:

您不能将格式应用于画布文本项中的单个字符。您需要为红色字符创建一个不同的项目,并进行一些数学运算以将其覆盖在字符串之上。

如果您没有 使用画布,我推荐使用文本小部件,因为它可以轻松地将格式应用于单个字符。这是一个完整的工作示例:

import Tkinter as tk

words = '''
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mi leo, vulputate a consectetur in, congue sit amet elit. Fusce lacinia placerat mi, vitae maximus leo congue sed. Donec non diam dapibus, fringilla risus at, interdum sem. Interdum et malesuada fames ac ante ipsum primis in faucibus. 
'''.strip().split()

class Example(tk.Frame):
   def __init__(self, parent):
      tk.Frame.__init__(self, parent)
      self.text = tk.Text(self, wrap=None, font="Helvetica 24",
                          highlightthickness=0)
      self.text.pack(side="top", fill="x")

      self.text.tag_configure("center", justify="center")
      self.text.tag_configure("red", foreground="red")

      self.show_words(0)

   def show_words(self, index):
      self.show_word(words[index])
      next = index + 1 if index < len(words)-1 else 0
      self.after(200, self.show_words, next)

   def show_word(self, word):
      self.text.configure(state="normal")
      self.text.delete("1.0", "end")
      self.text.insert("1.0", word, "center")
      offset = len(word)/2
      self.text.tag_add("red", "1.0 + %sc" % offset)
      self.text.configure(state="disabled")

if __name__ == "__main__":
   root = tk.Tk()
   Example(root).pack(fill="both", expand=True)
   root.mainloop()

【讨论】:

  • 谢谢我玩过这个,似乎更容易肯定。对于这个项目,我需要使用画布
猜你喜欢
  • 2020-09-18
  • 2015-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-27
  • 1970-01-01
相关资源
最近更新 更多