I am trying to be able to display labels that are tied to a frame. I am unable to get either to show. There is something going on because packing or adding these changes the window size.
Main creates the main app, from which I create several frames that would belong to server objects. When servers are updated, they should reflect the change in the frames (and the labels attached to those frames) that are their respective attributes. However neither the frame nor labels display any text or color.
main.py
from settings.gui_settings import GLOBAL_FONT, SERVER_HEADER_COLUMN_FONT
from tkinter import *
from server import Server
from server_gui import ServerGui
SERVERS = []
# get_servers()
SERVERS = [
Server(hostname='tyan-a320-1', ipv4='10.86.201.3', bmc_ipv4='10.86.203.88'),
Server(hostname='tyan-a320-2', ipv4='10.86.201.91', bmc_ipv4='10.86.202.250'),
Server(hostname='tyan-a320-3', ipv4='10.86.201.13', bmc_ipv4='10.86.201.2'),
Server(hostname='tyan-a320-4', ipv4='10.86.201.50', bmc_ipv4='10.86.200.243'),
]
main_window = Tk()
for server in SERVERS:
server.gui = ServerGui(main_window)
server.gui.create_label('title', textvariable=server.hostname, font=(GLOBAL_FONT, SERVER_HEADER_COLUMN_FONT),\
bg='red', container=True)
server.gui.labels['title'].pack()
server.gui.pack()
#server.update()
last_update_lbl = Label(master=main_window, text=f'Last Update: {time.ctime()}')
last_update_lbl.pack(side=TOP)
#main_window.update()
main_window.mainloop()
server.py
from settings.gui_settings import UP_COLOR, DOWN_COLOR
from diagnostics import poll
from diagnostics import ping
from diagnostics import SERVICES
class Server:
def __init__(self, *args, **kwargs):
for k, v in kwargs.items():
self.__setattr__(k, v)
self.services = {}
for service in SERVICES:
self.services[service] = None
def update(self):
address = None
for service in self.services.keys():
if 'bmc' in service:
address = self.bmc_ipv4
else:
address = self.ipv4
result = None
if service == 'icmp':
result = ping(address)
else:
result = poll(address, SERVICES[service])
if result:
self.services[service] = f'{service}: UP'
else:
self.services[service] = f'{service}: DOWN'
if hasattr(self, 'gui'):
self.gui.update_label(service, 'textvariable', self.services[service])
self.gui.update_label(service, 'bg', UP_COLOR if 'UP' in self.services[service] else DOWN_COLOR)
self.gui.master.update()
server_gui.py
from tkinter import Frame, Label, StringVar
class ServerGui(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.labels = {}
def create_label(self, name, **kwargs):
if not len(name):
print("Label not named")
exit(0)
text = None
label = Label(self)
for k, v in kwargs.items():
if k == 'textvariable':
text = StringVar(master=label, value=v)
label.textvariable = text
else:
label.__setattr__(k, v)
if 'bg' not in kwargs.keys():
label.bg = LABEL_DEFAULT_COLOR
if not text:
text = StringVar(master=label, value=name)
self.labels[name] = label
self.labels[name].pack()
print('Label %s created' % name)
def update_label(self, name, attr, value):
if name not in self.labels:
self.create_label(name,
textvariable=StringVar(self, f'{name}'),
font=(GLOBAL_FONT, SERVER_INFO_FONT),
padx=10,
pady=10
)
self.labels[name].pack()
if attr == 'textvariable':
self.labels[name].textvariable.set(value)
else:
setattr(self.labels[name], attr, value)
settings/gui_settings.py
MAIN_WINDOW_MIN_WIDTH = 700
GLOBAL_FONT = 'Tahoma'
SERVER_HEADER_COLUMN_FONT = 20
SERVER_INFO_FONT = 12
UP_COLOR = 'green'
DOWN_COLOR = 'red'
LABEL_DEFAULT_COLOR = 'green'
Related
How do I get and store the value that is written in a QLineEdit as well as close the widget after I click save? I have looked over the PyQt5 documentation and it says the function .text() will give access to the values in a QLineEdit but for some reason the value I keep getting is the default empty string.
class ImageSettingsWindow(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setWindowTitle("Image Settings")
self.setFixedWidth(250)
self.setFixedHeight(300)
allImg_checkBox = QCheckBox("All Images")
lastImg_checkBox = QCheckBox("Last Image")
timedImgs_checkBox = QCheckBox("Timed Images")
start_label = QLabel("Start Duration:")
txt_start = QLineEdit()
self.data_start = txt_start.ui.text()
end_label = QLabel("End Duration:")
txt_end = QLineEdit()
self.data_end = txt_end.ui.text()
timeInt_label = QLabel("Time Interval:")
txt_timeInt = QLineEdit()
self.data_timeint = txt_timeInt.ui.text()
maxDiff_label = QLabel("Max Difference:")
txt_maxDiff = QLineEdit()
self.data_maxdiff = txt_maxDiff.ui.txt()
buttonSave = QPushButton("Save")
layout.addWidget(allImg_checkBox)
layout.addWidget(lastImg_checkBox)
layout.addWidget(timedImgs_checkBox)
layout.addWidget(start_label)
layout.addWidget(txt_start)
layout.addWidget(end_label)
layout.addWidget(txt_end)
layout.addWidget(timeInt_label)
layout.addWidget(txt_timeInt)
layout.addWidget(maxDiff_label)
layout.addWidget(txt_maxDiff)
layout.addWidget(buttonSave)
print(self.data_start)
self.setLayout(layout)
buttonSave.clicked.connect(self.clickedSave)
def clickedSave(self):
#data_start = self.
print("button")
try:
json_image = open("config/image.json","r")
param = json.load(json_image)
json_image.close()
param["start_duration"] = int(self.data_start)
param["end_duration"] = int(self.data_end)
param["time_interval"] = float(self.data_timeint)
param["max_diff"] = int(self.data_maxdiff)
json_image = open("config/image.json", "w")
json.dump(param,json_image)
json_image.close()
except IOError as err :
print("Error writing the image.json file , please double check")
The problem is that you are obtaining the text when the window is being built, so the user cannot interact with the GUI, instead you must obtain the information within clickedSave.
On the other hand, I see that this code can never be executed (which is different from failing) since no QLineEdit has a "ui" attribute in addition to similar errors(typos), so please try to provide a better code for the next time.
class ImageSettingsWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Image Settings")
self.setFixedSize(250, 300)
allImg_checkBox = QCheckBox("All Images")
lastImg_checkBox = QCheckBox("Last Image")
timedImgs_checkBox = QCheckBox("Timed Images")
start_label = QLabel("Start Duration:")
self.txt_start = QLineEdit()
end_label = QLabel("End Duration:")
self.txt_end = QLineEdit()
timeInt_label = QLabel("Time Interval:")
self.txt_timeInt = QLineEdit()
maxDiff_label = QLabel("Max Difference:")
self.txt_maxDiff = QLineEdit()
buttonSave = QPushButton("Save")
layout = QVBoxLayout(self)
layout.addWidget(allImg_checkBox)
layout.addWidget(lastImg_checkBox)
layout.addWidget(timedImgs_checkBox)
layout.addWidget(start_label)
layout.addWidget(self.txt_start)
layout.addWidget(end_label)
layout.addWidget(self.txt_end)
layout.addWidget(timeInt_label)
layout.addWidget(self.txt_timeInt)
layout.addWidget(maxDiff_label)
layout.addWidget(self.txt_maxDiff)
layout.addWidget(buttonSave)
buttonSave.clicked.connect(self.clickedSave)
def clickedSave(self):
try:
param = dict()
with open("config/image.json", "r") as json_image:
param = json.load(json_image)
param["start_duration"] = int(self.txt_start.text())
param["end_duration"] = int(self.txt_end.text())
param["time_interval"] = float(self.txt_timeInt.text())
param["max_diff"] = int(self.txt_maxDiff.text())
with open("config/image.json", "w") as json_image:
json.dump(param, json_image)
except IOError as err:
print("Error writing the image.json file , please double check", err)
I am trying to add UserControl to a WinForm using PythonNet but not having any luck. For testing, I added a button and that shows up but not the UserControl and I am not sure what I a doing wrong.
All the code can be placed into one py file. I broke into a few sections hoping it will be easier to read.
USER CONTROL
class Field(WinForms.UserControl):
def __init__(self):
self.InitializeComponents()
pass
def InitializeComponents(self):
self.components = System.ComponentModel.Container()
self.label = WinForms.Label()
self.textBox = WinForms.Label()
## Label
# self.label.Anchor = ((WinForms.AnchorStyles)(
# ((WinForms.AnchorStyles.Top | WinForms.AnchorStyles.Bottom)
# | WinForms.AnchorStyles.Left)))
self.label.AutoSize = True
self.label.Location = System.Drawing.Point(3, 7)
self.label.Name = "label"
self.label.Size = System.Drawing.Size(29, 13)
self.label.TabIndex = 0
self.label.Text = "label"
## TextBox
# self.textBox.Anchor = ((WinForms.AnchorStyles)(
# (((WinForms.AnchorStyles.Top | WinForms.AnchorStyles.Bottom)
# | WinForms.AnchorStyles.Left)
# | WinForms.AnchorStyles.Right)))
# self.textBox.Location = System.Drawing.Point(115, 3)
self.textBox.Name = "textBox"
self.textBox.Size = System.Drawing.Size(260, 20)
self.textBox.TabIndex = 1
## Control
self.AutoScaleMode = WinForms.AutoScaleMode.Font
self.Controls.Add(self.textBox)
self.Controls.Add(self.label)
self.Name = "Field"
self.Size = System.Drawing.Size(378, 26)
# self.PerformLayout()
PARENT FORM
class ParentForm(WinForms.Form):
def __init__(self):
self.InitializeComponents()
# region Form Design
def InitializeComponents(self):
self.components = System.ComponentModel.Container()
self.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
self.ClientSize = System.Drawing.Size(385, 180)
self.Name = "ParentForm"
self.Text = "Parent Form"
self.field1 = Field()
self.field1.Location = Point(13, 13)
self.field1.Name = "field1"
self.field1.Size = Size(378, 26)
self.field1.TabIndex = 1
self.Controls.Add(self.field1)
self.button1 = WinForms.Button()
self.button1.Location = Point(13, 50)
self.button1.Size = Size(50, 20)
self.button1.Text = "Button1"
self.Controls.Add(self.button1)
pass
def Dispose(self):
self.components.Dispose()
WinForms.Form.Dispose(self)
ENTRY POINT AND IMPORTS
import clr
import System
import System.Windows.Forms as WinForms
from System.IO import File
from System.Text import Encoding
from System.Drawing import Color, Point, Size
from System.Threading import ApartmentState, Thread, ThreadStart
def appThread():
app = ParentForm()
WinForms.Application.Run(app)
app.Dispose()
def appEntry():
thread = Thread(ThreadStart(appThread))
thread.SetApartmentState(ApartmentState.STA)
thread.Start()
thread.Join()
if __name__ == '__main__':
appEntry()
I'm creating 3 groups of ttk.CheckButtons, using a class clsGrpCheckButton.
The problem is that I can access only the status of the last group created, and not for the previous group.
When clicking on one of the checkButtons of any group, I'm expecting to get the list of checkbuttons check in the group (by using method chkGrpGetValue with is the command parameter of each checkbutton)
However, whatever which button is clicked, the method only and always returns the status of the last group
Here is the code to recreate the issue, and in attachment a picture that shows the problems
Thks for your help.
Rgds
import tkinter as tk
from tkinter import ttk
import pandas as pd
class clsGrpCheckButton(ttk.Checkbutton):
def __init__(self,pContainer, pLstVal,pCommand,pInitValue=True):
self.grpChk=[0]*len(pLstVal)
self.grpKey=[0]*len(pLstVal)
self.grpLstVal = [0]*len(pLstVal)
self.grpVariable= [0]*len(pLstVal)
self.grpActiveKeys = [0]
for l,t in enumerate(pLstVal):
#l : index of the list of tuples
self.grpKey[l] = t[0]
self.grpLstVal[l] = t[1]
self.grpVariable[l] = tk.StringVar()
self.grpChk[l] = ttk.Checkbutton(pContainer, text=self.grpLstVal[l],
state='active' ,
onvalue= self.grpKey[l],
offvalue= '',
variable = self.grpVariable[l],
command=pCommand)
#get default value
if pInitValue :
self.grpVariable[l].set(self.grpKey[l])
self.grpActiveKeys.append(self.grpKey[l])
#get the index in the list of checkboxes
# depending on the key
def chkGrpGetIdx(self, pKey):
i=0
while i <len(self.grpKey):
if self.grpKey[i]==pKey :
return i
i=len(self.grpKey)
else:
i+=1
def chkGrpSetValue(self, pKey, pValue):
#need find correct index first
i=self.chkGrpGetIdx(pKey)
self.grpVariable[i] = pValue
#return the list of keys of the group
def chkGrpKeyLst(self):
return self.grpKey
#return the checkox element of the group of checkox
def chkGrpGetChkObj(self,pKey):
i=self.chkGrpGetIdx(pKey)
return self.grpChk[i]
#action when check/uncheck
#at list one element should be active
def chkGrpGetValue(self):
i=0
r=len(self.grpVariable)
self.grpActiveKeys.clear()
while i < len(self.grpVariable):
if self.grpVariable[i].get() =='':
r-=1
else:
self.grpActiveKeys.append(self.grpKey[i])
i+=1
if r==0:
self.grpVariable[0].set(self.grpKey[0])
self.grpActiveKeys.append(self.grpKey[0])
print(self.grpActiveKeys)
#to avoid accessing to class attribute directly
def chkGetCheckedValues(self):
return self.grpActiveKeys
class clsWindows(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
la = [1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3]
lb= [10,11,12,14,15,20,21,22,23,24,25,26,30,31,32,33]
lc=['d10','d11','d12','d14','d15','d20','d21','d22','d23','d24','d25','d26','d30','d31','d32','d33']
df = pd.DataFrame(
{'DIVISION': la,
'DEPT_CODE': lb,
'DEPT_NAME': lc
})
lW = list(zip(df['DIVISION'].astype(str) , df['DEPT_CODE'].astype(str)))
lpt = list(zip(df['DEPT_CODE'].astype(str) , df['DEPT_NAME'].astype(str)))
curHead = ""
r=0
c=-1
for head, DPT in lW:
if not curHead==head:
curHead = head
c+=1
r=0
dq=df.query('DIVISION=='+head)
lpt = list(zip(dq['DEPT_CODE'].astype(str) , dq['DEPT_NAME'].astype(str)))
t=ttk.Labelframe(self,text=head)
t.grid(column=c, row=0, sticky='nw')
self.checkGrpDept= clsGrpCheckButton(t,lpt,lambda:self.checkGrpDept.chkGrpGetValue(),True)
self.checkGrpDept.chkGrpGetChkObj(DPT).grid(column=c, row=r, sticky='nw')
t.rowconfigure(r, weight=1)
t.columnconfigure(c, weight=1)
r+=1
def wQuit(self):
self.destroy()
app = clsWindows()
app.mainloop()
Example of issue
finally, I came up with a solution by using partial when assigning the command to each checkbutton.
Here is the full code if someone faces a similar issue
import tkinter as tk
from tkinter import ttk
import pandas as pd
from functools import partial
class clsGrpCheckButton():
def __init__(self,pContainer, pLstVal,pCommand,pInitValue=True):
self.grpChk=[0]*len(pLstVal)
self.grpKey=[0]*len(pLstVal)
self.grpLstVal = [0]*len(pLstVal)
self.grpVariable= [0]*len(pLstVal)
self.grpActiveKeys = [0]
for l,t in enumerate(pLstVal):
#l : index of the list of tuples
self.grpKey[l] = t[0]
self.grpLstVal[l] = t[1]
self.grpVariable[l] = tk.StringVar()
self.grpChk[l] = ttk.Checkbutton(pContainer, text=self.grpLstVal[l],
state='active' ,
onvalue= self.grpKey[l],
offvalue= '',
variable = self.grpVariable[l],
command=partial(pCommand,self))
#get default value
if pInitValue :
self.grpVariable[l].set(self.grpKey[l])
self.grpActiveKeys.append(self.grpKey[l])
#get the index in the list of checkboxes
# depending on the key
def chkGrpGetIdx(self, pKey):
i=0
while i <len(self.grpKey):
if self.grpKey[i]==pKey :
return i
i=len(self.grpKey)
else:
i+=1
def chkGrpSetValue(self, pKey, pValue):
#need find correct index first
i=self.chkGrpGetIdx(pKey)
self.grpVariable[i] = pValue
#return the list of keys of the group
def chkGrpKeyLst(self):
return self.grpKey
#return the checkox element of the group of checkox
def chkGrpGetChkObj(self,pKey):
i=self.chkGrpGetIdx(pKey)
return self.grpChk[i]
#action when check/uncheck
#at list one element should be active
def chkGrpGetValue(self):
i=0
r=len(self.grpVariable)
self.grpActiveKeys.clear()
while i < len(self.grpVariable):
if self.grpVariable[i].get() =='':
r-=1
else:
self.grpActiveKeys.append(self.grpKey[i])
i+=1
if r==0:
self.grpVariable[0].set(self.grpKey[0])
self.grpActiveKeys.append(self.grpKey[0])
print(self.grpActiveKeys)
#to avoid accessing to class attribute directly
def chkGetCheckedValues(self):
return self.grpActiveKeys
class clsWindows(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
la = [1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3]
lb= [10,11,12,14,15,20,21,22,23,24,25,26,30,31,32,33]
lc=['d10','d11','d12','d14','d15','d20','d21','d22','d23','d24','d25','d26','d30','d31','d32','d33']
df = pd.DataFrame(
{'DIVISION': la,
'DEPT_CODE': lb,
'DEPT_NAME': lc
})
lW = list(zip(df['DIVISION'].astype(str) , df['DEPT_CODE'].astype(str)))
lpt = list(zip(df['DEPT_CODE'].astype(str) , df['DEPT_NAME'].astype(str)))
curHead = ""
r=0
c=-1
for head, DPT in lW:
if not curHead==head:
curHead = head
c+=1
r=0
dq=df.query('DIVISION=='+head)
lpt = list(zip(dq['DEPT_CODE'].astype(str) , dq['DEPT_NAME'].astype(str)))
t=ttk.Labelframe(self,text=head)
t.grid(column=c, row=0, sticky='nw')
checkGrpDept= clsGrpCheckButton(t,lpt,clsGrpCheckButton.chkGrpGetValue,True)
checkGrpDept.chkGrpGetChkObj(DPT).grid(column=c, row=r, sticky='nw')
t.rowconfigure(r, weight=1)
t.columnconfigure(c, weight=1)
r+=1
def wQuit(self):
self.destroy()
app = clsWindows()
app.mainloop()
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.
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()
I'm trying to create a custom ComboBox that behaves like the one in here: http://chir.ag/projects/name-that-color/
I've got two problems right now:
I can't seem to find a way to have a scrollbar on the side; the gtk.rc_parse_string function should do that, since the ComboBox widget has a "appears-as-list" style property, but my custom widget seems unaffected for some reason.
When you select a color from my widget, then click the ComboBox again, instead of showing the selected item and its neighbours, the scrolled window starts from the top, for no apparent reason.
This is the code, you can pretty much ignore the __load_name_palette method. You need the /usr/share/X11/rgb.txt file to run this code, it looks like this: http://pastebin.com/raw.php?i=dkemmEdr
import gtk
import gobject
from os.path import exists
def window_delete_event(*args):
return False
def window_destroy(*args):
gtk.main_quit()
class ColorName(gtk.ComboBox):
colors = []
def __init__(self, name_palette_path, wrap_width=1):
gtk.ComboBox.__init__(self)
liststore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING)
name_palette = self.__load_name_palette(name_palette_path)
for c in name_palette:
r, g, b, name = c
if ((r + g + b) / 3.) < 128.:
fg = '#DDDDDD'
else:
fg = '#222222'
bg = "#%02X%02X%02X" % (r, g, b)
liststore.append((name, bg, fg))
self.set_model(liststore)
label = gtk.CellRendererText()
self.pack_start(label, True)
self.set_attributes(label, background=1, foreground=2, text=0)
self.set_wrap_width(wrap_width)
if len(name_palette) > 0:
self.set_active(0)
self.show_all()
def __load_name_palette(self, name_palette_path):
if exists(name_palette_path):
try:
f = open(name_palette_path,'r')
self.colors = []
palette = set()
for l in f:
foo = l.rstrip().split(None,3)
try:
rgb = [int(x) for x in foo[:3]]
name, = foo[3:]
except:
continue
k = ':'.join(foo[:3])
if k not in palette:
palette.add(k)
self.colors.append(rgb + [name])
f.close()
return self.colors
except IOError as (errno, strerror):
print "error: failed to open {0}: {1}".format(name_palette_path, strerror)
return []
else:
return []
if __name__ == '__main__':
win = gtk.Window()
#colname = ColorName('./ntc.txt')
colname = ColorName('/usr/share/X11/rgb.txt')
gtk.rc_parse_string("""style "mystyle" { GtkComboBox::appears-as-list = 1 }
class "GtkComboBox" style "mystyle" """)
print 'appears-as-list:', colname.style_get_property('appears-as-list')
model = gtk.ListStore(gobject.TYPE_STRING)
hbox = gtk.HBox()
win.add(hbox)
hbox.pack_start(colname)
win.connect('delete-event', window_delete_event)
win.connect('destroy', window_destroy)
win.show_all()
gtk.main()
The problem was the self.show_all() line. Also, you can't have a list AND a wrap_width != 1