Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » Tkinter: выровнять тексты в 2 виджетах [RSS Feed]

#1 Март 27, 2017 15:31:52

MrViktor
Зарегистрирован: 2017-03-09
Сообщения: 83
Репутация: +  8  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

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

Офлайн

#2 Март 27, 2017 21:40:17

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

Вобщем ИМХО вы выбрали неудачный инструмент для решения вашей задачи. Tkinter весьма убог в плане возможностей по сравнению с тем же PyQt/PySide или WxPython.
Все что мы можем, это получить индекс текущей строки, и координаты начала строки/любого символа.
Соответвенно алгоритм прост, если У-координата строки слева больше У-координаты строки справа, нудно прокрутить левую сторону вверх, иначе прокрутить вниз.Вобщето можно(нужно) также контролировать и положения конца строки(на случай если “абзац” не будет полностью вмещаться в видимую область) и в случае чего ровняться по ней. Иначе начало строки уйдет вверх, TextWidget.bbox вернет None, и правая сторона не будет пролистываться.
А можно сравнивать координаты среднего символа в строке, и тогда оно будет равняться на середину, ну както так.
Примерный код, привел ниже, он конечно еще сырой и требует доработки, но думаю общий смысл вы уловите.

 import sys
from tkinter import *
class Editor(Frame):
    # это все херня, просто бросаем виджеты на форму
    def __init__(self, master, **kwargs):
        Frame.__init__(self, master)
        if 'width' not in kwargs:
            kwargs['width'] = 30
        self.left = Text(self, **kwargs)                  #Левый тексвиджет
        self.left['font']='Monoserif 12'
        self.left.pack(side=LEFT, fill=BOTH, expand=True)
        self.right = Text(self, **kwargs)                #Правый тексвиджет
        self.right['font']='Monoserif 12'
        self.scrollbarLeft = Scrollbar(self)
        self.scrollbarLeft.pack(side=LEFT, fill=Y)
        self.left.config(yscrollcommand=self.scrollbarLeft.set)
        self.scrollbarLeft.config(command=self.left.yview)
        self.right.pack(side=LEFT, fill=BOTH, expand=True)
        self.scrollbarRight = Scrollbar(self)
        self.scrollbarRight.pack(side=RIGHT, fill=Y)
        self.right.config(yscrollcommand=self.scrollbarRight.set)
        self.scrollbarRight.config(command=self.right.yview)
        commands = '<Up>','<Down>','<Left>','<Right>'
        for command in commands:
            self.right.bind(command,self.cursorMoved)
        self.currentString = 0
        self.markedString = 0
        self.left.tag_config('bold',font='Monoserif 12 bold')
        self.right.tag_config('bold',font='Monoserif 12 bold')
    def cursorMoved(self,event):
        # курсор перемещаеться на следующую строку, подсвечивается следующая строка
        _pos = self.right.index(INSERT)
        pos = _pos.split('.')
        if self.currentString != pos[0]:
            self.currentString = pos[0]
            if self.currentString != self.markedString:
                self.left.tag_remove('bold',str(self.markedString)+'.0',str(self.markedString)+'.end')
                self.right.tag_remove('bold',str(self.markedString)+'.0',str(self.markedString)+'.end')
                self.markedString = self.currentString
                self.left.tag_add('bold',str(self.markedString)+'.0',str(self.markedString)+'.end')
                self.right.tag_add('bold',str(self.markedString)+'.0',str(self.markedString)+'.end')
        self.left.see(_pos)
        # тут начинаеться алгоритм подгонки строк
        leftStrPos = self.left.bbox(str(pos[0])+'.0') # координаты начала текущей строки справа
        rightStrPos = self.right.bbox(str(pos[0])+'.0') # координаты начала текущей строки слева
        if leftStrPos == None:  # если символ невидим bbox возвращает None
            return True
        if rightStrPos == None:
            self.left.see(str(self.markedString)+'.0')
            return True
        leftY = leftStrPos[1]   # У координата начала левой строки
        rightY = rightStrPos[1] # У координата начала правой строки
        if leftY - rightY > 0:                                # если леваяч строка ниже правой
            while leftY >= rightY+8 and self.left.yview()[1]<1: # пока левая строка ниже прапвой или пока не упремся в низ
                self.left.yview_scroll(1, 'unit')             # сдвигаем левую строку на 1 строку вверх
                leftStrPos = self.left.bbox(str(pos[0])+'.0') # обновляем У координату левой строки
                leftY = leftStrPos[1]
        else:                                                 # иначе левая строка выше
            while leftY <= rightY-8 and self.left.yview()[0]>0: # пока левая строка выше правой или пока не упремся в верх
                self.left.yview_scroll(-1, 'unit')            # сдвигаем левую строку 1 строку вниз
                leftStrPos = self.left.bbox(str(pos[0])+'.0') # обновляем У координату левой строки
                leftY = leftStrPos[1]
