Reputation: 9620
EDIT:
The standard QScintilla software doesn't support this feature. But Matic Kukovec made some hacks to get it working anyhow. His solution is explained in his answer below, but also on this webpage: https://qscintilla.com/insert-images/
I would like to insert images in a QScintilla editor. Unfortunately, such a feature won't be added to the official Scintilla project anytime soon. Check out this post on the Scintilla-interest-group:
https://groups.google.com/forum/#!topic/scintilla-interest/Bwr4DY2Pv3Q
So I'll have to implement it myself. I have given it a try. Please copy-past the following code into a python-file and run it. Just make sure you have a qscintilla_logo.png
image in the same folder, of around 80 x 80 pixels:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.Qsci import *
# -----------------------------------
# | A sample of C-code, pasted into |
# | the QScintilla editor |
# -----------------------------------
myCodeSample = r"""#include <stdio.h>
/*
* I want an image
* right here =>
*/
int main()
{
char arr[5] = {'h', 'e', 'l', 'l', 'o'};
int i;
for(i = 0; i < 5; i++) {
printf(arr[i]);
}
return 0;
}""".replace("\n","\r\n")
# --------------------------------------------------
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# ---------------------------
# | Window setup |
# ---------------------------
# 1. Define the geometry of the main window
# ------------------------------------------
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("QScintilla Test")
# 2. Create frame and layout
# ---------------------------
self.__frm = QFrame(self)
self.__frm.setStyleSheet("QWidget { background-color: #ffeaeaea }")
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.__myFont = QFont("Consolas", 14, weight=QFont.Bold)
self.__myFont.setPointSize(14)
# 3. Place a button
# ------------------
self.__btn = QPushButton("Qsci")
self.__btn.setFixedWidth(50)
self.__btn.setFixedHeight(50)
self.__btn.clicked.connect(self.__btn_action)
self.__btn.setFont(self.__myFont)
self.__lyt.addWidget(self.__btn)
# ---------------------------
# | QScintilla editor setup |
# ---------------------------
# 1. Make instance of QSciScintilla class
# ----------------------------------------
self.__editor = QsciScintilla()
self.__editor.setText(myCodeSample)
self.__editor.setLexer(None)
self.__editor.setUtf8(True) # Set encoding to UTF-8
self.__editor.setFont(self.__myFont) # Can be overridden by lexer
# 2. Create an image
# -------------------
self.__myImg = QPixmap("qscintilla_logo.png")
self.__myImgLbl = QLabel(parent=self.__editor)
self.__myImgLbl.setStyleSheet("QLabel { background-color : white; }");
self.__myImgLbl.setPixmap(self.__myImg)
self.__myImgLbl.move(300,80)
# 3. Add editor to layout
# ------------------------
self.__lyt.addWidget(self.__editor)
self.show()
''''''
def __btn_action(self):
print("Hello World!")
''''''
''' End Class '''
if __name__ == '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
When running the Python script, you should get the following result:
I'm actually adding a QLabel on top of the editor, and give it an absolute position:
self.__myImgLbl = QLabel(parent=self.__editor)
self.__myImgLbl.move(300,80)
Unfortunately, the image won't move when scrolling down:
I think I know why. Let me explain. The editor is an object of the QsciScintilla
class, which is a subclass of QsciScintillaBase
, which is a subclass of QAbstractScrollArea
:
I believe that the key to success is adding the QLabel to the widget inside the QAbstractScrollArea
, not the scroll area itself. I have tried several things to find that widget, but didn't succeed.
If it were a QScrollArea
, then the function widget()
would be sufficient. But this function is not available on a QAbstractScrollArea
.
EDIT 1:
The editor in the python code sample doesn't perform any syntax highlighting, and even doesn't have line numbers. That's because I want to focus only on the actual issue - adding an image. The code sample is based on: https://qscintilla.com/an-editor-in-a-gui/
EDIT 2:
Please, kindly refrain from giving opinions on whether inserting images in an editor is a good or bad idea. Let us just stick to the technical issue at hand.
Upvotes: 2
Views: 597
Reputation: 436
Hey Kristof try this (your example with some modifications):
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.Qsci import *
# -----------------------------------
# | A sample of C-code, pasted into |
# | the QScintilla editor |
# -----------------------------------
myCodeSample = r"""#include <stdio.h>
/*
* I want an image
* right here =>
*/
int main()
{
char arr[5] = {'h', 'e', 'l', 'l', 'o'};
int i;
for(i = 0; i < 5; i++) {
printf(arr[i]);
}
return 0;
}""".replace("\n","\r\n")
# --------------------------------------------------
class MyScintilla(QsciScintilla):
def __init__(self, parent=None):
super().__init__(parent)
self.image = QImage("qscintilla_logo.png")
def paintEvent(self, e):
super().paintEvent(e)
# Get paint rectangle size
current_parent_size = self.size()
image_line = 4
image_column = 17
# Find the paint offset
line_height = 20
font_width = 10
first_visible_line = self.SendScintilla(self.SCI_GETFIRSTVISIBLELINE)
paint_offset_x = image_column * font_width
paint_offset_y = (image_line - first_visible_line) * line_height
# Paint the image
painter = QPainter()
painter.begin(self.viewport())
painter.drawImage(QPoint(paint_offset_x,paint_offset_y), self.image)
painter.end()
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# ---------------------------
# | Window setup |
# ---------------------------
# 1. Define the geometry of the main window
# ------------------------------------------
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("QScintilla Test")
# 2. Create frame and layout
# ---------------------------
self.__frm = QFrame(self)
self.__frm.setStyleSheet("QWidget { background-color: #ffeaeaea }")
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.__myFont = QFont("Consolas", 14, weight=QFont.Bold)
self.__myFont.setPointSize(14)
# 3. Place a button
# ------------------
self.__btn = QPushButton("Qsci")
self.__btn.setFixedWidth(50)
self.__btn.setFixedHeight(50)
self.__btn.clicked.connect(self.__btn_action)
self.__btn.setFont(self.__myFont)
self.__lyt.addWidget(self.__btn)
# ---------------------------
# | QScintilla editor setup |
# ---------------------------
# 1. Make instance of QSciScintilla class
# ----------------------------------------
self.__editor = MyScintilla()
self.__editor.setText(myCodeSample)
self.__editor.setLexer(None)
self.__editor.setUtf8(True) # Set encoding to UTF-8
self.__editor.setFont(self.__myFont) # Can be overridden by lexer
# 3. Add editor to layout
# ------------------------
self.__lyt.addWidget(self.__editor)
self.show()
''''''
def __btn_action(self):
print("Hello World!")
''''''
''' End Class '''
if __name__ == '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''''''
It is just subclassing the QsciScintilla class and overloading it's paintEvent method. The trick is to get the coordinates of where the image is in relation to the view of the document. For the example I justed guessed the line height and font width, it can probably be obtained from the QScintilla. The other thing is I just hard coded the position of the image with a line and column offset, it would probably be good to create a syntax that the editor could parse and insert the image automatically.
Regards
Upvotes: 1