예제 #1
20
class VMWidget(QWidget):
        
    def __init__(self,parent = None):
        super(VMWidget,self).__init__(parent)
        
        #self.setStyleSheet("QToolTip{background-color:white;color:black;font-size:12px;}")
        #self.setFont(QFont(u"微软雅黑",12))
        self.vmInfoList = []
        self.oldVmInfoList = [] 
        self.vmOffInfoList = []
        self.threadMap = {}
        self.processMap = {}
        self.parent = parent
        self.domainManager = DomainManager()
        self.progressMonitor = ProgressThread()
        self.vmstatusTimer = QTimer()
        #self.vmstatusTimer.start(7000)
        self.connect(self.vmstatusTimer, SIGNAL("timeout()"),self.postStatusToServer)
        self.startVmInfo = None
        if common.SCROLL_TYPE != "slider":
            self.scrollArea = QScrollArea(self)
            self.scrollArea.setStyleSheet("background-color:transparent;border:0px")
            self.vmButtonList = []
            self.vmTableWidget = QTableWidget()
            self.setTableWidgetStyle()
            
            self.mainLayout = QVBoxLayout()
            self.mainLayout.setMargin(0)
            self.mainLayout.setSpacing(0)
            self.mainLayout.addWidget(self.scrollArea)
            self.mainLayout.addSpacing(10)
        
            self.setLayout(self.mainLayout)
        
        
            self.downloadingList = []
        
            
            
        if common.SCROLL_TYPE == "slider":
            #三个自定义button,用于显示相应的虚拟机
            self.firstVM   = CustomVMButton(self)
            self.secondVM  = CustomVMButton(self)
            self.thirdVM   = CustomVMButton(self)
             
            self.vmButtonList = [self.firstVM, self.secondVM, self.thirdVM]
             
            for i in range(len(self.vmButtonList)):
                self.vmButtonList[i].setIcon(QIcon("images/windows.png"))
                self.vmButtonList[i].setIconSize(QSize(100,100))
                self.vmButtonList[i].setFlat(True)
                self.vmButtonList[i].setFont(QFont("Times", 15, QFont.Bold))
            
            #设置滑动条的样式
            self.slider = QSlider(Qt.Vertical,self)
            self.slider.setStyleSheet( "QSlider::groove:vertical {  "
                                        "border: 1px solid #4A708B;  "
                                        "background: #C0C0C0;  "
                                        "width: 5px;  "
                                        "border-radius: 1px;  "
                                        "padding-left:-1px;  "
                                        "padding-right:-1px;  "
                                        "padding-top:-1px;  "
                                        "padding-bottom:-1px;  "
                                        "}"
                                        "QSlider::handle:vertical {"
                                        "height: 100px;"
                                        "background: white;"
                                        "margin: 0 -4px;"
                                        "}" )
             
            self.btnLayout = QHBoxLayout(self)
            self.btnLayout.addStretch()
            self.btnLayout.addWidget(self.firstVM)
            self.btnLayout.addWidget(self.secondVM)
            self.btnLayout.addWidget(self.thirdVM)
            self.btnLayout.addStretch()
            self.btnLayout.addSpacing(10)
            self.btnLayout.addWidget(self.slider)
            self.btnLayout.setSpacing(10)
            self.btnLayout.setMargin(10)
             
            self.setLayout(self.btnLayout)
            
    def setTableWidgetStyle(self):
        
        #self.vmTableWidget.setColumnCount(3)
        #self.vmTableWidget.setRowCount(1)
        self.vmTableWidget.setFrameShape(QFrame.NoFrame)
        self.vmTableWidget.setEditTriggers(QTableWidget.NoEditTriggers)#不能编辑
        self.vmTableWidget.setSelectionMode(QAbstractItemView.NoSelection)
        self.vmTableWidget.verticalHeader().setVisible(False)#设置垂直头不可见
        self.vmTableWidget.horizontalHeader().setVisible(False)#设置垂直头不可见
        self.vmTableWidget.setShowGrid(False)
        self.vmTableWidget.setFocusPolicy(Qt.NoFocus)
        
        self.vmTableWidget.setStyleSheet("QTableWidget{background-color: rgb(235, 235, 235,0);}")    
    
    def getVmList(self):
        vmList = []
        vmInfo = {}
        vmInfo["name"] = "15615123021654541"
        vmInfo["status"] = "offline"
        vmInfo["name"] = "offline"
        vmInfo["os_distro"] = "windows_7"
        
        
        vmInfo1 = {}
        vmInfo1["name"] = "1561545421654541"
        vmInfo1["status"] = "offline"
        vmInfo1["name"] = "offline"
        
        vmInfo2 = {}
        vmInfo2["name"] = "1561afew21654541"
        vmInfo2["status"] = "offline"
        vmInfo2["name"] = "windows_7"
        
        vmInfo3 = {}
        vmInfo3["name"] = "afewfdsafewfa"
        vmInfo3["status"] = "offline"
        vmInfo3["name"] = "windows_7"
        
        vmList.append(vmInfo)
        vmList.append(vmInfo1)
        vmList.append(vmInfo2)
        vmList.append(vmInfo3)
        vmList.append(vmInfo1)
        vmList.append(vmInfo2)
        vmList.append(vmInfo3)
        
        return vmList
    def postStatusToServer(self):
        '''serverState = VMInfoManager.instance().getCurCloudServerState()
        if serverState != "enable":
            return'''
        postInfo = {}
        state = self.domainManager.getStatus()
        if self.vmOffInfoList == [] or len(self.downloadingList) != 0:
            #self.processMap.clear()
            #self.threadMap.clear()
            return
        if len(state) == 0:
            postInfo["state"] = "shutdown"
            postInfo["vmname"] = self.vmOffInfoList[0]["vmname"]
        else:
            for item in state:
                postInfo["state"] = "running"
                if state[item].has_key("disk_io"):
                    postInfo["io_throughput"] = state[item]["disk_io"]
                if state[item].has_key("max_disk_io"):
                    postInfo["io_throughput_peak"] = state[item]["max_disk_io"]
                if state[item].has_key("net_io"):
                    postInfo["net_throughput"] = state[item]["net_io"]
                if state[item].has_key("max_net_io"):
                    postInfo["net_throughput_peak"] = state[item]["max_net_io"]
                if state[item].has_key("memory_size"):
                    postInfo["memory"] = state[item]["memory_size"]*1024
                if state[item].has_key("vmname"):
                    postInfo["vmname"] = state[item]["vmname"]
                if state[item].has_key("name"):
                    postInfo["coursename"] = state[item]["name"]
                if state[item].has_key("cpu"):
                    postInfo["cpu_utilization"] = state[item]["cpu"]     
        self.emit(SIGNAL("vmstateinfo"),postInfo)
        
    def showVmList(self):
        subWidget = QWidget()
        if self.vmstatusTimer.isActive():
            pass
        else:
            #if globalvariable.CLASS_STATUS:
            self.vmstatusTimer.start(7000)
            #self.connect(self.vmstatusTimer, SIGNAL("timeout()"),self.postStatusToServer)
        
        if self.scrollArea.widget():
            self.scrollArea.takeWidget()
            
        self.vmTableWidget = QTableWidget()
        self.setTableWidgetStyle()
        #if self.vmTableWidget
        self.vmTableWidget.clear()
    
        #self.vmInfoList = self.getVmList()
        num = len(self.vmInfoList)
        columnCount = 1
        rowCount = 1
        if num == 0:
            return
        if num <=3:
            #self.scrollArea.verticalScrollBar().hide()
            rowCount = 1
            if num == 1:
                columnCount = 1
            elif num == 2:
                columnCount = 2
            elif num == 3:
                columnCount = 3
        else:
            rowCount = num/3
            rest = num%3
            if (rowCount == 2 and rest == 0)  or rowCount == 1 :
                pass
            if rest > 0:
                rowCount+=1
                
            columnCount = 3
        
        
        parentWidth = self.parent.width()
        resolutionValue = StoreInfoParser.instance().getResolutionValue()
        if resolutionValue:
            if len(resolutionValue.split("x")) >= 2:
                parentWidth = int(resolutionValue.split("x")[0])*5/9.0
        ratio = parentWidth/800.0
        tableWidth = (parentWidth - 20*2 -50*2*ratio)/3 + 5
        if len(self.vmInfoList) > 3:
            tableWidth = (parentWidth - 20*2 -50*2*ratio)/3 - 20*(1 - ratio) + 5
                
        self.vmTableWidget.setColumnCount(columnCount)
        self.vmTableWidget.setRowCount(rowCount)
        self.vmTableWidget.setFixedWidth(parentWidth-100)
        self.vmTableWidget.setFixedHeight((tableWidth+10)*rowCount)
        self.vmTableWidget.verticalHeader().setDefaultSectionSize(tableWidth+10)
        self.vmTableWidget.horizontalHeader().setDefaultSectionSize((parentWidth-100)/columnCount)
        #self.setWidget("status")
        if columnCount <= 3 and rowCount == 1:
            for i in range(columnCount):
                self.setRowColumnWidget(0,i,self.vmInfoList[i]["name"])
        else:
            for i in range(rowCount):
                for j in range(3):
                    if i*3 + j <= num-1:
                        self.setRowColumnWidget(i, j, self.vmInfoList[i*3+j]["name"])  
        
        mainLayout = QHBoxLayout()
        mainLayout.setMargin(0)
        mainLayout.addWidget(self.vmTableWidget)
        subWidget.setLayout(mainLayout)
        #if self.scrollArea.widget():
            #self.scrollArea.takeWidget()
        self.scrollArea.setWidget(subWidget) 
        self.scrollArea.setWidgetResizable(True) 
        #self.scrollArea.horizontalScrollBar().hide()   
    def setRowColumnWidget(self,row,column,vmid):
        status = "sfw"
        #self.vmTableWidget.clear()
        mainWidget = QWidget()
        #self.vmTableWidget.hideColumn()
        #vmRowButtonList = []
        parentWidth = self.parent.width()
        resolutionValue = StoreInfoParser.instance().getResolutionValue()
        if resolutionValue:
            if len(resolutionValue.split("x")) >= 2:
                parentWidth = int(resolutionValue.split("x")[0])*5/9.0
        ratio = parentWidth/800.0
        vmWidth = (parentWidth - 20*2 -50*2*ratio)/3
        if len(self.vmInfoList) > 3:
            vmWidth = (parentWidth - 20*2 -50*2*ratio)/3 - 20*(1 - ratio)
        
        vmButton = CustomVMButton()
        vmButton.setFixedSize(QSize(vmWidth, vmWidth + 5))
        vmButton.setIconSize(QSize(vmWidth - 60*ratio,vmWidth - 30*ratio))
        vmButton.setText(self.vmInfoList[row*3 + column]["name"])
        vmButton.setFlat(True)
        vmButton.setFont(QFont("Times", 15, QFont.Bold))
        vmInfo = self.vmInfoList[row*3 + column]
        vmButton.setVMInfo(vmInfo)
        imageName = "other.png"
        if self.vmInfoList[row*3 + column].has_key("os_distro"):
            if common.imageMap.has_key(self.vmInfoList[row*3 + column]["os_distro"]):
                imageName = common.imageMap[self.vmInfoList[row*3 + column]["os_distro"]]
            else:
                LogRecord.instance().logger.info(u'common.imageMap中未找到键值%s' % self.vmInfoList[row*3 + column]["os_distro"])
                        
        vmButton.setIcon(QIcon("images/systemImage/%s" % imageName))
                