if __name__ == '__main__':
    root = Tk()
    mainWin = Editor(root)
    mainWin.pack(fill=BOTH, expand=True)
    text1 = '''He broke the seal and glanced over the contents.
    "Oh, come, it may prove to be something of interest, after all."
    "Not social, then?"
    "No, distinctly professional."
    "And from a noble client?"
    "One of the highest in England."
    "My dear fellow. I congratulate you."
    '''
    text1 = text1.splitlines()
    for i in range(len(text1)):
    	text1[i] = (text1[i] + ' ') * 10
    text1 = '\n'.join(text1)
    text2 = '''Он сломал печать и быстро просмотрел содержимое.
	"Э, нет, здесь всё-таки может оказаться кое-что интересное."
	"Значит, это не светское письмо?"
	"Нет, сугубо деловое."
	"От знатного клиента?"
	"Одного из самых знатных в Англии."
	"Поздравляю Вас, мой друг."
    '''
    text2 = text2.splitlines()
    for i in range(len(text2)):
    	text2[i] = (text2[i] + ' ') * 10
    text2 = '\n'.join(text2)
    text1 = text1 * 5
    text2 = text2 * 5
    mainWin.left.insert(END,text1)
    mainWin.right.insert(END,text2)
    root.mainloop()
естественно в начале-конце синхронизация хромает, потому как листать некуда.



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Отредактировано PEHDOM (Март 27, 2017 22:53:57)

Офлайн

#3 Март 27, 2017 23:06:26

vanvanov
Зарегистрирован: 2013-03-31
Сообщения: 252
Репутация: +  4  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

MrViktor
PEHDOM
Спасибо! Буду разбираться.

Офлайн

#4 Март 28, 2017 08:54:36

MrViktor
Зарегистрирован: 2017-03-09
Сообщения: 83
Репутация: +  8  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

PEHDOM
Tkinter весьма убог в плане возможностей
Почти прочли мои мысли, но так как я с ним знаком поверхностно, то не стал этого озвучивать. Данная тема меня зацепила!
Вот у меня возник вопрос, как в QEditText определить длину видимой строки и количество видимых строк?
Набросал небольшой примерчик для визуализации проблемы (во вложении), в правом QTextEdit содержится длинный текст который автоматически переносится и визуально занимает три строки.

Прикреплённый файлы:
attachment textEdit.zip (1,6 KБ)

Офлайн

#5 Март 28, 2017 11:59:49

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

В QEditText нумерация идет сквозная, не зависимо от к-ва строк и всего прочего, тоесть если у вас там текст из 1000 символов(не важно сколько у вас там строк или оно идет одной строкой) то первый символ будет иметь индекс 0 а последний 999. Перевод ствроки тоже символ. поэтому мы можем получить срез текста в видимой области, и уже с ним делать все что угодно. Подсчитывать колличество строк, символов, или символов в строке. Я прицепил обработку на событие cursorPositionChanged
при смене позиции курсора будет выводиться информация в консоль о к-ве видимых строк и их длинне.

 #!-*-coding:utf-8-*-
import sys
# import PyQt4 QtCore and QtGui modules
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import uic
(Ui_MainWindow, QMainWindow) = uic.loadUiType('textedit_test.ui')
class MainWindow(QMainWindow):
    """MainWindow inherits QMainWindow"""
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
    def countRow(self, doc):
        return doc.lineCount()
    def slot1(self):
        l1 = self.countRow(self.ui.textEdit.document())
        self.ui.label.setText('Колличество строк: %s' %l1)
    def slot2(self):
        l2 = self.countRow(self.ui.textEdit_2.document())
        self.ui.label_2.setText('Колличество строк: %s' %l2)
    def __del__(self):
        self.ui = None
def cursorMoved(widget, name):
    #определяем размер области внутри границ виджета
    visibleArea = widget.contentsRect()
    #индекс символа в левом верхнем углу, я взял +4, но чтобы почти-/полу-скрытиый текст не попадал в выборку нужно добавлять высоту шрифта.
    startPos = widget.cursorForPosition(QPoint(visibleArea.x()+4,visibleArea.y()+4)).position()
    #индекс символа в правом нижнем углу(или любой крайний символ в последней строчке) чтобы почти-/полу-скрытиый текст не попадал в выборку нужно вычитать высоту шрифта.
    endPos = widget.cursorForPosition(QPoint(visibleArea.width()-4,visibleArea.height()-4)).position()
    #получаем срез видимого текста, первод строки это тоже символ
    visibleText = widget.toPlainText()[startPos:endPos]
    #print('Visible text from position {} to position {}:\n "{}"'.format(startPos,endPos, visibleText))
    strings = visibleText.split('\n')   #разбиваем строку на подстроки по символу \n
    print('Widget {} has {} visible strings.'.format(name , len(strings)))
    result = list()
    for i, string in enumerate(strings):
        result.append('string: {}, len: {}'.format(i, len(string)))
    print(result)
