更新了wxPython之后,运行程序,发现以前OK的程序莫名报错了……
wx._core.wxAssertionError: C++ assertion “!(flags & wxALIGN_BOTTOM)” failed at …\src\common\sizer.cpp(2136) in wxBoxSizer::DoInsert(): Vertical alignment flags are ignored in vertical sizers
python-BaseException
老版本的wxPython能忽略类似这样的错误,但是4.1之后就不被忽略了……
对于我们这些使用wxFormBuilder的人来说…… 遇到了一点点小麻烦……
当我们改变了某个Sizer的wxVERTICAL或wxHORIZONTAL后,在运行时就会遇到类似的错误
但是~ 此时的wxFormBuilder界面的ToolBar上无法对报错的对齐属性进行编辑了……
下图:我遇到的报错是 m_staticText2 的 wxALIGN_BOTTOM 不能被使用于 Sizer 的 wxVERTICAL 模式下,我需要把它去掉,但无法快捷使用ToolBar上的按钮了。
只能这样做:
打开有问题的控件的属性页的最后一项:flag,把有问题的属性去掉即可。
另外,这样的问题,每执行一次只能找到一个。如果想批量找出来的话,就只能自己写段分析代码了。
原始的.fbp文件是xml格式的,可以分析每个sizer的下一层(注1)里的控件是否flag属性里有冲突项。
注1:不是XML的层,实际的XML中多了一层 sizeritem
反正就是如下几种情况:
1. 竖着放的Sizer下的控件不能有Align Top Button 竖居中
2. 横着放的Sizer下的控件不能有Align Left Right 横居中
3. Sizer 和 Spacer 不能存在Align属性…
import time
import xml.etree.ElementTree as EleTree
class CheckFbp:
def __init__(self, fn):
with open(fn, "r") as f:
s = f.read()
self.fn = fn
self.root = EleTree.fromstring(s)
self.treeLvName = list()
self.errList = list()
def makeOneLevel(self, ele):
thisLv = dict()
children = list()
for i in ele: # type: EleTree.Element
if i.tag == "object":
if i.attrib['class'] == "spacer" or i.attrib['class'].endswith("Sizer"):
thisLv['class'] = i.attrib['class']
childLv = self.makeOneLevel(i)
children.append(childLv)
try:
if i.attrib['name'] == "name":
thisLv['name'] = i.text
except Exception:
pass
try:
if i.attrib['name'] == "orient":
thisLv['orient'] = i.text
except Exception:
pass
try:
if i.attrib['name'] == "flag" and i.text:
thisLv['flag'] = i.text
except Exception:
pass
thisLv['children'] = children
return thisLv
def makeTree(self):
self.theTree = self.makeOneLevel(self.root)
def check(self):
self.makeTree()
for i in self.theTree['children']:
self.checkOneLv(i)
for i in self.errList:
print(i)
def checkOneLv(self, data):
self.treeLvName.append(data['name'] if "name" in data.keys() else "(SIZERITEM)")
if "class" in data.keys():
if data['class'] == "spacer":
if "flag" in data.keys() and data['flag'].find("wxALIGN_") >= 0:
self.errList.append("spacerNoAlign: %s, %s" % (self.getNameTreeNow(), "spacer"))
else:
if "flag" in data.keys() and data['flag'].find("wxALIGN_") >= 0:
self.errList.append("sizerNoAlign: %s, %s" % (self.getNameTreeNow(), data['children'][0]['name']))
if data['children']:
for i in data['children']:
self.checkOneLv(i)
if "orient" in data.keys():
for i in data['children']:
if "flag" in i.keys() and "class" not in i.keys() and i['flag'] is not None:
if data['orient'] == "wxVERTICAL":
if i['flag'].find("wxALIGN_TOP") >= 0 \
or i['flag'].find("wxALIGN_BOTTOM") >= 0 \
or i['flag'].find("wxALIGN_CENTER_VERTICAL") >= 0:
self.errList.append("NoTopBottomV: %s, %s" % (self.getNameTreeNow(),
i['children'][0]['name']))
else:
if i['flag'].find("wxALIGN_LEFT") >= 0 \
or i['flag'].find("wxALIGN_RIGHT") >= 0 \
or i['flag'].find("wxALIGN_CENTER_HORIZONTAL") >= 0:
self.errList.append("NoLeftRightH: %s, %s" % (self.getNameTreeNow(),
i['children'][0]['name']))
self.treeLvName.pop(-1)
def getNameTreeNow(self):
s = ", ".join(self.treeLvName)
return s.replace(", (SIZERITEM)", "")
def isInErrList(self):
tmpList = list()
for i in self.nameList_whenUpdate:
tmpList.append(i['name'])
s = ", ".join(tmpList)
s = s.replace(", (SIZERITEM)", "")
for i in self.errList:
ss = i.split(": ")
if ss[1] == s:
return True, ss[0]
return False, ""
def updateSizerItemLine(self, line, n, m):
ss = line[n:m].split("|")
for i, s in enumerate(ss):
if s.find("wxALIGN_") >= 0:
ss.pop(i)
s = "|".join(ss)
s = line[:n] + s + line[m:]
return s
def updateErrFlagLine(self, flag2, line, n, m):
ss = line[n:m].split("|")
for i, s in enumerate(ss):
if flag2 == "NoTopBottomV":
if s in ["wxALIGN_TOP", "wxALIGN_BOTTOM", "wxALIGN_CENTER_VERTICAL"]:
ss.pop(i)
else: # flag2 == "NoLeftRightH"
if s in ["wxALIGN_LEFT", "wxALIGN_RIGHT", "wxALIGN_CENTER_HORIZONTAL"]:
ss.pop(i)
s = "|".join(ss)
s = line[:n] + s + line[m:]
return s
def updateSizerAndSpacer(self, lines):
i = 0
while i < len(lines):
if lines[i].find('<object class=') >= 0:
className = lines[i].split('"')[1]
if className.endswith("Sizer") or className == "spacer":
j = i
while True:
j -= 1
n = lines[j].find('<property name="flag">')
if n >= 0:
n += 22
m = lines[j].find("<", n)
lines.insert(j, self.updateSizerItemLine(lines.pop(j), n, m))
break
if lines[j].find('<object class=') >= 0:
break
i += 1
def make_UPDATE_fbp(self):
if not self.errList:
print("No Error... checked ???")
return
for i, n in enumerate(self.errList):
if not n.startswith("No"):
self.errList.pop(i)
with open(self.fn, "r") as f:
lines = f.readlines()
self.nameList_whenUpdate = list()
self.updateSizerAndSpacer(lines)
newDict = dict()
i = -1
while i < len(lines) - 1:
i += 1
line = lines[i]
if line.find('<object class=') >= 0:
classStr = line.split('"')[1]
newDict = {
'name': "(SIZERITEM)",
'class': classStr
}
self.nameList_whenUpdate.append(newDict)
continue
if line.find("</object>") >= 0:
self.nameList_whenUpdate.pop(-1)
continue
n = line.find('<property name="name">')
if n >= 0:
n += 22
m = line.find("<", n)
name = line[n:m]
newDict['name'] = name
flag1, flag2 = self.isInErrList()
if flag1:
j = i
while True:
j -= 1
n = lines[j].find('<property name="flag">')
if n >= 0:
n += 22
m = lines[j].find("<", n)
lines.insert(j, self.updateErrFlagLine(flag2, lines.pop(j), n, m))
break
with open(self.fn + time.strftime("_%Y%m%d%H%M%S", time.localtime()), "w") as f:
f.writelines(lines)
if __name__ == '__main__':
cf = CheckFbp(r"C:\ABC\test.fbp")
cf.check()
cf.make_UPDATE_fbp()
然后会输出结果:(会显示整条有问题的Tree Name)
NoLeftRightH: MyProject1, MyPanel14, fgSizer22, bSizer93, m_button58
NoTopBottomV: MyProject1, MyPanelSnComp, m_checkBox11
sizerNoAlign: MyProject1, MyDialog1, bSizer4, bSizer15
spacerNoAlign: MyProject1, MyDialog1, bSizer4, bSizer15, spacer
check() 负责检查与输出
make_UPDATE_fbp() 在check()之后自动修正fbp文件,并在原始文件名后加入时间戳。