Seite 1 von 1

Integration von Hyperlinks in QTableView

Verfasst: Montag 1. Mai 2023, 10:32
von Nobuddy
Hallo zusammen,
ich bin dabei eine Adressverwaltung zu erstellen.
Meine Adressdaten verwalte ich in einer json-Datei.
Zur Ausgabe und Verwaltung, verwende ich QTableView.
Bei EMail-Adressen verwende ich Hyperlinks.

Ich poste hier mal meinen Code, der lauffähig ist.

Code: Alles auswählen

import os
import sys
import copy
import operator  # used for sorting
from PyQt5.QtCore import (
	Qt,
	QAbstractTableModel,
	)
from PyQt5.QtWidgets import (
	QApplication,
	QMainWindow,
	QTableView,
	QAbstractItemView,
	QAbstractScrollArea,
	QLabel,
	QWidget,
	)
from PyQt5.QtGui import  (
	QStandardItemModel,
	)

TableStyleSheet = """
	QWidget{
		selection-background-color: lightgreen;
		selection-color: black;
		background-color: lightgrey;
		color: black;
		border: none;
		}
	QHeaderView::section {
		background-color: darkgrey;
		color: black;
		border: 1px solid #6c6c6c;
		padding: 10px;
		}
	QTableView::section {
		background-color: green;
		border-radius: 14px;
		border: 1px solid #6c6c6c;
		padding: 10px;
		}
	"""

jsonData = {
	"address": {
		"29614003": {
			"area": "Familie",
			"name": "Mustrermann",
			"first name": "Max",
			"name addition": "",
			"street": "Musterweg 1",
			"postal code": "47111",
			"location": "Musterhausen",
			"province": "",
			"country": "",
			"index": "29614003"
		}
	},
	"email": {
		"29614003": {
			"max": "max@mustermann.de",
			"maxine": "maxine@mustermann.de",
			"t0": "t0@mustermann.de",
			"t1": "t1@mustermann.de",
			"t2": "t2@mustermann.de",
			"t3": "t3@mustermann.de",
			"t4": "t4@mustermann.de"
		}
	}
}

class HyperlinkLabel(QLabel):
	def __init__(self, parent=None):
		super().__init__()
		self.setOpenExternalLinks(True)
		self.setParent(parent)

class Window(QMainWindow):
	def __init__(self):
		super().__init__()

		self.header = ['name', 'email']
		self.data = [[name.title(), value]
			for index, _dict_ in jsonData['email'].items()
			for name, value in _dict_.items()]
		self.object = View(parent=self)
		self.setCentralWidget(self.object)
		self.show()


class View(QTableView):

	def __init__(self, parent=None):
		super().__init__(parent=parent)

		self.setAttribute(Qt.WA_DeleteOnClose)
		self.installEventFilter(self)
		self.setStyleSheet(TableStyleSheet)
		self.parent = parent
		self.data = sorted(parent.data)
		self.header = parent.header
		self.rowIndex = 0
		# model
		self.model = Model(self.data, self.header)
		self.setModel(self.model)
		value = self.model.data

		self.ui()

	def setHyperlink(self):
		colPos = self.header.index('email')
		hhHeight = self.horizontalHeader().sizeHint().height()
		vhWidth = self.verticalHeader().sizeHint().width()
		xPos = self.columnViewportPosition(colPos) + vhWidth
		linkTemplate = '<a href={0}>{1}</a>'
		for index, _dict_ in jsonData['email'].items():
			for i, name in enumerate(_dict_):
				value = _dict_[name]
				# delete widget in cell
				index = self.model.index(i, colPos)
				# if change email, delete widget
				self.setIndexWidget(index, None)
				# create hyperlink to email with html format
				hyperlink = HyperlinkLabel(self)
				hyperlink.setText(linkTemplate.format('mailto:', value))
				# move hylerlink to position xy
				try:
					yPos += hhHeight
				except UnboundLocalError:
					yPos = hhHeight + round(self.rowHeight(i)/2)
				hyperlink.move(xPos, yPos)
				# set index widget
				self.setIndexWidget(index, hyperlink)

	def ui(self):
		self.setHyperlink()
		self.update_table()
		self.headerSize()

	def updateGeometryAsync(self):
		QTimer.singleShot(0, self.updateGeometry)

	def headerSize(self):
		self.setSizeAdjustPolicy(
			QAbstractScrollArea.AdjustToContentsOnFirstShow)
		self.resizeColumnsToContents()
		self.resizeRowsToContents()
		self.setWordWrap(True)

	def update_table(self):
		# ... when a row header label changes and makes the
		# width of the vertical header change too
		self.model.headerDataChanged.connect(self.updateGeometryAsync)
		self.setModel(self.model)
		# enable sorting
		self.setSortingEnabled(True)
		self.setSelectionBehavior(QAbstractItemView.SelectRows)
		self.setSelectionMode(QAbstractItemView.SingleSelection)
		self.setFocus()
		self.selectRow(self.rowIndex)


