Найти - Пользователи
Полная версия: Пролистывание экрана в Tkinter
Начало » GUI » Пролистывание экрана в Tkinter
1 2
vanvanov
Добрый день.

У меня есть задача: обеспечить навигацию в окне Tkinter по тексту. При этом стрелка влево выделяет слово, стоящее до текущего, соответственно, стрелка вправо выделяет слово, стоящее после текущего. Вот упрощенный код:
#!/usr/bin/python3
import tkinter as tk
elem='тест'
max_elems=1001
text=(elem+' ')*max_elems
text=text[:-1]
pos_lst=[]
pos2=-2
for i in range(max_elems):
	pos1=pos2+2
	pos2=pos1+len(elem)-1
	pos_lst+=[[pos1,pos2]]
def move_right(event):
	if res[0] < len(pos_lst)-1:
		res[0]+=1
		select_term()
		
def move_left(event):
	if res[0] > 0:
		res[0]-=1
		select_term()
def select_term():
	if len(pos_lst) > 0:
		pos1=pos_lst[res[0]][0]
		pos1='1.'+str(pos1)
		pos2=pos_lst[res[0]][1]
		pos2='1.'+str(pos2+1)
		txt.tag_remove('cur_term','1.0','end')
		txt.tag_add('cur_term',pos1,pos2)
		txt.mark_set('insert',pos2)
		txt.tag_config('cur_term',background='cyan',font='Sans 14')
		txt.yview('insert')
root=tk.Tk()
res=[0]
frame=tk.Frame(root)
frame.pack()
txt=tk.Text(frame)
txt.focus_set()
txt.insert('end',text)
txt.mark_set('insert','1.0')
txt.yview('1.0')
txt.config(state='disabled')
txt.pack(expand=1)
select_term()
txt.bind('<Left>',move_left)
txt.bind('<Right>',move_right)
root.wait_window()
root.mainloop()
Однако, если текста много, и он выходит за рамки окна Tkinter, то выделение оказывается вне поля видимости (если не вставлять ‘insert’ и далее не делать yview). Но, если все же делать yview, то получается не совсем удобно: 1-й строчкой становится текущее выделение. А мне хотелось бы, чтобы выделять текст можно было в границах всего экрана, затем, если выделение оказывается вне границ экрана, то переходить на 1-ю строчку нового текста и разрешать выделять текст в рамках экрана, т.е. чтобы это больше напоминало навигацию в текстовом редакторе.
Я так понимаю, задача решается, если определить, сколько символов помещается на экране. Но как это определить?
4kpt_III
Если я правильно понял, то Вам необходимо определить на сколько проматывать виджет при переходе на следующую строчку?

P.S. Для упрощения можно еще рассмотреть вариант с отображением скрытой строки. В этом случае просто используем метод .see(index)…
vanvanov
Если я правильно понял, то Вам необходимо определить на сколько проматывать виджет при переходе на следующую строчку?
В принципе, да. Я не могу понять, как определить, сколько строк/символов умещается на экране.
4kpt_III
P.S. Для упрощения можно еще рассмотреть вариант с отображением скрытой строки. В этом случае просто используем метод .see(index)…
Это в какой-то мере решает задачу (хотя на очень длинных строках see все равно уходит за пределы экрана, пока не понял, почему). Но мне хотелось бы, чтобы смещался не экран/текст, а выделение, а экран сменялся бы новым экраном только если выделение оказывается за границами видимости. Т.е., например, если на экране помещается 45 строк, то пусть выделение скачет между этими строками, а сам текст не смещается. А если вдруг выделение оказывается на строке 46, то пусть на экран выводится новый текст со строками 46-91 и выделение скачет уже между ними.
4kpt_III
Понял. Тогда придется просчитывать. По высоте: всегда фиксированное значение. Или то, которое Вы внесете сами. Или фиксированное системой. Явно предложить ничего не могу. Нужно писать ручками. Механизм скользящего окна. А насчет размеров. Нужно смотреть в сторону
 text["height"]
(предварительно сделав update()). Если возвращает не в символах, а в пикселях, то придется высчитывать самостоятельно.

Вообще адский оверхед, если честно. Вот что странно. Среди методов не вижу метода, который показывал бы - находится индекс в видимой зоне или нет. Может уже старею
vanvanov
А можно ли как-то вычислить координаты x, y в пикселях по координатам Tkinter типа ‘5.10’? Нашел event.x, event.y, но это касается мышки.
4kpt_III
Можно глянуть в сторону .bbox(index). Но предварительно не забудьте обновить характеристики Text атрибутом .update_idletasks().
4kpt_III
Вот кусочек кода. Он все пояснит. Ну только это если я Вас правильно понял

import Tkinter
#
root = Tkinter.Tk()
#
def call(event):
    event.widget.update_idletasks()
    print event.widget.bbox("current")
#
text = Tkinter.Text(root, width=20, height=20)
text.pack()
text.bind("<Return>", call)
#
root.mainloop()

P.S. Будут вопросы - пишите…
vanvanov
Подскажите, пожалуйста, как пролистать текст в виджете Text так, чтобы нужная строка/метка оказалась первой строкой виджета. Пробовал txt.yview('80.0'), но нужная строка оказывается пятой.

И еще, где мне найти дельные примеры, например, по yview, scroll, moveto, dragto, scanto? Каждый раз рою гугл, и там в 121 раз цитируется офиц. руководство, которое все равно без конкретных примеров малополезно.
4kpt_III
Я явно так только один раз высчитывал. Вообще в 90% случаев мне хватало какого-нибудь метода типа .see(pos). Могу поискать, если уж очень надо…
vanvanov
Мне достаточно будет понять, как сделать произвольную строку первой без удаления или разделения текста. Остальное я уже накостылял.

Возможно, я плохо знаю tkinter, но у меня реально больше всего времени уходит на интерфейс. Я выбрал tkinter, потому что прочитал, что у него самая подробная документация и его не нужно доустанавливать, что он хорош для начинающих и т.д. и т.п.

А что в итоге мы имеем? Да, документация подробная, но примеры извольте искать на stackoverflow (если еще найдете). На никсах интерфейс еще куда ни шло, а на винде лучше этого не видеть. Убогая система координат в виде ‘1.2’, ‘2.5’ и пр. Если мне понадобится манипулировать текстом, придется писать костыли, чтобы это расшифровывать (и костыли весьма и весьма объемные). Привязка к главному окну. А теперь еще и выясняется, что невозможно определить, находится ли текст в виджете Text в поле видимости (!) и для этого нужны очередные костыли, а некоторые методы как будто живут сами по себе и срабатывают непредсказуемым образом (see, yview).

Извините, накипело.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB