-1

I want to be able to select a piece of text in a non-custom way, the way you click on a website link in the middle of a text ( "Hello my name is www.google.com", www.google.com isn't highlighted when you press on it. When you press on it, it takes you to the website the same i want with my text. I want to make it like " hello my name is Jeff, i live in London, i eat potato every day" i want the user to be able to select each piece of sentence alone ("hello my name is Jeff,") alone, ("i live in London,") alone, so when the user moves the mouse cursor on a sentence it goes highlighted (like being ready to be selected) and after that i want to add some functionality to it.

Here is a similar project check the upper text, not the lower text and how it reacts with the mouse.

http://quran.ksu.edu.sa/tafseer/tabary/sura10-aya18.html

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Said Amir
  • 36
  • 6
  • I do not understand you, do you want that when you click on a word it must be selected? – eyllanesc May 29 '19 at 16:24
  • sorry if my explanation was bad.... i want to be able to select a word ( or a sequence of words ) instead of highlighting them .... and click on them the way you click on external link ..... www.google.com .... and be able to link that to some functions ( like: pushbutton.clicked.connect(function)).... the whole program is like a book and i want to be able to make further explanation to a sentence ... so when people click on it ... i want a new function to happen ( for example a new text appear to translate that sentence in another language ) – Said Amir May 29 '19 at 18:21
  • Okay, 1) the highlighting is part of the action of selecting 2) According to you I understand first you want to select certain text and then if you click on a selected element a signal is triggered that you can connect to a function that receives the selected text. I am right? – eyllanesc May 29 '19 at 18:25
  • On the other hand if you see that your explanation was not clear in your publication I invite you to edit it and improve it – eyllanesc May 29 '19 at 18:26
  • and yeah i will edit my Question once i realize that it's even a good question first ... maybe my question is just not in qt so i may delete it. Thanks so much for replying to me – Said Amir May 29 '19 at 19:03
  • As you indicate the selection implies highlighted, you can not separate it, the highlight is the way the application informs the user that something has been selected. Or what is the procedure that the user has to do for the selection ?, my idea of procedure is: 1) the user selects text, for example the user selects the text "Coding is easy" so it will be highlighted and then the user will click somewhere in the highlighted region which will trigger a signal that sends the highlighted(selected) information. As you realize the selection and highlighting are synonyms in this context – eyllanesc May 29 '19 at 19:07
  • Do the editing now to improve it, if you think that later you can improve it then you can do it too. Currently your question, just based on the publication, is off-topic because it is unclear. Read [ask] and review the [tour] – eyllanesc May 29 '19 at 19:08
  • Yes... exactly ... here is some link to how i want it to look like its in Arabic tho ... http://quran.ksu.edu.sa/tafseer/tabary/sura10-aya18.html – Said Amir May 29 '19 at 19:11
  • Read the links I have provided, edit your question (give a clearer explanation as we discuss in the comments, also add image that always helps), and when you do just try to help you. – eyllanesc May 29 '19 at 19:15
  • if it's still confusing i dont know what to do more, the website explains all i want to say in 2 seconds – Said Amir May 29 '19 at 19:58

1 Answers1

0

Here is a start. In the code below, the clickable phrases in the text are translated to a ordered list of text cursors. When the mouse is moved (i.e. when a mouseMove event occurs) the position in the text under the mouse pointer is compared to this list of text cursors. If this position falls within the bounds of a cursor in the list, the corresponding phrase is highlighted by setting the extra selections of the browser. When a mouse button is clicked and the mouse is over a clickable phrase, a signal is emitted containing a text cursor corresponding to the selected phrase in the text. This signal can then be connected to a slot of another widget for further actions (like opening a message box like in the example below).

from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QTextBrowser, QApplication, QMessageBox
from PyQt5.QtGui import QFont, QSyntaxHighlighter, QTextCharFormat, QTextCursor

import bisect, re


class MyHighlighter(QSyntaxHighlighter):
    def __init__(self, keywords, parent):
        super().__init__(parent)
        self.keywords = keywords

    def highlightBlock(self, block):
        if not self.keywords:
            return
        charFormat = QTextCharFormat()
        charFormat.setFontWeight(QFont.Bold)
        charFormat.setForeground(Qt.darkMagenta)
        regex = re.compile('|'.join(self.keywords), re.IGNORECASE)
        result = regex.search(block, 0)
        while result:
            self.setFormat(result.start(),result.end()-result.start(), charFormat)
            result = regex.search(block, result.end())