class Model(QAbstractTableModel):
	"""
	Build table
	"""

	def __init__(self, data, header, *args):
		QAbstractTableModel.__init__(self, *args)
		self.data = data
		self.header = header

	def rowCount(self, widget):
		try:
			return len(self.data)
		except TypeError:
			return 0

	def columnCount(self, widget):
		try:
			return len(self.data[0])
		except (IndexError, TypeError):
			return len(self.header)

	def data(self, index, role):
		if not index.isValid() or role != Qt.DisplayRole:
			return None
		return self.data[index.row()][index.column()]

	def headerData(self, col, orientation, role):
		if (orientation == Qt.Horizontal
				and role == Qt.DisplayRole):
			return self.header[col]
		elif (orientation == Qt.Vertical
				and role == Qt.DisplayRole):
			return col
		return None

	def sort(self, col, order):
		self.layoutAboutToBeChanged.emit()
		try:
			self.data = sorted(self.data, key=operator.itemgetter(col))
		except IndexError:
			pass
		if order == Qt.DescendingOrder:
			self.data #.reverse()
		self.layoutChanged.emit()

	def output_list(self):
		return self.data

if __name__ == '__main__':
	app = QApplication(sys.argv)
	Window()
	sys.exit(app.exec_())
Hoffe, Ihr konntet meinen Code testen!
Es gibt 2 Punkte, die ich vorrangig behandeln möchte.

Punkt 1:
Callback Funktion die es ermöglicht, bei Änderung der EMail-Adresse, den Hyperlink zu aktualisieren.

Punkt 2:
Beim klicken auf den Hyperlink der EMail-Adresse, öffnet sich dann das Standard EMail-Programm bei mir. Jedoch wird nicht die ausgewählte Ziel-Adresse in meinem EMail-Programm Thunderbird angezeigt.

Wünsche Euch einen schönen 1.Mai !
Grüße Nobuddy

Re: Integration von Hyperlinks in QTableView

Verfasst: Montag 1. Mai 2023, 12:38
von Sirius3
Eingerückt wird in Python immer mit 4 Leerzeichen pro Ebene und nicht mit Tabs.
Variablennamen schreibt man komplett klein. Sie enthalten keine cryptischen Abkürzungen und sind sprechend. _dict_ ist ein Beispiel für einen komischen total nichts-sagenden Namen.
UnboundLocalError ist immer ein Programmierfehler und es macht nie Sinn den abzufangen.
HTML ist kein einfacher Text, deshalb muss man jeden variablen Text passend Escapen, idealerweise indem man ein passendes Template-System benutzt. Bei "mailto:" fehlt die eMail-Adresse.

Re: Integration von Hyperlinks in QTableView

Verfasst: Montag 1. Mai 2023, 17:03
von Nobuddy
Hallo Sirius3, Danke für Dein Feedback, werde dies abarbeiten!
Beim Code schreiben, sind für mich Tabs einfacher zu handel.
Wenn der Code fertig ist, reicht ja wenn ich dann Tabs in Leerzeichen umwandle.
Grüße Nobuddy

Re: Integration von Hyperlinks in QTableView

Verfasst: Montag 1. Mai 2023, 21:29
von Sirius3
Das Argument mit den Tabs ist schwach, denn jeder Editor behandelt Tabs und Spaces für den Nutzer quasi identisch. Beim drücken der Tabtaste werden so viele Spaces eingefügt, wie als Tabbreite eingestellt. Und wenn Du das eh konvertieren möchtest, dann mach das doch gleich richtig.

Re: Integration von Hyperlinks in QTableView

Verfasst: Dienstag 2. Mai 2023, 08:37
von Nobuddy
Sirius3 hat geschrieben: Montag 1. Mai 2023, 12:38 Eingerückt wird in Python immer mit 4 Leerzeichen pro Ebene und nicht mit Tabs.
erledigt!
Sirius3 hat geschrieben: Montag 1. Mai 2023, 12:38 Variablennamen schreibt man komplett klein. Sie enthalten keine cryptischen Abkürzungen und sind sprechend. _dict_ ist ein Beispiel für einen komischen total nichts-sagenden Namen.
Bei _dict_, fiel mir nichts besseres ein, so dass ich das Datenformat für den Namen verwendet habe. Der letzte Unterstrich ist nicht zulässig. Vielleicht hast Du dazu einen besseren Vorschlag?
Variablennamen sollen sprechend sein, das weiß ich ... daran muss ich wohl noch arbeiten.
Bei Variablennamen die aus mehreren Wörten zusammengesetzt sind, verwende ich statt der Trennung durch einen Unterstrich, den ersten Buchstaben in Großschreibung. Dadurch wird die Zeichenlänge auch ein wenig kürzer.
Habe dazu gegoogelt, habe dazu aber nichts eindeutiges gefunden.
Sirius3 hat geschrieben: Montag 1. Mai 2023, 12:38 UnboundLocalError ist immer ein Programmierfehler und es macht nie Sinn den abzufangen.
So sollte das richtig sein:

Code: Alles auswählen

                if i == 0:
                    yPos = hhHeight + round(self.rowHeight(i)/2)
                else:
                    yPos += hhHeight
