def timerHandler(self, parentWindow):
        if (self.showResultsEvent.isSet()):
            self.showResultsEvent.clear()
            self.__progressDialog = ProgressDialog(parentWindow,
                                                   self.__bruteForceResults,
                                                   self.cancelEvent,
                                                   self.resumeEvent)
            self.__progressDialog.showDialog()
            return (True)

        # No need to "lift" the UI window
        return (False)
Exemplo n.º 2
0
    def OnRefresh(self, event):
        try:
            LBVM.SetLabel(u"虚拟机:")
            LBCPU.SetLabel(u"虚拟CPU:")
            LBRAM.SetLabel(u"内存:")
            LBDISK.SetLabel(u"存储:")
            LBDISPLAY.SetLabel(u"显示接口:")
            LBSTATUS.SetLabel(u"状态:")
            LBCOUNT.SetLabel(u"虚拟机总数:")
            LBUSB.SetLabel(u"USB策略:")
        except:
            pass

        self.dlg = ProgressDialog.ProgressDialog(self, u'获取当前用户的虚拟机信息...')
        wx.CallAfter(self.dlg.Update, 1, u'获取当前用户的虚拟机信息...')
        if self.refreshthread:
            self.refreshthread.stop()
        self.refreshthread = RefreshThread(self)
        self.refreshthread.start()
        #orig = -1
        orig = self.vmlist.GetFirstSelected()
        self.refreshthread.refresh_all()
        ret = self.dlg.ShowModal()
        if ret == wx.ID_CANCEL:
            self.refreshthread.refresh_all_stop()
            self.dlg.EndModal(ret)
        if self.dlg:
            self.dlg.Destroy()
        # add start
        if orig != -1:
            # add end
            self.vmlist.Select(orig, True)
            self.vmlist.SetFocus()
            event.Skip()
Exemplo n.º 3
0
    def OnLogin(self, event):
        global PASSWORD
        PASSWORD = self.password.GetValue()
        # Valid Check
        if self.username.GetValue() == '' or self.password.GetValue() == '':
            Util.MessageBox(self, u'缺少用户名或密码!', u'错误', wx.OK | wx.ICON_ERROR)
            return

        dlg = ProgressDialog.ProgressDialog(self, u'连接服务器...')

        url = 'http://%s:5000/v2.0' % (Setting.getServer())

        RestartDeviceRequests()

        loginthread = LoginThread(dlg, url, self.username.GetValue(),
                                  self.password.GetValue())
        loginthread.start()
        #ret = dlg.ShowModal()
        #dlg.Destroy()
        if dlg.ShowModal() == wx.ID_CANCEL:
            loginthread.stop()
            return
        if loginthread:
            loginthread.stop()
        dlg.Destroy()

        Logger.info("Connect to %s", url)
        Logger.info("UserId: %s, Password: ******", self.username.GetValue())
        ret, reason, detail = loginthread.getReturnValue()
        Logger.info("Result: %s, reason: %s, detail: %s", ret, reason, detail)

        if Setting.getSign().lower() == 'false':
            self.password.SetValue('')

        self.password.SetFocus()

        if not ret:
            Util.MessageBox(self, detail, reason, wx.OK | wx.ICON_ERROR)
            Session.logout()
        else:
            Setting.setLastLogin(FirstUser['firstuser'].username)
            if self.sign.GetValue() == True:
                Setting.setPasswd(self.password.GetValue())
            else:
                Setting.setPasswd('1df#$!cd123~')
            Setting.save()
            area = wx.Display().GetGeometry()
            width = area.GetWidth()
            height = area.GetHeight()
            f = MainFrame.MainFrame(self.GetParent(), (width, height))

            f.ShowFullScreen(True)
            self.GetParent().Hide()