#         if globalvariable.CLASS_STATUS and not globalvariable.TICHU_STATUS:
#             self.connect(vmButton, SIGNAL("clicked()"),self.slotOpenVMLesson)
#             self.connect(vmButton, SIGNAL("controlvm"),self.slotControlVM)
#         else:
#             self.connect(vmButton, SIGNAL("clicked()"),self.slotCreateVMLesson)
                    
        #vmRowButtonList.append(vmButton)
        self.vmButtonList.append(vmButton)
        #firMybutton.setStatus("undownload")
        vmButton.setObjectName(vmid + ":" + QString.number(row) + ":" + QString.number(column))
        self.connect(vmButton, SIGNAL("clicked()"),self.startDomain)
        
            
        progressBar = WidgetProgress()
        progressBar.setFixedSize(vmWidth, vmWidth + 5)
        #progressBar.progressBar.setValue(0)
        progressBar.setVmName(self.vmInfoList[row*3 + column]["name"])
        progressBar.setObjectName(vmid + ":" + QString.number(row) + ":" + QString.number(column))
        #progressBar.objectName()
        
        #self.firMybutton.setText(self.tr("确萨"))
        
        #progressBar = QProgressBar()
        
        myLayout = QHBoxLayout()
        myLayout.setMargin(0)
        myLayout.addStretch()
        myLayout.addWidget(vmButton)
        myLayout.addWidget(progressBar)
        myLayout.addStretch()
        
        if vmid in self.downloadingList:
            vmButton.hide()
        else:
            progressBar.hide()
             
        #firMybutton.hide()
        mainWidget.setLayout(myLayout)
        
        
        self.vmTableWidget.setCellWidget(row, column, mainWidget)
        #self.mainLayout.addWidget(mainWidget)  
    
    def setVMInfoList(self, vmInfoList, vmOffInfoList):
        
        self.oldVmInfoList = LocalImgManager.instance().getCompleteList()
        self.vmInfoList = vmInfoList
        self.vmOffInfoList = vmOffInfoList
        self.domainManager.deleteImgList(vmOffInfoList)
        