Sirius3 hat geschrieben: Montag 1. Mai 2023, 12:38 HTML ist kein einfacher Text, deshalb muss man jeden variablen Text passend Escapen, idealerweise indem man ein passendes Template-System benutzt.
Bezieht sich das auf 'mailto:', oder meinst Du etwas anderes zu 'passendes Template-System'?
Sirius3 hat geschrieben: Montag 1. Mai 2023, 12:38 Bei "mailto:" fehlt die eMail-Adresse.
Das funktioniert nun auch:

Code: Alles auswählen

                hyperlink.setText(linkTemplate.format(
                    'mailto:{}'.format(value), value))

Re: Integration von Hyperlinks in QTableView

Verfasst: Dienstag 2. Mai 2023, 10:20
von Sirius3
Funktionieren ist halt so eine Sache. Dass HTML kein einfacher Text ist, sondern eine Auszeichnungsspache bezieht sich natürlich auf das gesamte HTML.
Kann man natürlich auch händisch machen:

Code: Alles auswählen

link_html = '<a href="mailto:{0}">{0}</a>'.format(html.escape(email_address, quote=True))

Re: Integration von Hyperlinks in QTableView

Verfasst: Dienstag 2. Mai 2023, 14:13
von Nobuddy
Danke!
Verwende es so:

Code: Alles auswählen

	def linkHTML(self, mail):
		return '<a href="mailto:{0}">{0}</a>'.format(
			html.escape(mail, quote=True))
Kann man das auch mit Telefonnummern anwenden, so dass im besten Falle ein Telefon damit koppelt?

Grüße Nobuddy

Re: Integration von Hyperlinks in QTableView

Verfasst: Dienstag 2. Mai 2023, 19:17
von Sirius3
Und schon wieder mit Tabs eingerückt.
Methoden schreibt man komplett klein und benennt sie nach Tätigkeiten, also ›generate_html_link‹.
Das Argument ist keine Mail, sondern eine eMail-Adresse oder ein Empfänger, oder eine Empfängeraddresse.
Natürlich kann man auch Telefonnummern ansprechen, dafür gibt es das tel:-Protokoll.

Re: Integration von Hyperlinks in QTableView

Verfasst: Mittwoch 3. Mai 2023, 10:37
von Nobuddy
Hallo Sirius3,
versuche mich zu bessern :wink:

Code: Alles auswählen

def generate_html_link(self, email_address):
    return '<a href="mailto:{0}">{0}</a>'.format(
        html.escape(email_address, quote=True))
Danke für Deine Erklärung, das hilft mir sehr!

Das mit Telefonnummern, werde ich zu einem späteren Zeitpunkt anpacken.
Als nächstes, möchte ich versuchen in einem QTextEdit Feld, einen Link der EMail-Adresse zu integrieren.

Re: Integration von Hyperlinks in QTableView

Verfasst: Donnerstag 4. Mai 2023, 08:52
von Nobuddy
Mit QTextBrowser, sieht dies so aus:

Code: Alles auswählen

import sys
import html
from PyQt5.QtCore import (
    Qt,
    )
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QTextBrowser,
    QLabel,
    )
from PyQt5.QtGui import  (
    QColor,
    QTextBlockFormat,
    QTextCursor,
    )

TableStyleSheet = """
    QWidget{
        selection-background-color: lightgreen;
        selection-color: black;
        background-color: lightgrey;
        color: black;
        border: none;
        }
    QHeaderView::section {
        background-color: darkgrey;
        color: black;
        border: 1px solid #6c6c6c;
        padding: 10px;
        }
    QTableView::section {
        background-color: green;
        border-radius: 14px;
        border: 1px solid #6c6c6c;
        padding: 10px;
        }
    """

jsonData = {
    "address": {
        "29614003": {
            "area": "Familie",
            "name": "Mustrermann",
            "first name": "Max",
            "name addition": "",
            "street": "Musterweg 1",
            "postal code": "47111",
            "location": "Musterhausen",
            "province": "",
            "country": "",
            "index": "29614003"
        }
    },
    "email": {
        "29614003": {
            "max": "max@mustermann.de",
            "maxine": "maxine@mustermann.de",
            "t0": "t0@mustermann.de",
            "t1": "t1@mustermann.de",
            "t2": "t2@mustermann.de",
            "t3": "t3@mustermann.de",
            "t4": "t4@mustermann.de"
        }
    }
}

class HyperlinkLabel(QLabel):
    def __init__(self, parent=None):
        super().__init__()
        self.setOpenExternalLinks(True)
        self.setParent(parent)

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.header = ['name', 'email']
        self.data = [[name.title(), value]
            for index, d in jsonData['email'].items()
            for name, value in d.items()]

        editor = QTextBrowser()
        editor.setFontFamily('Monospace')
        editor.setOpenExternalLinks(True)

        cursor = editor.textCursor()
        cursor.clearSelection()
        cursor.select(QTextCursor.Document)
        editor.setTextCursor(cursor)

        blockFmt = QTextBlockFormat()
        blockFmt.setLineHeight(150, QTextBlockFormat.ProportionalHeight)
        cursor.mergeBlockFormat(blockFmt)

        fmt = cursor.charFormat()
        fmt.setForeground(QColor('blue'))

        max_distance = max([len(row[0]) for row in self.data])

        for row in self.data:
            for i, value in enumerate(row):
                if self.header[i] == 'email':
                    cursor.insertHtml(self.generate_html_link(value))
                else:
                    spaces = max_distance - len(value)
                    space = 4
                    if spaces > 0:
                        space += 8
                    cursor.setPosition(0)
                    cursor.insertText('{}{}'.format(value, space * ' '))
                    cursor.setPosition(
                        max_distance + spaces + 4, QTextCursor.KeepAnchor)
            cursor.insertText('\n')
        editor.setTextCursor(cursor)
        self.setCentralWidget(editor)

    def generate_html_link(self, mail):
        return '<a href="mailto:{0}">{0}</a>'.format(
            html.escape(mail, quote=True))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