Exemplo n.º 4
0
    def autoLogin(self):
        if Setting.getSign().lower() == 'false':
            return False
        if Setting.getAuto().lower() == 'true':
            pass
        else:
            return False

        username = Setting.getLastLogin()
        passwd = Setting.getPasswd()
        if username == '' or passwd == '':
            Util.MessageBox(self, u'缺少用户名或密码!', u'错误', wx.OK | wx.ICON_ERROR)
            return

        dlg = ProgressDialog.ProgressDialog(self, u'连接服务器...')
        dlg.CenterOnScreen()

        url = 'http://%s:5000/v2.0' % (Setting.getServer())

        RestartDeviceRequests()

        loginthread = LoginThread(dlg, url, username, passwd)
        loginthread.start()
        #dlg.SetPosition((100,100))
        #dlg.Move((Resource.screenX-dlg.))
        #dlg.CenterOnScreen()
        #ret = dlg.ShowModal()
        #dlg.Destroy()
        if dlg.ShowModal() == wx.ID_CANCEL:
            loginthread.stop()
            return
        if loginthread:
            loginthread.stop()
        dlg.Destroy()

        Logger.info("Connect to %s", url)
        Logger.info("UserId: %s, Password: ******", username)
        ret, reason, detail = loginthread.getReturnValue()
        Logger.info("Result: %s, reason: %s, detail: %s", ret, reason, detail)

        if not ret:
            Util.MessageBox(self, detail, reason, wx.OK | wx.ICON_ERROR)
            self.ShowFullScreen(True)
            Session.logout()
        else:
            f = MainFrame.MainFrame(self.GetParent(), wx.ScreenDC().GetSize())
            f.ShowFullScreen(True)
            self.GetParent().Hide()
Exemplo n.º 5
0
    def ConnectVM(self, p_id, vm):
        key = vm.name
        try:
            vmtype = vms_dict[key]
            Type = vmtype['vmtype']
        except:
            Type = "UNKNOWN"
        Logger.info("VM is a %s type", Type)

        if Type == 'JSP' or Type == 'UNKNOWN':
            dlg = ProgressDialog.ProgressDialog(self, u'连接服务器...')
            thread = Console.LaunchThread(p_id, vm, Type, dlg)
            thread.start()
            if dlg.ShowModal() == wx.ID_CANCEL:
                thread.stop()
            else:
                thread.join()
        elif Type == 'RDP':
            RDP = RDPLoginDialog.RDPLoginDialog(vm, Type)
            RDP.ShowModal()
Exemplo n.º 6
0
def autoLogin():
    if Setting.getSign().lower() == 'false':
        return False
    if Setting.getAuto().lower() == 'true':
        pass
    else:
        return False
    username = Setting.getLastLogin()
    passwd = Setting.getPasswd()
    if username == '' or passwd == '':
        Util.MessageBox(None, u'缺少用户名或密码!', u'错误', wx.OK | wx.ICON_ERROR)
        return

    dlg = ProgressDialog.ProgressDialog(None, u'连接到服务器...')

    url = 'http://%s:5000/v2.0' % (Setting.getServer())
    loginthread = LoginFrame.LoginThread(dlg, url, username, passwd)
    loginthread.start()

    dlg.CenterOnScreen()
    ret = dlg.ShowModal()
    dlg.Destroy()
    if ret == wx.ID_CANCEL:
        loginthread.stop()
        return

    Logger.info("Connect to %s", url)
    Logger.info("UserId: %s, Password: ******", username)
    ret, reason, detail = loginthread.getReturnValue()
    Logger.info("Result: %s, reason: %s, detail: %s", ret, reason, detail)

    if not ret:
        Session.logout()
        return False
    else:
        LoginFrame.PASSWORD = passwd
        return True
