Tkinter: выделение курсивом уже выделенного текста (перекрывающиеся теги)
У меня есть программа Python Tkinter, где я могу выделить текст, нажать ярлык, и он выделит выделенный текст жирным шрифтом. Вы можете использовать различные сочетания клавиш для других стилей, таких как курсив.
Однако у меня, как пользователя в запущенной программе, возникает проблема, когда, если я пытаюсь сделать уже выделенный жирным шрифтом текст, выделенный курсором, тогда текст отображается только курсивом, а не жирным шрифтом и курсивом. Я знаю, что это имеет смысл, потому что теги назначены для выполнения одного или другого, и выполнение обоих не собирается объединять эффект тегов. Однако я не знаю, как определить, что происходит, когда в вашем тексте несколько тегов.
Можете ли вы иметь тег, который каким-то образом представляет перекрытие двух других конкретных тегов?
Единственный способ справиться с этим (из того, что я вижу в документации) - это использовать Text.tag_bind
привязать функцию к каждому из моих тегов стиля, которая делает некоторые интересные вещи, чтобы сделать правильный текст, и только правильный текст, как жирный, так и курсив. Я полагаю, что это выполнимо, но если это не правильный способ, я хотел бы знать.
У меня нет проблем с созданием тега, который одновременно выделяет жирный шрифт и курсив. Что мне нужно, так это уметь обрабатывать перекрывающиеся теги.
Вот соответствующий код того, что я уже делаю:
def set_tag_styles(self):
self.myTextWidget.tag_config("bold", font=[self.d["font"][0], self.d["font"][1], "bold"])
self.myTextWidget.tag_config("italic", font=[self.d["font"][0], self.d["font"][1], "italic"])
self.myTextWidget.tag_config("underline", font=[self.d["font"][0], self.d["font"][1], "underline"])
self.myTextWidget.tag_config("overstrike", font=[self.d["font"][0], self.d["font"][1], "overstrike"])
def invert_tag(self, start, end=None, tag=SEL, w=None):
#This just makes text without the tag have the tag and text with the tag not have the tag anymore.
if w==None:
w=self.myTextWidget
i=0
if end==None:
if tag in w.tag_names(start):
w.tag_remove(tag, start)
else:
w.tag_add(tag, start)
else:
while w.compare(start+"+"+str(i)+"c", "<", end):
if tag in w.tag_names(start+"+"+str(i)+"c"):
w.tag_remove(tag, start+"+"+str(i)+"c")
else:
w.tag_add(tag, start+"+"+str(i)+"c")
i+=1
self.set_tag_styles()
def bold_text(self, event=None):
try:
self.invert_tag("sel.first", "sel.last", "bold")
except:
if self.myTextWidget.get("insert wordstart") in {" ", "\n", "\t", "\r", " "}:
pass
else:
self.invert_tag("insert wordstart", "insert wordend", "bold")
return "break"
def italic_text(self, event=None):
try:
self.invert_tag("sel.first", "sel.last", "italic")
except:
if self.myTextWidget.get("insert wordstart") in {" ", "\n", "\t", "\r", " "}:
pass
else:
self.invert_tag("insert wordstart", "insert wordend", "italic")
return "break"
РЕДАКТИРОВАТЬ: Для тех, кто заинтересован (и не хочет кодировать все с нуля), вот код того, что я сделал, чтобы заставить его работать (используя ответ Брайана Окли в качестве руководства):
self.style_tags={"bold", "italic", "underline", "overstrike", "bold italic", "bold italic underline", "bold italic underline overstrike", "italic underline", "italic overstrike", "italic underline overstrike", "underline overstrike", "bold underline", "bold underline overstrike", "bold overstrike", "bold italic overstrike"};
…
def clear_multiple_styles(self, pos, w=None):
#This gets rid of all multi-style tags (like "bold italic underline").
if w==None:
w=self.myTextWidget;
for x in self.style_tags:
s=Switch(); #This is my version of a switch statement (so I don't have to type my compare variable every time), with added flexibility.
s.switch(x);
if s.neq("bold", "italic", "underline", "overstrike"): #This means, if x isn't equal to any of them
if x in w.tag_names(pos):
w.tag_remove(x, pos);
def update_style(self, pos, w=None):
#This updates the styles of an index to take care of overlapping style tags.
if w==None:
w=self.myTextWidget;
self.clear_multiple_styles(pos, w);
s=Switch();
s.switch(w.tag_names(pos));
if s.ins("bold", "italic", "underline", "overstrike"): #i.e. If these args are all in w.tag_names(pos)
w.tag_add("bold italic underline overstrike", pos);
elif s.ins("bold", "italic", "underline"):
w.tag_add("bold italic underline", pos);
elif s.ins("bold", "italic", "overstrike"):
w.tag_add("bold italic overstrike", pos);
elif s.ins("bold", "italic"):
w.tag_add("bold italic", pos);
elif s.ins("bold", "underline", "overstrike"):
w.tag_add("bold underline overstrike", pos);
elif s.ins("bold", "underline"):
w.tag_add("bold underline", pos);
elif s.ins("bold", "overstrike"):
w.tag_add("bold overstrike", pos);
elif s.ins("italic", "underline", "overstrike"):
w.tag_add("italic underline overstrike", pos);
elif s.ins("italic", "underline"):
w.tag_add("italic underline", pos);
elif s.ins("italic", "overstrike"):
w.tag_add("italic overstrike", pos);
elif s.ins("underline", "overstrike"):
w.tag_add("underline overstrike", pos);
def invert_style_tag(self, start, end=None, tag="bold", w=None):
if w==None:
w=self.myTextWidget;
i=0;
if end==None:
if tag in w.tag_names(start):
w.tag_remove(tag, start);
else:
w.tag_add(tag, start);
self.update_style(start);
else:
while w.compare(start+"+"+str(i)+"c", "<", end):
if tag in w.tag_names(start+"+"+str(i)+"c"):
w.tag_remove(tag, start+"+"+str(i)+"c");
else:
w.tag_add(tag, start+"+"+str(i)+"c");
self.update_style(start+"+"+str(i)+"c");
i+=1;
self.set_tag_styles();
def set_tag_styles(self):
single_styles={"bold", "italic", "underline", "overstrike"};
for x in self.style_tags:
x_list=x.split();
self.myTextWidget.tag_config(x, font=[self.d["font"][0], self.d["font"][1]]+x_list); #You can add lists together to get the extra arguments in.
for y in single_styles:
if x not in single_styles:
self.myTextWidget.tag_raise(x); #Gives the multi-style tag higher priority than existing single-style tags
def style_text(self, style):
try:
self.invert_style_tag("sel.first", "sel.last", style);
except:
if self.myTextWidget.get("insert wordstart") in {" ", "\n", "\t", "\r", " "}:
pass;
else:
self.invert_style_tag("insert wordstart", "insert wordend", style);
def bold_text(self, event=None):
self.style_text("bold");
return "break";
def italic_text(self, event=None):
self.style_text("italic");
return "break";
def underline_text(self, event=None):
self.style_text("underline");
return "break";
def overstrike_text(self, event=None):
self.style_text("overstrike");
return "break";
Для тех, кому нужен мой код класса Switch (вместо того, чтобы переводить его в стандартные выражения), вот он (заранее извините, если для вас это слишком много кода):
class Switch:
def switch(self, item):
self.item=item;
def case(self, values, operator="=="):
#values must be a list, set, tuple or other sequence. This is to allow one not to have to define operator. If you don't like this, use the other methods.
if operator in "==":
return self.eq(*values);
elif operator=="!" or operator=="!=":
return self.neq(*values);
elif operator==">":
return self.gr(*values);
elif operator=="<":
return self.ls(*values);
elif operator==">=":
return self.gre(*values);
elif operator=="<=":
return self.lse(*values);
elif operator in "range" and operator[0]=="r":
if len(values)!=2:
raise ValueError("There must be two and only two values in a range.");
return self.range(values[0], values[1]);
elif operator in "nrange" and operator[0]=="n":
if len(values)!=2:
raise ValueError("There must be two and only two values in an nrange.");
return self.nrange(values[0], values[1]);
else:
raise ValueError("The operator "+operator+" is not currently defined.");
def ins(self, *values):
#If all the values are part of the string or sequence, self.item, return True. Else return False.
#Note: It doesn't take into account that "" is in every string and some tuples.
for x in values:
if x not in self.item:
return False;
return True;
def eq(self, *values):
#Equal to
return self.item in values;
def gr(self, *values):
#Greater than
for x in values:
if self.item<=x:
return False;
return True;
def gre(self, *values):
#Greater than or equal to
for x in values:
if self.item<x:
return False;
return True;
def ls(self, *values):
#Less than
for x in values:
if self.item>=x:
return False;
return True;
def lse(self, *values):
#Less than or equal to
for x in values:
if self.item>x:
return False;
return True;
def neq(self, *values):
return self.item not in values;
def range(self, min, max):
return self.item in range(min, max) or max==self.item or min==self.item;
def nrange(self, min, max):
return self.item not in range(min, max) and max!=self.item and min!=self.item;
1 ответ
Проблема не в том, что теги перекрываются как таковые, а в том, что вы пытаетесь использовать перекрывающиеся шрифты. Если два тега определяют свойство шрифта, будет использоваться только шрифт для тега с более высоким приоритетом.
Единственный вариант - создать третий тег для "полужирного курсива", для которого определен соответствующий шрифт. Затем, когда вы хотите выделить жирным шрифтом или курсивом, вам понадобится особый случай, чтобы правильно использовать третий тег (т. Е. Если у диапазона нет тегов, добавьте курсив, если он выделен жирным, измените его на жирный) -италик и т. д.).