Re: Integration von Hyperlinks in QTableView

Verfasst: Freitag 5. Mai 2023, 09:30
von Nobuddy
Was mir aufgefallen ist, dass die Ausgabe rekursiv sortiert ist.
Wie kann ich das beheben, bzw. wo liegt der Fehler im Code?

Grüße Nobuddy

Re: Integration von Hyperlinks in QTableView

Verfasst: Freitag 5. Mai 2023, 09:50
von Sirius3
Was meinst Du mit "rekursiv sortiert"?

Re: Integration von Hyperlinks in QTableView

Verfasst: Freitag 5. Mai 2023, 09:58
von Nobuddy

Re: Integration von Hyperlinks in QTableView

Verfasst: Freitag 5. Mai 2023, 10:09
von Nobuddy
recursive ist falsch, meinte reverse.

Re: Integration von Hyperlinks in QTableView

Verfasst: Freitag 5. Mai 2023, 10:33
von Nobuddy
Poste hier den aktuellen Code:

Code: Alles auswählen

import sys
import html
from PyQt5.QtCore import (
    Qt,
    )
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QTextBrowser,
    QLabel,
    )
from PyQt5.QtGui import  (
    QColor,
    QPalette,
    QFont,
    QTextBlockFormat,
    QTextOption,
    QTextCursor,
    )

jsonData = {
    "address": {
        "29614003": {
            "area": "Familie",
            "name": "Mustrermann",
            "first name": "Max",
            "name addition": "",
            "street": "Musterweg 1",
            "postal code": "47111",
            "location": "Musterhausen",
            "province": "",
            "country": "",
            "index": "29614003"
        }
    },
    "email": {
        "29614003": {
            "max": "max@mustermann.de",
            "maxine": "maxine@mustermann.de",
            "t0": "t0@mustermann.de",
            "t1": "t1@mustermann.de",
            "t2": "t2@mustermann.de",
            "t3": "t3@mustermann.de",
            "t4": "t4@mustermann.de"
        }
    }
}

class HyperlinkLabel(QLabel):
    def __init__(self, parent=None):
        super().__init__()
        self.setOpenExternalLinks(True)
        self.setParent(parent)

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.header = ['name', 'email']
        self.data = [[name.title(), value]
            for index, d in jsonData['email'].items()
            for name, value in d.items()]

        browser = QTextBrowser()
        browser.setOpenExternalLinks(True)
        browser.setWordWrapMode(QTextOption.NoWrap)
        #
        orange = QColor(255, 165, 0)
        palette = QPalette()
        palette.setColor(QPalette.Text, orange)
        browser.setPalette(palette)
        #
        font = QFont()
        font.setFamily('Monospace')
        font.setPointSize(16)
        browser.setFont(font)

        cursor = browser.textCursor()
        cursor.clearSelection()
        cursor.select(QTextCursor.Document)
        browser.setTextCursor(cursor)

        blockFmt = QTextBlockFormat()
        blockFmt.setLineHeight(150, QTextBlockFormat.ProportionalHeight)
        cursor.mergeBlockFormat(blockFmt)

        fmt = cursor.charFormat()
        fmt.setForeground(QColor('blue'))

        max_distance = max([len(row[0]) for row in self.data])
        rows = max([i for i in range(len(self.data))]) - 1
        for row in self.data:
            for i, value in enumerate(row):
                if self.header[i] == 'email':
                    cursor.insertHtml(self.generate_html_link(value))
                else:
                    spaces = max_distance - len(value)
                    space = 4
                    if spaces > 0:
                        space += 4
                    cursor.setPosition(0)
                    cursor.insertText('{}{}'.format(value, space * ' '))
                    distance = max_distance + 4
                    cursor.setPosition(distance, QTextCursor.KeepAnchor)
            cursor.insertText('\n')
        cursor.setPosition(QTextCursor.End - 1, QTextCursor.KeepAnchor)
        browser.setTextCursor(cursor)

        self.setCentralWidget(browser)

    def generate_html_link(self, mail):
        return '<a href="mailto:{0}">{0}</a>'.format(
            html.escape(mail, quote=True))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Window()
    win.show()
    sys.exit(app.exec_())

Re: Integration von Hyperlinks in QTableView

Verfasst: Samstag 6. Mai 2023, 15:53
von Nobuddy
Beim Googel, habe ich weitere Ansätze gefunden, die ich für mein Vorhaben umgesetzt habe.

Code: Alles auswählen

