Beispiel #1
0
 def editTest(self, testIdx):
     test = self.runner.getSuite()[testIdx]
     if self.editForm is None:
         self.editForm = TestEditForm(self.wHnd, testIdx, test, self.runner,
                                      self)
         self.loadIcon(self.editForm)
     else:
         self.editForm.updateTest(testIdx)
     self.editForm.Show()
Beispiel #2
0
 def editTest(self, testIdx):
     test = self.runner.getSuite()[testIdx]
     if self.editForm is None:
         self.editForm = TestEditForm(self.wHnd, testIdx, test, self.runner, self)
         self.loadIcon(self.editForm)
     else:
         self.editForm.updateTest(testIdx)
     self.editForm.Show()
Beispiel #3
0
class TestRunnerGui(wx.App):
    """Graphical User Interface"""
    modes = ["Continuous", "Halt on Fail", "Halt on Error"]
    benchtypes = [('nightmare', 'py'), ('All Files', '*')]
    duttypes = [('All Files', '*'), ('Executables', '*.exe')]

    def suiteSave(self, fn):
        self.runner.saveToFile(fn)

    def loadIcon(self, frame):
        try:
            if sys.platform == 'win32':
                exeName = sys.executable
                icon = wx.Icon(exeName + ';0', wx.BITMAP_TYPE_ICO)
                frame.SetIcon(icon)
        except:
            pass

    def saveSuite(self):
        """ Savedialog execution before saving the suite"""
        fname = self.saveFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.suiteSave(fname)

    def loadSuite(self):
        """ Load a testsuite"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.edtFile.SetValue(os.path.relpath(fname))
            self.runner.options['bench'] = fname
            self.updateFromRunner()

    def updateFromRunner(self):
        self.runner.loadSuite()
        self.lstTests.DeleteAllItems()
        self.applyToList(self.runner.getSuite().getTests(), self.insertTest)
        self.edtDUT.SetValue(str(self.runner.options['dut']))
        self.edtTests.SetValue(str(self.runner.countTests()))
        self.grpMode.SetSelection(self.runner.getSuite().mode)

    def selectDut(self):
        """Show filedialog and set the result as DUT"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.duttypes)
        if fname is not None:
            self.runner.setDUT(fname)
            self.edtDUT.SetValue(os.path.relpath(fname))

    def applyToList(self, l, f, gauge=True):
        cnt = self.runner.countTests()
        if gauge:
            self.prgGauge.SetRange(cnt - 1)
            self.prgGauge.SetValue(0)
        lastIdx = 0
        for idx, test in itertools.izip(xrange(cnt), l):
            lastIdx = idx
            if gauge:
                self.prgGauge.SetValue(idx)
                self.prgGauge.Update()
            if idx < self.lstTests.GetItemCount() - 1:
                self.lstTests.SetStringItem(idx + 1, 2, "RUNNING")
                self.lstTests.Update()
            f(idx, test)
            self.lstTests.Update()
        for i in range(lastIdx + 1, len(self.runner.getSuite())):
            self.lstTests.SetStringItem(i, 2, "CANCELED")

    def insertTest(self, idx, test):
        """Insert a new test into the test-list

        @type    idx: int
        @param     idx: The index of the test to add
        @type    test: pyTestCore.test.Test
        @param     test: The test to add
        """
        self.lstTests.InsertStringItem(idx, test.name)
        self.lstTests.CheckItem(idx)
        self.updateTest(idx, test)

    def updateTest(self, idx, test):
        """Update the information on one test in the test-list

        @type    idx: int
        @param     idx: The index of the test to change
        @type    test: pyTestCore.test.Test
        @param     test: The test to change
        """
        if test.state == TestState.Error:
            self.lstTests.CheckItem(idx, False)
            self.lstTests.SetItemBackgroundColour(idx, 'yellow')
        elif test.state == TestState.Success:
            self.lstTests.SetItemBackgroundColour(idx, 'green')
        elif test.state == TestState.Fail:
            self.lstTests.SetItemBackgroundColour(idx, 'red')
        elif test.state == TestState.Timeout:
            self.lstTests.SetItemBackgroundColour(idx, 'purple')
        elif test.state == TestState.Waiting:
            self.lstTests.SetItemBackgroundColour(idx, 'white')
        elif test.state == TestState.Disabled:
            self.lstTests.SetItemBackgroundColour(idx, 'gray')
        TermColor.active = False
        self.lstTests.SetStringItem(idx, 0, test.name)
        self.lstTests.SetStringItem(idx, 1, test.descr)
        self.lstTests.SetStringItem(idx, 2, TestState.toString(test.state))

    def setTestState(self, test, idx, state):
        """Update the state of one test, but only if the test is not enabled"""
        if test.state != TestState.Disabled:
            test.state = state
        self.updateTest(idx, test)

    def onListCheck(self, idx, flag):
        """Eventhandler for changes on the checkboxes in the list"""
        test = self.runner.getSuite()[idx]
        test.state = TestState.Waiting if flag else TestState.Disabled
        self.updateTest(idx, test)

    def __runthread(self, testIdx=None):
        """Run tests"""
        if testIdx is None:
            self.applyToList(
                self.runner.getSuite().getTests(),
                lambda i, t: self.setTestState(t, i, TestState.Waiting),
                gauge=False)
            self.applyToList(self.runner.run(), self.updateTest)
        else:
            test, = self.runner.getSuite().run(tests=[testIdx])
            self.updateTest(testIdx, test)
        self.testthread = None
        """Unset yourself for further processing"""

    def run(self, testIdx=None):
        """start running thread"""
        if self.testthread is None:
            self.testthread = threading.Thread(target=self.__runthread,
                                               args=(testIdx, ))
            self.testthread.start()
        else:
            self.displayError("Test is already running!")

    def addTest(self):
        newIdx = len(self.runner.getSuite())
        newTest = self.runner.addTest()
        self.insertTest(newIdx, newTest)
        self.editTest(newIdx)

    def selectTest(self, event):
        self.editTest(event.GetIndex())

    def editTest(self, testIdx):
        test = self.runner.getSuite()[testIdx]
        if self.editForm is None:
            self.editForm = TestEditForm(self.wHnd, testIdx, test, self.runner,
                                         self)
            self.loadIcon(self.editForm)
        else:
            self.editForm.updateTest(testIdx)
        self.editForm.Show()

    def __init__(self):
        """Initialise the gui"""
        wx.App.__init__(self, redirect=False, useBestVisual=True)
        logger.logListener = self.addLog
        TermColor.active = False
        self.wHnd = None
        self.testthread = None
        self.editForm = None
        self.log = []
        self.logForm = None
        self.runner = TestRunner()
        self.runner.options['quiet'] = True
        self.runner.parseArgv()
        self.runner.options['mode'] = TestSuiteMode.Continuous

    def addLog(self, line):
        self.log.append(line)
        if self.logForm is not None:
            self.logForm.add(line)

    def showLog(self):
        if self.logForm is None:
            self.logForm = LogWindow(self.wHnd, self.log)
        if self.logForm.IsShown():
            self.logForm.Hide()
        else:
            self.logForm.Show()

    def messageDialog(self,
                      message,
                      caption=wx.MessageBoxCaptionStr,
                      style=wx.OK | wx.ICON_INFORMATION):
        dial = wx.MessageDialog(None, message, caption, style)
        return dial.ShowModal()

    def fileDialog(self, mode, message, fileTypes=None, dir=wx.EmptyString):
        if fileTypes is None:
            wc = wx.FileSelectorDefaultWildcardStr
        else:
            wc = " | ".join([
                descr + " (*" + ext + ") | *" + ext for descr, ext in fileTypes
            ])
        diag = wx.FileDialog(self.wHnd,
                             message,
                             defaultDir=dir,
                             wildcard=wc,
                             style=mode)
        diag.ShowModal()
        return os.path.join(
            diag.Directory,
            path=diag.Filename) if diag.Filename != "" else None

    def displayError(self, message, caption='An error occured'):
        return self.messageDialog(message, caption,
                                  wx.OK | wx.ICON_ERROR) == wx.OK

    def displayInformation(self, message, caption='Warning'):
        return self.messageDialog(message, caption,
                                  wx.OK | wx.ICON_INFORMATION) == wx.OK

    def displayQuestion(self, message, caption='Question'):
        return self.messageDialog(message, caption,
                                  wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

    def loadFileDialog(self,
                       message="Load File",
                       fileTypes=None,
                       dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_OPEN,
                               message=message,
                               fileTypes=fileTypes,
                               dir=dir)

    def saveFileDialog(self,
                       message="Save File",
                       fileTypes=None,
                       dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
                               message=message,
                               fileTypes=fileTypes,
                               dir=dir)

    def buildWindow(self):
        """Creates the window with all its components"""
        self.wHnd = wx.Frame(None,
                             style=wx.DEFAULT_FRAME_STYLE,
                             title="nightmare GUI-Modus",
                             size=(600, 400))
        self.SetTopWindow(self.wHnd)
        self.loadIcon(self.wHnd)

        panel = wx.Panel(self.wHnd)
        sizer = wx.GridBagSizer(3, 3)
        # Create Controls
        self.btnLoad = wx.Button(panel, label="Load", id=wx.ID_OPEN)
        self.btnSave = wx.Button(panel, label="Save", id=wx.ID_SAVE)
        self.lblDUT = wx.StaticText(panel, label="DUT")
        self.lblFile = wx.StaticText(panel, label="File")
        self.lblTests = wx.StaticText(panel, label="Tests")
        self.edtDUT = wx.TextCtrl(panel)
        self.edtFile = wx.TextCtrl(panel)
        self.edtTests = wx.TextCtrl(panel)
        self.btnSelect = wx.Button(panel, label="...")
        self.btnAdd = wx.Button(panel, label=" + ", id=wx.ID_ADD)
        self.grpMode = wx.RadioBox(panel,
                                   choices=TestRunnerGui.modes,
                                   style=wx.RA_VERTICAL)
        self.prgGauge = wx.Gauge(panel)
        self.btnRun = wx.Button(panel, label="Run")
        self.lstTests = CheckListCtrl(panel)
        # Feature for wxPython 2.9 (currently in development)
        if hasattr(self.btnSave, 'SetBitmap'):
            self.btnSave.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE))
            self.btnLoad.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN))
        # Disable TextCtrl
        self.edtFile.Disable()
        self.edtTests.Disable()
        # Insert Columns into list and hook up the checkboxes
        self.lstTests.InsertColumn(0, 'Test', width=140)
        self.lstTests.InsertColumn(1, 'Description', width=220)
        self.lstTests.InsertColumn(2, 'State', width=100)
        self.lstTests.OnCheckItem = self.onListCheck
        # Create Layout
        sizer.Add(self.btnLoad,
                  pos=(0, 0),
                  span=(3, 1),
                  border=5,
                  flag=wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.btnSave,
                  pos=(0, 1),
                  span=(3, 1),
                  border=5,
                  flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.lblDUT,
                  pos=(0, 2),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.lblFile,
                  pos=(1, 2),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.lblTests,
                  pos=(2, 2),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.edtDUT,
                  pos=(0, 3),
                  span=(1, 1),
                  border=5,
                  flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.edtFile,
                  pos=(1, 3),
                  span=(1, 1),
                  border=5,
                  flag=wx.EXPAND)
        sizer.Add(self.edtTests,
                  pos=(2, 3),
                  span=(1, 1),
                  border=5,
                  flag=wx.EXPAND)
        sizer.Add(self.btnSelect,
                  pos=(0, 4),
                  span=(1, 1),
                  border=5,
                  flag=wx.TOP)
        sizer.Add(
            self.btnAdd,
            pos=(2, 4),
            span=(1, 1),
            border=5,
        )
        sizer.Add(self.grpMode,
                  pos=(0, 5),
                  span=(3, 1),
                  border=5,
                  flag=wx.TOP | wx.RIGHT | wx.EXPAND)
        sizer.Add(self.prgGauge,
                  pos=(3, 0),
                  span=(1, 5),
                  border=5,
                  flag=wx.LEFT | wx.EXPAND)
        sizer.Add(self.btnRun,
                  pos=(3, 5),
                  span=(1, 1),
                  border=5,
                  flag=wx.RIGHT | wx.EXPAND)
        sizer.Add(self.lstTests,
                  pos=(4, 0),
                  span=(1, 6),
                  border=5,
                  flag=wx.ALL | wx.EXPAND)
        sizer.AddGrowableCol(3)
        sizer.AddGrowableRow(4)
        panel.SetSizerAndFit(sizer)
        # Hook up window events
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.loadSuite(),
                       id=self.btnLoad.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.saveSuite(),
                       id=self.btnSave.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.run(),
                       id=self.btnRun.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.selectDut(),
                       id=self.btnSelect.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON,
                       lambda e: self.addTest(),
                       id=self.btnAdd.GetId())
        self.wHnd.Bind(wx.EVT_RADIOBOX,
                       lambda e: self.runner.options.__setitem__(
                           "mode", self.grpMode.GetSelection()),
                       id=self.grpMode.GetId())
        self.wHnd.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.lstTests.Bind(wx.EVT_LIST_ITEM_ACTIVATED,
                           self.selectTest,
                           id=self.lstTests.GetId())
        # Some Shortcuts
        shortcuts = [
            (wx.ACCEL_CTRL, ord('q'), self.OnCloseWindow),
            (wx.ACCEL_CTRL, ord('r'), lambda e: self.run()),
            (wx.ACCEL_CTRL, ord('o'), lambda e: self.loadSuite()),
            (wx.ACCEL_CTRL, ord('d'), lambda e: self.selectDut()),
            (wx.ACCEL_CTRL, ord('n'), lambda e: self.addTest()),
            (wx.ACCEL_CTRL, ord('e'), lambda e: self.addTest()),
            (wx.ACCEL_CTRL, ord('l'), lambda e: self.showLog()),
        ]
        entries = []
        for special, key, func in shortcuts:
            id = wx.NewId()
            entries.append((special, key, id))
            self.wHnd.Bind(wx.EVT_MENU, func, id=id)
        self.acceleratorTable = wx.AcceleratorTable(entries)
        self.wHnd.SetAcceleratorTable(self.acceleratorTable)
        # Change the DUT after the user left the associated TextCtrl
        self.edtDUT.Bind(wx.EVT_KILL_FOCUS,
                         lambda e: self.runner.setDUT(self.edtDUT.GetValue()),
                         id=self.edtDUT.GetId())
        #
        self.updateFromRunner()

    def OnCloseWindow(self, e):
        """Handler to make sure the window doesn't gets close by accident"""
        ret = self.displayQuestion('Are you sure, you want to quit?')
        if ret == wx.ID_YES:
            self.wHnd.Destroy()
        else:
            e.Veto()

    def show(self):
        self.wHnd.Centre()
        self.wHnd.Show()
        self.MainLoop()
