0

I'd like to make part of the text of a label clickable, like an inline hyperlink on a website. I know how to make an individual label clickable, but I'm not sure how to only make part of the label clickable and still maintain a consistent format.

I've placed the code for my first attempt below and included an image of the output.

The two issues I see are the noticeable space between the labels (which even a QStretchItem at the end doesn't fix) and the issues with word wrapping.

Any help would be greatly appreciated. Thank you!

from PyQt5.QtWidgets import *

app = QApplication([])

class MainWindow(QWidget):    
  def __init__(self, *args, **kwargs):
    super(MainWindow, self).__init__(*args, **kwargs)

    self.setWindowTitle('Title')
    self.setGeometry(1200, 200, 350, 500)
    self.layout = QVBoxLayout()
    self.setLayout(self.layout)

    # Dummy list to print
    place_list = { '2000': 'An event happened.', 
                  '2005': 'An event at {this place} happened long ago.', 
                  '2010': 'Another event happened at {a different place}, but it was not fun.' }
    # Initialize Grid of Notes
    grid = QGridLayout()

    # Create Headers for each column
    grid.addWidget(QLabel('Date'), 0, 0)
    grid.addWidget(QLabel('Note'), 0, 1)

    index = 1

    # Iterate through each entry in place_list
    for year in place_list:
      # Add index of entry (by year)
      grid.addWidget(QLabel(year), index, 0)

      # Get text of entry
      note = place_list[year]

      # Look for "{}" to indicate link
      if '{' in note:
        # Get location of link within the entry
        start = note.find('{')
        end = note.find('}')

        # Create a label for the text before the link
        lab_1 = QLabel(note[:start])
        lab_1.setWordWrap(True)

        # Create a label for the link
        # NOTE: It's a QLabel for formatting purposes only
        lab_2 = QLabel(note[start+1:end])
        lab_2.setWordWrap(True)

        # Create a label for the text after the link
        lab_3 = QLabel(note[end+1:])
        lab_3.setWordWrap(True)

        # Combine the labels in one layout
        note_lab = QHBoxLayout()
        note_lab.addWidget(lab_1)
        note_lab.addWidget(lab_2)
        note_lab.addWidget(lab_3)

        # Add the layout as the entry
        grid.addLayout(note_lab, index, 1)

      else:
        # Create the label for the whole entry if no link indicator is found
        note_lab = QLabel(note)
        note_lab.setWordWrap(True)
        grid.addWidget(note_lab, index, 1)

    # Go to next row in grid
    index += 1

    self.layout.addLayout(grid)

window = MainWindow()

window.show()
app.exec_()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
kairom13
  • 7
  • 4
  • might be overkill, but perhaps use a QWebEngineView and shrink it to label size. Set the HTML to the label text with a anchor tag around the clickable section. Then use it to call a python func, via JS, using a QWebChannel as outlined in this answer: https://stackoverflow.com/a/45362917 – Griffin May 22 '20 at 21:41

1 Answers1

0

The best solution I believe is to subclass QLabel and override the mousePressEvent method.

def mousePressEvent(event):
    # event.pos() or .x() and .y() to find the position of the click.

If you create a QRect in the area that you want in the initialization of your custom QLabel, you can easily check if the click is inside the rectangle by using the QRect.contains() method as well.

Other useful methods for this would be mouseReleaseEvent and mouseDoubleClickEvent.

And in general, when you are adding/changing functionality to widgets, look to subclass first.

Nicholas
  • 166
  • 3
  • Normally, I'd subclass the QLabel and override the event filter. Are you suggesting I add a QRect as well, over the part of the label I want to be clickable, and override its event filter? That's intriguing. My only issue is how to position the QRect properly without it also being very complicated, usually the size of the label is done automatically. – kairom13 May 22 '20 at 21:18
  • Sorry for the delayed reply, but I meant that you could subclass QLabel, and have a QRect variable that can be used in the mousePressEvent function to check if the event position was in the correct spot. And unfortunately, yes I do not know of a good way of accurately placing the rectangle especially if you have wordwrap on, or if you are using multiple fonts. So that will probably have to be done manually for such labels. Otherwise you can try to guess the position of the rectangle by just counting characters. – Nicholas May 26 '20 at 17:36