I’m working on my pet project. This project is a Windows app that displays ttf and otf fonts. Fonts mustn’t be installed on PC to be displayed. The code below is a prototype that shows symbols as wx.StaticText, and shows font metadata in another window if user wants to read it
wxPython perfectly shows all symbols, but I get such errors as wx._core.wxAssertionError when I click “Show Metadata” button. I click once, and metadata window is displayed perfectly, I click the second time, then I get this:
wx._core.wxAssertionError: C++ assertion “m_count > 0” failed at ….srccommonobject.cpp(336) in
**wxRefCounter::DecRef(): invalid ref data count
The above exception was the direct cause of the following exception:
SystemError: <class ‘wx._core.CommandEvent’> returned a result with an exception set**
If I click the third time, the metadata window opens, if I click another time, I get the same error.
import wx
import wx.grid
import sys
from fontTools import ttLib, otlLib
from fontTools.ttLib import ttFont
import random
ID_MEANINGS = ("Copyright Notice",
"Font Family Name",
"Font Subfamily Name",
"Unique Font Identifier",
"Full Font Name",
"Version String",
"PostScript Name",
"Trademark",
"Manufacturer Name",
"Designer",
"Description",
"URL Vendor",
"URL Designer",
"License Description",
"License Info URL",
"Preferred Family",
"Preferred Subfamily",
"Compatible Full Name",
"Sample Text",
"PostScript CID Findfont Name",
"WWS Family Name",
"WWS Subfamily Name",
"Light Background Palette",
"Dark Background Palette",
"Variations PostScript Name Prefix",
"PostScript Name Prefix",
"WWS Family Name",
"WWS Subfamily Name",
"Light Background Palette",
"Dark Background Palette",
"Variations PostScript Name Prefix",
"PostScript Name Prefix")
class MyFrame(wx.Frame):
def __init__(self, parent, title):
super().__init__(parent, title=title, pos=(0, 0), size=(1300, 720))
self.SetBackgroundColour(wx.Colour("#C4CDCC"))
toolbar = self.CreateToolBar()
open_button = toolbar.AddTool(-1, "Open Font File", wx.Bitmap("icons/icons8-open-file-32.png"))
open_button.SetShortHelp("Open File")
self.bold_button = toolbar.AddCheckTool(-1, "Bold", wx.Bitmap("icons/icons8-bold-32.png"))
self.italic_button = toolbar.AddCheckTool(-1, "Italic", wx.Bitmap("icons/icons8-italic-32.png"))
self.underlined_button = toolbar.AddCheckTool(-1, "Underlined", wx.Bitmap("icons/icons8-underline-32.png"))
self.metadata_button = toolbar.AddTool(-1, "Show Font Metadata", wx.Bitmap("icons/icons8-metadata-32.png"))
self.metadata_button.SetShortHelp("Metadata")
self.Bind(wx.EVT_TOOL, self.openFile, open_button)
self.Bind(wx.EVT_TOOL, self.checkValues, self.bold_button)
self.Bind(wx.EVT_TOOL, self.checkValues, self.italic_button)
self.Bind(wx.EVT_TOOL, self.checkValues, self.underlined_button)
self.Bind(wx.EVT_TOOL, self.showMetaData, self.metadata_button)
toolbar.Realize()
panel = wx.Panel(self) # label won't be positioned without wx.Panel
self.SetBackgroundColour(wx.Colour("#EAF044"))
self.label1_font = wx.Font(pointSize=15,
family=wx.FONTFAMILY_DEFAULT,
style=wx.FONTSTYLE_ITALIC,
weight=wx.FONTWEIGHT_NORMAL,
faceName="EmptyString",
encoding=wx.FONTENCODING_DEFAULT)
self.custom_font = wx.Font(pointSize=24,
family=wx.FONTFAMILY_DEFAULT,
style=wx.FONTSTYLE_NORMAL,
weight=wx.FONTWEIGHT_NORMAL,
faceName="EmptyString",
encoding=wx.FONTENCODING_DEFAULT)
self.label1 = wx.StaticText(self,
-1,
label="",
pos=(5, 5))
self.label1.SetFont(self.label1_font)
self.label2 = wx.StaticText(self,
-1,
label="nothing",
pos=(5, 40))
self.label1.SetFont(self.label1_font)
self.label3 = wx.StaticText(self,
-1,
label="No Font Open!",
pos=(5, 70))
self.label3.SetFont(self.custom_font)
def openFile(self, *args):
dlg = wx.FileDialog(self,
"Choose a font file",
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
if dlg.ShowModal() == wx.ID_OK:
self.path = dlg.GetPath()
# opens ttf or ots font with fontTools library
self.font = ttLib.TTFont(self.path)
# searches name table of selected file
self.name_table = self.font['name']
self.font_metadata = {}
id = 0
for nameRecord in self.name_table.names:
# print(f"{id}: {nameRecord}")
metadata_element = {id: str(nameRecord)}
self.font_metadata.update(metadata_element)
id += 1
# searching Windows font name using nameRecord.nameID == 1 and nameRecord.platformID == 3, otherwise
# font won't change if its Windows name is different from UNIX-like OS name
if nameRecord.nameID == 1 and nameRecord.platformID == 3: # platformID == 3 means Windows
windows_font_name = str(nameRecord.toStr())
# if user doesn't open any font
else:
return
glyphs_number = self.font['maxp'].numGlyphs
cmap = self.font['cmap'].getcmap(3, 1).cmap
viewed_font = wx.Font.AddPrivateFont(str(self.path))
self.custom_font = wx.Font(pointSize=24,
family=wx.FONTFAMILY_DEFAULT,
style=wx.FONTSTYLE_NORMAL,
weight=wx.FONTWEIGHT_NORMAL,
faceName=windows_font_name,
encoding=wx.FONTENCODING_DEFAULT)
source = list(cmap.keys())
string = ''
for i in range(0, 40):
i = chr(random.choice(source))
string += i
string += "n"
string += "Watch “Jeopardy!”, Alex Trebek’s fun TV quiz game.n"
nums = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '@', '#', '$', '%', '&', "'", '"','*', '/', '+', '-', '=']
marks = [',', '.', ':', ';', '<', '>', '\', '|', '^', '_', '(', ')', '{', '}', '[', ']']
string += "".join(nums) + "n" + "".join(marks)
self.label1.SetLabel(f"Font Name for Windows: {windows_font_name}, Number of Glyphs: {glyphs_number}")
self.label2.SetLabel(self.path)
self.label3.SetLabel(string)
self.label3.SetFont(self.custom_font)
self.checkValues(e=None)
dlg.Destroy()
def showMetaData(self, *args):
"""Window that shows ttf or otf metadata"""
# handle AttributeError if font isn't open
if not hasattr(self, 'font_metadata') or self.font_metadata is None:
error_message = "No font open!"
wx.MessageBox(error_message, "Error", wx.OK | wx.ICON_ERROR)
return
# If the metadata frame already exists, reuse it
if hasattr(self, 'metadata_frame') and self.metadata_frame is not None:
self.metadata_frame.Show()
return
# function that makes the 3rd column change its width if window is being resized
def resize_3rd_col(e):
col3_width = metadata_frame.Size[0] - COL1_WIDTH - COL2_WIDTH
grid.SetColSize(2, col3_width)
e.Skip() # Allow the event to be processed further by the default handler or other handlers
def on_close(e):
metadata_frame.Destroy()
COLUMNS = len(self.font_metadata)
metadata_frame = wx.Frame(None, -1, "Font Metadata", pos=(0, 0), size=(1200, 700))
metadata_frame.SetBackgroundColour(wx.Colour("#ffb6c1"))
panel = wx.Panel(metadata_frame)
grid = wx.grid.Grid(panel)
grid.CreateGrid(COLUMNS, 3)
grid.SetGridLineColour(wx.Colour('black'))
COL1_WIDTH = 30
COL2_WIDTH = 180
col3_width = metadata_frame.Size[0] - COL1_WIDTH - COL2_WIDTH
grid.SetColSize(0, COL1_WIDTH)
grid.SetColSize(1, COL2_WIDTH)
grid.SetColSize(2, col3_width)
# Hide row and column labels
grid.HideRowLabels()
grid.HideColLabels()
# Create cell attributes for alignment
attr = wx.grid.GridCellAttr()
attr.SetAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER)
# The same number used to set row number and id name index
row_and_ids_index = -1
for k in self.font_metadata.keys():
row_and_ids_index += 1
grid.SetCellValue(row_and_ids_index, 0, str(row_and_ids_index))
grid.SetCellValue(row_and_ids_index, 1, ID_MEANINGS[row_and_ids_index] if row_and_ids_index < len(ID_MEANINGS) else "")
grid.SetCellValue(row_and_ids_index, 2, self.font_metadata[k])
grid.SetAttr(row_and_ids_index, 0, attr)
grid.SetAttr(row_and_ids_index, 1, attr)
grid.SetReadOnly(row_and_ids_index, 0, True)
grid.SetReadOnly(row_and_ids_index, 1, True)
grid.SetReadOnly(row_and_ids_index, 2, True)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 1, wx.EXPAND)
panel.SetSizer(sizer)
grid.Bind(wx.EVT_SIZE, resize_3rd_col)
metadata_frame.Bind(wx.EVT_CLOSE, on_close)
metadata_frame.Show()
def checkValues(self, e):
isbold, isitalic, isundrlined = self.bold_button.IsToggled(), self.italic_button.IsToggled(), self.underlined_button.IsToggled()
if isbold:
self.custom_font.SetWeight(wx.FONTWEIGHT_HEAVY)
self.label3.SetFont(self.custom_font)
elif not isbold:
self.custom_font.SetWeight(wx.FONTWEIGHT_NORMAL)
self.label3.SetFont(self.custom_font)
if isitalic:
self.custom_font.SetStyle(wx.FONTSTYLE_ITALIC)
self.label3.SetFont(self.custom_font)
elif not isitalic:
self.custom_font.SetStyle(wx.FONTSTYLE_NORMAL)
self.label3.SetFont(self.custom_font)
if isundrlined:
self.custom_font.SetUnderlined(True)
self.label3.SetFont(self.custom_font)
if not isundrlined:
self.custom_font.SetUnderlined(False)
self.label3.SetFont(self.custom_font)
app = wx.App()
frame = MyFrame(None, "Font Viewer")
frame.Show()
app.MainLoop()
I tried to print self.path, self.font, self.name_table, and self.font_metadata variables, they were printed the 1st time, weren’t the second time, were the 3rd time, and weren’t 4th time. I created these variables in showMetaData method, it didn’t help.
When I worked with tkinter, I never faced this problem
Kanstantsin Mironau is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.