def get(self, F): if self.connected: self.connection.send("get") ans = self._get(1024) if ans == "get": self.connection.send(F) debugLog("sent file request to server") ans = self._get(1024) if ans[0:8] == "got it: ": fileSize = int(ans[8:]) self.connection.send("send file") f = open(F, 'w') fileData = "" while len(fileData) < fileSize: fileData += self._get(MAXFILESIZE) # could debug, make sure len is exactly right f.write(fileData) f.close() print("File Transfer Complete") return True else: print( "server did not acknowledge filename, may not be on server" ) return False else: debugLog( "server did not respond with 'get' to get request, may be in the wrong state" ) print("Error fetching file") return False else: print('Client has no connection to a server, cannot fetch')
def AddIndex(self, n): """ Adds n to the current slide index. May be positive or negative. Returns True on success; None if there is a slide(s) left by it is still downloading; False if there are no more slides in the queue. """ self._lock.acquire() util.debugLog("AddIndex() acquired lock", 2) if self._currentIndex+n >= len(self._slides) or self._currentIndex+n < 0: # out of slides util.debugLog("out of slides") status = False elif self._currentIndex+n >= len(self._readySlides): # wait for more to download util.debugLog("wait for more slides to download",2) status = None else: # found a slide util.debugLog("found a slide",2) status = True self._currentIndex += n util.debugLog("AddIndex() releasing lock", 2) self._lock.release() return status
def _OnPreviousSlide(self, evt): """ Handles event to trigger the previous slide. """ util.debugLog("SlideshowInteractor._OnPreviousSlide()") self.StopTimer() self.presenter.ShiftSlide(shift=-1, blockTimer=True)
def _OnNextSlide(self, evt): """ Handles event to trigger the next slide. """ util.debugLog("SlideshowInteractor._OnNextSlide()") self.StopTimer() self.presenter.ShiftSlide(blockTimer=True)
def Cleanup(self): # cleanup tmp files if hasattr(self, "_readySlides"): for slide in self._readySlides: util.debugLog("Cleanup: deleting " + slide.GetLocalPath(), 2) try: os.unlink(slide.GetLocalPath()) except IOError: pass
def close(self): self.isClosed = True for c in self.connections: try: self.connections[c].close() except Exception as e: pass debugLog("connection closed") self.sock.close() debugLog("socket closed")
def __init__(self, port): self.ourPort = port self.maxMessageSize = 1024 self.sock = EVIL.Evil() self.sock.bind('', self.ourPort) debugLog("socket bound to: " + self.sock.sock.getsockname()[0] + ":" + str(self.ourPort)) operateThread = threading.Thread(None, self.operate) operateThread.start()
def __init__(self, address, port): self.serverAddress = address self.serverPort = port self.maxMessageSize = 1024 self.connected = False self.sock = EVIL.Evil() self.sock.bind( '', 0 ) ##tells os to bind to all hostnames on this machine with a chosen available port debugLog("socket bound to: " + self.sock.sock.getsockname()[0] + ":" + str(self.sock.sock.getsockname()[1]))
def close(self): debugLog("connection.close() called!") if self.state == STATE.CLOSED: raise Exception("Cannot close an already-closed connection") dgram = self.new_dgram() dgram.setFlag(util.FLAG.FIN, True) for _ in range(10): self.socket.addToOutput(self.otherAddress, dgram) self.stateCond.acquire() self.state = STATE.CLOSED self.stateCond.notifyAll() self.stateCond.release()
def Find(self): """ Processes the entered search parameters. """ # init some of the searching things self._PreStart() self._slides = [] self.factory = FlickrSlideFactory() newSlides = self.factory.Build(self._searchParams) self._slides.extend(newSlides) util.debugLog("Find(): retrieved " + str(len(self._slides)) + " slides") # reset search params self._searchParams = {}
def c_thread(self): self.resendTimer = time.time() while True: cond = self.queue_cond cond.acquire() if self.dgram_queue_in.empty() and self.str_queue_out.empty(): ##debugLog("waiting") cond.wait(timeout=1) ##debugLog("wait interrupted") cond.release() while not self.dgram_queue_in.empty(): dgram = self.dgram_queue_in.get() debugLog("Got incoming packet from queue") self.process_dgram(dgram) while len(self.dgram_unconf) < self.max_send_size and len( self.dgram_unsent) > 0: dgram = self.dgram_unsent.pop(0) dgram.ack = self.ack debugLog("Sending delayed data") self.socket.addToOutput(self.otherAddress, dgram) self.dgram_unconf.append(dgram) while not self.str_queue_out.empty(): held = len(self.dgram_unconf) debugLog("Got incoming data from queue") data = self.str_queue_out.get() self.process_data_str(data) if self.state == STATE.CLOSED: debugLog("Connection closed") break self.checkTimeout()
def post(self, F): if not os.path.isfile(F): debugLog("Passed filename wasn't a file") return False if self.connected: f = open(F, 'r') fileSize = len(f.read()) f.close() self.connection.send("post: " + str(fileSize)) ans = self._get(1024) if ans == "post": self.connection.send(F) ans = self._get(1024) if ans == "send file": debugLog("server ready to receive") f = open(F, 'r') self.connection.send(f.read()) debugLog("sent file post to server") f.close() print("File Transfer Complete") return True else: print( "server did not correctly acknowledge for receipt of file\n" ) return False else: debugLog( "server did not respond with 'post' to 'post' request") print("Error sending file\n") return False else: print('Client has no connection to a server, cannot fetch\n')
def Start(self): """ Kicks off the fetching of slide images. Continues to run until worker threads have finished. """ THREAD_LIMIT = 5 self._isRunning = True for k,slide in enumerate(self._slides): # check to see if Stop() was called if not self._continue: break # limit number of threads while self._workerCounter.Get() >= THREAD_LIMIT: time.sleep(self._shortWaitSecs) util.debugLog("creating new thread") t = threading.Thread(target=self.FetchImage, kwargs={"slide":slide, "outPath":os.path.join("cache", str(k) + ".jpg")}) id = t.getName() util.debugLog("threadid: " + str(id),2) # increment the thread counter before we start self._workerCounter.Up() t.start() while not self._workerCounter.WasTouched() or self._workerCounter.Get() > 0: util.debugLog("waiting for threads " + str(self._workerCounter.Get()) + " to finish") time.sleep(self._shortWaitSecs) self._isRunning = False util.debugLog("All threads have completed") return True
def FetchImage(self, slide, outPath): """ Retrieves an image from Flickr and stores it in the model. """ slide.GetImage(outPath) self._lock.acquire() util.debugLog(outPath + " acquired lock",2) # make sure the model wasn't Stop()'d if not self._continue: return self._ShutdownWorker(slide, outPath) self._readySlides.append(slide) util.debugLog(outPath + " releasing lock",2) self._lock.release() self._workerCounter.Down()
def StartSlideshow(self): """ Starts the slideshow. Mostly sets up the model. """ util.debugLog("SlideshowPresenter.StartSlideshow()") self._first = True self._longWaitSecs = self.interactor.displayTime try: self._ModelSearch() except (slideshowmodel.SlideshowModelNoSlides, IOError), inst: msg = str(inst) util.debugLog(msg) self.view.UpdateStatus("Error") self.view.Popup(msg) return False
def operate(self): while True: if self.sock.isClosed: break try: newSessionConnection = self.sock.accept(timeout=5) except Exception as e: continue if self.sock.isClosed: break debugLog("accepted connection from " + newSessionConnection.otherAddress[0] + ":" + str(newSessionConnection.otherAddress[1])) sessionThread = threading.Thread(None, self.handleSession, "session_thread", (newSessionConnection, )) sessionThread.start()
def process_data_str(self, data): # seq will be added in EVIL.py dgram = self.new_dgram() dgram.data = data dgram.seq = self.seq + len(data) dgram.window = self.max_window_size dgram.checksum = dgram.generateCheckSum() self.seq += len(data) #TODO may change if len(self.dgram_unconf) >= self.max_send_size: debugLog("queue full; deferring packet") self.dgram_unsent.append(dgram) else: debugLog("appended packet to queue") self.dgram_unconf.append(dgram) self.socket.addToOutput(self.otherAddress, dgram)
def GetImage(self, outPath): """ Downloads the image file to the specified path. Returns True on success. """ self._localPath = outPath # get the URL if it's not already saved if not hasattr(self, "_url"): self.GetUrl() util.debugLog(self._localPath + " downloading image",2) data = urllib.urlopen(self._url).read() fd = open(outPath, "wb") try: fd.write(data) finally: fd.close() util.debugLog("Saved " + self._url + " to " + outPath) return True
def _PreStart(self): """ Sets up some status and synchronization members at the start of each show. """ while self.IsRunning(): # wait for previous slideshows to finish self.Stop() util.debugLog("waiting for " + str(self._workerCounter.Get()) + " threads to finish before PreStart") time.sleep(self._shortWaitSecs) self._continue = True self._currentIndex = 0 self._readySlides = [] # Thread-sensitive members self._lock = threading.Lock() self._workerCounter = util.ThreadCounter()
def ShowImage(self, imagePath): util.debugLog("slideshowview.ShowImage()") # added to fix problem with statusbar/title update not appearing wx.YieldIfNeeded() util.debugLog("ShowImage() YIELDING",2) # resize the image newImagePath = self._PrepareImageFile(imagePath) image = wx.Image(newImagePath, wx.BITMAP_TYPE_JPEG) x,y = imaging.GetCenterFromTopLeft(self._window.GetSize(), image.GetSize()) bmp = wx.BitmapFromImage(image) self.DestroyBmp() self._staticBmp = wx.StaticBitmap(self._window, wx.ID_ANY, bmp, wx.Point(x,y), wx.Size(image.GetWidth(), image.GetHeight())) try: os.unlink(newImagePath) except IOError, (errno, strerror): print "I/O error(%s): %s" % (errno, strerror)
def connect(self, host, port): """ Called by client code - socket and connection threads should not touch! blocks while attempting to establish a connection with specified server Return: a Connection instance (if successful) or an error Errors: if socket closed, throw exception """ self.connectionsLock.acquire() newConn = connection.Connection(self.sock.getsockname()[1], port, self.maxWindowSize, connection.STATE.SYN_SENT, host, self) self.connections[(host, port)] = newConn self.connectionsLock.release() newConn.establishConnection() debugLog("new connection created with: " + host + " on port " + str(port)) return newConn
def checkTimeout(self): if (time.time() - self.resendTimer) < self.DEFAULT_TIMEOUT: return new_dgram = self.new_dgram() self.stateCond.acquire() oldState = self.state if oldState == STATE.SYN_RECV: new_dgram.setFlag(util.FLAG.SYN, True) new_dgram.setFlag(util.FLAG.ACK, True) self.socket.addToOutput(self.otherAddress, new_dgram) debugLog("resent SYN+ACK") elif oldState == STATE.SYN_SENT: new_dgram.setFlag(util.FLAG.SYN, True) self.socket.addToOutput(self.otherAddress, new_dgram) debugLog("resent SYN") elif oldState == STATE.ESTABLISHED: #Need to resend any unACK-ed data for dgram in self.dgram_unconf: debugLog("Resending data") self.socket.addToOutput(self.otherAddress, dgram) if self.missedKeeps >= 3: self.state = STATE.CLOSED return if len(self.dgram_unconf) == 0: new_dgram.setFlag(util.FLAG.KEP, True) self.socket.addToOutput(self.otherAddress, new_dgram) self.missedKeeps += 1 self.resendTimer = time.time() self.stateCond.release()
def ShiftSlide(self, shift=1, blockTimer=False): """ Moves or "shifts" a 'shift' number of slides in the queue. If blockTimer is true, then the timer is not set after calling. """ # check see if we were even started if not self._isRunning: return False # don't go to the next picture if we've yet to show one if self._first is True: shift = 0 status = self.model.AddIndex(shift) if status == None: util.debugLog("need to wait more for another slide",1) self.view.UpdateStatus("Waiting for next image to download...") # Init timer to call again in a bit if not blockTimer: self.StartTimer(self._shortWaitSecs) elif status == False: util.debugLog("no more slides") self.view.UpdateStatus("Slideshow finished") else: self.view.UpdateTitle(self.model.CurrentTitle() + ', uploaded by ' + self.model.CurrentAuthor()) self.view.UpdateStatus("Drawing Image...") util.debugLog("showing current slide",2) path = self.model.CurrentImagePath() self._first = False self.view.ShowImage(path) if blockTimer: self.view.UpdateStatus("Ready") else: self.view.UpdateStatus("Playing slideshow...") # Init timer to call again in a while if not blockTimer: self.StartTimer(self._longWaitSecs)
def establishConnection(self): self.stateCond.acquire() debugLog(str(self.state)) new_dgram = self.new_dgram() new_dgram.setFlag(util.FLAG.SYN, True) if self.state == STATE.SYN_RECV: new_dgram.setFlag(util.FLAG.ACK, True) debugLog("Sending SYN+ACK") else: debugLog("Sending SYN") self.socket.addToOutput(self.otherAddress, new_dgram) while self.state != STATE.ESTABLISHED and self.state != STATE.CLOSED: self.stateCond.wait() debugLog(str(self.state)) self.stateCond.release() return
def accept(self, block=True, timeout=None): """ called by client code - Socket and connection threads should not touch! blocks until a new connection is received and a Connection object has been created. Return: a Connection instance Errors: if socket closed, throw exception """ unknownPacket = self.unknownPackets.get(block, timeout) debugLog("got unknown packet for accept call") self.connectionsLock.acquire() newConn = connection.Connection(self.sock.getsockname()[1], unknownPacket[1][1], self.maxWindowSize, connection.STATE.SYN_RECV, unknownPacket[1][0], self) self.connections[(unknownPacket[1][0], unknownPacket[1][1])] = newConn self.connectionsLock.release() newConn.establishConnection() return newConn
def speaker(self): debugLog("speakerThread started on port " + str(self.sock.getsockname()[1])) while True: if self.isClosed: break try: recipient, packet = self.outgoingPackets.get(timeout=5) except Exception as e: continue if self.isClosed: break debugLog("pack checksum: " + hex(packet.checksum) + '\n') debugLog("Recip: " + str(recipient) + '\n') #packet.printSelf() self.sock.sendto(packet.toString(), recipient) debugLog("sent: " + hex(packet.checksum))
def handleIncoming(self, packet): try: self.dgram_queue_in.put(packet, timeout=0.5) debugLog("added incoming packet to queue") self.queue_cond.acquire() self.queue_cond.notifyAll() debugLog("queue notification sent") self.queue_cond.release() self.queue_cond.release() debugLog("released twice") except Exception as e: pass
def _OnKey(self, evt): if evt.GetKeyCode() == wx.WXK_RIGHT: util.debugLog("wx.WXK_RIGHT",2) self._OnNextSlide(None) elif evt.GetKeyCode() == wx.WXK_LEFT: util.debugLog("wx.WXK_LEFT",2) self._OnPreviousSlide(None) elif evt.GetKeyCode() == wx.WXK_SPACE: util.debugLog("wx.WXK_SPACE",2) self.ToggleTimer() elif evt.GetKeyCode() == wx.WXK_F11: self.view.OnFullscreen(None) # Continue processing other keys else: evt.Skip()
def GetUrl(self): """ Returns the URL (string) of the image. """ util.debugLog(self._localPath + " fetching URL",2) try: url = self._photo.getURL(size='Original', urlType='source') except flickr.FlickrError: util.debugLog("Original size not found") try: url = self._photo.getURL(size='Large', urlType='source') except flickr.FlickrError: util.debugLog("Large size not found") url = self._photo.getURL(size='Medium', urlType='source') self._url = url return url
def _OnStartSlideshow(self, evt): """ Handles the starting of the slideshow after clicking on the menu option. """ util.debugLog("SlideshowInteractor._OnStartSlideshow()") self.StopTimer() dlg = slideshowsearch.SlideshowSearchDialog(self.view, -1, "New Slideshow", size=(350, 200), style=wx.DEFAULT_DIALOG_STYLE) dlg.CenterOnScreen() # this does not return until the dialog is closed. val = dlg.ShowModal() if val == wx.ID_OK: util.debugLog("You pressed OK\n") self.searchString = dlg.GetSearchString() self.displayTime = dlg.GetDisplayTime() self.presenter.StartSlideshow() else: util.debugLog("You pressed Cancel\n") dlg.Destroy()
def StartTimer(self, waitSecs): """ Initializes the timer in the presenter using wx.FutureCall. """ util.debugLog("starting timer",2) self._timer = wx.FutureCall(waitSecs*1000, self.presenter.ShiftSlide)
def StopTimer(self): """ Stops the running timer. """ if hasattr(self, "_timer"): self._timer.Stop() util.debugLog("stopping timer",2)
def DestroyBmp(self): util.debugLog("slideshowview.DestroyBmp()") if hasattr(self, "_staticBmp"): self._staticBmp.Destroy()
def _OnExitApp(self, evt): """ Handles the exit event. """ util.debugLog("OnExitApp()") self.view.Close(True)
def connect(self): self.connection = self.sock.connect(self.serverAddress, self.serverPort) self.connected = True debugLog("created connection with " + self.connection.otherAddress[0] + ":" + str(self.connection.otherAddress[1]))
def terminate(self): self.sock.close() debugLog("Server Closed") sys.exit()
def _ProcessUsername(self): util.debugLog("processing username " + self._searchParams["username"]) user = flickr.people_findByUsername(self._searchParams["username"]) self._ProcessUserPages(user)
btn.SetHelpText("Cancel the slideshow") btnsizer.AddButton(btn) btnsizer.Realize() sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5) self.SetSizer(sizer) sizer.Fit(self) def GetSearchString(self): return self._searchString.GetValue() def GetDisplayTime(self): return self._slider.GetValue() if __name__ == "__main__": import slideshowview, util view = slideshowview.SlideshowView() dlg = SlideshowSearchDialog(view, -1, "New Slideshow", size=(470, 200), #style = wx.CAPTION | wx.SYSTEM_MENU | wx.THICK_FRAME) style = wx.DEFAULT_DIALOG_STYLE) dlg.CenterOnScreen() # this does not return until the dialog is closed. val = dlg.ShowModal() print dlg.GetSearchString(), dlg.GetDisplayTime() if val == wx.ID_OK: util.debugLog("You pressed OK\n") else: util.debugLog("You pressed Cancel\n")
def _ShutdownWorker(self, slide, outPath): util.debugLog(outPath + " releasing lock",2) self._lock.release() os.unlink(slide.GetLocalPath()) self._workerCounter.Down()
def _ProcessUrl(self): util.debugLog("processing url " + self._searchParams["url"]) user = flickr.people_findByURL(self._searchParams["url"]) self._ProcessUserPages(user)
def process_dgram(self, dgram): self.stateCond.acquire() oldState = self.state self.max_send_size = dgram.window new_dgram = self.new_dgram() if dgram.checkFlag(util.FLAG.FIN): debugLog("FIN received. Closing connection.") self.state = STATE.CLOSED self.stateCond.notifyAll() elif oldState != STATE.ESTABLISHED: if oldState == STATE.CLOSED: pass if oldState == STATE.SYN_SENT: if dgram.checkFlag(util.FLAG.SYN) and dgram.checkFlag( util.FLAG.ACK): new_dgram.setFlag(util.FLAG.ACK, True) self.setState(STATE.ESTABLISHED) self.stateCond.notifyAll() self.socket.addToOutput(self.otherAddress, new_dgram) debugLog("Sent ACK") if oldState == STATE.SYN_RECV: if dgram.checkFlag(util.FLAG.ACK): self.setState(STATE.ESTABLISHED) self.stateCond.notifyAll() debugLog("Connection Established") if oldState == STATE.FIN_WAIT_1: pass if oldState == STATE.FIN_WAIT_2: pass if oldState == STATE.FIN_CLOSING: pass if oldState == STATE.TIME_WAIT: pass if oldState == STATE.CLOSE_WAIT: pass if oldState == STATE.LAST_ACK: pass pass else: if dgram.checkFlag(util.FLAG.KEP): if dgram.checkFlag(util.FLAG.ACK): self.missedKeeps = 0 else: new_dgram.setFlag(util.FLAG.ACK, True) new_dgram.setFlag(util.FLAG.KEP, True) self.socket.addToOutput(self.otherAddress, new_dgram) if dgram.checkFlag(util.FLAG.SYN) and dgram.checkFlag( util.FLAG.ACK): new_dgram.setFlag(util.FLAG.ACK, True) debugLog("Resending ACK") self.socket.addToOutput(self.otherAddress, new_dgram) dataLen = len(dgram.data) if self.ack + dataLen < dgram.seq: #Out of order packet. Will be dropped. # Re-acknowledge last received in-order packet with RET set new_dgram.setFlag(util.FLAG.RET, True) self.socket.addToOutput(self.otherAddress, new_dgram) debugLog( "Received packet out-of-order. Re-ACKing last received packet" ) return elif self.ack + dataLen == dgram.seq and dataLen != 0: self.resendTimer = time.time() self.ack += len(dgram.data) #TODO: need to change to fit data type rcvd_ack = dgram.ack j = len(self.dgram_unconf) i = 0 while i < j: if self.dgram_unconf[i].seq <= rcvd_ack: self.dgram_unconf.pop(i) self.resendTimer = time.time() i -= 1 j -= 1 i += 1 if len(dgram.data) != 0: self.str_queue_in.put(dgram.data) new_dgram = self.new_dgram() #get new ack number debugLog("ACKing received packet") self.socket.addToOutput(self.otherAddress, new_dgram) elif dgram.checkFlag(util.FLAG.RET): debugLog("Got RET - resending!") #need to resend unconfirmed packets. #will fake resendTimer so that resend happens right away self.resendTimer = time.time() - (self.DEFAULT_TIMEOUT + 1) self.stateCond.release()
def _ProcessEmail(self): util.debugLog("processing email " + self._searchParams["email"]) user = flickr.people_findByEmail(self._searchParams["email"]) self._ProcessUserPages(user)
def listener(self): debugLog("listenerThread started on port " + str(self.sock.getsockname()[1])) while True: debugLog("receiving") ready = select.select([self.sock], [], [], 5) if self.isClosed: break if (ready[0]): msg, address = self.sock.recvfrom(1024) else: continue if self.isClosed: break debugLog("received packet from: " + address[0] + ":" + str(address[1])) packet = util.EVILPacket() packet = packet.parseFromString(msg) packet.printSelf() if not packet.validateCheckSum(): debugLog("Invalid Checksum," + hex(packet.checksum) + " vs. " + hex(packet.generateCheckSum()) + ", tossing packet") continue if address in self.connections: debugLog("packet belonged to an existing connection") self.connectionsLock.acquire() self.connections[address].handleIncoming(packet) self.connectionsLock.release() else: debugLog("packet didn't belong to any existing connections") if packet.checkFlag(util.FLAG.SYN): debugLog("packet contained syn flag") self.unknownPackets.put((packet, address), False)
def handleSession(self, conn): debugLog("new session started with " + conn.otherAddress[0] + ":" + str(conn.otherAddress[1])) sessionState = SESSIONSTATE.IDLE filename = "" fileSize = 0 while True: if conn.state == connection.STATE.CLOSED: break try: string = conn.get(1024, timeout=5) except Exception as e: continue debugLog("received " + string + " from " + conn.otherAddress[0] + ":" + str(conn.otherAddress[1])) if sessionState == SESSIONSTATE.IDLE: if string == "get": sessionState = SESSIONSTATE.GET_1 debugLog("Session now get 1") conn.send("get") elif string[0:6] == "post: ": fileSize = int(string[6:]) sessionState = SESSIONSTATE.POST_1 debugLog("Session now post 1") conn.send("post") elif sessionState == SESSIONSTATE.GET_1: filename = string if os.path.isfile(filename): sessionState = SESSIONSTATE.GET_2 debugLog("Session now get 2") f = open(filename, 'r') fileLen = len(f.read()) conn.send("got it: " + str(fileLen)) f.close() else: sessionState == SESSIONSTATE.IDLE debugLog("Session now idle") conn.send("back to idle") elif sessionState == SESSIONSTATE.GET_2: if string == "send file": f = open(filename, 'r') conn.send(f.read()) f.close() # conn.send("back to idle") sessionState = SESSIONSTATE.IDLE else: sessionState == SESSIONSTATE.IDLE debugLog("Session now idle") conn.send("back to idle") elif sessionState == SESSIONSTATE.POST_1: filename = string sessionState = SESSIONSTATE.POST_2 debugLog("Session now post 2") conn.send("send file") elif sessionState == SESSIONSTATE.POST_2: while len(string) < fileSize: string += conn.get(1024) f = open(filename, 'w') f.write(string) f.close() debugLog("Post Complete") sessionState = SESSIONSTATE.IDLE debugLog("Session now idle")
def _Shutdown(self, evt): util.debugLog("Shutdown()") self.presenter.CleanupSlideshow() if evt: evt.Skip()
def terminate(self): self.sock.close() debugLog("Client Closed") sys.exit()