Consider this snippet:
import sys
from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import *
if __name__ == '__main__':
app = QApplication(sys.argv)
view = QsciScintilla()
# http://www.scintilla.org/ScintillaDoc.html#Folding
view.setFolding(QsciScintilla.BoxedTreeFoldStyle)
# view.setFolding(QsciScintilla.BoxedFoldStyle)
# view.setFolding(QsciScintilla.CircledFoldStyle)
# view.setFolding(QsciScintilla.CircledTreeFoldStyle)
# view.setFolding(QsciScintilla.NoFoldStyle)
# view.setFolding(QsciScintilla.PlainFoldStyle)
lines = [
(0, "def foo():"),
(1, " x = 10"),
(1, " y = 20"),
(1, " return x+y"),
(-1, ""),
(0, "def bar(x):"),
(1, " if x > 0:"),
(2, " print('this is')"),
(2, " print('branch1')"),
(1, " else:"),
(2, " print('and this')"),
(2, " print('is branch2')"),
(-1, ""),
(-1, ""),
(-1, ""),
(-1, "print('end')"),
]
view.setText("\n".join([b for a, b in lines]))
MASK = QsciScintilla.SC_FOLDLEVELNUMBERMASK
for i, tpl in enumerate(lines):
level, line = tpl
if level >= 0:
view.SendScintilla(view.SCI_SETFOLDLEVEL, i, level | QsciScintilla.SC_FOLDLEVELHEADERFLAG)
else:
view.SendScintilla(view.SCI_SETFOLDLEVEL, i, 0)
view.show()
app.exec_()
I'd like to figure out whether it's possible to change the folding icon to a custom icon different than the ones offered by QScintilla, specifically I'd like to have a down arrow similar to Sublime's:
By looking at the QSciScintilla foldstyle you can see there isn't anything that remains similar.
In fact, not only that, I was also wondering if it'd be possible to achieve this nice subtle effect of fading away the folding icon when the mouse position enters/leaves the margin area, take a look:
Which it's a really nice feature, that way while you're coding you won't get distracted by the "always visible" folding icons.
In this thread it seems there is something called SC_MARK_ARROWDOWN
but not sure if that could be used as a folding icon... in any case, i'd still prefer my custom picture as I'll be using a monokai theme and I'd like the icon to look as elegant as Sublime's.
Below you'll find 2 12x12 pngs I've created to represent the dark & light arrowdowns.
The Windows version of Scintilla is a Windows Control. As such, its primary programming interface is through Windows messages. Early versions of Scintilla emulated much of the API defined by the standard Windows Edit and RichEdit controls but those APIs are now deprecated in favour of Scintilla's own, more consistent API.
The GPL version of QScintilla can be installed from PyPI: The wheels include a statically linked copy of the QScintilla C++ library. pip will also build and install the bindings from the sdist package but Qt’s qmake tool must be on PATH.
In addition to messages performing the actions of a normal Edit control, Scintilla allows control of syntax styling, folding, markers, autocompletion and call tips. The GTK version also uses messages in a similar way to the Windows version. This is different to normal GTK practice but made it easier to implement rapidly.
In Flutter, it’s not that complicated, but there are some things you should be aware of so that you don’t make time-consuming mistakes. Instead of using the generic application icon Flutter provides, you can create your own. To do that, we will need to use a package called Flutter Launcher Icons.
Preliminary version that lacks hovered marker highlighting:
from PyQt5.Qsci import QsciScintilla
from PyQt5.Qt import *
def get_arrow(down, size, cmin, cmax):
from PIL import Image
from PIL.ImageQt import ImageQt
from math import atan2, cos, pi
pict = Image.new("RGBA", (size, size), tuple(cmin + [255]))
pixl = pict.load()
step = 3.25**2
for y in range(pict.size[1]):
for x in range(pict.size[0]):
xpos = x - pict.size[0] // 2 + 1
ypos = y - pict.size[1] // 2 + 1
retn = 1.5 * atan2(ypos, xpos) + (pi / 4, 0)[not down]
retn = ((abs(cos(retn))**2.5 + 3) * 2)**2 - xpos**2 - ypos**2
if (retn < 0):
retn = cmin
elif (retn >= step):
retn = cmax
else:
retn = [int((cmax[i] - cmin[i]) * retn / step + cmin[i])
for i in range(0, 3)]
pixl[x, y] = tuple(retn + [255])
return ImageQt(pict)
def color_to_sc(c):
return (c[0] & 0xFF) | ((c[1] & 0xFF) << 8) | ((c[2] & 0xFF) << 16)
def set_fold(prev, line, fold, full):
if (prev[0] >= 0):
fmax = max(fold, prev[1])
for iter in range(prev[0], line + 1):
view.SendScintilla(view.SCI_SETFOLDLEVEL, iter,
fmax | (0, view.SC_FOLDLEVELHEADERFLAG)[iter + 1 < full])
def line_empty(line):
return view.SendScintilla(view.SCI_GETLINEENDPOSITION, line) \
<= view.SendScintilla(view.SCI_GETLINEINDENTPOSITION, line)
def modify(position, modificationType, text, length, linesAdded,
line, foldLevelNow, foldLevelPrev, token, annotationLinesAdded):
full = view.SC_MOD_INSERTTEXT | view.SC_MOD_DELETETEXT
if (~modificationType & full == full):
return
prev = [-1, 0]
full = view.SendScintilla(view.SCI_GETLINECOUNT)
lbgn = view.SendScintilla(view.SCI_LINEFROMPOSITION, position)
lend = view.SendScintilla(view.SCI_LINEFROMPOSITION, position + length)
for iter in range(max(lbgn - 1, 0), -1, -1):
if ((iter == 0) or not line_empty(iter)):
lbgn = iter
break
for iter in range(min(lend + 1, full), full + 1):
if ((iter == full) or not line_empty(iter)):
lend = min(iter + 1, full)
break
for iter in range(lbgn, lend):
if (line_empty(iter)):
if (prev[0] == -1):
prev[0] = iter
else:
fold = view.SendScintilla(view.SCI_GETLINEINDENTATION, iter)
fold //= view.SendScintilla(view.SCI_GETTABWIDTH)
set_fold(prev, iter - 1, fold, full)
set_fold([iter, fold], iter, fold, full)
prev = [-1, fold]
set_fold(prev, lend - 1, 0, full)
def hover(position, xpos, ypos):
mask = view.SendScintilla(view.SCI_GETMARGINMASKN, 2)
mask = (mask | view.SC_MASK_FOLDERS, mask & ~view.SC_MASK_FOLDERS) \
[xpos > StandardMarginWidth]
view.SendScintilla(view.SCI_SETMARGINMASKN, 2, mask)
if __name__ == '__main__':
import sys
import textwrap
app = QApplication(sys.argv)
view = QsciScintilla()
view.SendScintilla(view.SCI_SETMULTIPLESELECTION, True)
view.SendScintilla(view.SCI_SETMULTIPASTE, 1)
view.SendScintilla(view.SCI_SETADDITIONALSELECTIONTYPING, True)
view.SendScintilla(view.SCI_SETINDENTATIONGUIDES, view.SC_IV_REAL);
view.SendScintilla(view.SCI_SETTABWIDTH, 4)
view.setFolding(view.BoxedFoldStyle)
StandardIconSize = 16
StandardMarginWidth = 20
StandardBackground = [64, 64, 64]
StandardForeground = [192, 192, 192] # [R, G, B]
view.SendScintilla(view.SCI_SETMARGINWIDTHN, 1, 0)
view.SendScintilla(view.SCI_SETMARGINWIDTHN, 2, StandardMarginWidth)
view.SendScintilla(view.SCI_STYLESETBACK, view.STYLE_LINENUMBER, color_to_sc(StandardBackground))
view.SendScintilla(view.SCI_SETFOLDMARGINHICOLOUR, True, color_to_sc(StandardBackground))
view.SendScintilla(view.SCI_SETFOLDMARGINCOLOUR, True, color_to_sc(StandardBackground))
view.SendScintilla(view.SCI_RGBAIMAGESETHEIGHT, StandardIconSize)
view.SendScintilla(view.SCI_RGBAIMAGESETWIDTH, StandardIconSize)
fldr = get_arrow(0, StandardIconSize, StandardBackground, StandardForeground)
open = get_arrow(1, StandardIconSize, StandardBackground, StandardForeground)
view.SendScintilla(view.SCI_MARKERDEFINERGBAIMAGE, view.SC_MARKNUM_FOLDER, fldr)
view.SendScintilla(view.SCI_MARKERDEFINERGBAIMAGE, view.SC_MARKNUM_FOLDEROPEN, open)
view.SendScintilla(view.SCI_SETMOUSEDWELLTIME, 50)
view.SCN_DWELLSTART.connect(hover)
view.SCN_DWELLEND.connect(hover)
view.SCN_MODIFIED.connect(modify)
NUM_CHUNKS = 1
chunk = textwrap.dedent("""\
def foo():
x = 10
y = 20
return x+y
def bar(x):
if x > 0:
print('this is')
print('branch1')
else:
print('and this')
print('is branch2')
print('end')
""")
view.setText("\n".join([chunk for i in range(NUM_CHUNKS)]))
view.show()
app.exec_()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With