Search code examples
pythonwxpythonlexerwxwidgets

wxPython StyleSetSpec and SetLexer not working?


Ok, let's get straight to the point. I got 2 questions
1. Why self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet) not working?
2. Why the custom lexer is not applied?

This is the source code

import wx, re, keyword
from wx import stc

class BaseLexer(object):
  """Defines simple interface for custom lexer objects"""
  def __init__(self):
    super(BaseLexer, self).__init__()

  def StyleText(self, event):
    raise NotImplementedError

class STC_LEX_VOTONUSA(BaseLexer):
  # Define some style IDs
  STC_VOTONUSA_DEFAULT = wx.NewId()
  STC_VOTONUSA_VALUE = wx.NewId()
  STC_VOTONUSA_OBJECT = wx.NewId()
  STC_VOTONUSA_TYPE = wx.NewId()
  STC_VOTONUSA_OPERATION = wx.NewId()
  STC_VOTONUSA_COMMENT =wx.NewId()
  STC_VOTONUSA_QUALIFIER = wx.NewId()

  def __init__(self):
    super(STC_LEX_VOTONUSA, self).__init__()
    # Attributes
    self.Comment = re.compile("""
     //
     """, re.X)
    self.Types = re.compile("""
     \\b
     (void|integer|shortint|(short)?word|float|boolean|char|record|program|module)
     \\b
     """, re.I|re.X)
    self.Operation = re.compile("""
     (read|write)(line)?|if|main|case|while|use|return|exit|in_case|repeat_until
     (\+|-|\*|/|%|\*\*|:|!|<|>)?=?|
     (\{|\}|\(|\)|\[|\])
     """, re.I|re.X)
    self.Value = re.compile("""
     [(`\d*)+\'\w*\']*|\'.*\'|[\+-]*\d*\.?\d*
     """, re.I|re.X)
    self.Qualifier = re.compile("""
     interface|implementation
     """, re.I|re.X)
    self.Object = re.compile("""
     s
     """, re.I|re.X)

  def GetLastWord(self, Line, CaretPos):
    """
    Get the last word from a line
    """
    LastWord = re.search(
     """
     \s*
     (
      ".*"|                           # String data type
      \w*|                            # Any letter/number
      (\+|-|\*|/|%|\*\*|:|!|<|>)?=?|  # Assignment, Comparison & Mathematical
      (\{|\}|\(|\)|\[|\])             # Brackets
     )
     \s*\Z
     """,
     Line[:CaretPos], re.VERBOSE)
    return LastWord

  def StyleText(self, event):
    """Handle the EVT_STC_STYLENEEDED event"""
    Obj = event.GetEventObject()

    # Get Last Correctly Styled
    LastStyledPos = Obj.GetEndStyled()

    # Get Styling Range
    Line = Obj.LineFromPosition(LastStyledPos)
    StartPos = Obj.PositionFromLine(Line)
    EndPos = event.GetPosition()

    # Walk the Range and Style Them
    while StartPos < EndPos:
      Obj.StartStyling(StartPos, 0x1f)
      LastWord = self.GetLastWord(Line, CaretPos)
      if self.Comment.search(LastWord):
        # Set Comment Keyword style
        Style = self.STC_VOTONUSA_COMMENT
      elif self.Type.search(LastWord):
        # Set Type Keyword style
        Style = self.STC_VOTONUSA_TYPE
      elif self.Operation.search(LastWord):
        # Set Operation Keyword style
        Style = self.STC_VOTONUSA_OPERATION
      elif self.Value.search(LastWord):
        # Set Value Keyword style
        Style = self.STC_VOTONUSA_VALUE
      elif self.Qualifier.search(LastWord):
        # Set Qualifier Keyqord style
        Style = self.STC_VOTONUSA_QUALIFIER
      elif self.Object.search(LastWord):
        # Set Object Keyword style
        Style = self.STC_VOTONUSA_OBJECT
      # Set the styling byte information for length of LastWord from
      # current styling position (StartPos) with the given style.
      Obj.SetStyling(len(LastWord), Style)
      StartPos += len(LastWord)

class CustomSTC(stc.StyledTextCtrl):
  def __init__(self, parent):
    super(CustomSTC, self).__init__(parent)
    # Attributes
    self.custlex = None
    Font = wx.Font(12, wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
    Face = Font.GetFaceName()
    Size = Font.GetPointSize()

    # Setup
    kwlist = u" ".join(keyword.kwlist)
    self.SetKeyWords(0, kwlist)
    self.StyleClearAll()
    self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)
    self.EnableLineNumbers()
    FontSet = "face:%s, size:%d" % (Face, Size)

    self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, FontSet)
    # Set Default to Black
    self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:#000000" + FontSet)
    # Set Comment to Pink
    self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)
    # Set Value to Green
    self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_VALUE, "fore:#00ff00" + FontSet)
    # Set Object to Brown
    self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OBJECT, "fore:#a52a2a" + FontSet)
    # Set Type to Red
    self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_TYPE, "fore:#ff0000" + FontSet)
    # Set Operation to Blue
    self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_OPERATION, "fore:#0000ff" + FontSet)
    # Set Qualifier to Orange
    self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_QUALIFIER, "fore:#cc3232" + FontSet)

    # Event Handlers
    self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)

  def EnableLineNumbers(self, enable=True):
    """Enable/Disable line number margin"""
    if enable:
      self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
      self.SetMarginMask(1, 0)
      self.SetMarginWidth(1, 25)
    else:
      self.SetMarginWidth(1, 0)

  def OnStyle(self, event):
    # Delegate to custom lexer object if one exists
    if self.custlex:
      self.custlex.StyleText(event)
    else:
      event.Skip()

  def SetLexer(self, lexerid, lexer=None):
    """
    Overrides StyledTextCtrl.SetLexer
    Adds optional param to pass in custom container
    lexer object.
    """
    self.custlex = lexer
    super(CustomSTC, self).SetLexer(lexerid)