#         needDeleteList = self.compareDelete(self.oldVmInfoList, vmOffInfoList)
        if self.vmOffInfoList == []:
            #self.progressMonitor.stop()
            #self.threadMap.clear()
            for item in self.processMap:
                self.processMap[item].stop()
                if self.processMap[item].isRunning():
                    self.processMap[item].exit()
            for item in self.threadMap:
                if self.threadMap[item].isRunning():
                    self.threadMap[item].exit()
                
            self.processMap.clear()
            self.threadMap.clear()
            #self.killRsyncThread()
            
#         if len(needDeleteList) == 0:
#             return
#         else:
#             self.domainManager.deleteImgList(needDeleteList)
    def stopAllDownload(self):
        for item in self.processMap:
            self.processMap[item].stop()
            if self.processMap[item].isRunning():
                self.processMap[item].exit()
                
        self.killScpThread()
        for item in self.threadMap:
            #self.threadMap[item].stop()
            if self.threadMap[item].isRunning():
                self.threadMap[item].exit()
        
        self.downloadingList = []
        self.processMap.clear()
        self.threadMap.clear()
    def killScpThread(self):
        pidList = self.getScpsProcessId()
        #sshPidList = self.getSshProcessId()
        for item in pidList:
            self.killVmId(item)
        '''for mtem in sshPidList:
            self.killVmId(mtem)'''
        
    def killVmId(self,killid):
        cmd = "kill -9 " + killid
        os.system(str(cmd))
        
    def getScpsProcessId(self):#得到运行虚拟机的进程号序列,可用于关闭某个虚拟机
        
        idList = []
        cmd = "ps aux | grep scp"
        output = ""
        statusOutput = []
        statusOutput = commands.getstatusoutput(cmd)
        if statusOutput[0] == 0:
                output = statusOutput[1]
        else:
            output = ""
        result = output.split("\n")
        for i in range(len(result)):
            if QString(result[i]).contains("scp"):
                idList.append(QString(result[i]).simplified().split(" ")[1])
    
        return idList
    
    def getSshProcessId(self):#得到运行虚拟机的进程号序列,可用于关闭某个虚拟机
        
        idList = []
        cmd = "ps aux | grep /usr/bin/ssh"
        output = ""
        statusOutput = []
        statusOutput = commands.getstatusoutput(cmd)
        if statusOutput[0] == 0:
                output = statusOutput[1]
        else:
            output = ""
        result = output.split("\n")
        for i in range(len(result)):
            if QString(result[i]).contains("/usr/bin/ssh"):
                idList.append(QString(result[i]).simplified().split(" ")[1])
    
        return idList
    
    def compareDelete(self,oldList,newList):
        nameList = []
        deleteList = []
        for item in newList:
            nameList.append(item["name"])
            
        for name in oldList:
            if name not in nameList:
                deleteList.append(name)
        return deleteList
    
    def autoStartDownload(self):
        #return
        num = len(self.vmInfoList)
        if num == 0:
            return
        else:
            #self.progressMonitor.setVmInfoList(self.vmInfoList)
            #self.progressMonitor.start()
            #self.connect(self.progressMonitor, SIGNAL("downloadover"),self.slotDownloadOver)
            imgMap = LocalImgManager.instance().getCompliteListSize()
            for i in range(len(self.vmInfoList)):
                mark = 0
                for item in imgMap:
                    if item == self.vmInfoList[i]["name"] and imgMap[item] == self.vmInfoList[i]["img_file_size"]:
                        mark = 1
                if mark == 1:
                    continue
                row = i/3
                column = i%3
                self.downloadingList.append(self.vmInfoList[i]["name"])
#                 self.downloadThread = DownloadThread()
#                 self.progressThread = ProgressThread()
                threadid = self.vmInfoList[i]["name"] + ":" + QString.number(row) + ":" + QString.number(column)
#                 self.downloadThread.setProcessId(threadid + ":" + QString.number(self.vmInfoList[i]["img_file_size"]))
#                 self.progressThread.setProcessId(threadid + ":" + QString.number(self.vmInfoList[i]["img_file_size"]))
#                 self.connect(self.progressThread, SIGNAL("currentprogress"),self.updateProgressBar)
#                 self.connect(self.progressThread, SIGNAL("downloadover"),self.slotDownloadOver)
#                 self.downloadThread.start()
#                 self.progressThread.start()
                self.setRowColumnWidget(int(row), int(column), self.vmInfoList[i]["name"])
                self.createThread(threadid,self.vmInfoList[i])
                
                #time.sleep(1)
                #return
            self.startFirstThread()
    def startFirstThread(self):
        for item in self.threadMap:
            self.threadMap[item].start()
            self.processMap[item].start()
            self.connect(self.processMap[item], SIGNAL("currentprogress"),self.updateProgressBar)
            #self.connect(self.processMap[item], SIGNAL("downloadover"),self.slotDownloadOver)
            self.connect(self.threadMap[item], SIGNAL("downloadover"),self.slotDownloadOver)
            self.connect(self.threadMap[item], SIGNAL("downloaderror"),self.slotDownloadError)
            return
        
    def slotDownloadError(self,signalid):
        pass 
    
    def createThread(self,threadid,vmInfo):
        if self.threadMap.has_key(threadid):
            if self.threadMap[threadid].isRunning():
                self.threadMap[threadid].exit()
            self.threadMap.pop(threadid)
        if self.processMap.has_key(threadid):
            if self.processMap[threadid].isRunning():
                self.processMap[threadid].exit()
            self.processMap.pop(threadid)
        
        downloadThread = DownloadThread()
        progressThread = ProgressThread()
        self.threadMap[threadid] = downloadThread
        self.processMap[threadid] = progressThread
        self.threadMap[threadid].setProcessId(threadid + ":" + str(vmInfo["img_file_size"]))
        self.processMap[threadid].setProcessId(threadid + ":" + str(vmInfo["img_file_size"]))
        #self.threadMap[threadid].start()
        #self.processMap[threadid].start()
        #self.connect(self.processMap[threadid], SIGNAL("currentprogress"),self.updateProgressBar)
        #self.connect(self.processMap[threadid], SIGNAL("downloadover"),self.slotDownloadOver)
        
        
    def checkAddDownload(self):
        #return
        
        num = len(self.vmInfoList)
        #existImgList = LocalImgManager.instance().getCompleteList()
        existImgList = LocalImgManager.instance().getCompliteListSize()
        if num == 0:
            return
        else:
            count = 0
            for i in range(len(self.vmInfoList)):
                row = i/3
                column = i%3
                if existImgList.has_key(self.vmInfoList[i]["name"]) and existImgList.get(self.vmInfoList[i]["name"]) == self.vmInfoList[i]["img_file_size"]:
                    pass
                elif self.vmInfoList[i]["name"] in self.downloadingList:
                    pass
                elif existImgList.has_key(self.vmInfoList[i]["name"]) and self.domainManager.getDom() != None and self.startVmInfo != None and self.startVmInfo == self.domainManager.getDomainInfo():
                    pass
                else:
                    if self.vmInfoList[i]["name"] not in self.downloadingList:
                        self.downloadingList.append(self.vmInfoList[i]["name"])
                    threadid = self.vmInfoList[i]["name"] + ":" + QString.number(row) + ":" + QString.number(column)
                    self.createThread(threadid, self.vmInfoList[i])
                    self.setRowColumnWidget(int(row), int(column), self.vmInfoList[i]["name"])
                    count+= 1
                    
            if len(self.threadMap) == count:
                self.startFirstThread()
                if count > 0:
                    self.emit(SIGNAL("startadddownload"))
                
    def startDomain(self): 
        pbp = CustomVMButton().sender()
        buttonName = pbp.objectName()
        vmInfo = pbp.vmInfo
        for item in self.vmOffInfoList:
            if item["name"] == vmInfo["name"]:
                self.startVmInfo = item
        #return
    
        vmid = buttonName.split(":")[0]
        row = buttonName.split(":")[1]
        column = buttonName.split(":")[2]
        
        self.domainManager.setDomainInfo(self.startVmInfo)
        
        LogRecord.instance().logger.info(u"开始开启虚拟机-----------------------------------------------00")
        self.domainManager.startDomainByName()
        LogRecord.instance().logger.info(u"结束开启虚拟机-----------------------------------------------11")
        

    def updateProgressBar(self,count,objectname):
        #if self.threadMap[objectname].isRunning():
#         for i in range(len(self.downloadingList)):
#             if objectname == self.downloadingList[i]:
#                 progress=self.vmTableWidget.findChild((WidgetProgress, ),objectname)
#                 progress.progressBar.setValue(count) 
        progress=self.vmTableWidget.findChild((WidgetProgress, ),objectname)
        if not progress:
            return
        progress.progressBar.setValue(count) 
        
    def slotDownloadOver(self,count,name):
        serverState = VMInfoManager.instance().getCurCloudServerState()
        if serverState != "enable":
            return
        if self.processMap.has_key(name):
            self.processMap[name].stop()
        #self.processMap.pop(name)
        #self.threadMap.pop(name)
        vmid = name.split(":")[0]
        row = name.split(":")[1]
        column = name.split(":")[2]
        self.downloadingList = []
        self.setRowColumnWidget(int(row), int(column), vmid)
        time.sleep(4)
        if self.processMap.has_key(name) and self.processMap[name].isRunning:
            self.processMap[name].exit()
        if self.threadMap.has_key(name) and self.threadMap[name].isRunning:
            self.threadMap[name].exit()
        self.processMap.pop(name)
        self.threadMap.pop(name)
        self.emit(SIGNAL("avmdownloadover"))
#         for item in self.threadMap:
#             self.threadMap[item].start()
#             self.processMap[item].start()
#             self.connect(self.processMap[item], SIGNAL("currentprogress"),self.updateProgressBar)
#             self.connect(self.processMap[item], SIGNAL("downloadover"),self.slotDownloadOver)
#             return
    def getDownloadingList(self):
        return self.downloadingList
            
    def updateVMList(self):
        language = StoreInfoParser.instance().getLanguage()
        m_pTranslator = QTranslator()
        exePath = "./"
        if language == "chinese":
            QmName = "zh_CN.qm"
            StoreInfoParser.instance().setLanguage("chinese")
        else:
            QmName = "en_US.qm"
            
        if(m_pTranslator.load(QmName, exePath)):
            QCoreApplication.instance().installTranslator(m_pTranslator)
        
        if self.vmstatusTimer.isActive():
            self.vmstatusTimer.stop()
        else:
            pass
        subWidget = QWidget()
        vmNum = len(self.vmInfoList)
        rowNum = 0
        subMainLayout = QVBoxLayout()
        subMainLayout.setMargin(0)
        #subMainLayout.addStretch()
        #subMainLayout.addSpacing(10)
        #subMainLayout.setSpacing(10)
        
        self.vmButtonList = []
        
        if vmNum % 3 != 0:
            rowNum = vmNum / 3 + 1
        else:
            rowNum = vmNum / 3
             
        for i in range(rowNum):
            indexEnd = 3
            if i == rowNum - 1:
                indexEnd = vmNum - (i*3)
                 
            parentWidth = self.parent.width()
            resolutionValue = StoreInfoParser.instance().getResolutionValue()
            if resolutionValue:
                if len(resolutionValue.split("x")) >= 2:
                    parentWidth = int(resolutionValue.split("x")[0])*5/9.0
            
            ratio = parentWidth/800.0
            vmWidth = (parentWidth - 20*2 -50*2*ratio)/3
            if vmNum > 3:
                vmWidth = (parentWidth - 20*2 -50*2*ratio)/3 - 20*(1 - ratio)
            vmRowButtonList = []
            for j in range(indexEnd):
                vmButton = CustomVMButton(self)
                vmButton.setFixedSize(QSize(vmWidth, vmWidth + 5))
                vmButton.setIconSize(QSize(vmWidth - 60*ratio,vmWidth - 30*ratio))
                vmButton.setText(self.vmInfoList[i*3 + j]["name"])
                vmButton.setToolTip(self.tr("course: ") + self.vmInfoList[i*3 + j]["name"])
                vmButton.setStyleSheet(u"QToolTip{border-radius:5px;background-color:white;color:black;font-size:20px;font-family:微软雅黑;}")
                vmButton.setFlat(True)
                vmButton.setFont(QFont("Times", 15, QFont.Bold))
                vmInfo = self.vmInfoList[i*3 + j]
                vmButton.setVMInfo(vmInfo)
                imageName = "other.png"
                if self.vmInfoList[i*3 + j].has_key("os_distro"):
                    if common.imageMap.has_key(self.vmInfoList[i*3 + j]["os_distro"]):
                        imageName = common.imageMap[self.vmInfoList[i*3 + j]["os_distro"]]
                    else:
                        LogRecord.instance().logger.info(u'common.imageMap中未找到键值%s' % self.vmInfoList[i*3 + j]["os_distro"])
                        
                vmButton.setIcon(QIcon("images/systemImage/%s" % imageName))
                if globalvariable.CLASS_STATUS and not globalvariable.TICHU_STATUS:
                    self.connect(vmButton, SIGNAL("clicked()"),self.slotOpenVMLesson)
                    self.connect(vmButton, SIGNAL("controlvm"),self.slotControlVM)
                else:
                    self.connect(vmButton, SIGNAL("clicked()"),self.slotCreateVMLesson)
                    
                vmRowButtonList.append(vmButton)
                self.vmButtonList.append(vmButton)
              
            btnLayout = QHBoxLayout()
            if vmNum > 3:
                btnLayout.setSpacing(10)
                #btnLayout.setMargin(10)
                btnLayout.addSpacing(30)
            for vmbtn in vmRowButtonList:
                btnLayout.addWidget(vmbtn)
            btnLayout.addStretch()
            subMainLayout.addLayout(btnLayout) 
             
        #subMainLayout.addStretch()
        subWidget.setLayout(subMainLayout)
        if self.scrollArea.widget():
            self.scrollArea.takeWidget()
        self.scrollArea.setWidgetResizable(False)
        self.scrollArea.setWidget(subWidget)
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    def startVM(self):
        return  
        
    def autoOpenVMLesson(self, vmName, allflag = False):
        LogRecord.instance().logger.info(u'自动打开相应的虚拟机')
        if self.vmButtonList:
            vmInfo = self.vmButtonList[0].getVMInfo()
            VMOperation.instance().setCurrentVMInfo(vmInfo)
            #thread.start_new_thread(VMOperation.instance().openVM,())
            if allflag == True:                      
                if VMOperation.instance().openVM():
                    LogRecord.instance().logger.info(u'start the lesson')  
            else:
                if VMOperation.instance().openVM():
                    LogRecord.instance().logger.info(u'start the lesson')  
        else:
            LogRecord.instance().logger.info(u'未监测到虚拟机%s的信息' % vmName)  
    
    def autoOpenOffVMLesson(self):
        LogRecord.instance().logger.info(u'自动打开相应的虚拟机')
        
        if len(self.vmInfoList) == 1:
            if self.vmInfoList[0]["name"] in self.downloadingList:
                return
            else:
                self.startVmInfo = self.vmInfoList[0]
                self.domainManager.setDomainInfo(self.startVmInfo)
                self.domainManager.startDomainByName()
        
        else:
            return
    def checkDownloadOver(self):
        if self.autoCourse in self.downloadingList:
            pass    
        else:
            self.courseTimer.stop()
            #self.autoCourse = ""
            for item in self.vmOffInfoList:
                if self.autoCourse == item["name"]:
                    vmInfo = item
                    self.domainManager.setDomainInfo(vmInfo)
                    self.domainManager.startDomainByName()
                    return
            
    def autoOpenCourse(self,name,classid = None):
        LogRecord.instance().logger.info(u'自动打开相应的虚拟机')
        self.autoCourse = name
        if name in self.downloadingList:
            self.courseTimer = QTimer()
            self.courseTimer.start(1000)
            self.connect(self.courseTimer, SIGNAL("timeout()"),self.checkDownloadOver)
        
        else:
            for item in self.vmOffInfoList:
                if name == item["name"]:
                    vmInfo = item
                    if classid != None:
                        vmInfo["classid"] = classid
                
                    self.domainManager.setDomainInfo(vmInfo)
                    self.domainManager.startDomainByName()
                    
                    return
                
    def slotCreateVMLesson(self):
        vmBtn = self.sender()
        if vmBtn:
            LogRecord.instance().logger.info(u'开始创建虚拟机')
            if globalvariable.VM_IS_CREATE_STATUS:
                LogRecord.instance().logger.info(u'重复点击!')
                return
            else:
                globalvariable.VM_IS_CREATE_STATUS = True

            LogRecord.instance().logger.info(u'准备获取虚拟机信息!')
            paramInfo = vmBtn.getVMInfo()
            LogRecord.instance().logger.info(u'准备获取虚拟机信息完成!')
            try:
                vmName = VMOperation.instance().createVMLesson(paramInfo)
            except:
                LogRecord.instance().logger.info(u'创建虚拟机异常.........!')
            LogRecord.instance().logger.info(u'得到创建的虚拟机信息!')
            if vmName:
                LogRecord.instance().logger.info(u'创建虚拟机%s成功' % vmName)
                vmInfo = NetworkManager.instance().getVMInfo(vmName)
                if vmInfo == None:
                    globalvariable.VM_IS_CREATE_STATUS = False
                    return
                if len(vmInfo) == 0:
                    globalvariable.VM_IS_CREATE_STATUS = False
                    return
                vmInfo[0]["vmname"] = vmInfo[0]["name"]
                if vmInfo:
                    VMOperation.instance().setCurrentVMInfo(vmInfo[0])
                    if VMOperation.instance().openVM(True):
                        #保存开启虚拟机的名称
                        #globalvariable.VM_IS_CREATE_STATUS = False
                        WindowMonitor.instance().insertVmId(vmName)
                    else:
                        globalvariable.VM_IS_CREATE_STATUS = False
                        #删除没有成功运行的虚拟机
                        if VMOperation.instance().removeVMLesson(vmName):
                            LogRecord.instance().logger.info(u"删除后台相应的虚拟机成功")
                else:
                    LogRecord.instance().logger.info(u'未查询到相应的虚拟机:%s' % vmName)
                    globalvariable.VM_IS_CREATE_STATUS = False
            else:
                LogRecord.instance().logger.info(u'创建虚拟机失败')
                globalvariable.VM_IS_CREATE_STATUS = False


            #刷新虚拟机界面
            self.emit(SIGNAL("refreshVMS"))


    def slotControlVM(self, action):
        vmBtn = self.sender()
        if vmBtn:
            vmInfo = vmBtn.getVMInfo()
            VMOperation.instance().setCurrentVMInfo(vmInfo)
            VMOperation.instance().controlVM(action)
            
            vmInfos = globalvariable.VM_INFO
            self.setVMInfoList(vmInfos,[])
            self.updateVMList()

        
    def slotOpenVMLesson(self):
        #vmInfo = []
        vmBtn = self.sender()
        if vmBtn:
            btnVMInfo = vmBtn.getVMInfo()
            VMOperation.instance().setCurrentVMInfo(btnVMInfo)
            VMOperation.instance().openVM(True)
            