class MyBrowser(QTextBrowser):
    text_clicked = pyqtSignal("QTextCursor")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setMouseTracking(True)
        self.setTextInteractionFlags(Qt.NoTextInteraction)

        # self.phrases contains all phrases that should be clickable.
        self.phrases = set()
        self.cursors = []

        # ExtraSelection object for highlighting phrases under the mouse cursor
        self.selection = QTextBrowser.ExtraSelection()
        self.selection.format.setBackground(Qt.blue)
        self.selection.format.setForeground(Qt.white)
        self.selected_cursor = None

        # custom highlighter for highlighting all phrases
        self.highlighter = MyHighlighter(self.phrases, self)
        self.document().contentsChange.connect(self.text_has_changed)

    @property
    def selected_cursor(self):
        return None if self.selection.cursor == QTextCursor() else self.selection.cursor

    @selected_cursor.setter
    def selected_cursor(self, cursor):
        if cursor is None:
            cursor = QTextCursor()
        if self.selection.cursor != cursor:
            self.selection.cursor = cursor
            self.setExtraSelections([self.selection])

    def mouseMoveEvent(self, event):
        ''' Update currently selected cursor '''
        cursor = self.cursorForPosition(event.pos())
        self.selected_cursor = self.find_selected_cursor(cursor)

    def mouseReleaseEvent(self, event):
        ''' Emit self.selected_cursor signal when currently hovering over selectable phrase'''
        if self.selected_cursor:
            self.text_clicked.emit(self.selected_cursor)
            self.selected_cursor = None

    def add_phrase(self, phrase):
        ''' Add phrase to set of phrases and update list of text cursors'''
        if phrase not in self.phrases:
            self.phrases.add(phrase)
            self.find_cursors(phrase)
            self.highlighter.rehighlight()

    def find_cursors(self, phrase):
        ''' Find all occurrences of phrase in the current document and add corresponding text cursor
        to self.cursors '''
        if not phrase:
            return
        self.moveCursor(self.textCursor().Start)
        while self.find(phrase):
            cursor = self.textCursor()
            bisect.insort(self.cursors, cursor)
        self.moveCursor(self.textCursor().Start)

    def find_selected_cursor(self, cursor):
        ''' return text cursor corresponding to current mouse position or None if mouse not currently
        over selectable phrase'''
        position = cursor.position()
        index = bisect.bisect(self.cursors, cursor)
        if index < len(self.cursors) and self.cursors[index].anchor() <= position:
            return self.cursors[index]
        return None

    def text_has_changed(self):
        self.cursors.clear()
        self.selected_cursor = None
        for phrase in self.phrases:
            self.find_cursors(phrase)
            self.highlighter.rehighlight()


def text_message(widget):
    def inner(cursor):
        text = cursor.selectedText()
        pos = cursor.selectionStart()
        QMessageBox.information(widget, 'Information',
                f'You have clicked on the phrase <b>{text}</b><br>'
                f'which starts at position {pos} in the text')
    return inner


if __name__=="__main__":
    app = QApplication([])
    window = MyBrowser()
    window.resize(400,300)
    information = text_message(window)

    text = '''
    <h1>Title</h1>
    <p>This is a random text with. The following words are highlighted</p>
    <ul>
    <li>keyword1</li>
    <li>keyword2</li>
    </ul>
    <p>Click on either keyword1 or keyword2 to get more info. 
    '''

    window.add_phrase('keyword1')
    window.add_phrase('keyword2')
    window.setText(text)
    window.text_clicked.connect(information)
    window.show()
    app.exec()
Heike
  • 24,102
  • 2
  • 31
  • 45
  • You are The Man ... thanks a lot ... that's exactly what i wanted. if i could upvote you with 1k i would do it ... but unfortunately i can't do visible upvotes cuz my reputation is less than 15 ( i am new to this site ) ..... again thanks a lot you saved my day. – Said Amir Jun 01 '19 at 16:33