Exemplo n.º 7
0
    def OnBtnOKButton(self, event):
        username = self.Input_username.GetValue()
        password = self.Input_password.GetValue()

        if Setting.getPublic().lower() == "true":
            try:
                image_lists = havclient.image_list(FirstUser['firstuser'])
                for i in image_lists:
                    if i.name == u'Port':
                        image_info = havclient.data(FirstUser['firstuser'],
                                                    i.id)
                        break
                havclient.download_templet(image_info)
                Logger.info("Download status:Download hosts successful!")
            except:
                Logger.info("Download status:Download hosts unsuccessful!")
        else:
            pass

        if username == '' or password == '':
            Util.MessageBox(self, u'缺少用户名或密码!', u'错误', wx.OK | wx.ICON_ERROR)
            return
        else:
            Setting.setUser(username)
            Setting.setCipher(password)
            Setting.save()
            dlg = ProgressDialog.ProgressDialog(self, u'连接服务器...')
            thread = Console.LaunchThread(self.vm.tenant_id, self.vm,
                                          self.Type, dlg)
            thread.start()
            if dlg.ShowModal() == wx.ID_CANCEL:
                thread.stop()
            else:
                thread.join()
            self.Destroy()
            event.Skip()
Exemplo n.º 8
0
    def on_actNew_triggered(self):
        """
        Slot: When the user click the New button, this slot will receive the New signal. 
        """
        import Global
        # initialize the class attribute
        self.path2method = {}

        # create a file dialog to open an apk file
        dlg = QFileDialog(self)
        filename = dlg.getOpenFileName(self, self.tr("Open APK File"),
                                       QString(),
                                       self.tr("APK Files Odex Files(*.*)"))
        filetype = filename.split('.', 1)[1]
        #        QMessageBox.warning(self ,'file', filetype)
        if filetype == 'apk':
            Global.CONFIG = {
                "CFG": 1,
                "Dalvik": 1,
                "Java": 0,
                "Bytecode": 1,
                "Smali": 1,
                "CallIn": 1,
                "CallOut": 1,
                "Permission": 1,
                "Manifest": 1
            }
#add support for odex
        if filetype == 'odex':
            Global.CONFIG = {
                "CFG": 1,
                "Dalvik": 1,
                "Java": 0,
                "Bytecode": 1,
                "Smali": 1,
                "CallIn": 1,
                "CallOut": 1,
                "Permission": 1,
                "Manifest": 0
            }

        if not zipfile.is_zipfile(filename):
            msgbox = QMessageBox()
            msgbox.setText("Please select the APK file correctly!")
            msgbox.setWindowTitle("Warning!")
            msgbox.show()
        else:
            # create a progress dialog to show the progress
            # all pre-processing are done in a thread
            progress = ProgressDialog()
            thread = MyThread(progress, str(filename))
            thread.start()
            progress.run()

            # judge this APK whether it is valid or invalid
            if not Global.APK.isVaildAPK():
                msgbox = QMessageBox()
                msgbox.setText("This APK is invalid!")
                msgbox.setWindowTitle("Error!")
                msgbox.exec_()
                return

            # clear all the last apk's info
            self.listWidget_strings.clear()
            self.listWidget_classes.clear()
            #           self.Graph.scene.clear()
            #           self.Graph_call.scene.clear()
            self.plainTextEdit_dalvik.setPlainText("")
            self.plainTextEdit_java.setPlainText("")
            self.plainTextEdit_bytecode.setPlainText("")
            self.plainTextEdit_smali.setPlainText("")
            self.textEdit_permission.setText("")
            self.textEdit_call.setText("")
            self.textBrowser.setText("")
            self.plainTextEdit_dalvik.reset()

            # start to show some infomation of the apk
            self.Tab_APKInfo(Global.APK)
            self.Tab_Methods(Global.APK, Global.VM, Global.VMX)
            self.Tab_Strings()
            self.Tab_Classes()
            print "Before show information"
            if Global.CONFIG["Java"] == 1:
                self.Tab_Files(str(filename))
            else:
                self.treeWidget_files.clear()

            if Global.CONFIG["Smali"] == 1 or Global.CONFIG["Manifest"] == 1:
                print "config to show apktool"
                self.apktool = APKtool()
        #yuan build callinout tree
            if Global.CONFIG["CallIn"] == 1 or Global.CONFIG["CallOut"] == 1:
                methodInvokeList = self.CL.get_methodInvoke()
                self.callInOut = CallInOut(methodInvokeList)