class NUSAIPT(wx.Frame):
  def __init__(self, *args, **kwargs):
    wx.Frame.__init__(self, None, wx.ID_ANY, 'VOTO NUSA IPT')
    self.TextArea = CustomSTC(self)
    self.Show()

app = wx.App()
frame = NUSAIPT()
app.MainLoop()

If I change

self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA)

into

self.SetLexer(stc.STC_LEX_CPP)

and

self.StyleSetSpec(STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT, "fore:#ff007f" + FontSet)

into

self.StyleSetSpec(stc.STC_C_COMMENT, "fore:#ff007f" + FontSet)

the comment highlighting worked. so the mistake should be in self.SetLexer(wx.NewId(), STC_LEX_VOTONUSA) or STC_LEX_VOTONUSA.STC_VOTONUSA_COMMENT

Thanks in advance. Hope to see some answer soon.


Solution

  • Just stuck with same problem yesterday, here are my results. Hope it's not too late :)

    First, i set lexer to this one, and internal lexer as you do:

    self.SetLexer(stc.STC_LEX_CONTAINER)
    self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
    

    Bind to event and OnStyle method are same.

    And also i changed id of styles from wx.newId() to just numbers starting from 1. Without it i didn't see any styling at all. Also stc.STC_STYLE_DEFAULT started to work too.

    Full listing:

    class SCT_LEX_ERLANG_IDNOISE(BaseLexer):
        STC_ERLANG_IDNOISE_DEFAULT = 1
        STC_ERLANG_IDNOISE_VARIABLE = 2
        STC_ERLANG_IDNOISE_ATOM = 3
        STC_ERLANG_IDNOISE_MODULE = 4
        STC_ERLANG_IDNOISE_KEYWORD = 5
        STC_ERLANG_IDNOISE_COMMENT = 6
        STC_ERLANG_IDNOISE_MACROS = 7
        STC_ERLANG_IDNOISE_NUMBER = 8
    
        def __init__(self, control):
            super(SCT_LEX_ERLANG_IDNOISE, self).__init__(control)
            self.typeFormatDict = {}
            self.typeFormatDict["other"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
            self.typeFormatDict["variable"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE
            self.typeFormatDict["atom"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM
            self.typeFormatDict["module"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE
            self.typeFormatDict["keyword"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD
            self.typeFormatDict["comment"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT
            self.typeFormatDict["macros"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS
            self.typeFormatDict["number"] = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER
    
        def StyleText(self, event):
            start = self.control.GetEndStyled()
            end = event.GetPosition()
            line = self.control.LineFromPosition(start)
            start = self.control.PositionFromLine(line)
            text = self.control.GetTextRange(start, end)
    
            self.control.StartStyling(start, 0x1f)
            lastEnd = 0
            for type, start, end, value in getHighlightRules(text):
                style = SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT
                if start > lastEnd:
                    self.control.SetStyling(start - lastEnd, style)
                if type in self.typeFormatDict:
                    style = self.typeFormatDict[type]
                self.control.SetStyling(len(value), style)
                lastEnd = end
    
    
    class CustomSTC(stc.StyledTextCtrl):
        def __init__(self, parent):
            super(CustomSTC, self).__init__(parent)
            self.custlex = SCT_LEX_ERLANG_IDNOISE(self)
    
            #self.SetKeyWords(0, kwlist)
    
            self.SetLexer(stc.STC_LEX_CONTAINER)
            self.EnableLineNumbers()
    
            self.StyleSetSpec(stc.STC_STYLE_DEFAULT, ColorSchema.formats["other"])
            self.StyleClearAll()
            self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, ColorSchema.lineFont)
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_DEFAULT, ColorSchema.formats["other"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_VARIABLE, ColorSchema.formats["variable"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_ATOM, ColorSchema.formats["atom"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MODULE, ColorSchema.formats["module"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_KEYWORD, ColorSchema.formats["keyword"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_COMMENT, ColorSchema.formats["comment"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_MACROS, ColorSchema.formats["macros"])
            self.StyleSetSpec(SCT_LEX_ERLANG_IDNOISE.STC_ERLANG_IDNOISE_NUMBER, ColorSchema.formats["number"])
    
            # Event Handlers
            self.Bind(stc.EVT_STC_STYLENEEDED, self.OnStyle)
    
        def EnableLineNumbers(self, enable=True):
            """Enable/Disable line number margin"""
            if enable:
                self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
                self.SetMarginMask(1, 0)
                self.SetMarginWidth(1, 35)
            else:
                self.SetMarginWidth(1, 0)
    
        def OnStyle(self, event):
            if self.custlex:
                self.custlex.StyleText(event)
            else:
                event.Skip()