def _OnFind(self, event): start, end = self.DoFind(event.GetFindString()) if start == -1: msg = MessageDialog(self, 'Find/Replace', 'Cannot find "' + event.GetFindString() + '"', icon='information') msg.ShowModal() else: self.fSetSelection(start, end)
def __houghLineTransformSlot(self): # 如果当前不是二值图像 则直接返回 if not isBinaryImage(self.__imgList[-1]): MessageDialog(self, '需要先在「分割」菜单中进行二值化') return newImg, flag = houghLineTransform(self.__imgList[-1]) if flag: self.__appendImg(newImg) else: MessageDialog(self, '未检测到直线')
def __contourTracingSlot(self): # 如果当前不是二值图像 则直接返回 if not isBinaryImage(self.__imgList[-1]): MessageDialog(self, '需要先在「分割」菜单中进行二值化') return self.__appendImg(contourTracing(self.__imgList[-1]))
def __histogramEqualizationBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先进行灰度化') return self.__appendImg(histogramEqualization(self.__imgList[-1]))
def __orbFeatureDetectionSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return self.__appendImg(orbFeatureDetection(self.__imgList[-1]))
def __morphologyPreviewSlot(self): # 如果当前不是二值图像 则直接返回 if not isBinaryImage(self.__imgList[-1]): MessageDialog(self, '需要先在「分割」菜单中进行二值化') self.ui.hs_morphology_radius.setValue(0) return if self.ui.radio_morphology_shape_rect.isChecked(): shape = 'rect' elif self.ui.radio_morphology_shape_circle.isChecked(): shape = 'circle' else: shape = 'cross' radius = self.ui.hs_morphology_radius.value() if self.ui.radio_morphology_type_dilate.isChecked(): self.__previewImgDict['morphology'] = dilateOperation( self.__imgList[-1], shape, radius) elif self.ui.radio_morphology_type_erode.isChecked(): self.__previewImgDict['morphology'] = erodeOperation( self.__imgList[-1], shape, radius) elif self.ui.radio_morphology_type_open.isChecked(): self.__previewImgDict['morphology'] = openOperation( self.__imgList[-1], shape, radius) else: self.__previewImgDict['morphology'] = closeOperation( self.__imgList[-1], shape, radius) self.__viewer.changeImage(self.__previewImgDict['morphology'])
def __otsuThresholdBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return self.__appendImg(otsuThresholdSegmentation(self.__imgList[-1]))
def __toGrayImageBtnSlot(self): # 如果已经是灰度图 则直接返回 if self.__imgList[-1].ndim == 2: MessageDialog(self, '当前已经是灰度图像') return self.__appendImg(cv.cvtColor(self.__imgList[-1], cv.COLOR_BGR2GRAY))
def __fixedThresholdPreviewSlot(self, threshold): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') self.ui.hs_fixed_threshold.setValue(127) return self.__previewImgDict['fixedThreshold'] = fixedThresholdSegmentation( self.__imgList[-1], threshold) self.__viewer.changeImage(self.__previewImgDict['fixedThreshold']) self.__fixedThresholdActiveFlag = True # 置为激活状态
def __laplacianEdgeDetectionBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return if self.ui.radio_laplacian_edge_detection_type_4.isChecked(): neighbourhood = 4 else: neighbourhood = 8 self.__appendImg( laplacianEdgeDetection(self.__imgList[-1], neighbourhood))
def __cannyEdgePreviewSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') self.ui.hs_canny_low_threshold.setValue(100) self.ui.hs_canny_high_threshold.setValue(200) return lowThreshold = self.ui.hs_canny_low_threshold.value() highThreshold = self.ui.hs_canny_high_threshold.value() self.__previewImgDict['cannyEdge'] = cannyEdgeDetection( self.__imgList[-1], lowThreshold, highThreshold) self.__viewer.changeImage(self.__previewImgDict['cannyEdge']) self.__cannyEdgeActiveFlag = True # 置为激活状态
def __sobelEdgeDetectionBtnSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') return if self.ui.radio_sobel_edge_detection_axis_x.isChecked(): axis = 'x' else: axis = 'y' if self.ui.radio_sobel_edge_detection_radius_1.isChecked(): radius = 1 elif self.ui.radio_sobel_edge_detection_radius_2.isChecked(): radius = 2 else: radius = 3 self.__appendImg(sobelEdgeDetection(self.__imgList[-1], axis, radius))
def __init__(self, debugEnabled=False, show=True, host=None): ''' Enabling debug does things like disable async so errors are more apparent show should be true to show the windows by default ''' self.debugEnabled = debugEnabled hosts = os.path.join(os.path.dirname(__file__), 'data', 'hosts') self.locator = labmap.Locator(open(hosts)) self.username = self._getUsername() if host is None: self.hostname = socket.gethostname().lower() else: self.hostname = host self.model = CascaderModel(self.locator, self.username, self.hostname) self.messageDialog = MessageDialog(self.locator, self.model.getCascaderData()) #slightly more sane method of setting things up that uses depency #tracking req = RequireFunctions() req.add('gui', self.initGui) req.add('tray', self.initTray, ['gui']) req.add('map', self.initMap, ['gui']) req.add('labs', self.initLabs, ['map']) req.add('modelcallbacks', self.initModelCallbacks) req.add('connection', self.initConnection) req.add('signals', self.initSignals, ['gui', 'settings']) req.add('settings', self.initSettings, ['gui', 'connection']) req.add('autostart', self.askAutostart, ['gui', 'settings']) req.run() if show: self.window.show_all()
def __adaptiveThresholdPreviewSlot(self): # 如果当前不是灰度图 则直接返回 if not self.__imgList[-1].ndim == 2: MessageDialog(self, '需要先在「基本」菜单中进行灰度化') self.ui.hs_adaptive_threshold_radius.setValue(0) self.ui.hs_adaptive_threshold_offset.setValue(0) return if self.ui.radio_adaptive_threshold_type_average.isChecked(): method = 'mean' else: method = 'gaussian' radius = self.ui.hs_adaptive_threshold_radius.value() offset = self.ui.hs_adaptive_threshold_offset.value() if radius == 0: self.__previewImgDict['adaptiveThreshold'] = self.__imgList[-1] else: self.__previewImgDict[ 'adaptiveThreshold'] = adaptiveThresholdSegmentation( self.__imgList[-1], method, radius, offset) self.__viewer.changeImage(self.__previewImgDict['adaptiveThreshold'])
def __init__(self, debugEnabled=False, show=True, host=None): """ Enabling debug does things like disable async so errors are more apparent show should be true to show the windows by default """ self.debugEnabled = debugEnabled hosts = os.path.join(os.path.dirname(__file__), "data", "hosts") self.locator = labmap.Locator(open(hosts)) self.username = self._getUsername() if host is None: self.hostname = socket.gethostname().lower() else: self.hostname = host self.model = CascaderModel(self.locator, self.username, self.hostname) self.messageDialog = MessageDialog(self.locator, self.model.getCascaderData()) # slightly more sane method of setting things up that uses depency # tracking req = RequireFunctions() req.add("gui", self.initGui) req.add("tray", self.initTray, ["gui"]) req.add("map", self.initMap, ["gui"]) req.add("labs", self.initLabs, ["map"]) req.add("modelcallbacks", self.initModelCallbacks) req.add("connection", self.initConnection) req.add("signals", self.initSignals, ["gui", "settings"]) req.add("settings", self.initSettings, ["gui", "connection"]) req.add("autostart", self.askAutostart, ["gui", "settings"]) req.run() if show: self.window.show_all()
class CascadersFrame: def __init__(self, debugEnabled=False, show=True, host=None): """ Enabling debug does things like disable async so errors are more apparent show should be true to show the windows by default """ self.debugEnabled = debugEnabled hosts = os.path.join(os.path.dirname(__file__), "data", "hosts") self.locator = labmap.Locator(open(hosts)) self.username = self._getUsername() if host is None: self.hostname = socket.gethostname().lower() else: self.hostname = host self.model = CascaderModel(self.locator, self.username, self.hostname) self.messageDialog = MessageDialog(self.locator, self.model.getCascaderData()) # slightly more sane method of setting things up that uses depency # tracking req = RequireFunctions() req.add("gui", self.initGui) req.add("tray", self.initTray, ["gui"]) req.add("map", self.initMap, ["gui"]) req.add("labs", self.initLabs, ["map"]) req.add("modelcallbacks", self.initModelCallbacks) req.add("connection", self.initConnection) req.add("signals", self.initSignals, ["gui", "settings"]) req.add("settings", self.initSettings, ["gui", "connection"]) req.add("autostart", self.askAutostart, ["gui", "settings"]) req.run() if show: self.window.show_all() def askAutostart(self): if self.settings["asked_autostart"] == False: self.settings["asked_autostart"] = True message = gtk.MessageDialog( None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, ("Do you want to autostart this " "program on login?"), ) resp = message.run() if resp == gtk.RESPONSE_YES: self.builder.get_object("cbAutostart").set_active(True) message.destroy() def initMap(self): self.map = labmap.Map(self.builder.get_object("tblMap"), self.locator, self.model.getCascaderData()) def initTray(self): icon = os.path.join(os.path.dirname(__file__), "icons", "cascade.ico") self.trayIcon = TrayIcon(self, icon) self.window.connect("delete-event", lambda w, e: w.hide() or True) def initSignals(self): """ Signals catch events and shut things down properly """ signal.signal(signal.SIGINT, self.quit) signal.signal(signal.SIGTERM, self.quit) def initGui(self): self.builder = gtk.Builder() dr = os.path.dirname(__file__) self.builder.add_from_file(os.path.join(dr, "gui", "main.glade")) initTreeView(self.builder.get_object("tvCascList")) initTreeView(self.builder.get_object("tvCascSubjects")) self.window = self.builder.get_object("wnCascader") self.window.connect("destroy", lambda *a: gtk.main_quit()) self.builder.connect_signals(self) pixbuf = gtk.gdk.pixbuf_new_from_file(os.path.join(dr, "icons", "cascade.ico")) self.window.set_icon(pixbuf) def initLabs(self): """ Sets up the labs drop down box stuff """ lst = gtk.ListStore(gobject.TYPE_STRING) lst.append(["All"]) for lab in self.locator.getLabs(): lst.append([lab]) cb = self.builder.get_object("cbFilterLab") cb.set_model(lst) cell = gtk.CellRendererText() cb.set_active(0) cb.pack_start(cell, True) cb.add_attribute(cell, "text", 0) def initSettings(self): self.settings = settings.loadSettings() autostart = self.settings["autostart"] self.builder.get_object("cbAutostart").set_active(autostart) autocascade = self.settings["autocascade"] self.builder.get_object("cbAutocascade").set_active(autocascade) debug("Got subjects from settings: %s" % str(self.settings["cascSubjects"])) self.addSubjects(self.settings["cascSubjects"]) if self.settings["cascading"] and self.settings["autocascade"]: self.startCascading() def _getUsername(self): try: logname = os.environ["LOGNAME"] # for debugging only, means multiple clients can be run at once if self.debugEnabled == True: import random logname = str(random.random()) except KeyError: errorDialog(("Couldn't get LOGNAME from the enviroment," " this only runs on Linux at the moment")) self.quit() return logname def initModelCallbacks(self): """ This sets up the service callbacks """ self.model.registerOnCascaderChanged(self.updateCascaderLists) self.model.registerOnSubjectChanged(self.updateAllSubjects) self.model.registerOnUserAskingForHelp(self.onUserAskingForHelp) self.model.registerOnDisconnected(self.onDisconnect) self.model.registerOnConnected(self.onLogin) def onDisconnect(self): status = self.builder.get_object("lbStatus") status.set("Connecting...") def onLogin(self): status = self.builder.get_object("lbStatus") status.set("Connected") def initConnection(self): """ called in the constructor. also does the setup post connect """ debug("Connecting...") self.builder.get_object("lbUsername").set(self.username) def loginErr(reason): reason = reason.trap(ValueError) errorDialog("Failed to login, server reported %s" % reason.getErrorMessage()) self.quit() def catchallLoginErr(reason): errorDialog("Failed to login, server reported %s" % reason.getErrorMessage()) self.quit() def connectErrRefused(reason): reason.trap(twisted.internet.error.ConnectionRefusedError) errorDialog("Failed to connect to the " "server, the connection was refused") self.quit() def catchallConnectErr(reason): errorDialog("Failed to connect to the " "server: %s" % reason.getErrorMessage()) self.quit() def connected(result): d = self.model.login() d.addErrback(loginErr) d.addErrback(catchallLoginErr) d = self.model.connect() d.addCallback(connected) d.addErrback(connectErrRefused) d.addErrback(catchallConnectErr) # -------------------------------------------------------------------------- def setupMessagingWindow(self, helpid, toUsername, remoteHost, isUserCasc): """ toUsername - the username of the remote remoteHost - the hostname of the remote """ self.messageDialog.addTab(helpid, toUsername, self.hostname, remoteHost, isUserCasc) # setup functions to write to the messages from the message dialog to # the server def onMessageFromServer(fromType, message): if fromType == "user": fromName = self.username elif fromType == "server": fromName = "Server" self.messageDialog.writeMessage(helpid, fromName, message) self.model.registerOnMessgeHandler(helpid, onMessageFromServer) def badHelpidError(reason): reason.trap(service.BadHelpid) msg = "The remote client didn't have a id for this conversation, " "it may be too old. Message not sent" self.messageDialog.writeMessage(helpid, "Server", msg) def writeError(reason): reason.trap("__main__.ClientNotConnected") msg = "The connection to the remote client was lost. " "Message not sent" self.messageDialog.writeMessage(helpid, "Server", msg) return reason def writeFunction(message): d = self.model.sendMessage(helpid, toUsername, message) d.addErrback(badHelpidError) d.addErrback(writeError) self.messageDialog.registerMessageCallback(helpid, writeFunction) self.messageDialog.window.show() # -------------------------------------------------------------------------- # Service callback functions, most of these are just simple wrappers # around the cascaders class. def onUserAskingForHelp(self, helpid, username, host, subject, description): """ Called from the server to the client cascader to see if help can be accepted """ debug("Help wanted by: %s with host %s" % (username, host)) dialog = AcceptHelpDialog(self.window, username, subject, description) # check if user can give help if dialog.isAccept(): debug("Help Accepted") self.setupMessagingWindow(helpid, username, host, True) return (True, "") debug("Help rejected") return (False, "") # -------------------------------------------------------------------------- def updateAllSubjects(self, subjects): """ Calling this ensures that the gui reflects the current list of subjects """ debug("Subjects: %s" % subjects) cascCb = self.builder.get_object("cbCascSubjectList") lst = gtk.ListStore(gobject.TYPE_STRING) [lst.append([subject]) for subject in subjects] cascCb.set_model(lst) cell = gtk.CellRendererText() cascCb.set_active(0) cascCb.pack_start(cell, True) cascCb.add_attribute(cell, "text", 0) cb = self.builder.get_object("cbFilterSubject") lst = gtk.ListStore(gobject.TYPE_STRING) lst.append(["All"]) [lst.append([subject]) for subject in subjects] cb.set_model(lst) cell = gtk.CellRendererText() cb.set_active(0) cb.pack_start(cell, True) cb.add_attribute(cell, "text", 0) def updateCascaderLists(self, cascaders): """ Cleans the list and updates the list of cascaders avaible. Call when filters have been changed """ ls = self.builder.get_object("lsCascList") ls.clear() cbSubjects = self.builder.get_object("cbFilterSubject") filterSub = getComboBoxText(cbSubjects) filterSub = [filterSub] if filterSub != "All" else None cbLab = self.builder.get_object("cbFilterLab") filterLab = getComboBoxText(cbLab) filterLab = filterLab if filterLab != "All" else None cascaders = list(cascaders.findCascaders(lab=filterLab, subjects=filterSub)) [ls.append([username]) for username, _ in cascaders] debug("Updating cascaders from: %s" % str(cascaders)) # -------------------------------------------------------------------------- # GUI events def quit(self, *a): """ This quits the application, doing the required shutdown stuff. If some thing goes in here, try to assume nothing about the state of the application. """ debug("Starting shutdown") if self.settings: debug("Updating Settings") self.settings["cascSubjects"] = list(self.model.cascadingSubjects()) self.settings["cascading"] = self.model.isCascading() self.settings["autostart"] = self.builder.get_object("cbAutostart").get_active() self.settings["autocascade"] = self.builder.get_object("cbAutocascade").get_active() # quit the gui to try and make everything look snappy (so we don't lock # when messing around doing IO. we can't use destory else other # things don't work if self.window: self.window.hide_all() if self.model is not None: debug("Logging out") try: gobject.timeout_add(5000, self._finishQuitTimeout) l = self.model.logout() l.addCallback(self._finishQuit) l.addErrback(self._finishQuitErr) except Exception as e: debug("..Failed %s " % e) self._finishQuit() else: debug("No client, going to second stage shutdown directly") self._finishQuit() def _finishQuitTimeout(self): debug("Logging out timed out") self._finishQuit() return False # we should return false here due to using timeout_add def _finishQuitErr(self, reason): debug("There was an error logging out %s" % str(reason)) self._finishQuit() def _finishQuit(self, result=None): debug("In second stage shutdown") if self.window: self.window.destroy() # seems to be a bug, but we need to clean up the threadpool if reactor.threadpool is not None: reactor.threadpool.stop() try: reactor.stop() except twisted.internet.error.ReactorNotRunning: debug("Reactor wasn't running, so couldn't stop it") if self.settings: settings.saveSettings(self.settings) debug("Finished shutdown, goodbye") def onStartStopCascading(self, event): """ Toggles cascading """ btn = self.builder.get_object("btStartStopCasc") btn.set_sensitive(False) if self.model.isCascading(): debug("Stopping Cascading") self.stopCascading() else: debug("Starting Cascading") self.startCascading() def stopCascading(self): btn = self.builder.get_object("btStartStopCasc") self.model.stopCascading().addCallback(lambda *a: btn.set_sensitive(True)) btn.set_label("Start Cascading") def startCascading(self): btn = self.builder.get_object("btStartStopCasc") # we offer the user to automatically start cascading askedAutoCascading = self.settings["asked_autocascade"] autoCascade = self.settings["autocascade"] == False if askedAutoCascading == False and autoCascade: self.settings["asked_autocascade"] = True msg = "Do you always want to start cascading on program startup" message = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, msg) resp = message.run() if resp == gtk.RESPONSE_YES: self.builder.get_object("cbAutocascade").set_active(True) message.destroy() self.model.startCascading().addCallback(lambda *a: btn.set_sensitive(True)) btn.set_label("Stop Cascading") def onCascaderClick(self, tv, event): if event.button != 1 or event.type != gtk.gdk._2BUTTON_PRESS: return model, itr = tv.get_selection().get_selected() if itr: cascaderUsername = model.get_value(itr, 0) self.askForHelp(cascaderUsername) def askForHelp(self, cascaderUsername): (_, (cascHost, cascSubjects)) = self.model.getCascaderData().findCascader(username=cascaderUsername) # ask user topic, brief description subject = None if getComboBoxText(self.builder.get_object("cbFilterSubject")) != "All": subject = getComboBoxText(self.builder.get_object("cbFilterSubject")) helpDialog = AskForHelp(self.window, cascSubjects, subject) if helpDialog.isOk(): debug("Dialog is ok, asking for help") helpid = (self.username, util.generateUnqiueId()) self.setupMessagingWindow(helpid, cascaderUsername, cascHost, False) writeSysMsg = lambda m: self.messageDialog.writeMessage(helpid, "SYSTEM", m) writeSysMsg("Waiting for response...") def onNotConnected(reason): reason.trap(client.ClientNotConnected) writeSysMsg("Error: The client was not connected") d = self.model.askForHelp(helpid, cascaderUsername, helpDialog.getSubject(), helpDialog.getDescription()) d.addErrback(onNotConnected) def onAddSubject(self, event): cb = self.builder.get_object("cbCascSubjectList") self.addSubjects([getComboBoxText(cb)]) def addSubjects(self, subjects): ls = self.builder.get_object("lsCascSubjects") for subject in subjects: if subject not in self.model.cascadeSubjects: debug("Adding subject: %s" % subject) ls.append([subject]) self.model.addSubjects(subjects) def onRemoveSubject(self, event): tv = self.builder.get_object("tvCascSubjects") model, itr = tv.get_selection().get_selected() if itr is not None: subject = model.get_value(itr, 0) model.remove(itr) self.model.removeSubjects([subject]) # Filter Stuff def onSubjectSelect(self, event): self.updateCascaderLists(self.model.getCascaderData()) def onLabSelect(self, event): self.updateCascaderLists(self.model.getCascaderData()) # -- ----------------------------------------------------------------------- def onFilterLabChange(self, evt): debug("Filter Lab Changed") self.updateCascaderLists(self.model.getCascaderData()) cbLab = self.builder.get_object("cbFilterLab") lab = getComboBoxText(cbLab) self.updateMap(lab) def onFilterSubjectChange(self, evt): debug("Filter Subject Changed") self.updateCascaderLists(self.model.getCascaderData()) def updateMap(self, lab): cbSubjects = self.builder.get_object("cbFilterSubject") filterSub = getComboBoxText(cbSubjects) filterSub = [filterSub] if filterSub != "All" else None def onHostClick(event, widgit, host): """ Function is passed into the map and called when the user clicks on a host """ casc = self.model.getCascaderData().findCascader(host=host, subjects=filterSub) if casc is None: debug("Clicked on a host (%s) that wasn't " "cascading for the given filter" % host) return (username, _) = casc self.askForHelp(username) self.map.applyFilter(lab, myHost=self.hostname, subjects=filterSub, onClick=onHostClick)
class CascadersFrame: def __init__(self, debugEnabled=False, show=True, host=None): ''' Enabling debug does things like disable async so errors are more apparent show should be true to show the windows by default ''' self.debugEnabled = debugEnabled hosts = os.path.join(os.path.dirname(__file__), 'data', 'hosts') self.locator = labmap.Locator(open(hosts)) self.username = self._getUsername() if host is None: self.hostname = socket.gethostname().lower() else: self.hostname = host self.model = CascaderModel(self.locator, self.username, self.hostname) self.messageDialog = MessageDialog(self.locator, self.model.getCascaderData()) #slightly more sane method of setting things up that uses depency #tracking req = RequireFunctions() req.add('gui', self.initGui) req.add('tray', self.initTray, ['gui']) req.add('map', self.initMap, ['gui']) req.add('labs', self.initLabs, ['map']) req.add('modelcallbacks', self.initModelCallbacks) req.add('connection', self.initConnection) req.add('signals', self.initSignals, ['gui', 'settings']) req.add('settings', self.initSettings, ['gui', 'connection']) req.add('autostart', self.askAutostart, ['gui', 'settings']) req.run() if show: self.window.show_all() def askAutostart(self): if self.settings['asked_autostart'] == False: self.settings['asked_autostart'] = True message = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, ('Do you want to autostart this ' 'program on login?')) resp = message.run() if resp == gtk.RESPONSE_YES: self.builder.get_object('cbAutostart').set_active(True) message.destroy() def initMap(self): self.map = labmap.Map(self.builder.get_object('tblMap'), self.locator, self.model.getCascaderData()) def initTray(self): icon = os.path.join(os.path.dirname(__file__), 'icons', 'cascade.ico') self.trayIcon = TrayIcon(self, icon) self.window.connect('delete-event', lambda w, e: w.hide() or True) def initSignals(self): ''' Signals catch events and shut things down properly ''' signal.signal(signal.SIGINT, self.quit) signal.signal(signal.SIGTERM, self.quit) def initGui(self): self.builder = gtk.Builder() dr = os.path.dirname(__file__) self.builder.add_from_file(os.path.join(dr, 'gui', 'main.glade')) initTreeView(self.builder.get_object('tvCascList')) initTreeView(self.builder.get_object('tvCascSubjects')) self.window = self.builder.get_object('wnCascader') self.window.connect('destroy', lambda *a: gtk.main_quit()) self.builder.connect_signals(self) def initLabs(self): ''' Sets up the labs drop down box stuff ''' lst = gtk.ListStore(gobject.TYPE_STRING) lst.append(['All']) for lab in self.locator.getLabs(): lst.append([lab]) cb = self.builder.get_object('cbFilterLab') cb.set_model(lst) cell = gtk.CellRendererText() cb.set_active(0) cb.pack_start(cell, True) cb.add_attribute(cell, 'text', 0) def initSettings(self): self.settings = settings.loadSettings() autostart = self.settings['autostart'] self.builder.get_object('cbAutostart').set_active(autostart) autocascade = self.settings['autocascade'] self.builder.get_object('cbAutocascade').set_active(autocascade) debug('Got subjects from settings: %s' % str(self.settings['cascSubjects'])) self.addSubjects(self.settings['cascSubjects']) if self.settings['cascading'] and self.settings['autocascade']: self.startCascading() def _getUsername(self): try: logname = os.environ['LOGNAME'] #for debugging only, means multiple clients can be run at once if self.debugEnabled == True: import random logname = str(random.random()) except KeyError: errorDialog(('Couldn\'t get LOGNAME from the enviroment,' ' this only runs on Linux at the moment')) self.quit() return logname def initModelCallbacks(self): ''' This sets up the service callbacks ''' self.model.registerOnCascaderChanged(self.updateCascaderLists) self.model.registerOnSubjectChanged(self.updateAllSubjects) self.model.registerOnUserAskingForHelp(self.onUserAskingForHelp) self.model.registerOnDisconnected(self.onDisconnect) self.model.registerOnConnected(self.onLogin) def onDisconnect(self): status = self.builder.get_object('lbStatus') status.set('Connecting...') def onLogin(self): status = self.builder.get_object('lbStatus') status.set('Connected') def initConnection(self): ''' called in the constructor. also does the setup post connect ''' debug('Connecting...') self.builder.get_object('lbUsername').set(self.username) def loginErr(reason): reason = reason.trap(ValueError) errorDialog('Failed to login, server reported %s' % reason.getErrorMessage()) self.quit() def connectErr(reason): reason.trap(twisted.internet.error.ConnectionRefusedError) errorDialog('Failed to connect to the ' 'server, the connection was refused') self.quit() def connected(result): d = self.model.login() d.addErrback(loginErr) d = self.model.connect() d.addCallback(connected) d.addErrback(connectErr) #-------------------------------------------------------------------------- def setupMessagingWindow(self, helpid, toUsername, remoteHost, isUserCasc): ''' toUsername - the username of the remote remoteHost - the hostname of the remote ''' self.messageDialog.addTab(helpid, toUsername, self.hostname, remoteHost, isUserCasc) #setup functions to write to the messages from the message dialog to #the server def onMessageFromServer(fromType, message): if fromType == 'user': fromName = toUsername elif fromType == 'server': fromName = 'Server' self.messageDialog.writeMessage(helpid, self.username, message) self.model.registerOnMessgeHandler(helpid, onMessageFromServer) def writeFunction(message): try: self.model.sendMessage(helpid, toUsername, message) except client.NotConnected: self.onServerLost() self.messageDialog.registerMessageCallback(helpid, writeFunction) self.messageDialog.window.show_all() #-------------------------------------------------------------------------- # Service callback functions, most of these are just simple wrappers # around the cascaders class. def onUserAskingForHelp(self, helpid, username, host, subject, description): ''' Called from the server to the client cascader to see if help can be accepted ''' debug('Help wanted by: %s with host %s' % (username, host)) dialog = AcceptHelpDialog(self.window, username, subject, description) #check if user can give help if dialog.isAccept(): debug('Help Accepted') self.setupMessagingWindow(helpid, username, host, True) return (True, '') debug('Help rejected') return (False, '') #-------------------------------------------------------------------------- def updateAllSubjects(self, subjects): ''' Calling this ensures that the gui reflects the current list of subjects ''' debug('Subjects: %s' % subjects) cascCb = self.builder.get_object('cbCascSubjectList') lst = gtk.ListStore(gobject.TYPE_STRING) [lst.append([subject]) for subject in subjects] cascCb.set_model(lst) cell = gtk.CellRendererText() cascCb.set_active(0) cascCb.pack_start(cell, True) cascCb.add_attribute(cell, 'text', 0) cb = self.builder.get_object('cbFilterSubject') lst = gtk.ListStore(gobject.TYPE_STRING) lst.append(['All']) [lst.append([subject]) for subject in subjects] cb.set_model(lst) cell = gtk.CellRendererText() cb.set_active(0) cb.pack_start(cell, True) cb.add_attribute(cell, 'text', 0) def updateCascaderLists(self, cascaders): ''' Cleans the list and updates the list of cascaders avaible. Call when filters have been changed ''' ls = self.builder.get_object('lsCascList') ls.clear() cbSubjects = self.builder.get_object('cbFilterSubject') filterSub = getComboBoxText(cbSubjects) filterSub = [filterSub] if filterSub != 'All' else None cbLab = self.builder.get_object('cbFilterLab') filterLab = getComboBoxText(cbLab) filterLab = filterLab if filterLab != 'All' else None cascaders = list(cascaders.findCascaders(lab=filterLab, subjects=filterSub)) [ls.append([username]) for username, _ in cascaders] debug('Updating cascaders from: %s' % str(cascaders)) #-------------------------------------------------------------------------- # GUI events def quit(self, *a): ''' This quits the application, doing the required shutdown stuff. If some thing goes in here, try to assume nothing about the state of the application. ''' debug('Starting shutdown') if self.settings: debug('Updating Settings') self.settings['cascSubjects'] = list(self.model.cascadingSubjects()) self.settings['cascading'] = self.model.isCascading() self.settings['autostart'] = self.builder.get_object('cbAutostart').get_active() self.settings['autocascade'] = self.builder.get_object('cbAutocascade').get_active() #quit the gui to try and make everything look snappy (so we don't lock #when messing around doing IO. we can't use destory else other #things don't work if self.window: self.window.hide_all() if self.model is not None: debug('Logging out') try: gobject.timeout_add(5000, self._finishQuitTimeout) l = self.model.logout() l.addCallback(self._finishQuit) l.addErrCallback(self._finishQuitErr) except Exception: self._finishQuit() else: debug('No client, going to second stage shutdown directly') self._finishQuit() def _finishQuitTimeout(self): debug('Logging out timed out') self._finishQuit() return False #we should return false here due to using timeout_add def _finishQuitErr(self, reason): debug('There was an error logging out %s' % str(reason)) self._finishQuit() def _finishQuit(self, result=None): debug('In second stage shutdown') if self.window: self.window.destroy() #seems to be a bug, but we need to clean up the threadpool if reactor.threadpool is not None: reactor.threadpool.stop() try: reactor.stop() except twisted.internet.error.ReactorNotRunning: debug('Reactor wasn\'t running, so couldn\'t stop it') if self.settings: settings.saveSettings(self.settings) debug('Finished shutdown, goodbye') def onStartStopCascading(self, event): ''' Toggles cascading ''' btn = self.builder.get_object('btStartStopCasc') btn.set_sensitive(False) if self.model.isCascading(): debug('Stopping Cascading') self.stopCascading() else: debug('Starting Cascading') self.startCascading() def stopCascading(self): btn = self.builder.get_object('btStartStopCasc') self.model.stopCascading().addCallback(lambda *a: btn.set_sensitive(True)) btn.set_label('Start Cascading') def startCascading(self): btn = self.builder.get_object('btStartStopCasc') #we offer the user to automatically start cascading askedAutoCascading = self.settings['asked_autocascade'] autoCascade = self.settings['autocascade'] == False if askedAutoCascading == False and autoCascade: self.settings['asked_autocascade'] = True message = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_YES_NO, "Do you want to enable auto cascading") resp = message.run() if resp == gtk.RESPONSE_YES: self.builder.get_object('cbAutocascade').set_active(True) message.destroy() self.model.startCascading().addCallback(lambda *a: btn.set_sensitive(True)) btn.set_label('Stop Cascading') def onCascaderClick(self, tv, event): if event.button != 1 or event.type != gtk.gdk._2BUTTON_PRESS: return model, itr = tv.get_selection().get_selected() if itr: cascaderUsername = model.get_value(itr, 0) self.askForHelp(cascaderUsername) def askForHelp(self, cascaderUsername): (_, (cascHost, cascSubjects)) = self.model.getCascaderData().findCascader(username=cascaderUsername) #ask user topic, brief description subject = None if getComboBoxText(self.builder.get_object('cbFilterSubject')) != 'All': subject = getComboBoxText(self.builder.get_object('cbFilterSubject')) helpDialog = AskForHelp(self.window, cascSubjects, subject) if helpDialog.isOk(): debug('Dialog is ok, asking for help') helpid = (self.username, util.generateUnqiueId()) self.setupMessagingWindow(helpid, cascaderUsername, cascHost, False) writeSysMsg = lambda m: self.messageDialog.writeMessage(helpid, 'SYSTEM', m) writeSysMsg('Waiting for response...') def onNotConnected(reason): reason.trap(client.ClientNotConnected) writeSysMsg('Error: The client was not connected') try: d = self.model.askForHelp(helpid, cascaderUsername, helpDialog.getSubject(), helpDialog.getDescription()) d.addErrback(onNotConnected) except client.NotConnected: self.onServerLost() def onAddSubject(self, event): cb = self.builder.get_object('cbCascSubjectList') self.addSubjects([getComboBoxText(cb)]) def addSubjects(self, subjects): ls = self.builder.get_object('lsCascSubjects') for subject in subjects: if subject not in self.model.cascadeSubjects: debug('Adding subject: %s' % subject) ls.append([subject]) self.model.addSubjects(subjects) def onRemoveSubject(self, event): tv = self.builder.get_object('tvCascSubjects') model, itr = tv.get_selection().get_selected() if itr is not None: subject = model.get_value(itr, 0) model.remove(itr) self.model.removeSubjects([subject]) # Filter Stuff def onSubjectSelect(self, event): self.updateCascaderLists(self.model.getCascaderData()) def onLabSelect(self, event): self.updateCascaderLists(self.model.getCascaderData()) #-- ----------------------------------------------------------------------- def onFilterLabChange(self, evt): ''' This is called before a lot of the things are created fully as it is set to its default value. This has to check that things fully exist before calling functions on them ''' debug('Filter Lab Changed') self.updateCascaderLists(self.model.getCascaderData()) cbLab = self.builder.get_object('cbFilterLab') lab = getComboBoxText(cbLab) self.updateMap(lab) def onFilterSubjectChange(self, evt): debug('Filter Subject Changed') self.updateCascaderLists(self.model.getCascaderData()) def updateMap(self, lab): cbSubjects = self.builder.get_object('cbFilterSubject') filterSub = getComboBoxText(cbSubjects) filterSub = [filterSub] if filterSub != 'All' else None def onHostClick(event, widgit, host): casc = self.cascaders.findCascader(host=host, subjects=filterSub) if casc is None: debug('Clicked on a host (%s) that wasn\'t ' 'cascading for the given filter' % host) return (username, _) = casc self.askForHelp(username) self.map.applyFilter(lab, myHost=self.hostname, subjects=filterSub, onClick=onHostClick)