#              mcalltree = self.callInOut.callTree()

            else:
                self.textEdit_call.setText("")

            if Global.CONFIG["Permission"] == 1:
                self.Tab_Permission()
            else:
                self.textEdit_permission.setText("")

            if Global.CONFIG["Manifest"] == 1:
                self.Tab_Manifest()
            else:
                self.textBrowser.setText("")

            self.tabWidget_2.setCurrentIndex(4)
            self.tabWidget.setCurrentIndex(7)
Exemplo n.º 9
0
    def on_actNew_triggered(self):
        """
        Slot: When the user click the New button, this slot will receive the New signal. 
        """
        import Global
        # initialize the class attribute
        self.path2method = {}

        # create a file dialog to open an apk file
        dlg = QFileDialog(self)
        filename = dlg.getOpenFileName(self, self.tr("Open APK File"), QString(),  self.tr("APK Files Odex Files(*.*)"))
        filetype = filename.split('.', 1)[1]
#        QMessageBox.warning(self ,'file', filetype)
        if filetype == 'apk':
             Global.CONFIG = {"CFG":1, "Dalvik":1, "Java":0, "Bytecode":1, "Smali":1, "CallIn":1, "CallOut":1, "Permission":1, "Manifest":1}
#add support for odex
        if filetype == 'odex':
             Global.CONFIG = {"CFG":1, "Dalvik":1, "Java":0, "Bytecode":1, "Smali":1, "CallIn":1, "CallOut":1, "Permission":1, "Manifest":0}
        

        if not zipfile.is_zipfile(filename):
            msgbox = QMessageBox()
            msgbox.setText("Please select the APK file correctly!")
            msgbox.setWindowTitle("Warning!")
            msgbox.show()
        else:
        # create a progress dialog to show the progress
        # all pre-processing are done in a thread
            progress = ProgressDialog()
            thread = MyThread(progress, str(filename))
            thread.start()            
            progress.run()


            # judge this APK whether it is valid or invalid
            if not Global.APK.isVaildAPK():
                msgbox = QMessageBox()
                msgbox.setText("This APK is invalid!")
                msgbox.setWindowTitle("Error!")
                msgbox.exec_()
                return
                
            # clear all the last apk's info
            self.listWidget_strings.clear()
            self.listWidget_classes.clear()
 #           self.Graph.scene.clear()
 #           self.Graph_call.scene.clear()
            self.plainTextEdit_dalvik.setPlainText("")
            self.plainTextEdit_java.setPlainText("")
            self.plainTextEdit_bytecode.setPlainText("")
            self.plainTextEdit_smali.setPlainText("")
            self.textEdit_permission.setText("")
            self.textEdit_call.setText("")
            self.textBrowser.setText("")
            self.plainTextEdit_dalvik.reset()
      
            # start to show some infomation of the apk
            self.Tab_APKInfo(Global.APK)
            self.Tab_Methods(Global.APK, Global.VM, Global.VMX)
            self.Tab_Strings()
            self.Tab_Classes()
            print "Before show information"
            if Global.CONFIG["Java"] == 1:
                self.Tab_Files(str(filename))
            else:
                self.treeWidget_files.clear()
            
            if Global.CONFIG["Smali"] ==1 or Global.CONFIG["Manifest"] ==1:            
                print "config to show apktool"
                self.apktool = APKtool()
        #yuan build callinout tree
            if Global.CONFIG["CallIn"] == 1 or Global.CONFIG["CallOut"] == 1:
                methodInvokeList = self.CL.get_methodInvoke()
                self.callInOut = CallInOut(methodInvokeList)
  #              mcalltree = self.callInOut.callTree()
                
            else:
                self.textEdit_call.setText("")
             
            if Global.CONFIG["Permission"] == 1:
                self.Tab_Permission()
            else:
                self.textEdit_permission.setText("")                
                        
            if Global.CONFIG["Manifest"] ==1:
                self.Tab_Manifest()
            else:
                self.textBrowser.setText("")
          
            self.tabWidget_2.setCurrentIndex(4)
            self.tabWidget.setCurrentIndex(7)
class BruteForceSolveWorkThread(WorkThread):

    # Define the direction of the line to be drawn
    LEFT = 0
    RIGHT = 1
    UP = 2
    DOWN = 3

    def __init__(self, solver, puzzleBoard):
        super().__init__(solver, puzzleBoard)

        # Initialize the variable holding the brute force results
        self.__bruteForceResults = None
        self.__wasCancelledByUser = False

    # Find the first black circle, which has one line, and determine which
    # line we should try drawing (based on which pathways out of the cell are
    # open).
    def __findBlackCircleWithOneLine(self, pb):
        numRows, numCols = pb.getDimensions()
        for rowNum in range(0, numRows):
            for colNum in range(0, numCols):
                if (pb.isBlackCircleAt(rowNum, colNum)):
                    numLines, l, r, u, d = pb.getLines(rowNum, colNum)
                    if (numLines == 1):
                        numOpen, l, r, u, d = pb.getOpenPaths(rowNum, colNum)
                        if (u):
                            return ((rowNum, colNum, self.UP))
                        if (d):
                            return ((rowNum, colNum, self.DOWN))
                        if (l):
                            return ((rowNum, colNum, self.LEFT))
                        if (r):
                            return ((rowNum, colNum, self.RIGHT))

                        return ((-1, -1, -1))

        return ((-1, -1, -1))

    # Find the first white circle, which has no lines, and determine which
    # line we should try drawing (based on which pathways out of the cell are
    # open).
    def __findWhiteCircleWithNoLines(self, pb):
        numRows, numCols = pb.getDimensions()
        for rowNum in range(0, numRows):
            for colNum in range(0, numCols):
                if (pb.isWhiteCircleAt(rowNum, colNum)):
                    numLines, l, r, u, d = pb.getLines(rowNum, colNum)
                    if (numLines == 0):
                        numOpen, l, r, u, d = pb.getOpenPaths(rowNum, colNum)
                        if (u and d):
                            return ((rowNum, colNum, self.UP))
                        if (l and r):
                            return ((rowNum, colNum, self.LEFT))

                        return ((-1, -1, -1))

        return ((-1, -1, -1))

    # Find the first black circle, which has no lines, and determine which
    # line we should try drawing (based on which pathways out of the cell are
    # open).
    def __findBlackCircleWithNoLines(self, pb):
        numRows, numCols = pb.getDimensions()
        for rowNum in range(0, numRows):
            for colNum in range(0, numCols):
                if (pb.isBlackCircleAt(rowNum, colNum)):
                    numLines, l, r, u, d = pb.getLines(rowNum, colNum)
                    if (numLines == 0):
                        numOpen, l, r, u, d = pb.getOpenPaths(rowNum, colNum)
                        if (u):
                            return ((rowNum, colNum, self.UP))
                        if (d):
                            return ((rowNum, colNum, self.DOWN))
                        if (l):
                            return ((rowNum, colNum, self.LEFT))
                        if (r):
                            return ((rowNum, colNum, self.RIGHT))

                        return ((-1, -1, -1))

        return ((-1, -1, -1))

    # Find the first dot cell, which has only one line, and determine which
    # line we should try drawing (based on which pathways out of the cell are
    # open).
    def __findDotWithOneLine(self, pb):
        numRows, numCols = pb.getDimensions()
        for rowNum in range(0, numRows):
            for colNum in range(0, numCols):
                if (pb.isDotAt(rowNum, colNum)):
                    numLines, l, r, u, d = pb.getLines(rowNum, colNum)
                    if (numLines == 1):
                        numOpen, l, r, u, d = pb.getOpenPaths(rowNum, colNum)
                        if (u):
                            return ((rowNum, colNum, self.UP))
                        if (d):
                            return ((rowNum, colNum, self.DOWN))
                        if (l):
                            return ((rowNum, colNum, self.LEFT))
                        if (r):
                            return ((rowNum, colNum, self.RIGHT))

                        return ((-1, -1, -1))

        return ((-1, -1, -1))

    # If we make a guess, but the solver still can't solve the puzzle, then
    # need to search the puzzle for the next guess.  We look for the next
    # cell to make a guess for, using the following order:
    #
    #   1) Find the first black circle which has only a single line
    #   2) Find the first white circle which has no lines
    #   3) Find the first black circle which has no lines
    #   4) Find the first dot with only a single line
    def __findNextGuess(self, pb):
        rowNum, colNum, direction = self.__findBlackCircleWithOneLine(pb)
        if ((rowNum != -1) and (colNum != -1)):
            return (rowNum, colNum, direction)

        rowNum, colNum, direction = self.__findWhiteCircleWithNoLines(pb)
        if ((rowNum != -1) and (colNum != -1)):
            return (rowNum, colNum, direction)

        rowNum, colNum, direction = self.__findBlackCircleWithNoLines(pb)
        if ((rowNum != -1) and (colNum != -1)):
            return (rowNum, colNum, direction)

        rowNum, colNum, direction = self.__findDotWithOneLine(pb)
        if ((rowNum != -1) and (colNum != -1)):
            return (rowNum, colNum, direction)

        # Unable to solve the puzzle!  No more brute force guess to be made.
        return ((-1, -1, -1))

    # When making a "guess", there is a specific order we attempt to draw a line;
    # the order is based upon the cell type (black circle, white circle, dot):
    #
    #   Black circle: up, down, left right
    #   White circle: up/down, left/right
    #   Dot: up, down, left, right
    #
    # If we attempt to draw a line in a certain direction, and an exception is
    # raised by the solver, then we need to determine which is the next direction
    # to try .. and if there are none left to try, then we need to discard the last
    # guess, and try a different direction for the previous guess.
    def __findNextDirection(self, pb, lastGuess):
        rowNum, colNum, direction = lastGuess
        if (pb.isBlackCircleAt(rowNum, colNum) or pb.isDotAt(rowNum, colNum)):
            numOpen, l, r, u, d = pb.getOpenPaths(rowNum, colNum)
            if (direction == self.UP):
                if (d):
                    return ((rowNum, colNum, self.DOWN))
                elif (l):
                    return ((rowNum, colNum, self.LEFT))
                elif (r):
                    return ((rowNum, colNum, self.RIGHT))

            elif (direction == self.DOWN):
                if (l):
                    return ((rowNum, colNum, self.LEFT))
                elif (r):
                    return ((rowNum, colNum, self.RIGHT))

            elif (direction == self.LEFT):
                if (r):
                    return ((rowNum, colNum, self.RIGHT))

        elif (pb.isWhiteCircleAt(rowNum, colNum)):
            numOpen, l, r, u, d = pb.getOpenPaths(rowNum, colNum)
            if (direction == self.UP):
                if (l):
                    return ((rowNum, colNum, self.LEFT))

        # Nothing left to try!
        return ((-1, -1, -1))

    # This method takes the next brute force "guess", and applies it
    # to a cloned copy of the indicated puzzle board.  It returns the
    # modified and cloned puzzle board
    #
    # The 'nextGuess' parameter is a tuple representing the row number
    # and column number of the cell to be modified, along with a direction
    # indicator, telling us which line to draw.
    def __applyNextGuess(self, pb, nextGuess):
        # We want to use the specified puzzle board as the basis to
        # which we apply the "guess", but we want to use a clone, so
        # as to not disturb the original
        pbClone = pb.cloneAll()
        rowNum, colNum, direction = nextGuess

        if (direction == self.UP):
            self.solver.drawLineUpWrapper(pbClone, rowNum, colNum)
        elif (direction == self.DOWN):
            self.solver.drawLineDownWrapper(pbClone, rowNum, colNum)
        elif (direction == self.LEFT):
            self.solver.drawLineLeftWrapper(pbClone, rowNum, colNum)
        elif (direction == self.RIGHT):
            self.solver.drawLineRightWrapper(pbClone, rowNum, colNum)

        return (pbClone)

    __enableShowInterimResults = False

    # This method is used during debugging, to ask the UI thread to
    # display a "progress" window, showing the current state of the
    # puzzle board being brute force solved.
    # It will block the work thread until either the UI thread signals
    # that it should continue with the solving process, or the UI thread
    # indicates that the user has cancelled the brute force operation.
    #
    # This method returns 'False' if the user cancelled the brute force
    # work, or 'True' if the work should continue
    def __showInterimResults(self, pb):
        # If this feature is disabled, then return without doing anything
        if not (self.__enableShowInterimResults):
            return (True)

        self.__bruteForceResults = pb
        self.showResultsEvent.set()

        while ((not self.cancelEvent.isSet())
               and (not self.resumeEvent.isSet())):
            time.sleep(0.1)

        if (self.cancelEvent.isSet()):
            self.cancelEvent.clear()
            self.__bruteForceResults = None
            self.__wasCancelledByUser = True
            return (False)
        else:
            self.resumeEvent.clear()
            return (True)

    # Way for determining if the user had cancelled the brute force solving request
    def wasRequestCancelledByUser(self):
        return (self.__wasCancelledByUser)

    # After repetitively applying all of the standard solving rules, if we
    # still have not found a solution to the puzzle, then the user is able
    # to request that we try brute force solving it.
    #
    # Brute force solving involves looking for a cell meeting one of the
    # following criteria:
    #
    #   1) Black circle with only 1 line
    #   2) White circle with 0 lines
    #   3) Black circle with 0 lines
    #   4) Dot with only 1 line
    #
    # Based on where the current line is, and the type of cell, the code
    # makes an educated guess for where to draw the next line.  After adding
    # the line, it calls the solver.
    #
    # If the solver doesn't raise an exception, then the process is repeated.
    # If the puzzle is solved, then we are done.
    # If the solver raises and exception, then the last guess we made was
    # invalid.  We then check if there was a different line we could have
    # drawn in the last cell.  If there was, then we draw that line, and
    # repeat the process of calling the solver.  In the case where there were
    # no more lines to try in the last cell, then we need to drop back to the
    # previous cell to see if there is a different line we could draw there.
    #
    # The code uses two stacks: one to track the guesses made so far, and a
    # second to track the puzzle board object resulting from adding a guess
    # to the previous puzzle board.
    def codeToRunInThread(self):
        # Start out by making a clone of the starting puzzle board, and then pushing
        # it onto the top of the puzzle board clone stack .. the top clone on the
        # stack is always the one to which the next "guess" is applied.
        pbClone = self.pb.cloneAll()
        cloneStack = []
        cloneStack.append(pbClone)
        guessStack = []

        self.__wasCancelledByUser = False

        # Determine the next "guess" to try
        nextGuess = self.__findNextGuess(pbClone)
        rowNum, colNum, direction = nextGuess

        if ((rowNum == -1) and (colNum == -1)):
            # Seems that wasn't even an initial guess for us to try!
            self.__bruteForceResults = None
            return

        # Keep iterating, until we either solve the puzzle, or run out of possible guesses.
        while (True):
            # Save the next guess information on the "guess" stack, and
            # then create a new puzzle board, based on the guess being applied
            # to the top clone on the clone stack.
            guessStack.append(nextGuess)
            pbClone = self.__applyNextGuess(pbClone, nextGuess)
            cloneStack.append(pbClone)

            # Debugging only .. pause while showing the puzzle board with the guess applied
            if (self.__showInterimResults(pbClone) == False):
                return

            try:
                # Check to see if the brute force request was cancelled by the user
                if (self.cancelEvent.isSet()):
                    self.cancelEvent.clear()
                    self.__bruteForceResults = None
                    self.__wasCancelledByUser = True
                    return

                # Let the solver work on the modified puzzle.
                self.solver.solve(pbClone)

                # Debugging only .. pause while showing the puzzle board after the solver has processed it
                if (self.__showInterimResults(pbClone) == False):
                    return

            except Exception as e:
                # If the solver raised an exception, then the last guess caused the puzzle to
                # become invalid.
                rowNum = -1
                colNum = -1

            else:
                # The solver was able to process the puzzle, so check if the puzzle was solved.
                if (Utilities.checkIfPuzzleIsSolved(pbClone)):
                    # Puzzle was solved!
                    self.__bruteForceResults = pbClone
                    return

                # Puzzle wasn't solved; determine next guess to try
                nextGuess = self.__findNextGuess(pbClone)
                rowNum, colNum, direction = nextGuess

            finally:
                # If the last guess caused the solver to raise an exception,
                # then we need to pop the last guess off the stack, and see if
                # there are any other directions in that cell we could have drawn
                # a line.  If so, then we will set up to try the same cell, but a
                # different line direction.  But if there are no other choices
                # remaining for that guess, then we need to backtrack to the
                # previous guess on the stack, and see if we can draw a different
                # line in it.  If we backtrack all the way through the stack without
                # finding another guess to try, then we can't solve the puzzle.
                while ((rowNum == -1) and (colNum == -1)):
                    if (len(guessStack) <= 0):
                        # Stack is now empty .. puzzle can't be solved!
                        self.__bruteForceResults = None
                        return

                    # Pop the top items from both the clone stack and the guess stack.
                    cloneStack.pop()
                    lastGuess = guessStack.pop()
                    if (len(cloneStack) <= 0):
                        self.__bruteForceResults = None
                        return

                    # Revert to using the clone now on the top of the stack, and see if the
                    # previous guess can be used with a different line direction
                    pbClone = cloneStack[-1]
                    nextGuess = self.__findNextDirection(pbClone, lastGuess)
                    rowNum, colNum, direction = nextGuess

    # Returns either 'None' (if a solution wasn't found, or if the user
    # cancelled the request), or a PuzzleBoard object containing the
    # solution to the puzzle.
    def getBruteForceResults(self):
        return (self.__bruteForceResults)

    # The brute force solver allows the user to cancel the request, so we
    # need to let the UI thread know that, so that it will display a
    # "Cancel" button.
    def supportsCancelRequest(self):
        return (True)

    # This is invoked by the main UI thread, each time it "wakes up" to check
    # whether the thread has completed.  It gives us the opportunity to monitor
    # UI requests from the thread code .. for now, it is primarily used during
    # debugging, to allow the thread code to request that the current "work" be
    # displayed in a progress dialog.
    #
    # It returns 'True' if a modal dialog has been displayed by the timer handler,
    # so that the main UI code can then "lift" the UI windows back to the top of
    # the window stack, since they mysteriously jump to the bottom of the window
    # stack after we unpost the 'progress' dialog!
    def timerHandler(self, parentWindow):
        if (self.showResultsEvent.isSet()):
            self.showResultsEvent.clear()
            self.__progressDialog = ProgressDialog(parentWindow,
                                                   self.__bruteForceResults,
                                                   self.cancelEvent,
                                                   self.resumeEvent)
            self.__progressDialog.showDialog()
            return (True)

        # No need to "lift" the UI window
        return (False)