应用需求:failcase出现的太多,导致在一个静态文本区不能完全显示出来。
知识搜索:前期考虑用Tk,实践发现这个模块和staicText是不兼用的,会出现两个界面;后来搜索到wx.ScrolledWindow,在wxPython中,滚动条不是框架本身的一个元素,而是被类wx.ScrolledWindow控制。你可以在任何你要使用wx.Panel的地方使用wx.ScrolledWindow,并且滚动条移动所有在滚动窗口中的项目。
额外说一点就是TextCtrl,它有一个参数也是控制滚动条的。
最终效果图:
代码区(标注的地方是本人做的修改):
'''
Created on 2014-7-17
@author: penny_jin
'''
import wx
import wx.grid
import subprocess
import platform
import os
import sys
import shutil
import time
import re
from xml.etree import ElementTree
import glob
import logging
import tkMessageBox
from Tkinter import Tk
IsDLL = "--dll"
class MainFrame(wx.Frame):
def __init__(self, parent, ID, title):
logging.info("Start DCERF Check Frame")
wx.Frame.__init__(self,parent,ID,title,wx.DefaultPosition,wx.Size(750,750))
# add scrollbar by xuan_qi
panel=wx.ScrolledWindow(self,-1)
panel.SetScrollbars(1,1,1500,1500)
self.btn0 = wx.Button(panel,-1,'Cancel',pos=(0,2),size=(50,22))
self.Bind(wx.EVT_BUTTON, self.OnClickBtn0, self.btn0)
self.btn0.SetDefault()
self.btn2 = wx.Button(panel,-1,'Run',(80,2))
self.Bind(wx.EVT_BUTTON, self.OnClickBtn2, self.btn2)
self.btn2.SetDefault()
self.btn3 = wx.Button(panel,-1,'RunAll',(160,2))
self.Bind(wx.EVT_BUTTON, self.OnClickBtn3, self.btn3)
self.btn3.SetDefault()
self.btn4 = wx.Button(panel,-1,'RunDebugMode2',(240,2))
self.Bind(wx.EVT_BUTTON, self.OnClickBtn4, self.btn4)
self.btn4.SetDefault()
self.btn5 = wx.Button(panel,-1,'OpenDCELog',(345,2))
self.Bind(wx.EVT_BUTTON, self.OnClickBtn5, self.btn5)
self.btn5.SetDefault()
samplelist = ['dll','exe']
rx = wx.RadioBox(panel,-1,"Change Current TSC Version",(440,0),wx.DefaultSize,samplelist,1,wx.RA_SPECIFY_ROWS)
self.Bind(wx.EVT_RADIOBOX, self.OnRadio, rx)
self.btn1 = wx.Button(panel,-1,'RunRIDE',pos=(650,2))
self.Bind(wx.EVT_BUTTON, self.OnClickBtn1, self.btn1)
self.btn1.SetDefault()
selectAll = wx.CheckBox(panel,-1,"",pos=(10,30),name='SelectedAll')
self.Bind(wx.EVT_CHECKBOX, self.OnSelectedAll, selectAll)
wx.StaticText(panel,-1,"CaseID",pos=(50,30))
wx.StaticText(panel,-1,"FailReason",pos=(160,30))
#----- create fail case list table --------#
## get fail case count ##
(self.fail_case_list,fail_case_reasons) = self.getFailCaseStatus()
if len(self.fail_case_list) == 0:
logging.warning("get fail case count 0")
Tk().withdraw()
tkMessageBox.showinfo("Please Check", "No fail case found,please check by manual.")
return
else:
logging.info("Fail case count %i" % len(self.fail_case_list))
self.case_directory_info = self.get_case_directory(self.fail_case_list)
i = 0
self.cb_list = []
for caseid in self.fail_case_list:
cb = wx.CheckBox(panel,-1,"",pos=(10,50+i*20),name=self.case_directory_info[caseid])
wx.HyperlinkCtrl(panel,-1,caseid,url="", pos=(40,50+i*20))
wx.StaticText(panel,-1,fail_case_reasons[caseid],pos=(150,50+i*20))
self.Bind(wx.EVT_HYPERLINK, self.OnClickCaseID)
self.cb_list.append(cb)
i+=1
# cancel selected checkbox, and set them to disable
def OnClickBtn0(self,event):
for cb in self.cb_list:
cb.SetValue(False)
# open RIDE for case content view #
def OnClickBtn1(self, event):
subprocess.Popen("python C:\\Python27\\Scripts\\ride.py",creationflags=subprocess.CREATE_NEW_CONSOLE)
# Run multi or single case in new console#
def OnClickBtn2(self,event):
#global IsDLL
singleCmd = r"python C:\TestDCE\StartTest.py --pause %s --case=" %IsDLL
for i, cb in enumerate(self.cb_list):
if cb.GetValue():
singleCmd += self.fail_case_list[i] + ','
if ',' not in singleCmd:
logging.warn("Not select case to run")
return
else:
singleCmd = singleCmd[0:len(singleCmd)-1]
logging.info("RunCaseCmd:%s" %singleCmd)
#self.btn2.Disable()
subprocess.Popen(singleCmd,creationflags=subprocess.CREATE_NEW_CONSOLE)
#max wait process terminate time = 600s 10mins
"""
x = 1
while x < 300:
if process.poll()== None:
time.sleep(2)
x+=1
else:
self.btn2.Enable()
break
self.btn2.Enable()
"""
"""
caselist = ''
for line in process.stdout.readlines():
m2 = re.match('(.*) :: .* | FAIL |$',line)
if m2 is not None and m2.group(1) is not None:
caselist+=m2.group(1)
print m2.group(1)
print line
if 0 != len(caselist):
Tk().withdraw()
tkMessageBox.showinfo("Case Fail!", caselist)
return
Tk().withdraw()
tkMessageBox.showinfo("Pass!", singleCmd)
"""
# Run all failed cases in new console#
def OnClickBtn3(self,event):
commandLine = r"python C:\TestDCE\StartTest.py %s --pause --failcase=NewFailCase.xml" %IsDLL
subprocess.Popen(commandLine,creationflags=subprocess.CREATE_NEW_CONSOLE)
# Run only one case step by step(in debugmode=2)
def OnClickBtn4(self,event):
singleCmd = r"python C:\TestDCE\StartTest.py --pause %s --debug=2 --case=" %IsDLL
for i, cb in enumerate(self.cb_list):
if cb.GetValue():
singleCmd += self.fail_case_list[i] + ','
if ',' not in singleCmd:
logging.error("Not select case to run")
Tk().withdraw()
tkMessageBox.showinfo("Error", "Not select case to run")
return
elif len(re.findall(',',singleCmd)) > 1:
logging.error("Debug mode only support execute one case at one time")
Tk().withdraw()
tkMessageBox.showinfo("Error", "Debug mode only support run one case at one time")
return
else:
singleCmd = singleCmd[0:len(singleCmd)-1]
logging.info("RunCaseCmd:%s" %singleCmd)
subprocess.Popen(singleCmd,creationflags=subprocess.CREATE_NEW_CONSOLE)
# Open selected case dce debug log folder,only support open one log folder one time
def OnClickBtn5(self,event):
singleCmd = "explorer.exe c:\TestDCE\dcelogs\\"
for i, cb in enumerate(self.cb_list):
if cb.GetValue():
singleCmd += self.fail_case_list[i] + ','
if ',' not in singleCmd:
logging.error("Not select case")
wx.MessageBox("Not select case to open log")
return
elif len(re.findall(',',singleCmd)) > 1:
logging.error("Only support Open one dcelog at one time")
wx.MessageBox("Only support Open one dcelog at one time")
return
else:
singleCmd = singleCmd[0:len(singleCmd)-1]
bitness = platform.machine()
if '64' in bitness:
singleCmd +='_X64'
logging.info(singleCmd)
subprocess.call(singleCmd)
def OnRadio(self,event):
global IsDLL
text = event.GetEventObject().GetStringSelection()
if 'dll' in text:
IsDLL = '--dll'
else:
IsDLL = ''
logging.info("Change tsc version to %s" %text)
def OnSelectedAll(self,event):
if event.GetEventObject().GetValue():
logging.info("Select all fail case" )
for i, cb in enumerate(self.cb_list):
cb.SetValue(True)
def OnClickCaseID(self,event):
caseID = event.GetEventObject().GetLabel()
#print '\n---- OnButton_FrameHandler() for %s', %(caseID.GetLabelText())
commandline = r"explorer.exe c:\TestDCE\testcases\%s\"" %self.case_directory_info[caseID]
logging.info(commandline)
subprocess.call(commandline)
def getFailCaseStatus(self):
"""get fail case count
Get names of test cases from output.xml by status
"""
file_names = glob.glob('C:\\TestDCE\\testlogs\\*.xml')
if 0 == len(file_names):
logging.ERROR("Can't find output file")
return
else:
file_path = file_names[0]
if not os.access(file_path, os.F_OK):
logging.warning('%s does not exist, maybe you are run normal case with rebootcase mode' % file_path)
fail_case_list = []
#case_directory_info={};
cases_reason={}
root = ElementTree.parse(file_path)
test_case_list = root.getiterator('test')
# execute case count
#casecount = len(test_case_list)
for test_case in test_case_list:
if 'FAIL' == test_case.find('status').attrib['status']:
fail_case_list.append(test_case.attrib['name'])
#case_directory_info[test_case.attrib['name']]=test_case.get
flag = 0
messagelist = list(test_case.iter('msg'))
for msg in messagelist:
if 'FAIL' == msg.attrib['level']:
#print test_case.attrib['name'] + '--------' +msg.text
cases_reason[test_case.attrib['name']]=msg.text
flag = 1
break
##may be setup or teardown failed
if flag == 0:
statuslist = list(test_case.iter('status'))
for sts in statuslist:
if 'yes' == sts.get('critical'):
#print test_case.attrib['name'] + '--------' +sts.text
cases_reason[test_case.attrib['name']]=sts.text
break
return (fail_case_list, cases_reason)
def get_case_directory(self,fail_case_list):
test_command = ''
for case_id in fail_case_list:
test_command +='--test %s ' %case_id
pybot_command = 'pybot '+ test_command +' --runmode DryRun --output NONE c:\\Testdce\\testcases'
process = subprocess.Popen(pybot_command, shell=True,stdout=subprocess.PIPE)
return_code = process.stdout.readlines()
caseSuite = ""
caseOperator = ""
flag = 0
case_directory_info={}
for line in return_code:
if flag ==2:
flag =0
continue
if flag == 0:
#m = re.match("(.*) :: .* | Pass |",line)
m = re.match('Testcases\.(.*)\.(.*)\.(.*)',line)
if m is not None:
if ':' in m.group(1) or '.' in m.group(1):
casepath = line.split('.')
caseSuite = casepath[1].replace(' ','_')
caseOperator = casepath[2].replace(' ','_')
else:
caseSuite = m.group(1).replace(' ','_')
caseOperator = m.group(2).replace(' ','_')
flag = 1
m = None
continue
if flag == 1:
if "=======" in line:
continue
if "------" in line:
continue
m = re.match('(.*) :: .* | Pass |',line)
if m is not None and m.group(1) is not None:
if '.' in m.group(1):
continue
case_directory_info[m.group(1)]= caseSuite + "\\" + caseOperator
logging.warning("%s >> %s" % (m.group(1),case_directory_info[m.group(1)]))
else:
flag = 2
return case_directory_info
def OnCloseWindow(self, event):
dial = wx.MessageDialog(None, 'Are you sure to quit?', 'QUIT?',
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
ret = dial.ShowModal()
if ret == wx.ID_YES:
self.Destroy()
else:
event.Veto()
class DiceApp(wx.App):
def OnInit(self):
frame = MainFrame(None, -1, "DCERF.py")
frame.Show(True)
self.SetTopWindow(frame)
return True
def OnCloseFrame(self, evt):
evt.Skip()
def set_logger(logFileName):
if os.path.exists(logFileName) and os.path.getsize(logFileName) > 30 * 1024 * 1024:
index = 1
while os.path.exists(logFileName + '.' + str(index)):
index = index + 1
#logfileName = logFile + '.' + str(index)
shutil.move(logFileName, logFileName + '.' + str(index))
logging.basicConfig(filename=logFileName, format='%(asctime)s %(levelname)-8s %(message)s', level=logging.DEBUG)
#define a Handler which writes INFO messages or higher to the sys.stderr
console = logging.StreamHandler()
console.setLevel(logging.INFO)
#set a format which is simpler for console use
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
#tell the handler to use this format
console.setFormatter(formatter)
#add the handler to the root logger
logging.getLogger('').addHandler(console)
if __name__ == '__main__':
set_logger('c:\TestDCE\DCERF_handle_result.log')
app = DiceApp(False)
app.MainLoop()