Tkinter : Syntax highlighting for Text widget

2024/9/23 13:37:07

Can anyone explain how to add syntax highlighting to a Tkinter Text widget ?

Every time the program finds a matching word, it would color that word to how I want. Such as : Color the word tkinter in pink and in in blue. But when I type in Tkinter, it color Tk--ter in yellow and in in blue.

How can I fix this ? Thanks !

Answer

This is an extension of tfpf's answer.

When you call ic.make_pat() it returns the entire regular expression for python formatting. Whereas it may seem convenient to OR in some extra expressions, to one side or the other, it doesn't really give you much control, and it becomes cumbersome quickly. A potentially more useful and definitely more customizable approach would be to print/copy/paste ic.make_pat(), and break it up similar to below. This also has the bonus side-effect that you don't have to worry about how to call ic.make_pat() in regards to python versions because, after you do this you aren't going to use ic.make_pat(), at all.

#syntax highlighter patterns
KEYWORD   = r"\b(?P<KEYWORD>False|None|True|and|as|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|raise|return|try|while|with|yield)\b"
EXCEPTION = r"([^.'\"\\#]\b|^)(?P<EXCEPTION>ArithmeticError|AssertionError|AttributeError|BaseException|BlockingIOError|BrokenPipeError|BufferError|BytesWarning|ChildProcessError|ConnectionAbortedError|ConnectionError|ConnectionRefusedError|ConnectionResetError|DeprecationWarning|EOFError|Ellipsis|EnvironmentError|Exception|FileExistsError|FileNotFoundError|FloatingPointError|FutureWarning|GeneratorExit|IOError|ImportError|ImportWarning|IndentationError|IndexError|InterruptedError|IsADirectoryError|KeyError|KeyboardInterrupt|LookupError|MemoryError|ModuleNotFoundError|NameError|NotADirectoryError|NotImplemented|NotImplementedError|OSError|OverflowError|PendingDeprecationWarning|PermissionError|ProcessLookupError|RecursionError|ReferenceError|ResourceWarning|RuntimeError|RuntimeWarning|StopAsyncIteration|StopIteration|SyntaxError|SyntaxWarning|SystemError|SystemExit|TabError|TimeoutError|TypeError|UnboundLocalError|UnicodeDecodeError|UnicodeEncodeError|UnicodeError|UnicodeTranslateError|UnicodeWarning|UserWarning|ValueError|Warning|WindowsError|ZeroDivisionError)\b"
BUILTIN   = r"([^.'\"\\#]\b|^)(?P<BUILTIN>abs|all|any|ascii|bin|breakpoint|callable|chr|classmethod|compile|complex|copyright|credits|delattr|dir|divmod|enumerate|eval|exec|exit|filter|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|isinstance|issubclass|iter|len|license|locals|map|max|memoryview|min|next|oct|open|ord|pow|print|quit|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|sum|type|vars|zip)\b"
DOCSTRING = r"(?P<DOCSTRING>(?i:r|u|f|fr|rf|b|br|rb)?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?|(?i:r|u|f|fr|rf|b|br|rb)?\"\"\"[^\"\\]*((\\.|\"(?!\"\"))[^\"\\]*)*(\"\"\")?)"
STRING    = r"(?P<STRING>(?i:r|u|f|fr|rf|b|br|rb)?'[^'\\\n]*(\\.[^'\\\n]*)*'?|(?i:r|u|f|fr|rf|b|br|rb)?\"[^\"\\\n]*(\\.[^\"\\\n]*)*\"?)"
TYPES     = r"\b(?P<TYPES>bool|bytearray|bytes|dict|float|int|list|str|tuple|object)\b"
NUMBER    = r"\b(?P<NUMBER>((0x|0b|0o|#)[\da-fA-F]+)|((\d*\.)?\d+))\b"
CLASSDEF  = r"(?<=\bclass)[ \t]+(?P<CLASSDEF>\w+)[ \t]*[:\(]" #recolor of DEFINITION for class definitions
DECORATOR = r"(^[ \t]*(?P<DECORATOR>@[\w\d\.]+))"
INSTANCE  = r"\b(?P<INSTANCE>super|self|cls)\b"
COMMENT   = r"(?P<COMMENT>#[^\n]*)"
SYNC      = r"(?P<SYNC>\n)"

Then you can concat all of those patterns, in whatever order suits you, as below:

PROG   = rf"{KEYWORD}|{BUILTIN}|{EXCEPTION}|{TYPES}|{COMMENT}|{DOCSTRING}|{STRING}|{SYNC}|{INSTANCE}|{DECORATOR}|{NUMBER}|{CLASSDEF}"

You may notice that DEFINITION is not present in any of the above patterns. That's because the above patterns are for .prog, but the DEFINITION pattern is determined by .idprog. Below is mine. I wanted class definitions to be a different color so, my pattern ignores definitions that are preceded with class. If you don't intend to make some exceptions to DEFINITION you don't have to mess with it, at all.

#original - r"\s+(\w+)"
IDPROG = r"(?<!class)\s+(\w+)"

