简体   繁体   中英

Selecting multiple text in tkinter

Is there any way to select multiple text in tkinter?

Here's the code:

from tkinter import *

root = Tk()

text = Text(root , width = 65 , height = 20 , font = "consolas 14")
text.pack()

text.insert('1.0' , "This is the first line.\nThis is the second line.\nThis is the third line.")

mainloop()

Here, I want be able to select multiple text from where ever I want.

Here is an Image(GIF) that explains what I mean:

在此处输入图像描述

Is there any way to achieve this in tkinter?

It would be great if anyone could help me out.

I made a short demo, with Control key hold you could select multiple text. Check this:

import tkinter as tk


class SelectableText(tk.Text):

    def __init__(self, master, **kwarg):
        super().__init__(master, **kwarg)
        self.down_ind = ''
        self.up_ind = ''
        self.bind("<Control-Button-1>", self.mouse_down)
        self.bind("<B1-Motion>", self.mouse_drag)
        self.bind("<ButtonRelease-1>", self.mouse_up)
        self.bind("<BackSpace>", self.delete_)

    def mouse_down(self, event):
        self.down_ind = self.index(f"@{event.x},{event.y}")

    def mouse_drag(self, event):
        self.up_ind = self.index(f"@{event.x},{event.y}")
        if self.down_ind and self.down_ind != self.up_ind:
            self.tag_add(tk.SEL, self.down_ind, self.up_ind)
            self.tag_add(tk.SEL, self.up_ind, self.down_ind)

    def mouse_up(self, event):
        self.down_ind = ''
        self.up_ind = ''

    def delete_(self, event):
        selected = self.tag_ranges(tk.SEL)
        if len(selected) > 2:
            not_deleting = ''
            for i in range(1, len(selected) - 1):
                if i % 2 == 0:
                    not_deleting += self.get(selected[i-1].string, selected[i].string)
            self.delete(selected[0].string, selected[-1].string)
            self.insert(selected[0].string, not_deleting)
            return "break"


root = tk.Tk()

text = SelectableText(root, width=50, height=10)
text.grid()
text.insert('end', "This is the first line.\nThis is the second line.\nThis is the third line.")

root.mainloop()

So I was trying to delete each selection with the Text.delete(index1, index2) but when the first selection in one line is deleted, the indices changes, making the subsequent delete deleting indices not selected (or out of range in the particular line.

I had to work around another way - first deleting from the first selected to the last selected, just like what BackSpace would do by default, then put back every unselected part in the middle. The Text.tag_ranges gives you a list of ranges selected in this way:

[start1, end1, start2, end2, ...]

where each entry is a <textindex object> with a string property (the index). So you can extract the text between end1 and start2 , between end2 and start3 , etc. to the end, and store these into a variable ( not_deleting ) so you can insert them back into the text.

There should be better and neater solutions but for now this is what it is... Hope it helps.

Short answer: set the exportselection attribute of each Text widget to False

Tkinter gives you control over this behaviour with the exportselection configuration option for the Text widget as well as the Entry and Listbox widgets. Setting it to False prevents the export of the selection to the X selection, allowing the widget to retain its selection when a different widget gets focus.

For example:

import tkinter as tk
...
text1 = tk.Text(..., exportselection=False)
text2 = tk.Text(..., exportselection=False)

you can find more info here: http://tcl.tk/man/tcl8.5/TkCmd/options.htm#M-exportselection

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM