2012-03-13 48 views
4

我正在使用Python編寫簡單的GUI代碼編輯器,並且我希望光標所在的文本行始終處於高亮狀態。如何突出顯示Text小部件的當前行?

現在,我的TextEditor類的樣子:

class TextEditor: 

    def __init__(self, container): 
     self.scrollbar = Scrollbar(container) 
     self.scrollbar.pack(side=RIGHT, fill=Y) 

     self.textbox = Text(container, height=40, undo=True, width=80, 
          font=tkFont.Font(family="Consolas", size=12)) 
     self.textbox.pack(side=LEFT) 

     self.textbox.config(yscrollcommand=self.scrollbar.set) 
     self.scrollbar.config(command=self.textbox.yview) 

我怎樣才能做到這一點?

回答

6

沒有什麼內置tkinter,直接支持。然而,對於大多數目的而言,足夠好的一件事是編寫一個函數來輪詢光標位置並定期更新突出顯示。

例如:

import Tkinter as tk 

class MyApp(tk.Tk): 
    def __init__(self, *args, **kwargs): 
     tk.Tk.__init__(self, *args, **kwargs) 
     self.text = tk.Text(self) 
     self.text.pack(side="top", fill="both", expand=True) 
     self.text.tag_configure("current_line", background="#e9e9e9") 
     self._highlight_current_line() 

    def _highlight_current_line(self, interval=100): 
     '''Updates the 'current line' highlighting every "interval" milliseconds''' 
     self.text.tag_remove("current_line", 1.0, "end") 
     self.text.tag_add("current_line", "insert linestart", "insert lineend+1c") 
     self.after(interval, self._highlight_current_line) 

app = MyApp() 
app.mainloop() 

顯然,較長的間隔越「滯後」,將被引入,而較短的間隔中使用的更多的CPU,但有在之間存在相當大的甜區極端情況下幾乎沒有可察覺的滯後,並且CPU利用率不可察覺。

還有另一種方法可以做到不涉及輪詢,絕對是萬無一失的。您可以在插入光標實際移動時精確移動突出顯示,但它涉及編寫一些嵌入式Tcl代碼,以創建在Tkinter Text對象的實現中隱藏的實際tk小部件的代理。

最後,第三種方法是爲修改光標位置的所有可能事件設置自定義綁定。雖然有可能,但很難100%正確,因爲必須考慮修改光標位置的所有事件,以及處理代碼中可能移動光標而不使用事件的位置。儘管如此,使用綁定是一個非常好的解決方案,它只需要更多的工作。

+0

爲什麼要投票?我只是檢查了一下,我可以綁定到「文本」中的按鍵和鼠標點擊事件。除非我錯過了某些東西,否則在我看來,這意味着您可以綁定到任何可能移動光標的用戶事件。至於編程原因,您的光標可能會移動 - 您的代碼負責執行此操作,所以無論何時發生,只需在之後調用_highlight_current_line即可。 – ArtOfWarfare 2015-05-10 14:53:56

+1

@ArtOfWarfare:爲什麼要投票?主要是因爲它是一個簡單的解決方案。是的,你可以綁定到每一個移動光標的位置,但是你知道所有這些事件是什麼嗎?你也許可以弄明白,但是當你用程序插入文本時,你還需要記住調用你的代碼。投票簡單,有效,幾乎萬無一失。 – 2015-05-10 18:21:28

2

絕對不需要投票like Bryan Oakley says in his answer,也不需要在您的Python代碼中嵌入Tcl代碼。我的解決方案是綁定到可能最終移動光標的事件,即<Key><Button-1>

import tkinter as tk 

class CurrentHighlightedLineText(tk.Text): 

    """Text widget with current line highlighted""" 

    def __init__(self, root, *args, **kwargs): 
     tk.Text.__init__(self, root, *args, **kwargs) 

     self.tag_configure('currentLine', background='#e9e9e9') 
     self.bind('<Key>', lambda _: self.highlightCurrentLine()) 
     self.bind('<Button-1>', lambda _: self.highlightCurrentLine()) 
     self.highlightCurrentLine(delay=0) 

    def highlightCurrentLine(self, delay=10): 

     def delayedHighlightCurrentLine(): 
      self.tag_remove('currentLine', 1.0, "end") 
      self.tag_add('currentLine', 'insert linestart', 'insert lineend+1c') 
     # This bound function is called before the cursor actually moves. 
     # So delay checking the cursor position and moving the highlight 10 ms. 

     self.after(delay, delayedHighlightCurrentLine) 


if __name__ == "__main__": 
    root = tk.Tk() 

    text = CurrentHighlightedLineText(root) 
    text.grid(row=0, column=0, sticky='nesw') 

    root.grid_rowconfigure(0, weight=1) 
    root.grid_columnconfigure(0, weight=1) 

    root.mainloop() 
+1

這是一個很好的例子,說明爲什麼輪詢是一樣好,如果不是更好的解決方案。有些東西可能會導致遊標移動,代碼無法處理(例如,通過右鍵單擊或從菜單粘貼代碼)。這並不意味着你的代碼無法處理它們,只是很難說明所有邊界情況。此外,可以通過在類綁定後添加一個特殊的綁定標籤來更加優雅地解決這個「延遲」問題,以便在光標移動之後而不是之前發生綁定。 – 2015-05-10 18:24:34

+0

@BryanOakley:我想象通過編程來修改文本可能是通過重寫'tk.Text'定義的任何方法和處理一些魔法方法(比如'__setitem__')的組合來處理的。我絕對不會建議輪詢,而不是這個。如果您在使用延遲方面有不可逾越的改進,請編輯我的答案以處理此問題。我從來沒有設法使用特殊的綁定標籤在Tkinter中工作,但也許我剛纔錯誤地使用了它們。 – ArtOfWarfare 2015-05-10 18:47:24

+0

是的,您對如何處理小部件的編程修改是正確的。問題是,你的解決方案開始變得非常複雜。我並不是說這是一個糟糕的解決方案,只是它是一個非常複雜的解決方案。 – 2015-05-10 19:13:35

相關問題