import sys
import html
from PyQt5.QtCore import (
	Qt,
	QRegExp,
	)
from PyQt5.QtWidgets import (
	QApplication,
	QMainWindow,
	QDesktopWidget,
	QLineEdit,
	QTextBrowser,
	QWidget,
	QLabel,
	QPushButton,
	QVBoxLayout,
	)
from PyQt5.QtGui import  (
	QColor,
	QPalette,
	QFont,
	QTextBlockFormat,
	QTextCharFormat,
	QTextOption,
	QTextCursor,
	)

jsonData = {
	"addresses": {
		"Familie": {
			"29614003": {
				"area": "Familie",
				"name": "Mustrermann",
				"first name": "Max",
				"name addition": "",
				"street": "Musterweg 1",
				"postal code": "47111",
				"location": "Musterhausen",
				"province": "",
				"country": "",
				"index": "29614003"
				}
		}
	},
	"email": {
		"29614003": {
			"max": "max@mustermann.de",
			"maxine": "maxine@mustermann.de",
			"t0": "t0@mustermann.de",
			"t1": "t1@mustermann.de",
			"t2": "t2@mustermann.de",
			"t3": "t3@mustermann.de",
			"t4": "t4@mustermann.de"
		}
	}
}


class VisualData(QWidget):

	def __init__(self, json, area, index):
		super().__init__()

		# data to job
		self.jsonData = jsonData
		self.area = area
		self.index = index
		# font
		self.font = 'Monospace'
		self.font_size = 16
		# setting
		self.row_counter = 0
		self.space_line = 55
		# create browser
		self.initUI()
		# create view data
		self.view_data()
		# set geometry
		self.set_geometry()

	def initUI(self):

		self.le = QLineEdit()

		self.tb = QTextBrowser()
		self.tb.setAcceptRichText(True)
		self.tb.setOpenExternalLinks(True)
		self.cursor = self.tb.textCursor()
		self.cursor.clearSelection()
		self.cursor.select(QTextCursor.Document)
		self.tb.setTextCursor(self.cursor)

		orange = QColor(255, 165, 0)
		palette = QPalette()
		palette.setColor(QPalette.Text, orange)
		self.tb.setPalette(palette)

		font = QFont()
		font.setFamily(self.font)
		font.setPointSize(self.font_size)
		self.tb.setFont(font)

		self.clear_btn = QPushButton('Close')
		self.clear_btn.pressed.connect(self.close_window)

		vbox = QVBoxLayout()
		vbox.addWidget(self.tb, 0)
		vbox.addWidget(self.clear_btn, 1)

		self.setLayout(vbox)

		self.setWindowTitle('QTextBrowser')
		self.setGeometry(300, 300, 300, 300)
		self.show()

	def view_data(self):
		#self.max_distance = max([len(row[0]) for row in self.data])
		#rows = max([i for i in range(len(self.data))]) - 1

		splitter = '{}'.format(self.space_line * '-')
		for header, source in self.jsonData.items():
			# set the beginning dividing line
			self.le.setText(splitter)
			self.append_text()
			self.row_counter += 1
			# set text of head area
			text = header
			if header == 'addresses':
				text = 'address'
			text = '{}:'.format(text)
			self.le.setText(text)
			self.append_text()
			# set values to area
			if header == 'addresses':
				self.create_address(source[self.area][self.index])
			else:
				self.create_address(source[self.index])
		# set the end cutting line
		self.le.setText(splitter)
		self.append_text()
		self.row_counter += 1

	def create_address(self, dict):
		self.max_distance = max([len(n) for n, v in dict.items()])
		self.start_spaces = 12 * ' '
		for name in dict:
			value = dict[name]
			spaces = (self.max_distance - len(name))
			space = 4
			distance = (spaces + space) * ' '
			text = '{}{}{}{}'.format(
				self.start_spaces, name, distance, value)
			self.le.setText(text)
			self.append_text()
			self.row_counter += 1
		return

	def append_text(self):
		text = self.le.text()
		if '@' in text:
			t = text.split(' ')
			#email = t[-1]
			#name = text.replace(email, '')
			#text = '{}{}'.format(name, self.generate_html_link(email))
		self.tb.append(text)
		self.le.clear()

	def generate_html_link(self, mail):
		return '<a href="mailto:{0}">{0}</a>'.format(
			html.escape(mail, quote=True))

	def set_geometry(self):
		# screen size
		screen_geometry = QDesktopWidget().screenGeometry()
		screen_width = screen_geometry.width()
		screen_hight = screen_geometry.height()
		# window size
		height = self.row_counter * 37
		width = round(self.space_line * 13.6)
		left = int((screen_width - width) / 2)
		top = int((screen_hight - height) / 2)
		self.setGeometry(left, top, width, height)

	def close_window(self):
		self.close()


if __name__ == '__main__':
	app = QApplication(sys.argv)
	area = areaKey = "Familie"
	index = comKey = '29614003'
	win = VisualData(jsonData, area, index)
	win.show()
	sys.exit(app.exec_())

Re: Integration von Hyperlinks in QTableView

Verfasst: Samstag 6. Mai 2023, 16:18
von Nobuddy
Beim Googel, habe ich weitere Ansätze gefunden, die ich für mein Vorhaben umgesetzt habe.