#         if globalvariable.CLASS_STATUS:
#             vmInfo = globalvariable.VM_INFO
#         else:
#             vmInfo = VMInfoManager.instance().getLessonListInfo()
#             
        #self.setVMInfoList(vmInfo,[])
        #self.updateVMList()
    #slider        
    def setSliderMaxValue(self, value):
        """设在滑动条的最大值"""
        maxValue = 1
        if value % 3 == 0:
            maxValue = (value / 3)
        else:
            maxValue = (value / 3 + 1)
         
        maxValue = maxValue - 1
         
        self.slider.setMinimum(1)
        self.slider.setMaximum(maxValue*3)
        self.slider.setValue(self.slider.maximum())
        self.currentIndex = maxValue
        self.maxValue = maxValue
        self.updateVMList()
        
    def paintEvent(self,event):
        if common.SCROLL_TYPE == "slider":
            self.setNodeSize(QSize(self.geometry().width()*4/15,self.geometry().height()*4/9))
        elif len(self.vmInfoList) <= 6:
            subWidget = self.scrollArea.widget()
            if subWidget != None:
                if len(self.vmInfoList) <= 3:
                    subWidget.move((self.geometry().width() - subWidget.width())/2, (self.geometry().height() - subWidget.height())/2 - 20)
                else:
                    subWidget.move((self.geometry().width() - subWidget.width())/2 - 15, (self.geometry().height() - subWidget.height())/2)
        QWidget.paintEvent(self, event)
    
    #slider               
    def setNodeSize(self,size):
        self.btnLayout.setSpacing(size.width()/20)
        self.btnLayout.setMargin(size.width()*3/20)
        
        self.firstVM.setIconSize(size)
        self.secondVM.setIconSize(size)
        self.thirdVM.setIconSize(size)
        
        self.slider.resize(QSize(self.slider.frameGeometry().width(),self.height()-100))
        
    def wheelEvent(self,event):
        """鼠标滚轮滚动事件,显示对应的虚拟机"""
        if common.SCROLL_TYPE == "slider":
            if event.delta() < 0 and self.currentIndex > 0:
                self.currentIndex -= 1
                self.updateVMListSlider()
            elif event.delta() > 0 and self.currentIndex < self.maxValue:
                self.currentIndex += 1
                self.updateVMListSlider()
   
            self.slider.wheelEvent(event)
        else:
            maxValue = self.scrollArea.verticalScrollBar().maximum()
            minValue = self.scrollArea.verticalScrollBar().minimum()
            currentValue = self.scrollArea.verticalScrollBar().value()
            if event.delta() < 0 and currentValue < maxValue:
                self.scrollArea.wheelEvent(event)
            elif event.delta() > 0 and currentValue > minValue:
                self.scrollArea.wheelEvent(event)
        
    #slider     
    def updateVMListSlider(self):
        """更新虚拟机列表"""
        for i in range(len(self.vmButtonList)):
            self.vmButtonList[i].hide()
 
        vmsInfoList = VMInfoManager.instance().getVMSInfo()
        if vmsInfoList:
            self.slider.show()
            startIndex = (self.maxValue - self.currentIndex) * 3
             
            if startIndex/3 == (self.maxValue):
                endIndex = len(vmsInfoList)
            else:
                endIndex = startIndex + 3
                 
            for i in range(startIndex, endIndex):
                self.vmButtonList[i - startIndex].setText(vmsInfoList[i])
                self.vmButtonList[i - startIndex].show()  
        else:
            self.slider.hide()
         
            
            
    """      echo <graphics type='sdl display='$DISPLAY' xauth='$XAUTHORITY'/>
예제 #2
0
class ScanRecordTable(QGroupBox):
    """ GUI component. Displays a list of previous scan results. Selecting a scan causes
    details of the scan to appear in other GUI components (list of barcodes in the barcode
    table and image of the puck in the image frame).
    """
    COLUMNS = [
        'Date', 'Time', 'Plate Barcode', 'Plate Type', 'Valid', 'Invalid',
        'Empty'
    ]

    def __init__(self, barcode_table, image_frame, options,
                 to_run_on_table_clicked):
        super(ScanRecordTable, self).__init__()

        # Read the store from file
        self._store = Store(options.store_directory.value(),
                            options.store_capacity, FileManager())
        self._options = options

        self._barcodeTable = barcode_table
        self._imageFrame = image_frame

        self.setTitle("Scan Records")
        self._init_ui(to_run_on_table_clicked)

        self._load_store_records()

    def _init_ui(self, to_run_on_table_clicked):
        # Create record table - lists all the records in the store
        self._table = QTableWidget()
        self._table.setFixedWidth(440)
        self._table.setFixedHeight(600)
        self._table.setColumnCount(len(self.COLUMNS))
        self._table.setHorizontalHeaderLabels(self.COLUMNS)
        self._table.setColumnWidth(0, 70)
        self._table.setColumnWidth(1, 55)
        self._table.setColumnWidth(2, 85)
        self._table.setColumnWidth(3, 70)
        self._table.setColumnWidth(4, 45)
        self._table.setColumnWidth(5, 50)
        self._table.setColumnWidth(6, 45)
        self._table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self._table.cellPressed.connect(to_run_on_table_clicked)
        self._table.cellPressed.connect(self._record_selected)

        # Delete button - deletes selected records
        btn_delete = QtGui.QPushButton('Delete')
        btn_delete.setToolTip('Delete selected scan/s')
        btn_delete.resize(btn_delete.sizeHint())
        btn_delete.clicked.connect(self._delete_selected_records)

        hbox = QHBoxLayout()
        hbox.setSpacing(10)
        hbox.addWidget(btn_delete)
        hbox.addStretch(1)

        vbox = QVBoxLayout()
        vbox.addWidget(self._table)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

    def add_record_frame(self, holder_barcode, plate, holder_img, pins_img):
        """ Add a new scan frame - creates a new record if its a new puck, else merges with previous record"""
        self._store.merge_record(holder_barcode, plate, holder_img, pins_img)
        self._load_store_records()
        if self._options.scan_clipboard.value():
            self._barcodeTable.copy_to_clipboard()

    def _load_store_records(self):
        """ Populate the record table with all of the records in the store.
        """
        self._table.clearContents()
        self._table.setRowCount(self._store.size())

        for n, record in enumerate(self._store.records):
            items = [
                record.date, record.time, record.holder_barcode,
                record.plate_type, record.num_valid_barcodes,
                record.num_unread_slots, record.num_empty_slots
            ]

            if (record.num_valid_barcodes +
                    record.num_empty_slots) == record.num_slots:
                color = self._options.col_ok()
            else:
                color = self._options.col_bad()

            color.a = 192
            for m, item in enumerate(items):
                new_item = QtGui.QTableWidgetItem(str(item))
                new_item.setBackgroundColor(color.to_qt())
                new_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                self._table.setItem(n, m, new_item)

        # Display the first (most recent) record
        self._table.setCurrentCell(0, 0)
        self._record_selected()

    def _record_selected(self):
        """ Called when a row is selected, causes details of the selected record to be
        displayed (list of barcodes in the barcode table and image of the scan in the
        image frame).
        """
        try:
            row = self._table.selectionModel().selectedRows()[0].row()
            record = self._store.get_record(row)
            self._barcodeTable.populate(record.holder_barcode, record.barcodes)
            marked_image = record.marked_image(self._options)
            self._imageFrame.display_puck_image(marked_image)
        except IndexError:
            self._barcodeTable.clear()
            self._imageFrame.clear_frame(
                "Record table empty\nNothing to display")

    def _delete_selected_records(self):
        """ Called when the 'Delete' button is pressed. Deletes all of the selected records
        (and the associated images) from the store and from disk. Asks for user confirmation.
        """
        # Display a confirmation dialog to check that user wants to proceed with deletion
        quit_msg = "This operation cannot be undone.\nAre you sure you want to delete these record/s?"
        reply = QtGui.QMessageBox.warning(self, 'Confirm Delete', quit_msg,
                                          QtGui.QMessageBox.Yes,
                                          QtGui.QMessageBox.No)

        # If yes, find the appropriate records and delete them
        if reply == QtGui.QMessageBox.Yes:
            rows = self._table.selectionModel().selectedRows()
            records_to_delete = []
            for row in rows:
                index = row.row()
                record = self._store.get_record(index)
                records_to_delete.append(record)

            self._store.delete_records(records_to_delete)
            self._load_store_records()

    def is_latest_holder_barcode(self, holder_barcode):
        return self._store.is_latest_holder_barcode(holder_barcode)
예제 #3
0
    def setupTabs(self):
        #self.tabs.currentChanged.disconnect(self.currentTabChanged)
        while(self.tabs.count() > 0):
            self.tabs.removeTab(0)

        self.tables = {}
        allcounters = Counter.findAll()
        for c in allcounters:
            tab = QWidget(self.tabs)
            hl = QHBoxLayout()
            tab.setLayout(hl)
            
            recordings = Recording.findByIdCounter(c.id)

            tw = QTableWidget(len(recordings), 5)
            self.tables["%i"%c.id] = tw
            column_names = ("Id","Counter", "Date", "Value", "Remove ?")
            tw.setHorizontalHeaderLabels(column_names)

            # Fill the table with the recordings
            for i, r in enumerate(recordings):
                # The id of the recording in the table of recordings
                item = QTableWidgetItem("{}".format(r.id))
                item.setFlags(QtCore.Qt.NoItemFlags)
                tw.setItem (i, 0, item)
                
                # The id of the associated counter
                item = QTableWidgetItem("{}".format(r.idcounter))
                item.setFlags(QtCore.Qt.NoItemFlags)
                tw.setItem (i, 1, item)

                # The date when the recording has been made
                item = QTableWidgetItem(r.date.strftime("%Y-%m-%d %H:%M:%S"))
                item.setFlags(QtCore.Qt.NoItemFlags)
                tw.setItem (i, 2, item)

                # The value can be edited
                item = QTableWidgetItem("{}".format(r.value))
                item.setFlags(QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled)

                tw.setItem (i, 3, item)

                but = QPushButton("Remove")
                but.clicked.connect(partial(self.on_removeClicked, counter_id=c.id, recording_id=r.id))
                #item = QTableWidgetItem(but)
                #tw.setItem(i, 4, but)
                #tw.setIndexWidget()
                tw.setCellWidget(i, 4, but)

            tw.cellChanged.connect(partial(self.on_cellChanged, counter_id=c.id))

            # We allow sorting and we sort by decreasing date
            # to get the most recent recordings at the top
            tw.setSortingEnabled(True)
            tw.sortByColumn(2, QtCore.Qt.DescendingOrder)

            # Ajust the width of the columns to better see the contents
            tw.resizeColumnsToContents()
            tw.setFixedWidth(600)
            hl.addWidget(tw)

            #### Plot the data
            canvas = self.plot_data(recordings)
            hl.addWidget(canvas)


            self.tabs.addTab(tab,str(c.id) + "-" + c.name)   
예제 #4
0
class BarcodeTable(QGroupBox):
    """ GUI component. Displays a list of barcodes for the currently selected puck.
    """
    def __init__(self, options):
        super(BarcodeTable, self).__init__()

        self._barcodes = []

        self._options = options

        self.setTitle("Barcodes")
        self._init_ui()

    def _init_ui(self):
        # Create record table - lists all the records in the store
        self._table = QTableWidget()

        # Create barcode table - lists all the barcodes in a record
        self._table = QtGui.QTableWidget()
        self._table.setFixedWidth(110)
        self._table.setFixedHeight(600)
        self._table.setColumnCount(1)
        self._table.setRowCount(10)
        self._table.setHorizontalHeaderLabels(['Barcode'])
        self._table.setColumnWidth(0, 100)

        # Clipboard button - copy the selected barcodes to the clipboard
        self._btn_clipboard = QtGui.QPushButton('Copy To Clipboard')
        self._btn_clipboard.setToolTip(
            'Copy barcodes for the selected record to the clipboard')
        self._btn_clipboard.resize(self._btn_clipboard.sizeHint())
        self._btn_clipboard.clicked.connect(self.copy_selected_to_clipboard)
        self._btn_clipboard.setEnabled(False)

        hbox = QHBoxLayout()
        hbox.setSpacing(10)
        hbox.addWidget(self._btn_clipboard)
        hbox.addStretch(1)

        vbox = QVBoxLayout()
        vbox.addWidget(self._table)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

    def populate(self, barcodes):
        """ Called when a new row is selected on the record table. Displays all of the
        barcodes from the selected record in the barcode table. By default, valid barcodes are
        highlighted green, invalid barcodes are highlighted red, and empty slots are grey.
        """
        num_slots = len(barcodes)
        self._table.clearContents()
        self._table.setRowCount(num_slots)

        for index, barcode in enumerate(barcodes):
            # Select appropriate background color
            if barcode == NOT_FOUND_SLOT_SYMBOL:
                color = self._options.col_bad()
            elif barcode == EMPTY_SLOT_SYMBOL:
                color = self._options.col_empty()
            else:
                color = self._options.col_ok()

            color.a = 192

            # Set table item
            barcode = QtGui.QTableWidgetItem(barcode)
            barcode.setBackgroundColor(color.to_qt())
            barcode.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self._table.setItem(index, 0, barcode)

        self._barcodes = barcodes[:]
        for i, barcode in enumerate(self._barcodes):
            if barcode in [NOT_FOUND_SLOT_SYMBOL, EMPTY_SLOT_SYMBOL]:
                self._barcodes[i] = ""

        self._update_button_state()

    def _update_button_state(self):
        if self._barcodes is None or len(self._barcodes) == 0:
            self._btn_clipboard.setEnabled(False)
        else:
            self._btn_clipboard.setEnabled(True)

    def copy_selected_to_clipboard(self):
        """ Called when the copy to clipboard button is pressed. Copies the list/s of
        barcodes for the currently selected records to the clipboard so that the user
        can paste it elsewhere.
        """
        sep = os.linesep
        if self._barcodes:
            pyperclip.copy(sep.join(self._barcodes))
예제 #5
0
class BarcodeTable(QGroupBox):
    """ GUI component. Displays a list of barcodes for the currently selected puck.
    """
    def __init__(self, options):
        super(BarcodeTable, self).__init__()

        self._barcodes = []

        self._options = options

        self.setTitle("Barcodes")
        self._init_ui()

    def _init_ui(self):
        # Create record table - lists all the records in the store
        self._table = QTableWidget()

        # Create barcode table - lists all the barcodes in a record
        self._table = QtGui.QTableWidget()
        self._table.setFixedWidth(110)
        self._table.setFixedHeight(600)
        self._table.setColumnCount(1)
        self._table.setRowCount(10)
        self._table.setHorizontalHeaderLabels(['Barcode'])
        self._table.setColumnWidth(0, 100)

        # Clipboard button - copy the selected barcodes to the clipboard
        self._btn_clipboard = QtGui.QPushButton('Copy To Clipboard')
        self._btn_clipboard.setToolTip('Copy barcodes for the selected record to the clipboard')
        self._btn_clipboard.resize(self._btn_clipboard.sizeHint())
        self._btn_clipboard.clicked.connect(self.copy_selected_to_clipboard)
        self._btn_clipboard.setEnabled(False)

        hbox = QHBoxLayout()
        hbox.setSpacing(10)
        hbox.addWidget(self._btn_clipboard)
        hbox.addStretch(1)

        vbox = QVBoxLayout()
        vbox.addWidget(self._table)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

    def populate(self, barcodes):
        """ Called when a new row is selected on the record table. Displays all of the
        barcodes from the selected record in the barcode table. By default, valid barcodes are
        highlighted green, invalid barcodes are highlighted red, and empty slots are grey.
        """
        num_slots = len(barcodes)
        self._table.clearContents()
        self._table.setRowCount(num_slots)

        for index, barcode in enumerate(barcodes):
            # Select appropriate background color
            if barcode == NOT_FOUND_SLOT_SYMBOL:
                color = self._options.col_bad()
            elif barcode == EMPTY_SLOT_SYMBOL:
                color = self._options.col_empty()
            else:
                color = self._options.col_ok()

            color.a = 192

            # Set table item
            barcode = QtGui.QTableWidgetItem(barcode)
            barcode.setBackgroundColor(color.to_qt())
            barcode.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self._table.setItem(index, 0, barcode)

        self._barcodes = barcodes[:]
        for i, barcode in enumerate(self._barcodes):
            if barcode in [NOT_FOUND_SLOT_SYMBOL, EMPTY_SLOT_SYMBOL]:
                self._barcodes[i] = ""

        self._update_button_state()

    def _update_button_state(self):
        if self._barcodes is None or len(self._barcodes) == 0:
            self._btn_clipboard.setEnabled(False)
        else:
            self._btn_clipboard.setEnabled(True)

    def copy_selected_to_clipboard(self):
        """ Called when the copy to clipboard button is pressed. Copies the list/s of
        barcodes for the currently selected records to the clipboard so that the user
        can paste it elsewhere.
        """
        sep = os.linesep
        if self._barcodes:
            pyperclip.copy(sep.join(self._barcodes))
예제 #6
0
class ScanRecordTable(QGroupBox):
    """ GUI component. Displays a list of previous scan results. Selecting a scan causes
    details of the scan to appear in other GUI components (list of barcodes in the barcode
    table and image of the puck in the image frame).
    """
    COLUMNS = ['Date', 'Time', 'Plate Type', 'Valid', 'Invalid', 'Empty']

    def __init__(self, barcode_table, image_frame, options):
        super(ScanRecordTable, self).__init__()

        # Read the store from file
        self._store = Store(options.store_directory.value(), options)
        self._options = options

        self._barcodeTable = barcode_table
        self._imageFrame = image_frame

        self.setTitle("Scan Records")
        self._init_ui()

        self._load_store_records()

    def _init_ui(self):
        # Create record table - lists all the records in the store
        self._table = QTableWidget()
        self._table.setFixedWidth(440)
        self._table.setFixedHeight(600)
        self._table.setColumnCount(len(self.COLUMNS))
        self._table.setHorizontalHeaderLabels(self.COLUMNS)
        self._table.setColumnWidth(0, 70)
        self._table.setColumnWidth(1, 55)
        self._table.setColumnWidth(2, 85)
        self._table.setColumnWidth(3, 60)
        self._table.setColumnWidth(4, 60)
        self._table.setColumnWidth(5, 60)
        self._table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self._table.cellPressed.connect(self._record_selected)

        # Delete button - deletes selected records
        btn_delete = QtGui.QPushButton('Delete')
        btn_delete.setToolTip('Delete selected scan/s')
        btn_delete.resize(btn_delete.sizeHint())
        btn_delete.clicked.connect(self._delete_selected_records)

        hbox = QHBoxLayout()
        hbox.setSpacing(10)
        hbox.addWidget(btn_delete)
        hbox.addStretch(1)

        vbox = QVBoxLayout()
        vbox.addWidget(self._table)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

    def add_record(self, plate, image):
        """ Add a new scan record to the store and display it. """
        self._store.add_record(plate, image)
        self._load_store_records()

    def add_record_frame(self, plate, image):
        """ Add a new scan frame - creates a new record if its a new puck, else merges with previous record"""
        self._store.merge_record(plate, image)
        self._load_store_records()
        if self._options.scan_clipboard.value():
            self._barcodeTable.copy_selected_to_clipboard()

    def _load_store_records(self):
        """ Populate the record table with all of the records in the store.
        """
        self._table.clearContents()
        self._table.setRowCount(self._store.size())

        for n, record in enumerate(self._store.records):
            items = [record.date, record.time, record.plate_type, record.num_valid_barcodes,
                     record.num_unread_slots, record.num_empty_slots]

            if (record.num_valid_barcodes + record.num_empty_slots) == record.num_slots:
                color = self._options.col_ok()
            else:
                color = self._options.col_bad()

            color.a = 192
            for m, item in enumerate(items):
                new_item = QtGui.QTableWidgetItem(str(item))
                new_item.setBackgroundColor(color.to_qt())
                new_item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
                self._table.setItem(n, m, new_item)

        # Display the first (most recent) record
        self._table.setCurrentCell(0, 0)
        self._record_selected()

    def _record_selected(self):
        """ Called when a row is selected, causes details of the selected record to be
        displayed (list of barcodes in the barcode table and image of the scan in the
        image frame).
        """
        try:
            row = self._table.selectionModel().selectedRows()[0].row()
            record = self._store.get_record(row)
            self._barcodeTable.populate(record.barcodes)
            marked_image = record.marked_image(self._options)
            self._imageFrame.display_puck_image(marked_image)
        except IndexError:
            pass
            self._barcodeTable.populate([])
            self._imageFrame.clear_frame()

    def _delete_selected_records(self):
        """ Called when the 'Delete' button is pressed. Deletes all of the selected records
        (and the associated images) from the store and from disk. Asks for user confirmation.
        """
        # Display a confirmation dialog to check that user wants to proceed with deletion
        quit_msg = "This operation cannot be undone.\nAre you sure you want to delete these record/s?"
        reply = QtGui.QMessageBox.warning(self, 'Confirm Delete',
                         quit_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        # If yes, find the appropriate records and delete them
        if reply == QtGui.QMessageBox.Yes:
            rows = self._table.selectionModel().selectedRows()
            records_to_delete = []
            for row in rows:
                index = row.row()
                record = self._store.get_record(index)
                records_to_delete.append(record)

            self._store.delete_records(records_to_delete)
            self._load_store_records()