应用需求: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()