Code: Alles auswählen

import sys
import html
from PyQt5.QtCore import (
	Qt,
	QRegExp,
	)
from PyQt5.QtWidgets import (
	QApplication,
	QMainWindow,
	QDesktopWidget,
	QLineEdit,
	QTextBrowser,
	QWidget,
	QLabel,
	QPushButton,
	QVBoxLayout,
	)
from PyQt5.QtGui import  (
	QColor,
	QPalette,
	QFont,
	QTextBlockFormat,
	QTextCharFormat,
	QTextOption,
	QTextCursor,
	)

jsonData = {
	"addresses": {
		"Familie": {
			"29614003": {
				"area": "Familie",
				"name": "Mustrermann",
				"first name": "Max",
				"name addition": "",
				"street": "Musterweg 1",
				"postal code": "47111",
				"location": "Musterhausen",
				"province": "",
				"country": "",
				"index": "29614003"
				}
		}
	},
	"email": {
		"29614003": {
			"max": "max@mustermann.de",
			"maxine": "maxine@mustermann.de",
			"t0": "t0@mustermann.de",
			"t1": "t1@mustermann.de",
			"t2": "t2@mustermann.de",
			"t3": "t3@mustermann.de",
			"t4": "t4@mustermann.de"
		}
	}
}


class VisualData(QWidget):

	def __init__(self, json, area, index):
		super().__init__()

		# data to job
		self.jsonData = jsonData
		self.area = area
		self.index = index
		# font
		self.font = 'Monospace'
		self.font_size = 16
		# setting
		self.row_counter = 0
		self.space_line = 55
		# create browser
		self.initUI()
		# create view data
		self.view_data()
		# set geometry
		self.set_geometry()

	def initUI(self):

		self.le = QLineEdit()

		self.tb = QTextBrowser()
		self.tb.setAcceptRichText(True)
		self.tb.setOpenExternalLinks(True)
		self.cursor = self.tb.textCursor()
		self.cursor.clearSelection()
		self.cursor.select(QTextCursor.Document)
		self.tb.setTextCursor(self.cursor)

		orange = QColor(255, 165, 0)
		palette = QPalette()
		palette.setColor(QPalette.Text, orange)
		self.tb.setPalette(palette)

		font = QFont()
		font.setFamily(self.font)
		font.setPointSize(self.font_size)
		self.tb.setFont(font)

		self.clear_btn = QPushButton('Close')
		self.clear_btn.pressed.connect(self.close_window)

		vbox = QVBoxLayout()
		vbox.addWidget(self.tb, 0)
		vbox.addWidget(self.clear_btn, 1)

		self.setLayout(vbox)

		self.setWindowTitle('QTextBrowser')
		self.setGeometry(300, 300, 300, 300)
		self.show()

	def view_data(self):
		#self.max_distance = max([len(row[0]) for row in self.data])
		#rows = max([i for i in range(len(self.data))]) - 1

		splitter = '{}'.format(self.space_line * '-')
		for header, source in self.jsonData.items():
			# set the beginning dividing line
			self.le.setText(splitter)
			self.append_text()
			self.row_counter += 1
			# set text of head area
			text = header
			if header == 'addresses':
				text = 'address'
			text = '{}:'.format(text)
			self.le.setText(text)
			self.append_text()
			# set values to area
			if header == 'addresses':
				self.create_address(source[self.area][self.index])
			else:
				self.create_address(source[self.index])
		# set the end cutting line
		self.le.setText(splitter)
		self.append_text()
		self.row_counter += 1

	def create_address(self, dict):
		self.max_distance = max([len(n) for n, v in dict.items()])
		self.start_spaces = 12 * ' '
		for name in dict:
			value = dict[name]
			spaces = (self.max_distance - len(name))
			space = 4
			distance = (spaces + space) * ' '
			text = '{}{}{}{}'.format(
				self.start_spaces, name, distance, value)
			self.le.setText(text)
			self.append_text()
			self.row_counter += 1
		return

	def append_text(self):
		text = self.le.text()
		if '@' in text:
			t = text.split(' ')
			#email = t[-1]
			#name = text.replace(email, '')
			#text = '{}{}'.format(name, self.generate_html_link(email))
		self.tb.append(text)
		self.le.clear()

	def generate_html_link(self, mail):
		return '<a href="mailto:{0}">{0}</a>'.format(
			html.escape(mail, quote=True))

	def set_geometry(self):
		# screen size
		screen_geometry = QDesktopWidget().screenGeometry()
		screen_width = screen_geometry.width()
		screen_hight = screen_geometry.height()
		# window size
		height = self.row_counter * 37
		width = round(self.space_line * 13.6)
		left = int((screen_width - width) / 2)
		top = int((screen_hight - height) / 2)
		self.setGeometry(left, top, width, height)

	def close_window(self):
		self.close()


if __name__ == '__main__':
	app = QApplication(sys.argv)
	area = areaKey = "Familie"
	index = comKey = '29614003'
	win = VisualData(jsonData, area, index)
	win.show()
	sys.exit(app.exec_())