#-----------------------------------------------------#
if __name__ == '__main__':
    # create application
    app = QApplication(sys.argv)
    app.setApplicationName('test_my')
    # create widget
    w = MainWindow()
    w.setWindowTitle('test_my')
    w.ui.textEdit.setText('Первая строка текста')
    w.ui.textEdit.append('Вторая строка текста')
    w.ui.textEdit_2.setText('Очень длинная строка текста, такая длинная, что не помещается в одну строку')
    doc1 = w.ui.textEdit.document()
    w.ui.label.setText('Колличество строк: %s'%w.countRow(doc1))
    doc2 = w.ui.textEdit_2.document()
    w.ui.label_2.setText('Колличество строк: %s' % w.countRow(doc2))
    w.show()
    # connection
    QObject.connect(app, SIGNAL('lastWindowClosed()'), app, SLOT('quit()'))
    textEdit2 = w.ui.textEdit_2
    textEdit2.cursorPositionChanged.connect(lambda: cursorMoved(textEdit2,'Right'))
    textEdit1 = w.ui.textEdit
    textEdit1.cursorPositionChanged.connect(lambda: cursorMoved(textEdit1,'Left'))
    # execute application
    sys.exit(app.exec_())
примерно так
 Widget Left has 2 visible strings.
['string: 0, len: 20', 'string: 1, len: 20']
Widget Right has 1 visible strings.
['string: 0, len: 75']
Exit code:  0



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Отредактировано PEHDOM (Март 28, 2017 12:03:50)

Офлайн

#6 Март 28, 2017 12:38:01

MrViktor
Зарегистрирован: 2017-03-09
Сообщения: 83
Репутация: +  8  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

PEHDOM
позиции курсора будет выводиться информация в консоль о к-ве видимых строк и их длинне.
Немного не то. В результате выводится, что в правом QTextEdit - содержится одна строка текста (по идее так и есть), но если ее длинна больше видимой области QTextEdit осуществляется перенос текста на следующую “визуальную” строку и таким образом мы видим наш текст в трех строках - так вот как вычислить сколько “визуальных” строк займет тест в QTextEdit?
Уфф… как мог объяснил

Офлайн

#7 Март 28, 2017 13:59:24

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

логика по сути таже самая, только берем начало-конец строки а не всего виджета. поменяйте cursorMoved на:

 def cursorMoved(widget, name):
    #определяем размер области внутри границ виджета
    visibleArea = widget.contentsRect()
   
    cursor = widget.cursorRect() #текущая паозиция и размер кусора
    startPos = widget.cursorForPosition(QPoint(visibleArea.x(),cursor.y())).position() # начало визуальной строки
    endPos = widget.cursorForPosition(QPoint(visibleArea.width(),cursor.y())).position() #конец визуальной строки
    string = widget.toPlainText()[startPos:endPos]
    print('visual string is:',string)
на выходе получаете визуальную строку.
вывод
 >>> 
visual string is: Очень длинная строка текста, такая длинная, что не
visual string is: помещается в одну строку

уж как подсчитать к-во строк в цикле догадаетесь?



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Отредактировано PEHDOM (Март 28, 2017 13:59:52)

Офлайн

#8 Март 28, 2017 14:31:55

MrViktor
Зарегистрирован: 2017-03-09
Сообщения: 83
Репутация: +  8  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

PEHDOM
логика по сути таже самая, только берем начало-конец строки а не всего виджета. поменяйте cursorMoved на:
Сразу не догнал, что пытались донести, глянул на вывод первого вашего примера и подумал: “Нафига так муторно получать длину текста”
А я пытаюсь высчитать из размера QTextBlock и QFont, но пока не получается.

Офлайн

#9 Март 28, 2017 14:35:08

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

MrViktor
“Нафига так муторно получать длину текста”
это не длинна текста, это длинна отображаемого текста , учитывает только то что мы видим глазами.



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Офлайн

#10 Март 28, 2017 14:47:17

MrViktor
Зарегистрирован: 2017-03-09
Сообщения: 83
Репутация: +  8  -
Профиль   Отправить e-mail  

Tkinter: выровнять тексты в 2 виджетах

PEHDOM
это не длинна текста, это длинна отображаемого текста , учитывает только то что мы видим глазами.
Теперь-то я это понял, уже первый пример детальней разобрал
Вот натолкнули вы меня в нужное русло своим кодом, вот и более правильное решение моего вопроса

 textEdit.document().begin().layout().lineCount()
В лейаутах вся сила брат

Отредактировано MrViktor (Март 28, 2017 15:16:24)

Офлайн

  • Начало
  • » GUI
  • » Tkinter: выровнять тексты в 2 виджетах[RSS Feed]

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version