#!/usr/bin/python # PyClicAdmin application, Administration interface. User loads an image, then places lables on them with circled # numbers in colour of their choice (application has colour changing ability), then user writes meanings of labels on # right hand side, and stores all this in an XML file. XML file contains tags as listed in pylic-common.py file, # along with base 64 encoded image. This is for transportability reasons, specifically, transporting across xmpp # tubes for collaboration. Application uses cairo and pango libraries for drawing and labeling image. It uses the # minidom library for xml importing/exporting. # # This is the pycliccommon libary which was made in order modularize things a bit, though because was created only # with hindsight and no planning, it doesnt exactly fit like a glove. Some elements should be common to pyclic-user though import pygtk pygtk.require('2.0') import gtk import cairo import math from xml.dom import minidom import os import sys import locale from gettext import gettext as _ APP_NAME = "pyqclic" # Create XML dictionary, for xml_tags below MAIN_TAG = 0 QUIZ_TAG = 1 CATEGORY_TAG = 2 TITLE_TAG = 3 COLOR_TAG = 4 IMAGE_TAG = 5 AUTHOR_TAG = 6 QUESTIONS_TAG = 7 QUESTION_TAG = 8 COORDINATES_TAG = 9 ENTRY_TAG = 10 # set extension file to be used for opening and saving quiz files/projects. Currently uses gpx, as that seams unused. FILE_EXT = "gpx" class PyQClicCommon: _xml_tags = { MAIN_TAG : "PyClicCommon" , QUIZ_TAG: "Quiz" , CATEGORY_TAG : "Category" , TITLE_TAG: "Title" , IMAGE_TAG : "Image" , AUTHOR_TAG : "Author" , COLOR_TAG : "Colour" , QUESTIONS_TAG : "Questions" , QUESTION_TAG : "Question" , COORDINATES_TAG : "Coordinates" , ENTRY_TAG : "Answer" } # when invoked (via signal delete_event), terminates the application. def close_application(self, widget, event, data=None): gtk.main_quit() return False def file_browse(self, dialog_action, file_name=""): """This function is used to browse for a pyClic file. It can be either a save or open dialog depending on what dialog_action is. The path to the file will be returned if the user selects one, however a blank string will be returned if they cancel or do not select one. dialog_action - The open or save mode for the dialog either gtk.FILE_CHOOSER_ACTION_OPEN, gtk.FILE_CHOOSER_ACTION_SAVE file_name - Default name when doing a save""" if (dialog_action==gtk.FILE_CHOOSER_ACTION_OPEN): dialog_buttons = (gtk.STOCK_CANCEL , gtk.RESPONSE_CANCEL , gtk.STOCK_OPEN , gtk.RESPONSE_OK) else: dialog_buttons = (gtk.STOCK_CANCEL , gtk.RESPONSE_CANCEL , gtk.STOCK_SAVE , gtk.RESPONSE_OK) file_dialog = gtk.FileChooserDialog(title="Select PyQClic Project" , action=dialog_action , buttons=dialog_buttons) """set the filename if we are saving""" if (dialog_action==gtk.FILE_CHOOSER_ACTION_SAVE): file_dialog.set_current_name(file_name) """Create and add the pyclic filter""" filter = gtk.FileFilter() filter.set_name("PyQClic File") filter.add_pattern("*." + FILE_EXT) file_dialog.add_filter(filter) """Create and add the 'all files' filter""" filter = gtk.FileFilter() filter.set_name("All files") filter.add_pattern("*") file_dialog.add_filter(filter) """Init the return value""" result = "" if file_dialog.run() == gtk.RESPONSE_OK: result = file_dialog.get_filename() file_dialog.destroy() return result def set_window_title_from_file(self,xml_file,window): """Set the windows title, take it from xml_file. @param xml_file - string - The xml file name that we will base the window title off of """ if (xml_file): window.set_title("PyQClic - %s" % (os.path.basename(xml_file))) else: window.set_title("PyQClic - Untitled") def decode64(self,data): filew = open('/tmp/pyqclic_decode.png','w') filew.write(data.decode('base64')) filew.close() image = cairo.ImageSurface.create_from_png("/tmp/pyqclic_decode.png") return image def grab_click(self,widget,event): x, y = event.get_coords() self.coordinates.append((x,y)) gc = widget.window.new_gc() widget.queue_draw() self.label=gtk.Label(self.coordinates.index((x,y))+1) self.label_list.append(self.label) self.vbox2.pack_start(self.label,False,False,0) self.label.show() self.entry=gtk.Entry(max=30) self.entry_list.append(self.entry) self.vbox2.pack_start(self.entry,False,False,0) self.entry.show() def expose(self, widget, event): cr = widget.window.cairo_create() cr.set_source_surface(self.image) cr.paint() cr.select_font_face("Arial",cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_BOLD) cr.set_font_size(16) for x,y in self.coordinates: cr.set_source_rgb(self.colorred,self.colorgreen,self.colorblue) cr.arc(x,y,14,0,2 * math.pi) cr.stroke cr.fill() self.string="%s" %(self.coordinates.index((x,y))+1) cr.move_to(x-4,y+5) cr.set_source_rgb(1, 1, 1) cr.show_text(self.string) def about_program(self,widget): about = gtk.AboutDialog() about.set_program_name("PyQClic") about.set_version("0.2") about.set_copyright("(c) David Van Assche / ") about.set_comments("PyClic is a tool similar to Jclic but written in python and containing collaboration") about.set_website("http://git.sugarlabs.org/projects/pyclic") about.set_logo(gtk.gdk.pixbuf_new_from_file("aboutpyclic.png")) about.run() about.destroy() def on_file_new(self,widget): for entry in self.entry_list: self.vbox2.remove(entry) for label in self.label_list: self.vbox2.remove(label) self.entry_list=[] self.Entries=[] self.coordinates=[] self.author_entry.set_text("") self.title_entry.set_text("") self.question_list=[] self.xml_file = None self.entry="" self.window.queue_draw() def show_error_dlg(self, error_string): """This Function is used to show an error dialog when an error occurs. error_string - The error string that will be displayed on the dialog. """ error_dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR , message_format=error_string , buttons=gtk.BUTTONS_OK) error_dlg.run() error_dlg.destroy()