Below is a small working code to display what I am trying to achieve.
import sys
import os
import sqlite3
from PyQt5.QtSql import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
# createDB()
# con_1 = sqlite3.connect("temp.db")
# # create a table
# con_1.execute('''create table t1(
# id INTEGER PRIMARY KEY, 'First Name' TEXT, 'Last Name' TEXT, 'Full Name' TEXT);''')
# # populate the table
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Wade', 'Wilson', 'Wade Wilson');")
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Norman', 'Osborn', 'Norman Osborn');")
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Stephen', 'Strange', 'Stephen Strange');")
# con_1.commit()
# con_1.close()
class MultilineTextEditor(QStyledItemDelegate):
def createEditor(self, parent, options, index):
return QTextEdit(parent)
def setEditorData(self, editor, index):
editor.setText(index.data())
def setModelData(self, editor, model, index):
model.setData(index, editor.toPlainText())
def paint(self, painter, option, index):
value = index.data(Qt.DisplayRole)
if option.state & QStyle.State_Selected:
text1 = index.data()
doc = QTextDocument(text1)
cursor = QTextCursor(doc)
cursor.selectedTableCells()
# cursor.selectedText() <-- How to implement this?
# Trying out to simply select the first line!
cursor.movePosition(QTextCursor.Start)
cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
format = QTextCharFormat()
format.setFontWeight(QFont.Bold)
format.setFontPointSize(20)
weight = QFont.DemiBold
format.setFontWeight(weight)
cursor.mergeCharFormat(format)
print(cursor.selectedText())
# painter commands??
QStyledItemDelegate.paint(self, painter, option, index)
else:
QStyledItemDelegate.paint(self, painter, option, index)
class TableViewer(QMainWindow):
def __init__(self):
super().__init__()
working_dir = os.getcwd()
db_path = f"{working_dir}\\temp.db"
print(db_path)
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName(db_path)
self.db.open()
self.model = QSqlTableModel(self, self.db)
self.model.setTable('t1')
self.model.select()
self.tableView = QTableView(self)
self.tableView.setModel(self.model)
self.tableView.resizeColumnsToContents()
layout = QVBoxLayout()
layout.addWidget(self.tableView)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.tableView.setItemDelegateForColumn(3, MultilineTextEditor())
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
tv = TableViewer()
sys.exit(app.exec_())
How would you set the format (bold, Italics, underline), size or colour of selected string? I would eventually bind the selection with say keyboard shorcuts (eg cntr + B for bold) or display a toolbar for user selection. Found an example here: https://forum.qt.io/topic/138318/how-do-you-bold-and-un-bold-a-selected-text-pyqt-or-pyside. I am unable to implement it, even rudimentarily in a Qtableview cell.
The rich text data should be saved back to be retrieved from the DB. Unfortunately I am unable to find any good explanitions on delegate --> paint.
I have found a solution to my question.
Courtesy:
import sys
import os
import sqlite3
from functools import partial
from PyQt5.QtSql import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from wid_comments_editor_v1 import Ui_CommEdit
# createDB()
# con_1 = sqlite3.connect("temp.db")
# # create a table
# con_1.execute('''create table t1(
# id INTEGER PRIMARY KEY, 'First Name' TEXT, 'Last Name' TEXT, 'Full Name' TEXT);''')
# # populate the table
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Wade', 'Wilson', 'Wade Wilson');")
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Norman', 'Osborn', 'Norman Osborn');")
# con_1.execute("insert into t1 ('First Name', 'Last Name', 'Full Name') values('Stephen', 'Strange', 'Stephen Strange');")
# con_1.commit()
# con_1.close()
class CommentEditor(QWidget, Ui_CommEdit):
"""
UI code (Ui_CommEdit) not attached.
Contains a simple Qtextedit with 5 buttons for bold, italics, underline,
strikeout and colour
"""
closed = pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
self.setupUi(self)
def closeEvent(self, event):
self.closed.emit()
class MultilineTextEditor(QStyledItemDelegate):
def createEditor(self, parent, options, index):
return QTextEdit(parent)
def setEditorData(self, editor, index):
txt = index.data()
editor.setHtml(txt)
return editor.setHtml(txt)
def setModelData(self, editor, model, index):
model.setData(index, editor.toHtml())
def paint(self, painter, option, index):
painter.save()
rich_txt = index.data()
doc = QTextDocument()
doc.setTextWidth(option.rect.width())
doc.setHtml(rich_txt)
option.text = ""
option.widget.style().drawControl(
QStyle.CE_ItemViewItem, option, painter
)
painter.translate(option.rect.left(), option.rect.top())
doc.drawContents(painter)
painter.restore()
class TableViewer(QMainWindow):
# close_comment_editor = pyqtSignal()
def __init__(self):
super().__init__()
working_dir = os.getcwd()
db_path = f"{working_dir}\\temp.db"
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName(db_path)
self.db.open()
self.model = QSqlTableModel(self, self.db)
self.model.setTable('t1')
self.model.select()
self.tableView = QTableView(self)
self.tableView.setModel(self.model)
self.tableView.resizeColumnsToContents()
layout = QVBoxLayout()
layout.addWidget(self.tableView)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
# Adjust width of columns and rows
header_hor = self.tableView.horizontalHeader()
header_hor.setSectionsMovable(True)
header_hor.resizeSection(3, 800)
self.tableView.resizeRowsToContents()
self.tableView.setItemDelegateForColumn(3, MultilineTextEditor())
self.showMaximized()
def contextMenuEvent(self, event):
# Imp! Create new model from existing items on view
# self.self.tableView != self.tableView.model
model = self.tableView.model()
# Indices
col_indx = self.tableView.currentIndex().column()
row_indx = self.tableView.currentIndex().row()
index = model.index(row_indx, col_indx)
# Get Value
data = model.data(index)
# header names
header_name = []
for x in range(self.model.columnCount()):
header_name.append(self.model.headerData(x, Qt.Horizontal))
selectionModel = self.tableView.selectionModel()
selectionModel.isSelected(index)
if header_name[col_indx] == "Full Name" and selectionModel.isSelected(
index
):
self.menu = QMenu(self)
edit = QAction("Edit", self)
self.menu.addAction(edit)
self.menu.popup(QCursor.pos())
edit.triggered.connect(
partial(
self.edit_frontend,
data,
col_indx,
row_indx,
header_name,
model,
)
)
def edit_frontend(self, data, col_indx, row_indx, header_name, model):
# Open editor
self.comm_edit =CommentEditor()
self.comm_edit.show()
self.comm_edit.te_comment_editor.setHtml(data)
# Code for bold
self.comm_edit.pb_bold.clicked.connect(self.bold_text)
self.comm_edit.pb_bold.setShortcut(Qt.CTRL | Qt.Key_B)
# Code for italic
self.comm_edit.pb_italic.clicked.connect(self.italic_text)
self.comm_edit.pb_italic.setShortcut(Qt.CTRL | Qt.Key_I)
# Code for underline
self.comm_edit.pb_underline.clicked.connect(self.underline_text)
self.comm_edit.pb_underline.setShortcut(Qt.CTRL | Qt.Key_U)
# Code for strikeout
self.comm_edit.pb_strikeout.clicked.connect(self.strikeout_text)
self.comm_edit.pb_strikeout.setShortcut(Qt.CTRL | Qt.Key_S)
# Code for colour
self.comm_edit.pb_colour.clicked.connect(self.colour_text)
# Close event handler
self.comm_edit.closed.connect(
partial(
self.win_closed,
col_indx,
row_indx,
model,
)
)
def win_closed(self, col_indx, row_indx, model ):
txt = self.comm_edit.te_comment_editor.toHtml()
model.setData(model.index(row_indx, col_indx), txt)
def bold_text(self):
fmt = QTextCharFormat()
if self.comm_edit.pb_bold.isChecked():
weight = QFont.Bold
else:
weight = QFont.Normal
fmt.setFontWeight(weight)
self.merge_format_on_word_or_selection(fmt)
def italic_text(self):
fmt = QTextCharFormat()
fmt.setFontItalic(self.comm_edit.pb_italic.isChecked())
self.merge_format_on_word_or_selection(fmt)
def underline_text(self):
fmt = QTextCharFormat()
fmt.setFontUnderline(self.comm_edit.pb_underline.isChecked())
self.merge_format_on_word_or_selection(fmt)
def strikeout_text(self):
fmt = QTextCharFormat()
fmt.setFontStrikeOut(self.comm_edit.pb_strikeout.isChecked())
self.merge_format_on_word_or_selection(fmt)
def colour_text(self):
color = QColorDialog.getColor()
if color.isValid():
fmt = QTextCharFormat()
fmt.setForeground(color)
self.merge_format_on_word_or_selection(fmt)
def merge_format_on_word_or_selection(self, format):
cursor = self.comm_edit.te_comment_editor.textCursor()
if not cursor.hasSelection():
cursor.select(QTextCursor.WordUnderCursor)
cursor.mergeCharFormat(format)
self.comm_edit.te_comment_editor.mergeCurrentCharFormat(format)
def closeEvent(self, event):
try :
self.comm_edit.close()
except AttributeError:
pass
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
tv = TableViewer()
sys.exit(app.exec_())
As mentioned earlier the commented block contains the code to create a SQLLite temp.db.
The entries in Last name column can be modified with opening an UI acessed via a custom context menu.
The edited rich text data is stored as HTML but diplayed as text.
Hope this helps someone!