The next thing to consider is tagdefs. Instead of line by line adding/modifying a key, you can just predefine tagdefs. Below is an example. Note that every regex group name used in the first set of patterns above, is represented with a key in the below object. Also note that DEFINITION is included here. Each object below becomes options for tag_configure, and you can use any option that tag_configure accepts. Colors and fonts are my own, and including them is unnecessary to the example.

TAGDEFS   = {   'COMMENT'    : {'foreground': CHARBLUE  , 'background': None},'TYPES'      : {'foreground': CLOUD2    , 'background': None},'NUMBER'     : {'foreground': LEMON     , 'background': None},'BUILTIN'    : {'foreground': OVERCAST  , 'background': None},'STRING'     : {'foreground': PUMPKIN   , 'background': None},'DOCSTRING'  : {'foreground': STORMY    , 'background': None},'EXCEPTION'  : {'foreground': CLOUD2    , 'background': None, 'font':FONTBOLD},'DEFINITION' : {'foreground': SAILOR    , 'background': None, 'font':FONTBOLD},'DECORATOR'  : {'foreground': CLOUD2    , 'background': None, 'font':FONTITAL},'INSTANCE'   : {'foreground': CLOUD     , 'background': None, 'font':FONTITAL},'KEYWORD'    : {'foreground': DK_SEAFOAM, 'background': None, 'font':FONTBOLD},'CLASSDEF'   : {'foreground': PURPLE    , 'background': None, 'font':FONTBOLD},}'''
#what literally happens to this data when it is applied
for tag, cfg in self.tagdefs.items():self.tag_configure(tag, **cfg)
'''

Once you have that setup you can easily plug everything in. If you make a custom text widget you could put the below in __init__ and change YourTextWidget to self. Otherwise, just change YourTextWidget to the instance name of the text widget you want to connect this to (as it is in tfpf's answer).

cd         = ic.ColorDelegator()
cd.prog    = re.compile(PROG, re.S|re.M)
cd.idprog  = re.compile(IDPROG, re.S)
cd.tagdefs = {**cd.tagdefs, **TAGDEFS}
ip.Percolator(YourTextWidget).insertfilter(cd)

cd.tagdefs = {**cd.tagdefs, **TAGDEFS}

Why did I do it this way? We don't omit any values with this method. What if KEYWORD was defined in tagdefs, but not in TAGDEFS? If we didn't first unpack tagdefs into itself we would lose KEYWORD.

To sum up this end of the system: one big regex is run, and whatever regex group name matches becomes the name of the tag to apply. Whatever new regex groups you create should (maybe must) have an identically named key in .tagdefs.

https://en.xdnf.cn/q/71790.html

Related Q&A

how to use pkgutils.get_data with csv.reader in python?

I have a python module that has a variety of data files, (a set of csv files representing curves) that need to be loaded at runtime. The csv module works very well # curvefile = "ntc.10k.csv"…

How to make celery retry using the same worker?

Im just starting out with celery in a Django project, and am kinda stuck at this particular problem: Basically, I need to distribute a long-running task to different workers. The task is actually broke…

Make an AJAX call to pass drop down value to the python script

I want to pass the selected value from dropdown which contains names of databases and pass it to the python script in the background which connects to the passed database name. Following is the ajax co…

PyLint 1.0.0 with PyDev + Eclipse: include-ids option no longer allowed, breaks Eclipse integration

As noted in this question: How do I get Pylint message IDs to show up after pylint-1.0.0?pylint 1.0.0 no longer accepts "include-ids" option. (It returns "lint.py: error: no such optio…

Shifting all rows in dask dataframe

In Pandas, there is a method DataFrame.shift(n) which shifts the contents of an array by n rows, relative to the index, similarly to np.roll(a, n). I cant seem to find a way to get a similar behaviour …

Pandas dataframe: omit weekends and days near holidays

I have a Pandas dataframe with a DataTimeIndex and some other columns, similar to this:import pandas as pd import numpy as nprange = pd.date_range(2017-12-01, 2018-01-05, freq=6H) df = pd.DataFrame(ind…

How to dump a boolean matrix in numpy?

I have a graph represented as a numpy boolean array (G.adj.dtype == bool). This is homework in writing my own graph library, so I cant use networkx. I want to dump it to a file so that I can fiddle wit…

Cant append_entry FieldList in Flask-wtf more than once

I have a form with flask-wtf for uploading images, also file field can be multiple fields. my form: class ComposeForm(Form):attachment = FieldList(FileField(_(file)), _(attachment))add_upload = SubmitF…

What is the best way to use python code from Scala (or Java)? [duplicate]

This question already has answers here:Closed 11 years ago.Possible Duplicate:Java Python Integration There is some code written in Python and I need to use it from Scala. The code uses some native C.…

Pandas groupby week given a datetime column

Lets say I have the following data sample:df = pd.DataFrame({date:[2011-01-01,2011-01-02,2011-01-03,2011-01-04,2011-01-05,2011-01-06,2011-01-07,2011-01-08,2011-01-09,2011-12-30,2011-12-31],revenue:[5,3…