Das Layout wäre ok, aber kein Hyperlink bei EMails.
Erst bei aktivieren der ausgerauten Zeilen unter der Funktion append_text, funktionieren Hyperlinks, jedoch funktioniert das Layout nicht mehr.

Re: Integration von Hyperlinks in QTableView

Verfasst: Montag 8. Mai 2023, 08:42
von Nobuddy
Habe jetzt eine Lösung mit QGridLayout erstellt.

Code: Alles auswählen

import sys
import html
import subprocess
from PyQt5.QtCore import (
    Qt,
    )
from PyQt5.QtWidgets import (
    QApplication,
    QDesktopWidget,
    QWidget,
    QLabel,
    QPushButton,
    QGridLayout,
    QVBoxLayout,
    )
from PyQt5.QtGui import  (
    QColor,
    QPalette,
    QFont,
    )


class VisualData(QWidget):

    def __init__(self, json, area, index):
        super().__init__()

        # data to job
        self.jsonData = json
        self.area = area
        self.index = index
        # create layout
        self.initUI()
        # create view data
        self.view_data()
        # set geometry
        self.set_geometry()

    def initUI(self):

        # font
        font_family = 'Monospace'
        font_size = 16
        self.setFont(QFont(font_family, font_size))

        orange = QColor(255, 165, 0)
        palette = QPalette()
        palette.setColor(QPalette.Text, orange)
        self.setPalette(palette)

        self.close_btn = QPushButton('Close')
        self.close_btn.pressed.connect(self.close_window)

        # grid layout
        self.grid = QGridLayout()
        # vertical box for vbox
        vbox = QVBoxLayout()
        vbox.addLayout(self.grid)
        vbox.addWidget(self.close_btn, 0)
        self.setLayout(vbox)

        self.setWindowTitle('Address + Communication')
        self.show()

    def view_data(self):
        area_separator = '-'
        steps = 28
        head_separation = '{}'.format(steps * area_separator)
        value_separation = '{}'.format(steps * area_separator)
        self.steps = steps * 2
        self.y_count = 0
        for header, source in self.jsonData.items():
            # set the beginning dividing line
            self.append_text(head_separation, 0)
            self.append_text(value_separation, 1)
            self.y_count += 1
            # set text of head area
            text = header
            if header == 'addresses':
                text = 'address'
            text = '{}:'.format(text)
            self.append_text(text, 0)
            # set values to area
            if header == 'addresses':
                self.create_address(source[self.area][self.index])
            else:
                self.create_address(source[self.index])
            self.y_count += 1
        # set the end cutting line
        self.append_text(head_separation, 0)
        self.append_text(value_separation, 1)
        self.y_count += 1

    def create_address(self, dict):
        self.max_distance = max([len(n) for n, v in dict.items()])
        start_spaces = 12 * ' '
        for name in dict:
            value = dict[name]
            # set name
            name = '{}{}:'.format(start_spaces, name)
            self.append_text(name, 0)
            # set value
            self.append_text(value, 1)
            self.y_count += 1
        return

    def append_text(self, value, x):
        label = QLabel(self)
        if '@' in value:
            value = self.generate_html_link(value)
            label.linkActivated.connect(self.clicked)
        label.setText(value)
        self.grid.addWidget(label, self.y_count, x)

    def generate_html_link(self, mail):
        return '<a href="mailto:{0}">{0}</a>'.format(
            html.escape(mail, quote=True))

    def clicked(self, job):
        if job.startswith('mailto:'):
            subprocess.run(["xdg-email", job])

    def set_geometry(self):
        # screen size
        screen_geometry = QDesktopWidget().screenGeometry()
        screen_width = screen_geometry.width()
        screen_hight = screen_geometry.height()
        # window size
        height = self.y_count * 35
        width = self.steps * 13
        left = int((screen_width - width) / 2)
        top = int((screen_hight - height) / 2)
        self.setGeometry(left, top, width, height)

    def close_window(self):
        self.close()


jsonData = {
    "addresses": {
        "Familie": {
            "29614003": {
                "area": "Familie",
                "name": "Mustrermann",
                "first name": "Max",
                "name addition": "",
                "street": "Musterweg 1",
                "postal code": "47111",
                "location": "Musterhausen",
                "province": "",
                "country": "",
                "index": "29614003"
                }
        }
    },
    "email": {
        "29614003": {
            "max": "max@mustermann.de",
            "maxine": "maxine@mustermann.de",
            "t0": "t0@mustermann.de",
            "t1": "t1@mustermann.de",
            "t2": "t2@mustermann.de",
            "t3": "t3@mustermann.de",
            "t4": "t4@mustermann.de"
        }
    }
}

if __name__ == '__main__':
    app = QApplication(sys.argv)
    area = areaKey = "Familie"
    index = comKey = '29614003'
    win = VisualData(jsonData, area, index)
    win.show()
    sys.exit(app.exec_())
Das Einzige, was nicht funktioniert, dass die Textfarbe orange ist.
Vielleicht kann mir da jemand von Euch eine Lösung aufzeigen!

Grüße Nobuddy

Re: Integration von Hyperlinks in QTableView

