def initializeapp(self): """ Widget initialization for application """ # Editor objects initialization self.feditor = SRCEditor( self.widgets["ntbEditor"], "Fragment", self.widgets["lblEditorStatus"], lang="text/x-csrc" ) self.veditor = SRCEditor( self.widgets["ntbEditor"], "Vertex", self.widgets["lblEditorStatus"], lang="text/x-csrc" ) # Project info dictionary self.prjinfo = {"name": "", "comments": ""} # Texture list objects initialization self.texturelist = gtk.ListStore(str, gtk.gdk.Pixbuf, str) self.widgets["icvTextures"].set_model(self.texturelist) self.widgets["icvTextures"].set_text_column(0) self.widgets["icvTextures"].set_pixbuf_column(1) self.widgets["icvTextures"].set_text_column(2) # OpenGL Preview widget initialization display_mode = gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE self.glarea = glwidget.GLDrawingArea(display_mode) self.widgets["vbxPreviewBox"].add(self.glarea) self.glarea.show()
class GUI: """ Clase principal del GUI de la aplicación """ def __init__(self, metadata, config, gladetree): """ Constructor de la clase de control del GUI """ # Almacenar metadatos de la aplicación self.metadata = metadata # Almacenar configuración self.config = config # Almacenar objeto de Glade para definiciones del GUI self.gladetree = gladetree # Contenedor para los widgets utilizados self.widgets = {} # Inicialización de la aplicación self.apprun() def getwindows(self): """ Carga de las ventanas de la aplicación """ self.winMain = self.gladetree.get_widget("winMain") self.winAbout = self.gladetree.get_widget("winAbout") self.winFileChooser = self.gladetree.get_widget("winFileChooser") self.winGoTo = self.gladetree.get_widget("winGoTo") self.winTextureInfo = self.gladetree.get_widget("winTextureInfo") self.winPreferences = self.gladetree.get_widget("winPreferences") self.winProjectInfo = self.gladetree.get_widget("winProjectInfo") self.winFindReplace = self.gladetree.get_widget("winFindReplace") def getwidgets(self): """ Cargar widgets necesarios para el control de la aplicación """ # Lista de widgets a cargar widgetlist = [ # Menu items "mnuFileClose", "mnuViewLineNums", # Toolbar buttons "tlbtClose", # Status bars "stbMain", "sbtEditor", # Editor widgets "ntbEditor", "lblEditorStatus", # Go to entry "entGoTo", # Texture sizes combo box "cmbTextureSizes", # Texture icon view "icvTextures", # Project info "entProjectInfoName", "txtProjectInfoComments", # Texture info dialog widgets "lblTextureInfoID", "lblTextureInfoFile", "lblTextureInfoSize", "imgTextureInfo", "imgTextureInfoOrig", "scrTextureInfo", # OpenGL Shader preview widgets "vbxPreviewBox", "cmbPreviewPrimitive", ] for widgetname in widgetlist: self.widgets[widgetname] = self.gladetree.get_widget(widgetname) def connectsignals(self): """ Conectar señales del GUI """ # Conectar las señales signals = { # Main signals "quitApplication": self.quitApplication, # Application quit "showAbout": self.showAbout, # Show about dialog # File operation signals "fileNew": self.fileNew, "fileOpen": self.fileOpen, "fileClose": self.fileClose, "fileSave": self.fileSave, # TODO: SaveAs Functionality #'fileSaveAs': self.fileSaveAs, # Edition signals "editUndo": self.editUndo, "editRedo": self.editRedo, "editCopy": self.editCopy, "editCut": self.editCut, "editPaste": self.editPaste, "editDelete": self.editDelete, "editSelectAll": self.editSelectAll, "editSelectNone": self.editSelectNone, "editGoTo": self.editGoTo, "editPreferences": self.editPreferences, # Viewing signals "viewLineNums": self.viewLineNums, # Formatting signals "formatUnindent": self.formatUnindent, "formatIndent": self.formatIndent, "formatToLower": self.formatToLower, "formatToUpper": self.formatToUpper, # Texture signals "toolsProjectProperties": self.toolsProjectProperties, "toolsAddTexture": self.toolsAddTexture, "toolsDelTexture": self.toolsDelTexture, "toolsRefreshTextures": self.toolsRefreshTextures, "toolsTextureInfo": self.toolsTextureInfo, "toolsReloadShader": self.toolsReloadShader, "toolsAnimatePreview": self.toolsAnimatePreview, "toolsStopAnimatePreview": self.toolsStopAnimatePreview, "toolsSetPrimitive": self.toolsSetPrimitive, # Texture info dialog signals "textureZoomIn": self.textureZoomIn, "textureZoomOut": self.textureZoomOut, "textureZoomOrig": self.textureZoomOrig, "textureZoomAdjust": self.textureZoomAdjust, "textureZoomWheel": self.textureZoomWheel, # Background parameters signals "changeBGColor": self.changeBGColor, } self.gladetree.signal_autoconnect(signals) def loadmetadata(self): """ Metadata loading method """ # Load main window metadata self.winMain.set_title(self.metadata["APP_CODENAME"] + " " + self.metadata["APP_VERSION"]) # Load about dialog metadata self.winAbout.set_name(self.metadata["APP_CODENAME"]) self.winAbout.set_version(self.metadata["APP_VERSION"]) self.winAbout.set_copyright(self.metadata["APP_COPYRIGHT"]) self.winAbout.set_website(self.metadata["APP_WEBSITE"]) self.winAbout.set_comments(self.metadata["APP_DESC"]) self.winAbout.set_logo(gtk.gdk.pixbuf_new_from_file("pixmaps/" + self.metadata["APP_CODENAME"] + ".png")) def loadconfig(self): """ Configuration loading method """ # Disable close file actions self.widgets["tlbtClose"].set_sensitive(False) self.widgets["mnuFileClose"].set_sensitive(False) # Set default texture size self.widgets["cmbTextureSizes"].set_active(0) # Set default primitive for preview self.widgets["cmbPreviewPrimitive"].set_active(0) # Project file selection filter self.ffglsle = gtk.FileFilter() self.ffglsle.set_name("Proyecto de GLSL Editor (*.glsle)") self.ffglsle.add_pattern("*.glsle") # File chooser window parameters self.winFileChooser.set_select_multiple(False) # Image file selection filter self.ffimage = gtk.FileFilter() self.ffimage.set_name("Archivos de imagen soportados (*.jpg, *.jpeg, *.png, *.bmp)") self.ffimage.add_pattern("*.jpg") self.ffimage.add_pattern("*.jpeg") self.ffimage.add_pattern("*.png") self.ffimage.add_pattern("*.bmp") def initializeapp(self): """ Widget initialization for application """ # Editor objects initialization self.feditor = SRCEditor( self.widgets["ntbEditor"], "Fragment", self.widgets["lblEditorStatus"], lang="text/x-csrc" ) self.veditor = SRCEditor( self.widgets["ntbEditor"], "Vertex", self.widgets["lblEditorStatus"], lang="text/x-csrc" ) # Project info dictionary self.prjinfo = {"name": "", "comments": ""} # Texture list objects initialization self.texturelist = gtk.ListStore(str, gtk.gdk.Pixbuf, str) self.widgets["icvTextures"].set_model(self.texturelist) self.widgets["icvTextures"].set_text_column(0) self.widgets["icvTextures"].set_pixbuf_column(1) self.widgets["icvTextures"].set_text_column(2) # OpenGL Preview widget initialization display_mode = gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE self.glarea = glwidget.GLDrawingArea(display_mode) self.widgets["vbxPreviewBox"].add(self.glarea) self.glarea.show() def apprun(self): """ GUI initialization and main event loop """ # Window and dialog widgets loading self.getwindows() # Widget loading self.getwidgets() # Signal connection self.connectsignals() # Load metadata self.loadmetadata() # Initialize required objects self.initializeapp() # Load config self.loadconfig() # Enter main event loop gtk.main() def cleanup(self): """ Program cleanup """ gtk.main_quit() ######################################################################## # Auxiliar methods ######################################################################## def getCurrentEditor(self): """ Gets the editor object associated to the current page """ curpage = self.widgets["ntbEditor"].get_current_page() child = self.widgets["ntbEditor"].get_nth_page(curpage) if child == self.veditor.scroll: return self.veditor elif child == self.feditor.scroll: return self.feditor return None def setFileChooserFilters(self, filters): """ Clear current filechooser filters and add the specified ones """ for filter in self.winFileChooser.list_filters(): self.winFileChooser.remove_filter(filter) for filter in filters: self.winFileChooser.add_filter(filter) def getTextureSize(self): """ Returns the current width,height texture size parameters """ w, h = self.widgets["cmbTextureSizes"].get_active_text().split("x") return (int(w), int(h)) def packTextureData(self): """ Packs texture data for saving """ texlist = [] iter = self.texturelist.get_iter_first() if iter: while iter: file = self.texturelist.get_value(iter, 0) id = self.texturelist.get_value(iter, 2) iter = self.texturelist.iter_next(iter) texlist.append((id, file)) return texlist def getProjectInfo(self): """ Saves project info to project info dictionary """ self.prjinfo["name"] = self.widgets["entProjectInfoName"].get_text() buffer = self.widgets["txtProjectInfoComments"].get_buffer() startiter, enditer = buffer.get_bounds() self.prjinfo["comments"] = startiter.get_text(enditer) def clearProject(self): """ Clears project data """ if self.confirmNotSaving(): if not self.msgDialog( "question", "Aún no ha grabado los cambios en el proyecto. ¿Confirma que desea cerrar el proyecto actual?", ): return False self.widgets["entProjectInfoName"].set_text("") self.widgets["txtProjectInfoComments"].get_buffer().set_text("") self.veditor.clear() self.feditor.clear() self.texturelist.clear() return True def loadProject(self, data): """ Loads project data """ self.widgets["entProjectInfoName"].set_text(data.name) self.widgets["txtProjectInfoComments"].get_buffer().set_text(data.comments) self.veditor.set_text(data.vertex) self.feditor.set_text(data.fragment) self.loadTextures(data.textures) def loadTextures(self, texturedata): """ Loads texture list """ self.texturelist.clear() for texture in texturedata: self.texturelist.append([texture[1], None, texture[0]]) self.toolsRefreshTextures() def confirmNotSaving(self): """ Confirms not to save the current project """ return self.veditor.ismodified() or self.feditor.ismodified() ######################################################################## # Signal callbacks ######################################################################## def fileNew(self, widget): """ Creates a new project """ self.clearProject() def fileOpen(self, widget): """ Open an existing project """ # Check if it's an already loaded project or modified files self.clearProject() # Set dialog to open mode self.winFileChooser.set_action(gtk.FILE_CHOOSER_ACTION_OPEN) # Select filter and open dialog self.setFileChooserFilters([self.ffglsle]) resp = self.openDialog(self.winFileChooser, close=True) file = self.winFileChooser.get_filename() if file and resp == gtk.RESPONSE_OK: # Convert filename to UTF8 file = unicode(file, "utf-8") # Load file data # try: data = GLSLEFile(file) self.loadProject(data) # except: # self.msgDialog('error','No se ha podido cargar el archivo %s' % file) return else: # No file selected return def fileClose(self, widget): """ Closing current loaded project """ # Comprobar si hay un fichero cargado if True: # Confirmar cierre if self.msgDialog("question", "¿Confirma que desea cerrar el proyecto actual?"): # Limpieza de variables self.clearProject() # Deshabilitar opciones de cierre de archivo self.widgets["tlbtClose"].set_sensitive(False) self.widgets["mnuFileClose"].set_sensitive(False) return True else: return False def fileSave(self, widget): """ Save an existing project """ # Check if project has information set if self.prjinfo["name"] == "": self.toolsProjectProperties() # Set dialog to save mode self.winFileChooser.set_action(gtk.FILE_CHOOSER_ACTION_SAVE) # Select filter and open dialog self.setFileChooserFilters([self.ffglsle]) resp = self.openDialog(self.winFileChooser, close=True) file = self.winFileChooser.get_filename() if file and resp == gtk.RESPONSE_OK: # Convert filename to UTF8 file = unicode(file, "utf-8") # Save data to file data = GLSLEFile() vertexdata = self.veditor.get_text() fragmentdata = self.feditor.get_text() # try: data.save(file, self.prjinfo, "", vertexdata, fragmentdata, self.packTextureData()) # except: # self.msgDialog('error','No se ha podido grabar el archivo %s' % file) # Change editors state self.veditor.unmodifiedbuffer() self.feditor.unmodifiedbuffer() else: # No file selected return def editUndo(self, widget): """ Undo last action """ editor = self.getCurrentEditor() if editor: if not editor.undo(): self.msgDialog("warning", "No hay acciones que puedan deshacerse") def editRedo(self, widget): """ Redo last undo """ editor = self.getCurrentEditor() if editor: if not editor.redo(): self.msgDialog("warning", "No hay acciones que puedan rehacerse") def editCopy(self, widget): """ Cut text to clipboard """ editor = self.getCurrentEditor() if editor: editor.copy() def editCut(self, widget): """ Cut text to clipboard """ editor = self.getCurrentEditor() if editor: editor.cut() def editPaste(self, widget): """ Paste text from clipboard """ editor = self.getCurrentEditor() if editor: editor.paste() def editDelete(self, widget): """ Delete selected text """ editor = self.getCurrentEditor() if editor: editor.delete() def editSelectAll(self, widget): """ Select all text in current editor """ editor = self.getCurrentEditor() if editor: editor.selectall() def editSelectNone(self, widget): """ Clear selection in current editor """ editor = self.getCurrentEditor() if editor: editor.selectnone() def editGoTo(self, widget): """ Place cursor at selected line """ editor = self.getCurrentEditor() if editor: self.widgets["entGoTo"].set_text("") resp = self.openDialog(self.winGoTo) if resp == gtk.RESPONSE_OK: line = int(self.widgets["entGoTo"].get_text().strip()) editor.goto(line) self.closeDialog(self.winGoTo) else: self.msgDialog("error", "No hay ningún editor de código en uso") def editPreferences(self, widget): """ Shows preferences dialog """ resp = self.openDialog(self.winPreferences) if resp == gtk.RESPONSE_OK: # Save and apply new preferences print "Grabar preferencias" else: # Reload actual preferences print "Recargar preferencias" self.closeDialog(self.winPreferences) def viewLineNums(self, widget=None): """ Show line numbers """ if widget: status = widget.get_active() else: status = self.widgets["mnuViewLineNums"].get_active() self.veditor.linenums(status) self.feditor.linenums(status) self.widgets["mnuViewLineNums"].set_active(status) def formatIndent(self, widget): """ Indents current selection or line """ editor = self.getCurrentEditor() if editor: editor.indent() def formatUnindent(self, widget): """ Unindents current selection or line """ editor = self.getCurrentEditor() if editor: editor.unindent() def formatToUpper(self, widget): """ Converts current selection to upper case """ editor = self.getCurrentEditor() if editor: editor.toupper() def formatToLower(self, widget): """ Converts current selection to upper case """ editor = self.getCurrentEditor() if editor: editor.tolower() def toolsProjectProperties(self, widget=None): """ Shows project properties dialog """ while True: resp = self.openDialog(self.winProjectInfo, close=True) if self.widgets["entProjectInfoName"].get_text() == "": self.msgDialog("error", "El proyecto debe tener un nombre") else: break # Get project properties and save to project dictionary self.getProjectInfo() def toolsAddTexture(self, widget): """ Adds a new texture to list """ # Open texture file selection dialog self.winFileChooser.set_action(gtk.FILE_CHOOSER_ACTION_OPEN) self.winFileChooser.set_select_multiple(False) # Clear filters self.setFileChooserFilters([self.ffimage]) resp = self.openDialog(self.winFileChooser, close=True) file = self.winFileChooser.get_filename() if file and resp == gtk.RESPONSE_OK: # Convert filename to UTF8 file = unicode(file, "utf-8") self.texturelist.append([file, None, ""]) self.toolsRefreshTextures(msg="Añadida nueva textura desde fichero %s" % file) else: return def toolsDelTexture(self, widget): """ Deletes currently selected texture """ paths = self.widgets["icvTextures"].get_selected_items() for path in paths: iter = self.texturelist.get_iter(path) textureid = self.texturelist.get_value(iter, 2) if self.msgDialog("question", "¿Confirma eliminar la textura %s?" % textureid): self.texturelist.remove(iter) self.toolsRefreshTextures(reload=False, msg="Eliminada textura. Lista de texturas actualizada") def toolsRefreshTextures(self, widget=None, reload=True, msg="Lista de texturas actualizada"): """ Refresh texture list """ # TODO: Problem with iconview refreshing self.glarea.redraw = False iter = self.texturelist.get_iter_first() id = 0 w, h = self.getTextureSize() if iter: while iter: id += 1 file = self.texturelist.get_value(iter, 0) if reload: self.statusMessage("info", "Cargando fichero de textura %s" % file) icon = gtk.gdk.pixbuf_new_from_file(file) # Resize pixbuf to preferred size icon = icon.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR) self.texturelist.set_value(iter, 1, icon) self.texturelist.set_value(iter, 2, "Texture" + str(id)) iter = self.texturelist.iter_next(iter) self.statusMessage("info", msg) if widget == self.widgets["cmbTextureSizes"]: self.widgets["icvTextures"].set_item_width(w) else: self.statusMessage("info", "") self.glarea.redraw = True def toolsTextureInfo(self, widget, event=None): """ Shows texture info dialog """ paths = self.widgets["icvTextures"].get_selected_items() for path in paths: # Retrieve texture info iter = self.texturelist.get_iter(path) file = self.texturelist.get_value(iter, 0) id = self.texturelist.get_value(iter, 2) img = gtk.gdk.pixbuf_new_from_file(file) size = str(img.get_width()) + "x" + str(img.get_height()) # Show info in texture info dialog self.widgets["lblTextureInfoID"].set_text(id) self.widgets["lblTextureInfoFile"].set_text(file) self.widgets["lblTextureInfoSize"].set_text(size) self.widgets["imgTextureInfoOrig"].set_from_pixbuf(img) self.widgets["imgTextureInfo"].set_from_pixbuf(img) resp = self.openDialog(self.winTextureInfo, close=True) def toolsReloadShader(self, widget): """ Recompile shader to be shown in preview window """ self.glarea.compileshader(self.veditor.get_text(), self.feditor.get_text()) self.glarea.queue_draw() def toolsAnimatePreview(self, widget): """ Start shader preview animation """ self.glarea.redraw = True self.glarea.queue_draw() def toolsStopAnimatePreview(self, widget): """ Stop shader preview animation """ self.glarea.redraw = False def toolsSetPrimitive(self, widget): """ Set the currently selected primitive to be rendered """ self.glarea.setprimitive(self.widgets["cmbPreviewPrimitive"].get_active_text()) def changeBGColor(self, widget): """ Set the currently selected color to background clearing color """ color = widget.get_color() r = color.red / 65535.0 g = color.green / 65535.0 b = color.blue / 65535.0 a = widget.get_alpha() / 65535.0 print (r, g, b, a) self.glarea.setbackground(r, g, b, a) ######################################################################## # Texture info dialog signals ######################################################################## def textureZoomIn(self, widget=None): """ Zoom in texture """ orig = self.widgets["imgTextureInfoOrig"].get_pixbuf() img = self.widgets["imgTextureInfo"].get_pixbuf() w = int(img.get_width() * 1.5) h = int(img.get_height() * 1.5) if w > 1500: w = 1500 if h > 1500: h = 1500 # Disable zoom in img = orig.scale_simple(w + 1, h + 1, gtk.gdk.INTERP_BILINEAR) self.widgets["imgTextureInfo"].set_from_pixbuf(img) def textureZoomOut(self, widget=None): """ Zoom out texture """ orig = self.widgets["imgTextureInfoOrig"].get_pixbuf() img = self.widgets["imgTextureInfo"].get_pixbuf() w = int(img.get_width() * 0.5) h = int(img.get_height() * 0.5) if w < 5: w = 5 if h < 5: h = 5 # Disable zoom out img = orig.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR) self.widgets["imgTextureInfo"].set_from_pixbuf(img) def textureZoomOrig(self, widget): """ Set original texture size """ self.widgets["imgTextureInfo"].set_from_pixbuf(self.widgets["imgTextureInfoOrig"].get_pixbuf()) def textureZoomAdjust(self, widget): """ Adjust texture size to image widget size TODO: Width and height of viewing area has to be got """ orig = self.widgets["imgTextureInfoOrig"].get_pixbuf() img = self.widgets["imgTextureInfo"].get_pixbuf() x, y = self.widgets["scrTextureInfo"].get_child_requisition() img = orig.scale_simple(x, y, gtk.gdk.INTERP_BILINEAR) self.widgets["imgTextureInfo"].set_from_pixbuf(img) def textureZoomWheel(self, widget, event): """ Texture mouse-wheel zooming callback """ if event.direction == gtk.gdk.SCROLL_UP and event.state & gtk.gdk.CONTROL_MASK == gtk.gdk.CONTROL_MASK: self.textureZoomIn() if event.direction == gtk.gdk.SCROLL_DOWN and event.state & gtk.gdk.CONTROL_MASK == gtk.gdk.CONTROL_MASK: self.textureZoomOut() # Event propagation return False ######################################################################## # About dialog signals ######################################################################## def showAbout(self, widget): """ Mostrar diálogo Acerca de... """ self.openDialog(self.winAbout, close=True) ######################################################################## # Other tool methods ######################################################################## def msgDialog(self, mode, msg, desc=None, parent=None, cancel=False): """ Shows a message dialog """ dialogmodes = { "info": (gtk.MESSAGE_INFO, gtk.BUTTONS_OK), "warning": (gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE), "question": (gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO), "error": (gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE), } # Comprobamos si el modo de diálogo es correcto if dialogmodes.has_key(mode): mode = dialogmodes[mode] if not parent: parent = self.winMain else: parent = self.winMain mode = dialogmodes["error"] msg = "Error interno" desc = "Se ha producido un error al definir un diálogo de mensaje" # Creación del diálogo dialog = gtk.MessageDialog(parent, gtk.DIALOG_MODAL and gtk.DIALOG_DESTROY_WITH_PARENT, mode[0], mode[1]) if cancel: dialog.add_button("Cancelar", 1) # Añadir el mensaje del diálogo dialog.set_markup("<b>" + msg + "</b>") # Añadir mensaje secundario si se ha especificado if desc: dialog.format_secondary_markup(desc) # Mostrar diálogo y recoger respuesta resp = dialog.run() # Interpretación de la respuesta if cancel: if resp == 1: resp = "cancel" else: resp = resp == gtk.RESPONSE_YES else: resp = resp == gtk.RESPONSE_YES # Cierre del diálogo dialog.destroy() return resp def statusMessage(self, context, message, desc=None, showdialog=False): """ Shows a message in main status bar """ contextid = self.widgets["stbMain"].get_context_id(context) self.widgets["stbMain"].pop(contextid) self.widgets["stbMain"].push(contextid, message) if desc or showdialog: self.msgDialog(context, message) def preOpenDialog(self): """ Executes dialog pre-opening operations """ self.animstatus = self.glarea.redraw self.glarea.redraw = False def postOpenDialog(self): """ Executes dialog post-opening operations """ self.glarea.redraw = self.animstatus def openDialog(self, dialog, delete=False, close=False): """ Open a dialog """ self.preOpenDialog() resp = dialog.run() if (not delete and resp == gtk.RESPONSE_DELETE_EVENT) or close: self.closeDialog(dialog) self.postOpenDialog() return resp def closeDialog(self, dialog): """ Close a dialog """ dialog.hide() return True def quitApplication(self, widget, event=None): """ Salida del programa """ if self.msgDialog("question", "¿Realmente desea salir de %s?" % self.metadata["APP_NAME"]): self.cleanup() return True def htmlColor(self, color): """ Convertir una instancia de gtk.gdk.Color() en color HTML """ r = hex(color.red / 256)[2:].upper() g = hex(color.green / 256)[2:].upper() b = hex(color.blue / 256)[2:].upper() if len(r) < 2: r = "0" + r if len(g) < 2: g = "0" + g if len(b) < 2: b = "0" + b return "#%s%s%s" % (r, g, b)