Assignment 1 PIVARC Code PIVARC User Manual PIVARC Code Documentation Imported Code Project Code """PIVARC (Python Image Viewing and Rating Client). This module exports four classes, all threads. It also initialises the UserInput thread and AutoAdvance thread. class AutoAdvance: Automatically scrolls through all of the images. class SaveState: On load will import from history.txt, and on quit will dump it back. class UserInput: For the user to direct the flow of the program as required. class ImageDisplay: Displays the image and makes calls to the server. Finally, we instantiate all of these items and tell them about each other, then start the two main threads running. """ from threading import * from fit2070io import printf from urllib2 import urlopen from time import sleep import webbrowser class AutoAdvance(Thread): """Automatically advances the slideshow. Has a default waiting time, on top of which the score will be used to determine display time. Has two functions, __init__, and run. """ time = 10.0 imageDisplay = None begin = False quit = False scroll = True def run(self): """Advances the slideshow. Displays the next image, then sleeps for n seconds (user specified, default 10). Then sleeps for between 0 and 10 seconds depending on the score of the image. Checks ten times a second to see whether we're done here. """ while self.begin is False: pass sleep(0.5) while not self.quit: printf("\n") self.imageDisplay.next() score = self.imageDisplay.score() print ">>> ", score *= score score /= 10.0 score += self.time while not self.quit and score > 0: sleep(0.1) if self.scroll: score -= 0.1 class SaveState(Thread): """Loads and saves the state. Has three functions, load, save and output. """ imageDisplay = None autoAdvance = None commands = [] def load(self): """Read the history file. First tries to open and read in the history file (history.txt). Splits the history file on newlines and adds it to the array of commands. The final element of the file will be the last image viewed carried from the previous session. Remove stray newlines. Remove this final element and tell the image display thread. Remove the new final element and tell the auto advance thread. If the file doesn't exist or is blank then we will get an exception and proceed with a blank history. """ try: historyFile = open("history.txt") history = historyFile.read() historyFile.close() commands = history.split("\n") while commands[-1:] == ['']: commands.pop() self.imageDisplay.imageNo = int(commands.pop()) self.autoAdvance.time = int(commands.pop()) self.commands = commands except: pass def save(self): """Save the history file. Adds the last image viewed from the image display thread to the list of commands. Opens history.txt in overwrite mode. Converts each historical command to a string (mainly for the last image, but just in case). Write the elements separated by newlines to the history file. Close the history file. """ self.autoAdvance.begin = True self.autoAdvance.quit = True self.commands.append(self.autoAdvance.time) self.commands.append(self.imageDisplay.imageNo) historyFile = open("history.txt", "w") for i in range(len(self.commands)): self.commands[i] = self.commands[i].__str__() historyFile.write("\n".join(self.commands)) historyFile.close() def output(self, text, *strings): """Includes some output in the history. Strips off newlines for the history, and then outputs to stdout. """ self.commands.append((text % strings).replace('\n', '')) printf(text % strings) class UserInput(Thread): """For the user to interact with the program. Has two main functions, __init__ and run. Also has subsidiary functions for dealing with the commands (command_h/s/d/p/n/man/help/q). """ imageDisplay = None saveState = None autoAdvance = None def command_h(self, command): """History command. With no arguments, defaults to 10, otherwise n. If there are 2+ arguments, complains and defaults to 10. If the argument doesn't boil down to an integer, complains and defaults to 10. Trims off the last n elements of the commands array, and iterates through printing them. """ if len(command) == 1: iList = 10 elif len(command) == 2: try: iList = int(command[1]) except: iList = None else: iList = None if iList < 1: iList = None if iList is None: self.saveState.output("Invalid input for history - using 'h 10'\n") iList = 10 displayList = self.saveState.commands[(0-iList):] printf("Displaying last %d commands:\n", iList) for s in displayList: printf(" %s\n", s) def command_s(self, command): """Score command. With no arguments, asks the image display thread to get the score for the current image. With 2+ arguments, complains and sulks (does nothing). If the argument doesn't boil down to a float between 0 and 10 inclusive, complains and sulks. Asks the image display thread to tell the server about the score. """ if len(command) == 1: self.imageDisplay.score() return elif len(command) == 2: try: iScore = float(command[1]) except: iScore = None if iScore < 0 or iScore > 10: iScore = None if iScore is not None: self.imageDisplay.putScore(iScore) self.imageDisplay.score() return self.saveState.output("Invalid input for score - format is 's [0-10]'\n") def command_d(self, command): """Display command. With no arguments, checks whether the image display thread is aware of a previous image. If not, we are running without a history file, so go to 1. If it is, ask the image display thread to redisplay that image. With 2+ arguments, complains and sulks (does nothing). If the argument doesn't boil down to an integer, complains and sulks. If the argument is not within 1 and the max image (from the image display thread), complains and sulks. Ask the image display thread to display the image specified by the argument. """ if len(command) == 1: if self.imageDisplay.imageNo: self.imageDisplay.display(self.imageDisplay.imageNo) else: self.imageDisplay.display(1) return elif len(command) == 2: try: iImage = int(command[1]) if iImage < 1 or iImage > self.imageDisplay.imageMax: self.saveState.output("Images range from 1 to %d\n", self.imageDisplay.imageMax) else: self.imageDisplay.display(iImage) return except: pass self.saveState.output("Invalid input for display - format is 'd [n]'\n") def command_p(self, command): """Previous command. With no arguments, asks the image display thread to display the previous image. With 1+ arguments, complains and then pretends it has no arguments. """ if len(command) != 1: self.saveState.output("Invalid input for previous - ignoring tail\n") self.imageDisplay.previous() def command_n(self, command): """Next command. With no arguments, asks the image display thread to display the next image. With 1+ arguments, complains and then pretends it has no arguments. """ if len(command) != 1: self.saveState.output("Invalid input for next - ignoring tail\n") self.imageDisplay.next() def command_t(self, command): """Time command. With no arguments, tells the auto advance thread to stop displaying images. With one argument, tells the auto advance thread how long to wait before displaying the next image. With 2+ arguments, complains and then sulks. """ if len(command) == 1: if self.autoAdvance.scroll: self.saveState.output("Halting scroll\n") else: self.saveState.output("Starting scroll\n") self.autoAdvance.scroll = not self.autoAdvance.scroll return if len(command) == 2: try: time = int(command[1]) self.autoAdvance.time = time self.saveState.output("Changing base display time to %d\n", time) return except: pass self.saveState.output("Invalid input for time - format is 't [n]'\n") def command_man(self, command): """Manual command. With no arguments, asks the operating system to display the user manual (UserManual.html). With 1+ arguments, complains and then pretends it has no arguments. """ if len(command) != 1: self.saveState.output("Invalid input for manual - ignoring tail\n") self.saveState.output("Displaying User Manual\n") webbrowser.open("UserManual.html") def command_help(self, command): """Help command. With no arguments, prints off the list of commands below. With 1+ arguments, complains and then pretends it has no arguments. """ if len(command) != 1: self.saveState.output("Invalid input for help - ignoring tail\n") printf("Available commands:\n") printf(" h [n] Print previous n commands (or previous 10 if n is not specified)\n") printf(" d [n] Display image n (or most recent image if n is not specified)\n") printf(" s View score for current image\n") printf(" s 0-10 Score image from 0 to 10 (inclusive)\n") printf(" p Display previous image\n") printf(" n Display next image\n") printf(" q Quit\n") printf(" t Halt the scroll of the images\n") printf(" t n Change the length of time the image is displayed\n") printf(" man Display user manual\n") printf(" help Display this list\n") def command_q(self, command): """Quit command. Can only complain. With 1+ arguments, complains. """ if len(command) != 1: self.saveState.output("Invalid input for quit - ignoring tail\n") def run(self): """Run the program. Loads the state. Then prompts the user to enter a command: First adds the command to the history of commands. h -> UserInput.command_h s -> UserInput.command_s d -> UserInput.command_d p -> UserInput.command_p n -> UserInput.command_n t -> UserInput.command_t man -> UserInput.command_man help -> UserInput.command_help q -> breaks the loop, continue execution Then prompts the user to enter another command (back to start of loop), as long as we didn't q. Saves the state. """ self.saveState.load() self.autoAdvance.begin = True command = raw_input("Enter commands (help to display available commands):\n>>> ") cont = True while cont: self.saveState.commands.append(command) command = command.split() if len(command) == 0: command = ["null"] elif command[0] == "h": self.command_h(command) elif command[0] == "s": self.command_s(command) elif command[0] == "d": self.command_d(command) elif command[0] == "p": self.command_p(command) elif command[0] == "n": self.command_n(command) elif command[0] == "t": self.command_t(command) elif command[0] == "man": self.command_man(command) elif command[0] == "help": self.command_help(command) elif command[0] == "q": self.command_q(command) cont = False else: self.saveState.output("Unrecognised command - help to display commands\n") if command[0] != "q": command = raw_input(">>> ") self.saveState.save() class ImageDisplay(Thread): """For all your image displaying needs. Has 6 functions, __init__, display, next, previous, putscore and score. Defines the server location. """ server = "http://ajhurst.org/~ajh/cgi-bin/imageServer.py" imageNo = None saveState = None def __init__(self): """Called once on thread creation. Extends Thread.__init__. Finds the maximum image stored on the server and puts it in a variable for safe keeping. The readline will be of the form 'xx\\n', so we split it (to remove the newline) and convert to integer. """ Thread.__init__(self) imageServer = urlopen("%s?%s" % (self.server, "task=maximum")) self.imageMax = int(imageServer.readline().split()[0]) def display(self, imageNo): """Display the image in the browser. Lets the user know which image we're displaying. Sets the image number for future reference. Asks the webbrowser to display the image thank you very much. No way to interact with the webbrowser, alas. """ self.saveState.output("Displaying image %d\n", imageNo) self.imageNo = imageNo webbrowser.open("%s?%s%d" % (self.server, "task=image&number=", imageNo)) def next(self): """Next image. If we have never seen an image before, starts at number 1. If we have got to the end of the images, goes back to number 1 (and tell the user). Otherwise, goes to the next image. Asks itself to display the image it has thus selected. """ if self.imageNo is None: imageNo = 1 elif self.imageNo == self.imageMax: self.saveState.output("%d is last image\n", self.imageMax) imageNo = 1 else: imageNo = self.imageNo + 1 self.display(imageNo) def previous(self): """Previous image. If we have never seen an image before, starts at the end. If we have got to the beginning of the images, goes back to number the end (and tell the user). Otherwise, goes to the previous image. Asks itself to display the image it has thus selected. """ if self.imageNo is None: imageNo = self.imageMax elif self.imageNo == 1: self.saveState.output("1 is first image\n") imageNo = self.imageMax else: imageNo = self.imageNo - 1 self.display(imageNo) def putScore(self, score): """Tell the server about the score. Nice and simple - assembles the string, then opens the url. Doesn't care about the return - already knows what it's up to. Tells the user so that they know that we did it. """ urlopen("%s?%s%d%s%0.2f" % (self.server, "task=putscore&number=", self.imageNo, "&data=", score)) self.saveState.output("Giving image %d a score of %0.2f\n", self.imageNo, score) def score(self): """Ask the server about the score. Assembles the string, then opens the url. Splits the return to get rid of the newline, then converts it to a float. Tells the user what we've just found out. """ imageServer = urlopen("%s?%s%d" % (self.server, "task=score&number=", self.imageNo)) score = float(imageServer.readline().split()[0]) self.saveState.output("Score for image %d is %0.2f\n", self.imageNo, score) return score if __name__ == '__main__': client = UserInput() background = AutoAdvance() display = ImageDisplay() save = SaveState() background.imageDisplay = display client.autoAdvance = background client.imageDisplay = display client.saveState = save save.autoAdvance = background save.imageDisplay = display display.saveState = save client.start() background.start() client.join() background.join() User Manual Documentation (generated by pydoc)