#python #tkinter #richtext #italics
#python #tkinter #richtext #курсив
Вопрос:
У меня есть программа 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:
Проблема не в перекрывающихся тегах как таковых, а в том, что вы пытаетесь использовать перекрывающиеся шрифты. Если два тега определяют свойство шрифта, будет использоваться только шрифт для тега с более высоким приоритетом.
Единственный вариант — создать третий тег для «bold-italic», в котором определен соответствующий шрифт. Затем, когда вы хотите выделить что-то жирным шрифтом или курсивом, вам понадобится специальный регистр для обработки использования третьего тега соответствующим образом (т. Е.: если в диапазоне нет тегов, добавьте курсив, если он выделен жирным шрифтом, измените его на bold-italic и т. Д.).
Комментарии:
1. Потрясающе. Это сработало отлично. Я опубликовал рабочий код в редактировании моего вопроса.
2. Однако я не избавился от тегов в одном стиле и не заменил их. Я просто сделал теги с несколькими стилями более приоритетными и разрешил использовать только один тег с несколькими стилями за раз. Это решение состоит в том, чтобы упростить преобразование в HTML, если / когда я решу это сделать, поскольку я могу просто оставить все как есть и анализировать только теги в одном стиле.
3. @user2962794: имеет смысл. Хорошая работа!