Verfasst: Dienstag 9. Mai 2023, 13:30
von Nobuddy
Habe nun den Code soweit fertig, inklusive Farbe für den Text.

Code: Alles auswählen

import sys
import html
import subprocess
from PyQt5.QtCore import (
    Qt,
    )
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QDesktopWidget,
    QScrollArea,
    QWidget,
    QLabel,
    QPushButton,
    QGridLayout,
    )
from PyQt5.QtGui import  (
    QFont,
    )


class VisualData(QWidget):

    def __init__(self, json, area, index):
        super().__init__()

        self.setAttribute(Qt.WA_DeleteOnClose)
        # data to job
        self.jsonData = json
        self.area = area
        self.index = index
        # create layout
        self.initUI()
        # create view data
        self.view_data()

    def initUI(self):

        # font
        #font_family = 'Monospace'
        font_family = 'NimbusSansL'
        font_size = 18
        self.font = QFont(font_family, font_size)
        # grid layout
        self.grid = QGridLayout()
        self.setLayout(self.grid)

    def view_data(self):
        self.y_count = 0
        area_separator = '-'
        steps0 = 18
        steps1 = 25
        steps2 = 45
        self.steps = steps0 + steps1 + steps2
        area_separation = '{}'.format(steps0 * area_separator)
        head_separation = '{}'.format(steps1 * area_separator)
        value_separation = '{}'.format(steps2 * area_separator)
        # set the beginning dividing line
        self.header = False
        self.append_text(area_separation, 0)
        self.append_text(head_separation, 1)
        self.append_text(value_separation, 2)
        for header, source in self.jsonData.items():
            self.y_count += 1
            # set text of head area
            self.header = text = header
            if header == 'addresses':
                text = 'address'
            text = '{}:'.format(text.title())
            # set values to area
            if header == 'addresses':
                self.append_text(text, 0)
                self.create_address(source[self.area][self.index])
            else:
                try:
                    source[self.index]
                    self.append_text(text, 0)
                    self.create_address(source[self.index])
                except KeyError:
                    continue
            # set the beginning dividing line
            self.append_text(area_separation, 0)
            self.append_text(head_separation, 1)
            self.append_text(value_separation, 2)
            self.y_count += 1

    def create_address(self, dict):
        for name in dict:
            value = dict[name]
            # set name
            name = '{}:'.format(name.title())
            self.append_text(name, 1)
            # set value
            self.append_text(value, 2)
            self.y_count += 1
        return

    def append_text(self, value, col):
        label = QLabel()
        if self.header == 'email' and '@' in value:
            value = self.generate_html_link_email(value)
            label.linkActivated.connect(self.clicked)
        elif self.header == 'phone' and col == 2:
            try:
                # no hyperlink to phone
                # at the moment no way to use this function
                # create error
                int('error')
                int(value.strip().replace(' ', ''))
                value = self.generate_html_link_phone(value)
                label.linkActivated.connect(self.clicked)
            except ValueError:
                pass
        if col == 0:
            label.setStyleSheet("color: lightgreen")
        elif col == 1:
            label.setStyleSheet("color: yellow")
        elif col == 2:
            label.setStyleSheet("color: orange")
            label.setTextInteractionFlags(Qt.TextSelectableByMouse)
        label.setFont(self.font)
        label.setText(value)
        self.grid.addWidget(label, self.y_count, col)

    def generate_html_link_email(self, mail):
        return '<a href="mailto:{0}">{0}</a>'.format(
            html.escape(mail, quote=True))

    def generate_html_link_phone(self, number):

        return '<a href="tel:+{0}">{0}</a>'.format(
            html.escape(number, quote=True))

    def clicked(self, job):
        if job.startswith('mailto:'):
            subprocess.run(["xdg-email", job])
        elif job.startswith('tel:'):
            # job to call phone
            pass


class VisualWindow(QMainWindow):

    def __init__(self, jsonData, area, index):
        super().__init__()

        self.setWindowTitle('Address + Communication')
        # scroll area properties
        self.scroll = QScrollArea(self)
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)
        VD = VisualData(jsonData, area, index)
        self.scroll.setWidget(VD)
        self.setCentralWidget(self.scroll)
        self.show()


jsonData = {
    "addresses": {
        "Familie": {
            "29614003": {
                "area": "Familie",
                "name": "Mustrermann",
                "first name": "Max",
                "name addition": "",
                "street": "Musterweg 1",
                "postal code": "47111",
                "location": "Musterhausen",
                "province": "",
                "country": "",
                "index": "29614003"
                }
        }
    },
    "email": {
        "29614003": {
            "max": "max@mustermann.de",
            "maxine": "maxine@mustermann.de",
            "t0": "t0@mustermann.de",
            "t1": "t1@mustermann.de",
            "t2": "t2@mustermann.de",
            "t3": "t3@mustermann.de",
            "t4": "t4@mustermann.de"
        },
    },
    "phone": {
        "29614003": {
            "max": "04711 123456"
        }
    }
}

if __name__ == '__main__':
    app = QApplication(sys.argv)
    area = areaKey = "Familie"
    index = comKey = '29614003'
    win = VisualWindow(jsonData, area, index)
    sys.exit(app.exec_())