Beispiel #4
0
class TestRunnerGui(wx.App):
    """Graphical User Interface"""
    modes = ["Continuous", "Halt on Fail", "Halt on Error"]
    benchtypes = [('nightmare', 'py'), ('All Files', '*')]
    duttypes = [('All Files', '*'), ('Executables', '*.exe')]

    def suiteSave(self, fn):
        self.runner.saveToFile(fn)

    def loadIcon(self, frame):
        try:
            if sys.platform == 'win32':
                exeName = sys.executable
                icon = wx.Icon(exeName + ';0', wx.BITMAP_TYPE_ICO)
                frame.SetIcon(icon)
        except:
            pass

    def saveSuite(self):
        """ Savedialog execution before saving the suite"""
        fname = self.saveFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.suiteSave(fname)

    def loadSuite(self):
        """ Load a testsuite"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.benchtypes)
        if fname is not None:
            self.edtFile.SetValue(os.path.relpath(fname))
            self.runner.options['bench'] = fname
            self.updateFromRunner()

    def updateFromRunner(self):
        self.runner.loadSuite()
        self.lstTests.DeleteAllItems()
        self.applyToList(self.runner.getSuite().getTests(), self.insertTest)
        self.edtDUT.SetValue(str(self.runner.options['dut']))
        self.edtTests.SetValue(str(self.runner.countTests()))
        self.grpMode.SetSelection(self.runner.getSuite().mode)

    def selectDut(self):
        """Show filedialog and set the result as DUT"""
        fname = self.loadFileDialog(fileTypes=TestRunnerGui.duttypes)
        if fname is not None:
            self.runner.setDUT(fname)
            self.edtDUT.SetValue(os.path.relpath(fname))

    def applyToList(self, l, f, gauge=True):
        cnt = self.runner.countTests()
        if gauge:
            self.prgGauge.SetRange(cnt - 1)
            self.prgGauge.SetValue(0)
        lastIdx = 0
        for idx, test in itertools.izip(xrange(cnt), l):
            lastIdx = idx
            if gauge:
                self.prgGauge.SetValue(idx)
                self.prgGauge.Update()
            if idx < self.lstTests.GetItemCount() - 1:
                self.lstTests.SetStringItem(idx + 1, 2, "RUNNING")
                self.lstTests.Update()
            f(idx, test)
            self.lstTests.Update()
        for i in range(lastIdx + 1, len(self.runner.getSuite())):
            self.lstTests.SetStringItem(i, 2, "CANCELED")

    def insertTest(self, idx, test):
        """Insert a new test into the test-list

        @type    idx: int
        @param     idx: The index of the test to add
        @type    test: pyTestCore.test.Test
        @param     test: The test to add
        """
        self.lstTests.InsertStringItem(idx, test.name)
        self.lstTests.CheckItem(idx)
        self.updateTest(idx, test)

    def updateTest(self, idx, test):
        """Update the information on one test in the test-list

        @type    idx: int
        @param     idx: The index of the test to change
        @type    test: pyTestCore.test.Test
        @param     test: The test to change
        """
        if test.state == TestState.Error:
            self.lstTests.CheckItem(idx, False)
            self.lstTests.SetItemBackgroundColour(idx, 'yellow')
        elif test.state == TestState.Success:
            self.lstTests.SetItemBackgroundColour(idx, 'green')
        elif test.state == TestState.Fail:
            self.lstTests.SetItemBackgroundColour(idx, 'red')
        elif test.state == TestState.Timeout:
            self.lstTests.SetItemBackgroundColour(idx, 'purple')
        elif test.state == TestState.Waiting:
            self.lstTests.SetItemBackgroundColour(idx, 'white')
        elif test.state == TestState.Disabled:
            self.lstTests.SetItemBackgroundColour(idx, 'gray')
        TermColor.active = False
        self.lstTests.SetStringItem(idx, 0, test.name)
        self.lstTests.SetStringItem(idx, 1, test.descr)
        self.lstTests.SetStringItem(idx, 2, TestState.toString(test.state))

    def setTestState(self, test, idx, state):
        """Update the state of one test, but only if the test is not enabled"""
        if test.state != TestState.Disabled:
            test.state = state
        self.updateTest(idx, test)

    def onListCheck(self, idx, flag):
        """Eventhandler for changes on the checkboxes in the list"""
        test = self.runner.getSuite()[idx]
        test.state = TestState.Waiting if flag else TestState.Disabled
        self.updateTest(idx, test)

    def __runthread(self, testIdx=None):
        """Run tests"""
        if testIdx is None:
            self.applyToList(self.runner.getSuite().getTests(), lambda i, t: self.setTestState(t, i, TestState.Waiting), gauge=False)
            self.applyToList(self.runner.run(), self.updateTest)
        else:
            test, = self.runner.getSuite().run(tests=[testIdx])
            self.updateTest(testIdx, test)
        self.testthread = None
        """Unset yourself for further processing"""

    def run(self, testIdx=None):
        """start running thread"""
        if self.testthread is None:
            self.testthread = threading.Thread(target=self.__runthread, args=(testIdx,))
            self.testthread.start()
        else:
            self.displayError("Test is already running!")

    def addTest(self):
        newIdx = len(self.runner.getSuite())
        newTest = self.runner.addTest()
        self.insertTest(newIdx, newTest)
        self.editTest(newIdx)

    def selectTest(self, event):
        self.editTest(event.GetIndex())

    def editTest(self, testIdx):
        test = self.runner.getSuite()[testIdx]
        if self.editForm is None:
            self.editForm = TestEditForm(self.wHnd, testIdx, test, self.runner, self)
            self.loadIcon(self.editForm)
        else:
            self.editForm.updateTest(testIdx)
        self.editForm.Show()

    def __init__(self):
        """Initialise the gui"""
        wx.App.__init__(self, redirect=False, useBestVisual=True)
        logger.logListener = self.addLog
        TermColor.active = False
        self.wHnd = None
        self.testthread = None
        self.editForm = None
        self.log = []
        self.logForm = None
        self.runner = TestRunner()
        self.runner.options['quiet'] = True
        self.runner.parseArgv()
        self.runner.options['mode'] = TestSuiteMode.Continuous

    def addLog(self, line):
        self.log.append(line)
        if self.logForm is not None:
            self.logForm.add(line)

    def showLog(self):
        if self.logForm is None:
            self.logForm = LogWindow(self.wHnd, self.log)
        if self.logForm.IsShown():
            self.logForm.Hide()
        else:
            self.logForm.Show()

    def messageDialog(self, message, caption=wx.MessageBoxCaptionStr, style=wx.OK | wx.ICON_INFORMATION):
        dial = wx.MessageDialog(None, message, caption, style)
        return dial.ShowModal()

    def fileDialog(self, mode, message, fileTypes=None, dir=wx.EmptyString):
        if fileTypes is None:
            wc = wx.FileSelectorDefaultWildcardStr
        else:
            wc = " | ".join([descr + " (*" + ext + ") | *" + ext for descr, ext in fileTypes])
        diag = wx.FileDialog(self.wHnd, message, defaultDir=dir, wildcard=wc, style=mode)
        diag.ShowModal()
        return os.path.join(diag.Directory, path=diag.Filename) if diag.Filename != "" else None

    def displayError(self, message, caption='An error occured'):
        return self.messageDialog(message, caption, wx.OK | wx.ICON_ERROR) == wx.OK

    def displayInformation(self, message, caption='Warning'):
        return self.messageDialog(message, caption, wx.OK | wx.ICON_INFORMATION) == wx.OK

    def displayQuestion(self, message, caption='Question'):
        return self.messageDialog(message, caption, wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

    def loadFileDialog(self, message="Load File", fileTypes=None, dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_OPEN, message=message, fileTypes=fileTypes, dir=dir)

    def saveFileDialog(self, message="Save File", fileTypes=None, dir=wx.EmptyString):
        return self.fileDialog(mode=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, message=message, fileTypes=fileTypes, dir=dir)

    def buildWindow(self):
        """Creates the window with all its components"""
        self.wHnd = wx.Frame(None, style=wx.DEFAULT_FRAME_STYLE, title="nightmare GUI-Modus", size=(600, 400))
        self.SetTopWindow(self.wHnd)
        self.loadIcon(self.wHnd)

        panel = wx.Panel(self.wHnd)
        sizer = wx.GridBagSizer(3, 3)
        # Create Controls
        self.btnLoad   = wx.Button(panel, label="Load", id=wx.ID_OPEN)
        self.btnSave   = wx.Button(panel, label="Save", id=wx.ID_SAVE)
        self.lblDUT    = wx.StaticText(panel, label="DUT")
        self.lblFile   = wx.StaticText(panel, label="File")
        self.lblTests  = wx.StaticText(panel, label="Tests")
        self.edtDUT    = wx.TextCtrl(panel)
        self.edtFile   = wx.TextCtrl(panel)
        self.edtTests  = wx.TextCtrl(panel)
        self.btnSelect = wx.Button(panel, label="...")
        self.btnAdd    = wx.Button(panel, label=" + ", id=wx.ID_ADD)
        self.grpMode   = wx.RadioBox(panel, choices=TestRunnerGui.modes, style=wx.RA_VERTICAL)
        self.prgGauge  = wx.Gauge(panel)
        self.btnRun    = wx.Button(panel, label="Run")
        self.lstTests  = CheckListCtrl(panel)
        # Feature for wxPython 2.9 (currently in development)
        if hasattr(self.btnSave, 'SetBitmap'):
            self.btnSave.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE))
            self.btnLoad.SetBitmap(wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN))
        # Disable TextCtrl
        self.edtFile.Disable()
        self.edtTests.Disable()
        # Insert Columns into list and hook up the checkboxes
        self.lstTests.InsertColumn(0, 'Test', width=140)
        self.lstTests.InsertColumn(1, 'Description', width=220)
        self.lstTests.InsertColumn(2, 'State', width=100)
        self.lstTests.OnCheckItem = self.onListCheck
        # Create Layout
        sizer.Add(self.btnLoad,   pos=(0, 0), span=(3, 1), border=5, flag=wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.btnSave,   pos=(0, 1), span=(3, 1), border=5, flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.lblDUT,    pos=(0, 2), span=(1, 1), border=5, flag=wx.RIGHT | wx.LEFT | wx.TOP | wx.EXPAND)
        sizer.Add(self.lblFile,   pos=(1, 2), span=(1, 1), border=5, flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.lblTests,  pos=(2, 2), span=(1, 1), border=5, flag=wx.RIGHT | wx.LEFT | wx.EXPAND)
        sizer.Add(self.edtDUT,    pos=(0, 3), span=(1, 1), border=5, flag=wx.TOP | wx.EXPAND)
        sizer.Add(self.edtFile,   pos=(1, 3), span=(1, 1), border=5, flag=wx.EXPAND)
        sizer.Add(self.edtTests,  pos=(2, 3), span=(1, 1), border=5, flag=wx.EXPAND)
        sizer.Add(self.btnSelect, pos=(0, 4), span=(1, 1), border=5, flag=wx.TOP)
        sizer.Add(self.btnAdd,    pos=(2, 4), span=(1, 1), border=5, )
        sizer.Add(self.grpMode,   pos=(0, 5), span=(3, 1), border=5, flag=wx.TOP | wx.RIGHT | wx.EXPAND)
        sizer.Add(self.prgGauge,  pos=(3, 0), span=(1, 5), border=5, flag=wx.LEFT | wx.EXPAND)
        sizer.Add(self.btnRun,    pos=(3, 5), span=(1, 1), border=5, flag=wx.RIGHT | wx.EXPAND)
        sizer.Add(self.lstTests,  pos=(4, 0), span=(1, 6), border=5, flag=wx.ALL | wx.EXPAND)
        sizer.AddGrowableCol(3)
        sizer.AddGrowableRow(4)
        panel.SetSizerAndFit(sizer)
        # Hook up window events
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.loadSuite(), id=self.btnLoad.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.saveSuite(), id=self.btnSave.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.run(), id=self.btnRun.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.selectDut(), id=self.btnSelect.GetId())
        self.wHnd.Bind(wx.EVT_BUTTON, lambda e: self.addTest(), id=self.btnAdd.GetId())
        self.wHnd.Bind(wx.EVT_RADIOBOX, lambda e: self.runner.options.__setitem__("mode", self.grpMode.GetSelection()), id=self.grpMode.GetId())
        self.wHnd.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
        self.lstTests.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.selectTest, id=self.lstTests.GetId())
        # Some Shortcuts
        shortcuts = [
            (wx.ACCEL_CTRL,  ord('q'), self.OnCloseWindow),
            (wx.ACCEL_CTRL,  ord('r'), lambda e:self.run()),
            (wx.ACCEL_CTRL,  ord('o'), lambda e:self.loadSuite()),
            (wx.ACCEL_CTRL,  ord('d'), lambda e:self.selectDut()),
            (wx.ACCEL_CTRL,  ord('n'), lambda e:self.addTest()),
            (wx.ACCEL_CTRL,  ord('e'), lambda e:self.addTest()),
            (wx.ACCEL_CTRL,  ord('l'), lambda e:self.showLog()),
        ]
        entries = []
        for special, key, func in shortcuts:
            id = wx.NewId()
            entries.append((special, key, id))
            self.wHnd.Bind(wx.EVT_MENU, func, id=id)
        self.acceleratorTable = wx.AcceleratorTable(entries)
        self.wHnd.SetAcceleratorTable(self.acceleratorTable)
        # Change the DUT after the user left the associated TextCtrl
        self.edtDUT.Bind(wx.EVT_KILL_FOCUS, lambda e: self.runner.setDUT(self.edtDUT.GetValue()), id=self.edtDUT.GetId())
        #
        self.updateFromRunner()

    def OnCloseWindow(self, e):
        """Handler to make sure the window doesn't gets close by accident"""
        ret = self.displayQuestion('Are you sure, you want to quit?')
        if ret == wx.ID_YES:
            self.wHnd.Destroy()
        else:
            e.Veto()

    def show(self):
        self.wHnd.Centre()
        self.wHnd.Show()
        self.MainLoop()