""" Classes to draw GUI elements on the screen like buttons, menus, textboxes etc. """ __version__ = "alpha" __author__ = "Nard Janssens, Vinicius Silva, Robert Gowans, Ivan Antalec, Leonardo Marques - See Github PhotonFileUtils" import math import time import pygame from pygame.locals import * import GUIhelpers from GUIhelpers import * # Common constants which make up the Theme of the GUI defFontName="Verdana" #Fontname None will take default system font defFontSize=16#24 defFormBackground=(232,232,232) defFormForeground=(0,0,0) defMenuBackground=defFormBackground defMenuForeground=(0,0,0) defHighMenuForeground=(255,255,255) defHighMenuBackground=(68,123,213) defHighSelectForeground=defHighMenuForeground defHighSelectBackground=defHighMenuBackground defEditorBackground=(255,255,255) defEditorForeground=(0,0,0) defHighEditorBackground=(255,255,12*16) defHighEditorForeground=(0,0,0) defTitlebarBackground=(215,215,215) #defButtonBackground=(68,123,213) defButtonBackground=(205,205,205) defBorder=(173,173,173) defBorderHover=defHighMenuBackground ######################################################################################################################## ## Class MenuBar ######################################################################################################################## class MenuBar(): menus=[] margins = GRect(4, 4, 4, 4) height = -1 spacing = 4 minwidth=50 isVisible=True textcolor=defMenuForeground backcolor=defMenuBackground highbackcolor=defHighMenuBackground highforecolor=defHighMenuForeground bordercolor = defBorder activeMenu=None def __init__(self, pyscreen,fontname=defFontName, fontsize=defFontSize): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.font = pygame.font.SysFont(fontname, fontsize) self.fontsize=fontsize #store to init menuitems self.fontname=fontname #store to init menuitems # Enlarge menubar height if text does not fit height = self.font.get_linesize() * 0.5 if (height + self.margins.y + self.margins.height) > self.height: self.height = height + self.margins.y + self.margins.height def addMenu(self, menutitle,shortcutChar): """ Adds new menu in the menubar (with empty menulist).""" # Determine left position of new menu prevIdx=len(self.menus)-1 if prevIdx==-1: x=self.margins.x else: prevTitle = self.menus[prevIdx]["title"] prevLeft =self.menus[prevIdx]["left"] prevWidth = self.menus[prevIdx]["width"]#, height = self.font.size(prevTitle) #if prevWidth<self.minwidth: prevWidth=self.minwidth x= prevLeft+prevWidth#+ self.spacing scNr=0 # Determine width of new menu width, height = self.font.size(menutitle) width=width+self.spacing if width<self.minwidth: width=self.minwidth+self.spacing # Get position of shortcutchar for idx,ch in enumerate(menutitle): if ch==shortcutChar: scNr=idx #print (scNr) # Make menulist loc=(x,self.height+self.margins.x+self.margins.height+1) menulist=MenuList(pyscreen=self.pyscreen,location=loc, fontname = self.fontname, fontsize = self.fontsize, title=menutitle) # Store (menu title, position, position of shortcutchar and menulist) in menudata menudata={"title":menutitle,"left":x,"width":width,"scChar":scNr, "menulist":menulist} self.menus.append(menudata) def addItem(self, menutitle, menuitem, func_on_click): """ Adds new item to menulist. """ # Find menu in menus, retrieve menulist and add item for menu in self.menus: if menu["title"]==menutitle: menulist=menu["menulist"] menulist.addItem(menuitem,func_on_click) def redraw(self): """ Redraws MenuBar. """ # If not visible, nothing to do if not self.isVisible: return # Draw menubar background and border. w, dummy = pygame.display.get_surface().get_size() h=self.height+self.margins.y+self.margins.height pygame.draw.rect(self.pyscreen, self.backcolor, (0,0,w,h), 0) pygame.draw.rect(self.pyscreen, self.bordercolor , (0, h, w, 1),1) for menudata in self.menus: menuleft = menudata["left"] menuwidth = menudata["width"] # Highlight current menu if mouse hovers and set textcolor depending if mouse hover or not. if menudata==self.activeMenu: #pygame.draw.rect(self.pyscreen, self.highbackcolor, (menuleft-self.spacing, 0,menuwidth, h), 0) pygame.draw.rect(self.pyscreen, self.highbackcolor, (menuleft, 0, menuwidth, h), 0) localtextcolor=defHighMenuForeground else: localtextcolor=defMenuForeground # Shortcut letter should be underlines, so we cut menu title in different parts for regular and underline scNr=menudata["scChar"] preStr=menudata["title"][:scNr] scStr=menudata["title"][scNr:scNr+1] postStr = menudata["title"][scNr + 1:] #print (scNr,preStr,scStr,postStr) wPre, dummy = self.font.size(preStr) wSc, dummy = self.font.size(scStr) wPost, dummy = self.font.size(postStr) # Draw text before shorcut Char self.font.set_underline(False) textsurface = self.font.render(preStr, True, localtextcolor) self.pyscreen.blit(textsurface, (menuleft+self.margins.x, self.margins.y)) # Draw shorcut Char self.font.set_underline(True) textsurface = self.font.render(scStr,True, localtextcolor) self.pyscreen.blit(textsurface, (menuleft+self.margins.x +wPre, self.margins.y)) # Draw text after shorcut Char self.font.set_underline(False) textsurface = self.font.render(postStr, True, localtextcolor) self.pyscreen.blit(textsurface, (menuleft+self.margins.x+wPre+wSc, self.margins.y)) menudata["menulist"].redraw() def handleMouseDown(self, pos,button): """ Updates menu states if user clicked on menubar. """ # If not clicked, nothing to do if not button == 1: return # Check if mouse clicked within menubar area if pos[1]<=(self.height+self.margins.y+self.margins.height): # We are handling this so clear queue for others pygame.event.clear() # If menu visible/active then hide menu, else show if self.activeMenu==None: for menu in self.menus: if pos[0]>menu["left"] and pos[0]<(menu["left"]+menu["width"]): self.activeMenu=menu menulist=menu["menulist"] menulist.isVisible=True else: menulist = menu["menulist"] menulist.isVisible = False return True else: self.activeMenu["menulist"].isVisible=False self.activeMenu=None return # Call upon menulists to handle mouse (if above menulists). """ for menu in self.menus: if menu["menulist"].handleMouseDown(pos,button): return True def handleMouseUp(self, pos,button): """ Updates menu states if user clicked on menubar. """ if not button == 1: return # Call upon menulists to handle mouse (if above menulists). """ for menu in self.menus: # If menulist accepts MouseUp we can close menulist and active item in menubar if menu["menulist"].handleMouseUp(pos,button): # We are handling this so clear queue for others pygame.event.clear() self.activeMenu["menulist"].isVisible = False self.activeMenu = None return True # If we are below menubar and nothing in menulists (not returned True) then user clicks on workarea of window to hide all menus if pos[1]>=(self.height+self.margins.y+self.margins.height): if self.activeMenu: # We are handling this so clear queue for others pygame.event.clear() self.activeMenu["menulist"].isVisible=False self.activeMenu=None def handleMouseMove(self, pos): """ Switch open menu if in menubar else call upon menulists to handle mouse (if above menulists). """ # Move activemenu if mouse is moving within menubar area and menu is active if pos[1] <= (self.height + self.margins.y + self.margins.height): if not self.activeMenu == None: for menu in self.menus: if pos[0] > menu["left"] and pos[0] < (menu["left"] + menu["width"]): self.activeMenu = menu menulist = menu["menulist"] menulist.isVisible = True else: menulist = menu["menulist"] menulist.isVisible = False # We are handling this so clear queue for others pygame.event.clear() return True # Let menulist check if mouse moved within their areas for menu in self.menus: if menu["menulist"].handleMouseMove(pos): return True def handleKeyDown(self,key,unicode): isAlt = (pygame.key.get_mods() & pygame.KMOD_ALT) if isAlt: for menu in self.menus: scNr=menu["scChar"] title=menu["title"] scChr=title[scNr] scNr=ord(scChr)-ord("A") keyNr=key-pygame.K_a if keyNr == scNr: self.activeMenu=menu menulist=menu["menulist"] menulist.isVisible=True else: menulist = menu["menulist"] menulist.isVisible = False # We are handling this so clear queue for others pygame.event.clear() def openMenu(self,menutitle): """ Opens menu. """ for idx,(title, shortcutChar, items, state) in enumerate(self.menus): if title == menutitle: self.menus[idx][3]= True else: self.menus[idx][3] = False ######################################################################################################################## ## Class MenuList ######################################################################################################################## class MenuList(): title="" pos=GRect(0, 0, 0, 0) items=[] margins=GRect(4, 4, 4, 4) rowheight=0 spacing=4 activeItem=-1 isVisible=False textcolor = defMenuForeground backcolor = defMenuBackground highbackcolor = defHighMenuBackground highforecolor = defHighMenuForeground bordercolor = defBorder activeItem=-1 def __init__(self, pyscreen, location, margins=GRect(6, 6, 6, 6), fontname=defFontName, fontsize=defFontSize, title="unknown"): """ Saves all values to internal variables. """ self.pyscreen = pyscreen l_x = location[0] l_y = location[1]-1 self.margins=margins self.font = pygame.font.SysFont(fontname, fontsize) self.title=title self.items=[] # Enlarge width and height if text does not fit width, height = self.font.size("MinimalText") l_width = width + self.margins.x + self.margins.width l_height= 1*height++ self.margins.y + self.margins.height self.pos=GRect(l_x,l_y,l_width,l_height) self.rowheight = height def addItem(self,menuitem,func_on_click): """ Add menulist item and adjust menulist height and width (if needed) """ #Add item itemdata = (menuitem,func_on_click) self.items.append(itemdata) # Adjust height and width only if needed width, height = self.font.size(menuitem) if (width+self.margins.x+self.margins.width) >self.pos.width: self.pos.width=width+self.margins.x+self.margins.width self.pos.height= len(self.items)*(self.rowheight+self.spacing)+ self.margins.y + self.margins.height-self.spacing # extract 1x spacing at the bottom def redraw(self): """ Redraws MenuList. """ # If not visible nothing to do if not self.isVisible: self.activeItem=-1 # so on reopening we don have floating cursor return # Draw background and border pygame.draw.rect(self.pyscreen, self.backcolor, self.pos.tuple (), 0) pygame.draw.rect(self.pyscreen, self.bordercolor, (self.pos.tuple()), 1) # Draw item text for row,(text,func_on_click) in enumerate(self.items): rowtop=self.pos.y+self.margins.y+row*(self.rowheight+self.spacing) if row==self.activeItem: pygame.draw.rect(self.pyscreen, self.highbackcolor,(self.pos.x+self.margins.x, rowtop-int(self.spacing/2), self.pos.width-self.margins.x-self.margins.width, self.rowheight), 0) localtextcolor = defHighMenuForeground else: localtextcolor = defMenuForeground textsurface = self.font.render(text,True, localtextcolor) self.pyscreen.blit(textsurface, (self.pos.x+self.margins.x, rowtop)) def handleMouseMove(self, pos): """ Highlights menulist item if mouse hover above.""" if not self.isVisible: return if pos[0] > self.pos.x and pos[0] < (self.pos.x+self.pos.width) and \ pos[1] < (self.pos.y + self.pos.height): rely=pos[1]-self.pos.y self.activeItem=int((rely-self.margins.y)/(self.rowheight+self.spacing)) # We are handling this so clear queue for others pygame.event.clear() return True def handleMouseDown(self, pos,button): return def handleMouseUp(self, pos,button): """ Calls on user function if clicked on menu item.""" if not button == 1: return if not self.isVisible: return gpos=GPoint.fromTuple(pos) if gpos.inGRect(self.pos): # We are handling this so clear queue for others pygame.event.clear() for row, (item, func_on_click) in enumerate(self.items): if row == self.activeItem: if not func_on_click==None: func_on_click() self.isVisible=False return True ######################################################################################################################## ## Class ImgBox ######################################################################################################################## class ImgBox(): rect=GRect() img=None hoverimg=None hoverActive=False action=None visible=True drawBorder=False resizeto=GPoint() #Tooltip vars toolTipLabel=None firstHoverTime = 0 firstHoverPos=GPoint(0,0) def __init__(self, pyscreen, filename,filename_hover=None, pos=(0,0),bordercolor=defBorder,borderhovercolor=defBorderHover,drawBorder=False, toolTip="", resizeto=None,func_on_click=None): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.bordercolor = bordercolor self.borderhovercolor=borderhovercolor self.drawBorder=drawBorder self.func_on_click=func_on_click # Load image and resize to fit self.img=pygame.image.load(filename) if not filename_hover==None: self.hoverimg = pygame.image.load(filename_hover) # If user want other size we try to resize try: if not resizeto == None: self.img=pygame.transform.scale(self.img, resizeto) if not filename_hover==None: self.hoverimg = pygame.transform.scale(self.hoverimg, resizeto) except Exception as err: print(err) # Determine size of control based on image size imgrect=self.img.get_rect() self.rect=GRect(imgrect[0],imgrect[1],imgrect[2],imgrect[3]) self.rect.x=pos[0] self.rect.y=pos[1] #And setup tooltip # We need to figure out the correct width correct width of tooltip if not toolTip=="": self.toolTipLabel = Label(pyscreen, rect=GRect(self.rect.right, self.rect.height, 1024, 20), text=toolTip, drawBorder=True, borderwidth=1, backcolor=defHighEditorBackground, fontsize=defFontSize-2, textcolor=defHighEditorForeground, autowrap=True) max_width=0 for line in toolTip.split("\n"): text_width, text_height = self.toolTipLabel.font.size(line) # needed for self.autoheight if text_width>max_width: max_width=text_width self.toolTipLabel.rect.width=max_width+self.toolTipLabel.margin.x+self.toolTipLabel.margin.width self.toolTipLabel.visible=False def redraw(self): """ Redraws ImgBox. """ # If not visible nothing to do. if not self.visible: return # Draw Image to screen self.pyscreen.blit(self.img,self.rect.tuple()) # On hover draw hover image available if self.hoverActive and not self.hoverimg==None: self.pyscreen.blit(self.hoverimg, self.rect.tuple()) else: self.pyscreen.blit(self.img, self.rect.tuple()) # On hover drawborder if drawBorder is True if self.drawBorder: if self.hoverActive: pygame.draw.rect(self.pyscreen,self.borderhovercolor, self.rect.tuple(), 1) else: pygame.draw.rect(self.pyscreen, self.bordercolor, self.rect.tuple(), 1) def handleMouseMove(self, pos): """ Updates state of ImgBox on hover. """ gpos=GPoint(pos[0],pos[1]) if gpos.inGRect(self.rect): self.hoverActive=True # We are handling this so clear queue for others pygame.event.clear() else: self.hoverActive=False def handleMouseUp(self, pos,button): """ Calls on user function if clicked.""" if not button==1: return gpos = GPoint(pos[0], pos[1]) if gpos.inGRect(self.rect): # We are handling this so clear queue for others pygame.event.clear() if not self.func_on_click==None: self.func_on_click() def handleMouseDown(self,pos,button): return def handleKeyDown(self,key,unicode): return def handleToolTips(self,pos): """ Returns label control with tooltip if hovered long enough. """ # if user did not set a tooltip nothing to do if self.toolTipLabel==None: return None #Check if mouse above control, if not exit gpos=GPoint.fromTuple(pos) if not gpos.inGRect(self.rect): self.firstHoverTime=0 self.firstHoverPos=GPoint(0,0) self.toolTipLabel.visible = False return None #Check if first time hovering, if so set timer if self.firstHoverTime==0: self.firstHoverPos=gpos self.firstHoverTime=time.time() #Check if moved since hovering, if so reset counter and exit dif=self.firstHoverPos-gpos if math.sqrt(dif.x*dif.x+dif.y*dif.y)>5: self.firstHoverPos=GPoint(0,0) self.firstHoverTime=0 self.toolTipLabel.visible = False return None #Check if hovered enough time and return tooltip timeHovered=time.time()-self.firstHoverTime if timeHovered>1.5 and not self.toolTipLabel.text=="" : self.toolTipLabel.visible=True self.toolTipLabel.rect.x = gpos.x self.toolTipLabel.rect.y = gpos.y #check if tooltip overflow right edge of pyscreen if self.toolTipLabel.rect.right>self.pyscreen.get_size()[0]: self.toolTipLabel.rect.x=gpos.x-self.toolTipLabel.rect.width return self.toolTipLabel ######################################################################################################################## ## Class Button ######################################################################################################################## class Button(): # We need 3 buttonstates so boolean var does not suffice normal=0 hover=1 down=2 state= False rect=None img=None hoverimg=None downimg = None action=None borderwidth=1 visible=True def __init__(self, pyscreen, rect=GRect(0,0,60,40),text="Button", textcolor=(0,0,0),fontname=defFontName, fontsize=defFontSize, backcolor=defButtonBackground,filename=None,filename_hover=None,filename_down=None, bordercolor=defBorder, borderhovercolor=defBorderHover,func_on_click=None): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.text=text self.textcolor=textcolor self.font = pygame.font.SysFont(fontname, fontsize) text_width, text_height = self.font.size(text) self.twidth=text_width self.theight=text_height self.bordercolor = bordercolor self.borderhovercolor=borderhovercolor self.backcolor=backcolor self.func_on_click=func_on_click if func_on_click==None: print ("None") if not filename_hover==None: self.hoverimg = pygame.image.load(filename_hover) if not filename_down==None: self.downimg = pygame.image.load(filename_down) # If button is image than dimensions are determined by image size if not filename == None: self.img = pygame.image.load(filename) imgrect = self.img.get_rect() self.rect=GRect(rect.x,rect.y,imgrect[2],imgrect[3]) # Otherwise we listen to the dimensions the user gave us else: self.rect=rect def redraw(self): """ Redraws Button. """ # If not visible nothing to do. if not self.visible: return # Draw image (if available) corresponding to button state if self.state==self.hover and not self.hoverimg==None: self.pyscreen.blit(self.hoverimg, self.rect.tuple()) elif self.state==self.down and not self.downimg==None: self.pyscreen.blit(self.downimg, self.rect.tuple()) elif not self.img==None: if self.state==self.down:#shift image if down self.pyscreen.blit(self.img, (self.rect.x + self.borderwidth, self.rect.y + self.borderwidth)) else: self.pyscreen.blit(self.img, self.rect.tuple()) else: pygame.draw.rect(self.pyscreen, self.backcolor,self.rect.tuple(), 0) # Draw border (if available) corresponding to button state if self.borderwidth>0: if self.borderwidth == 1: if self.state==self.down or self.state==self.hover: pygame.draw.rect(self.pyscreen, self.borderhovercolor, self.rect.tuple(), 1) else: pygame.draw.rect(self.pyscreen, self.bordercolor, self.rect.tuple(), 1) else:#make 3d border if self.state==self.down: topleftcolor=(64,64,64) bottomrightcolor=(192,192,192) else: topleftcolor = (192, 192, 192) bottomrightcolor = (64, 64, 64) for inset in range (0,self.borderwidth): #top and left border pygame.draw.rect(self.pyscreen, topleftcolor, (self.rect.x,self.rect.y+inset,self.rect.width-inset,1), 1) pygame.draw.rect(self.pyscreen, topleftcolor, (self.rect.x+inset, self.rect.y, 1, self.rect.height-inset), 1) #bottom en right border pygame.draw.rect(self.pyscreen, bottomrightcolor, (self.rect.x+inset,self.rect.y+self.rect.height-inset,self.rect.width-inset,1), 1) pygame.draw.rect(self.pyscreen, bottomrightcolor, (self.rect.x+self.rect.width-inset, self.rect.y+inset, 1, self.rect.height-inset), 1) # Draw text (if available) and shift text if down if not self.text=="": textsurface = self.font.render(self.text, True,self.textcolor) dx=int(self.rect.width-self.twidth)/2 dy=int(self.rect.height-self.theight)/2 if self.state == self.down: # shift text if down dx=dx+self.borderwidth dy=dy+self.borderwidth self.pyscreen.blit(textsurface, (self.rect.x + dx, self.rect.y+dy)) def handleMouseMove(self, pos): """ Updates state of Button on hover. """ gpos=GPoint(pos[0],pos[1]) if gpos.inGRect(self.rect): self.state=self.hover # We are handling this so clear queue for others pygame.event.clear() else: self.state=self.normal #print (self.state) def handleMouseUp(self, pos,button): """ Calls on user function if clicked.""" if not button == 1: return gpos=GPoint(pos[0],pos[1]) if gpos.inGRect(self.rect): self.state=self.normal # We are handling this so clear queue for others pygame.event.clear() if not self.func_on_click==None: self.func_on_click() def handleMouseDown(self,pos,button): """ Updates state of Button on down. """ if not button == 1: return gpos=GPoint(pos[0],pos[1]) if gpos.inGRect(self.rect): # We are handling this so clear queue for others pygame.event.clear() self.state=self.down def handleKeyDown(self,key,unicode): return ######################################################################################################################## ## Class ScrollBarV ######################################################################################################################## class ScrollBarV(): # We need 3 buttonstates so boolean var does not suffice normal=0 hover=1 down=2 state= False rect = None action = None borderwidth = 1 visible = True #todo: replace forecolor with def constant def __init__(self, pyscreen, rect=GRect(0, 0, 60, 40), forecolor=(0,0,0), sfontsize=defFontSize, sbackcolor=defButtonBackground, sbordercolor=defBorder, sborderhovercolor=defBorderHover, func_on_click=None,minScroll=0, maxScroll=128,curScroll=0,smallScroll=1, largeScroll=8): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.rect=rect self.bordercolor = sbordercolor self.borderhovercolor = sborderhovercolor self.backcolor = sbackcolor self.forecolor=forecolor self.func_on_click = func_on_click self.minScroll=minScroll self.maxScroll=maxScroll self.curScroll = curScroll self.smallScroll=smallScroll self.largeScroll=largeScroll # Add up and down button self.btnUp = Button(pyscreen, text="^", bordercolor=sbordercolor, borderhovercolor=sborderhovercolor, textcolor=forecolor, func_on_click=self.scrollUp) self.btnDown = Button(pyscreen, text="v", bordercolor=sbordercolor, borderhovercolor=sborderhovercolor, textcolor=forecolor, func_on_click=self.scrollDown) def scrollDown(self,isLargeScroll=False): """ Decreases current scroll value. """ # Check if we have to take a small step or a large step if not isLargeScroll: self.curScroll=self.curScroll+self.smallScroll else: self.curScroll = self.curScroll + self.largeScroll if self.curScroll>self.maxScroll: self.curScroll=self.maxScroll # Send curScroll to parent if not self.func_on_click ==None: self.func_on_click(self.curScroll) #print("scroll Up: ",self.curScroll) def scrollUp(self,isLargeScroll=False): """ Inreases current scroll value. """ # Check if we have to take a small step or a large step if not isLargeScroll: self.curScroll = self.curScroll - self.smallScroll else: self.curScroll=self.curScroll - self.largeScroll if self.curScroll < self.minScroll: self.curScroll = self.minScroll # Send curScroll to parent if not self.func_on_click==None: self.func_on_click(self.curScroll) #print("scroll Down: ", self.curScroll, isLargeScroll, self.smallScroll) def redraw(self): """ Redraws ScrollBarV. """ # If not visible nothing to do. if not self.visible: return # Position up and down buttons self.btnUp.rect= GRect(self.rect.x, self.rect.y, self.rect.width, self.rect.width) self.btnDown.rect = GRect(self.rect.x, self.rect.y + self.rect.height - self.rect.width, self.rect.width, self.rect.width) # Draw background pygame.draw.rect(self.pyscreen, self.backcolor, self.rect.tuple(), 0) # Determine scroll area between up and down buttons innerRect=self.rect.copy() innerRect.y=self.rect.y+self.btnUp.rect.height innerRect.height=self.rect.height-self.btnUp.rect.height-self.btnDown.rect.height # Determine at what percentage of this area we clicked indYrel=(self.curScroll-self.minScroll)/(self.maxScroll-self.minScroll) #indY=innerRect.bottom-indYrel*innerRect.height indY=innerRect.top+indYrel*innerRect.height # Draw indicator at the height we clicked. indRect=self.rect.copy() indRect.y=indY-1 indRect.height=3 pygame.draw.rect(self.pyscreen, self.forecolor, indRect.tuple(), 0) # Draw border depending on state of ScrollbarV. if self.borderwidth == 1: if self.state == self.down or self.state == self.hover: pygame.draw.rect(self.pyscreen, self.borderhovercolor, self.rect.tuple(), 1) else: pygame.draw.rect(self.pyscreen, self.bordercolor, self.rect.tuple(), 1) # Call upon up and down buttons to redraw themselves self.btnDown.redraw() self.btnUp.redraw() def handleMouseMove(self, pos): """ Updates state of Scroll Area and Up and Down buttons on hover. """ gpos=GPoint(pos[0],pos[1]) if gpos.inGRect(self.rect): # We are handling this so clear queue for others pygame.event.clear() self.state=self.hover else: self.state=self.normal # Call upon buttons to check if MouseMove (hover) above them self.btnUp.handleMouseMove(pos) self.btnDown.handleMouseMove(pos) def handleMouseUp(self, pos,button): """ Transmits MouseUp to up and down button. """ #If not left mousebutton then nothing to do if not button == 1: return # Call upon buttons to check for MouseUp above them self.btnUp.handleMouseUp(pos,button) self.btnDown.handleMouseUp(pos, button) def handleMouseDown(self,pos,button): """ Updates state of Scroll Area and Up and Down buttons on mousedown. """ #If not left mousebutton then nothing to do if not button == 1: return #Check if in rect gpos=GPoint(pos[0],pos[1]) if not gpos.inGRect(self.rect): return # We are handling this so clear queue for others pygame.event.clear() # Set state if clicked within area self.state=self.down # Determine scroll area between up and down buttons innerRect = self.rect.copy() innerRect.y = self.rect.y + self.btnUp.rect.height innerRect.height = self.rect.height - self.btnUp.rect.height - self.btnDown.rect.height # Check if mouseclick inside scroll area if gpos.inGRect(innerRect): # Determine at what screen position the scroll indicator is. indYrel = (self.curScroll - self.minScroll) / (self.maxScroll - self.minScroll) indY = innerRect.top + indYrel * innerRect.height # If clicked above indicator we scroll up and vice versa if gpos.y > indY: self.scrollDown(True) if gpos.y < indY: self.scrollUp(True) # Call upon buttons to check for MouseDown above them self.btnUp.handleMouseDown(pos, button) self.btnDown.handleMouseDown(pos, button) def handleKeyDown(self,key,unicode): return ######################################################################################################################## ## Class ListBox ######################################################################################################################## class ListBox(): items=[] rect = GRect(0, 0, 80, 32) margins = GRect(4, 4, 4, 4) bordercolor = (128, 128, 128) backcolor = defEditorBackground textcolor = defEditorForeground highbackcolor = defHighSelectBackground hightextcolor = defHighSelectForeground font=None fontname = "Consolas" fontsize = 16 activeItem=-1 rowheight=0 spacing=4 offset=0 visible=True def __init__(self, pyscreen, rect=GRect(100, 40, 80, 80), items=None,fontname=defFontName, fontsize=defFontSize,func_on_click=None): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.rect=rect self.items=items self.font = pygame.font.SysFont(fontname, fontsize) text_width, text_height = self.font.size("M[].j") self.rowheight=text_height self.items=items if not func_on_click==None: self.func_on_click=func_on_click # Resize ListBox so the height is a full number of rows self.nritems = int(self.rect.height / (self.rowheight + self.spacing)) self.rect.height=self.nritems*(self.rowheight + self.spacing) # Add scrollbar self.scrollbarV=ScrollBarV(pyscreen,func_on_click=self.scrollItems) def setItems(self, items): """ Update items in ListBox """ self.items=items self.offset=0 self.activeItem=-1 def items(self): """ Return items in ListBox """ return self.items def scrollItems(self,curScroll): """ Sets first listitem to show ListBox to curScroll""" self.offset=curScroll def redraw(self): """ Redraws ListBox. """ # If not visible nothing to do. if not self.visible: return # Draw background and border pygame.draw.rect(self.pyscreen, self.backcolor, self.rect.tuple(), 0) pygame.draw.rect(self.pyscreen, self.bordercolor, self.rect.tuple(),1) # If no items to display nothing to do. if self.items==None: return #set Scrollbar so if listbox is moved or listitems are added/removed, the scrollbar is still correct. # Resize ListBox so the height is a full number of rows self.nritems = int(self.rect.height / (self.rowheight + self.spacing)) self.rect.height = self.nritems * (self.rowheight + self.spacing) # Draw all items beginning with with offset determined by curScroll of scrollbarV for row in range(0,self.nritems): idx=row+self.offset if idx<len(self.items): item=self.items[idx] rowtop = self.rect.y + self.margins.y + row * (self.rowheight + self.spacing) if idx==self.activeItem: pygame.draw.rect(self.pyscreen, self.highbackcolor,(self.rect.x+self.margins.x, rowtop-int(self.spacing/2), self.rect.width-self.margins.x-self.margins.width, self.rowheight), 0) textsurface = self.font.render(item, True, self.hightextcolor) else: textsurface = self.font.render(item, True, self.textcolor) self.pyscreen.blit(textsurface, (self.rect.x + self.margins.x, self.rect.y + self.margins.y+row*(self.rowheight+self.spacing)),(0,0,self.rect.width,self.rowheight+self.spacing)) # Position scrollbar and call upon scrollbarV to redraw itself scrollRect = self.rect.copy() scrollRect.width = self.fontsize scrollRect.x = self.rect.right - scrollRect.width self.scrollbarV.rect=scrollRect self.scrollbarV.maxScroll = len(self.items)-self.nritems self.scrollbarV.largeScroll = self.nritems self.scrollbarV.visible = True if (len(self.items) > self.nritems) else False self.scrollbarV.redraw() def activeText(self): """ Returns the selected item in ListBox. """ try: return self.items[self.activeItem] except: print ("Error from ListBox.activeText()") return "" def handleMouseDown(self,pos,button): """ Handles scroll with mousewheel and mousedown on item in ListBox. """ # Check if we clicked inside the area where the items are displayed gpos=GPoint.fromTuple(pos) innerRect=self.rect.copy() if self.scrollbarV.visible: innerRect.width=innerRect.width-self.scrollbarV.rect.width if gpos.inGRect(innerRect): # Mousedown on item, store it in activeItem if button == 1: rely=pos[1]-self.rect.y self.activeItem=self.offset+ int((rely-self.margins.y)/(self.rowheight+self.spacing)) #print ("down on: ", self.activeItem,self.activeText()) # Mousewheel UP, so scroll up by setting offset (first item to be displayed) if button==4: # mousewheel up self.offset=self.offset-1 if self.offset<0: self.offset=0 self.scrollbarV.curScroll = self.offset # Tell scrollBarV our new position. # Mousewheel Down, so scroll down by setting offset (first item to be displayed) if button==5: self.offset = self.offset + 1 if self.offset>(len(self.items)-self.nritems): self.offset=len(self.items)-self.nritems self.scrollbarV.curScroll = self.offset # Tell scrollBarV our new position. # We are handling this so clear queue for others pygame.event.clear() # Else ask to check if clicked on scrollbarV else: self.scrollbarV.handleMouseDown(pos,button) def handleMouseUp(self,pos,button): """ Calls on user function if clicked.""" # Check if we clicked with left mouse button if not button == 1: return # Check if we clicked inside the area where the items are displayed gpos=GPoint.fromTuple(pos) innerRect=self.rect.copy() if self.scrollbarV.visible: innerRect.width=innerRect.width-self.scrollbarV.rect.width if gpos.inGRect(innerRect): # We are handling this so clear queue for others pygame.event.clear() if not self.func_on_click==None: self.func_on_click(self.activeText()) else: # Else ask to check if clicked on scrollbarV self.scrollbarV.handleMouseUp(pos,button) def handleMouseMove(self,pos): """ Calls upon scrollbarV to handle MouseMove if needed """ self.scrollbarV.handleMouseMove(pos) return def handleKeyDown(self,key,unicode): return ######################################################################################################################## ## Class Combobox ######################################################################################################################## class Combobox(): items=[] rect = GRect(0, 0, 80, 32) margins = GRect(4, 4, 4, 4) font=None fontname = "Consolas" fontsize = 16 activeItem=-1 rowheight=0 spacing=4 offset=0 visible=True text="" #stores text if clicked func_on_click=None def __init__(self, pyscreen, rect=GRect(100, 40, 80, 80), items=None,defitemnr=0,fontname=defFontName, fontsize=defFontSize,func_on_click=None): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.rect=rect self.items=items self.font = pygame.font.SysFont(fontname, fontsize) text_width, text_height = self.font.size("M[].j") self.rowheight=text_height self.items=items if not func_on_click==None: self.func_on_click=func_on_click #Combobox is made up of Label, Button to show Listbox and Listbox itself fname=fontname fsize=fontsize # Calc positions labelRect=GRect(rect.left,rect.top,rect.width-rect.height,rect.height) buttonRect=GRect(rect.right-rect.height,rect.top,rect.height,rect.height) listboxRect=GRect(rect.left,rect.bottom,rect.width,120) # Add Label defitem=items[defitemnr] self.label=Label(pyscreen,rect=labelRect,text=defitem,fontname=fname,fontsize=fsize,autowrap=False) self.label.backcolor=defEditorBackground self.label.textcolor=defEditorForeground self.label.bordercolor=defBorder self.label.borderwidt=1 self.label.drawBorder=True labelRect=self.label.rect # Label will resize itself in height self.label.rect.width=rect.width-self.label.rect.height buttonRect.height=self.label.rect.height buttonRect.left=self.label.rect.right buttonRect.width=self.label.rect.height listboxRect.top=labelRect.bottom # Add Button self.button=Button(pyscreen,rect=buttonRect,text="V",fontname=fname,fontsize=fsize,func_on_click=self.buttonClick) # Add Listbox self.listbox=ListBox(pyscreen,rect=listboxRect,items=items,fontname=fname,fontsize=fsize,func_on_click=self.listClick) # Initially combobox is not expanded / listbox is not visible self.listbox.visible=False """ def reposControls(self): #called after winrect is moved # Recalculates all positions after moving dialog box. rect=self.rect #buttons is as wide as height labelRect=GRect(rect.left,rect.top,rect.width-rect.height,rect.height) buttonRect=GRect(rect.right-rect.height,rect.top,rect.height,rect.height) listboxRect=GRect(rect.left,rect.bottom,rect.width,40) self.button.rect=buttonRect self.label.rect=labelRect #todo: listbox below label, but this should depend on room self.listbox.rect=listboxRect """ def redraw(self): self.label.redraw() self.button.redraw() self.listbox.redraw() def listClick(self,clickedtext): print (clickedtext,self.listbox.activeText()) self.label.setText(clickedtext) self.listbox.visible=False self.text=clickedtext if not self.func_on_click==None: self.func_on_click(clickedtext) def buttonClick(self): self.listbox.visible= not self.listbox.visible def handleMouseUp(self,pos,button): """ Passes event to children. """ if not self.visible: return # Pass event to children self.listbox.handleMouseUp(pos,button) self.button.handleMouseUp(pos,button) self.label.handleMouseUp(pos,button) def handleMouseDown(self,pos,button): """ Passes event to children. """ if not self.visible: return # Pass event to children self.listbox.handleMouseDown(pos,button) self.button.handleMouseDown(pos,button) self.label.handleMouseDown(pos,button) def handleMouseMove(self,pos): """ Passes event to children. """ if not self.visible: return # Pass event to children self.listbox.handleMouseMove(pos) self.button.handleMouseMove(pos) self.label.handleMouseMove(pos) def handleKeyDown(self,key,unicode): """ Passes event to children. """ if not self.visible: return # Pass event to children self.listbox.handleKeyDown(key,unicode) self.button.handleKeyDown(key,unicode) self.label.handleKeyDown(key,unicode) ######################################################################################################################## ## Class Label ######################################################################################################################## class Label(): rect=GRect(0, 0, 80, 32) margin = GRect(4, 4, 4, 4) innerRect=GRect() bordercolor = (128, 128, 128) backcolor = defFormBackground textcolor = defFormForeground borderwidth = 1 autoheight=True text = ["text"] font=None fontname = "Consolas" fontsize = 16 drawBorder=False visible=True center=False autowrap=False istransparent=False def __init__(self, pyscreen, rect=GRect(0, 0, 80, 32), margin=GRect(4, 4, 4, 4), bordercolor=defBorder, backcolor=defFormBackground, textcolor=defFormForeground, borderwidth=1, drawBorder=False,center=False,istransparent=False, text="text", fontname=defFontName, fontsize=defFontSize, autoheight=True, autowrap=False): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.rect = rect self.margin=margin self.center=center self.bordercolor=bordercolor self.backcolor=backcolor self.istransparent=istransparent self.textcolor=textcolor self.borderwidth=borderwidth self.autoheight=autoheight self.autowrap = autowrap self.font = pygame.font.SysFont(fontname, fontsize) self.setText(text) self.drawBorder=drawBorder # Make innerRect (available room for text itself) accounting for margins self.innerRect=rect.copy() self.innerRect.shrink(self.margin) def setText(self,text): """ Wraps received text if needed (autowrap=True. """ # We want to make sure the text fully fits in the TextBox self.innerRect = self.rect.copy() self.innerRect.shrink(self.margin) if self.autowrap: # Split sentences on boundaries user inserted a.k.a. newline code '\n\ lines=text.split("\n") # Make room for all the sentences newlines=[] # For each sentences check if it fits in text area width (innerRect.width) for line in lines: newline="" words=line.split(" ") # print ("["+line+"]") lastIdx=len(words)-1 # print("------") # Add word for word until it overflows innerRect.width for idx,word in enumerate(words): oldline=newline newline=newline+word+" " # Determine width of sentence with new word text_width, text_height = self.font.size(newline) # On overflow, we save current build sentence (oldLine) and begin a new sentence with current word (newLine) if text_width>self.innerRect.width: #todo: check for words which are longer than width, in which case oldline is empty text_width > self.innerRect.width newlines.append(oldline.strip()) newline = word + " " # print("|"+ oldline.strip()+ "|") # Add newline to newlines array newlines.append(newline.strip()) #print("|" + newline.strip() + "|") #print("------") #print ("Total result:") #print (newlines) self.text=newlines else: #If user doesn't want autowrap we just store his/her new text newlines = [text] self.text = newlines text_width, text_height = self.font.size(text) #needed for self.autoheight # If autoheight is set, we recalculate height of label depending on line height and number of sentences if self.autoheight: self.rect.height=len(self.text)*text_height+self.margin.y+self.margin.height def redraw(self): """ Redraws Label. """ # If not visible nothing to do. if not self.visible: return # Determine area the text can be drawn accounting for margins self.innerRect=self.rect.copy() self.innerRect.shrink(self.margin) # If label is not transparent then we draw background if not self.istransparent: pygame.draw.rect(self.pyscreen, self.backcolor, self.rect.tuple(), 0) if self.drawBorder: pygame.draw.rect(self.pyscreen, self.bordercolor, self.rect.tuple(), self.borderwidth) # If user want vertical center we determine the offset (dY) the text should be drawn in the label area dummy, lineHeight = self.font.size(self.text[0]) if self.center: dY=int((self.innerRect.height-len(self.text)*lineHeight)/2) else: dY=0 # Draw line by line and center if use set this to True for row,line in enumerate(self.text): if len(line)>255: line=line[0:255] # really a debug statement textsurface = self.font.render(line, True, self.textcolor) lineWidth, dummy= self.font.size(line) if self.center: dX = int((self.innerRect.width - lineWidth)/ 2) else: dX=0 self.pyscreen.blit(textsurface, (self.innerRect.x+dX, self.innerRect.y + dY+row*lineHeight)) def handleMouseMove(self, pos): return def handleMouseUp(self, pos,button): return def handleMouseDown(self,pos,button): return def handleKeyDown(self,key,unicode): return ######################################################################################################################## ## Class TextBox ######################################################################################################################## class TextBox(): rect=GRect(0, 0, 80, 32) margin = GRect(4, 4, 4, 4) bordercolor = defBorder backcolor = defEditorBackground textcolor = defEditorForeground borderwidth = 1 text = "text" maxlength=10 font=None fontname = defFontName fontsize = defFontSize editable=True cursorActive=False cursorChar=0 drawBorder=True visible=True allSelected=False #Tooltip vars toolTipLabel=None firstHoverTime = 0 firstHoverPos=GPoint(0,0) # We need constants which dictate what the allowes userinput is TEXT = 0 INT=1 FLOAT=2 HEX=3 inputType=TEXT def __init__(self, pyscreen, rect=GRect(0, 0, 80, 32), margin=GRect(4, 4, 4, 4), bordercolor=defBorder, backcolor=defEditorBackground, textcolor=defEditorForeground, borderwidth=1, drawBorder=True, text="text", maxlength=-1, fontname=defFontName, fontsize=defFontSize, editable=True, inputType=TEXT, toolTip="", onEnter=None, linkedData=None): """ Saves all values to internal variables. """ self.pyscreen = pyscreen self.rect = rect self.margin=margin self.bordercolor=bordercolor self.backcolor=backcolor self.textcolor=textcolor self.borderwidth=borderwidth self.text = text if not maxlength==-1: self.maxlength = maxlength else: self.maxlength = 99 self.font = pygame.font.SysFont(fontname, fontsize) self.editable=editable self.inputType=inputType self.drawBorder=drawBorder self.onEnter=onEnter self.linkedData=linkedData # We truncate text if larger than allowed maximum length given by user if len(self.text)>self.maxlength: self.text=self.text[0:self.maxlength] #And that the textbox has enough height to show each letter text_width, text_height = self.font.size("M[].j") if self.rect.height<(text_height+2*self.margin.y): self.rect.height=text_height+2*self.margin.y #And setup tooltip # We need to figure out the correct width correct width of tooltip if not toolTip=="": self.toolTipLabel = Label(pyscreen, rect=GRect(self.rect.right, self.rect.height, 1024, 20), text=toolTip, drawBorder=True, borderwidth=1, backcolor=defHighEditorBackground, fontsize=defFontSize - 2, textcolor=defHighEditorForeground, autowrap=True) max_width=0 for line in toolTip.split("\n"): text_width, text_height = self.toolTipLabel.font.size(line) # needed for self.autoheight if text_width>max_width: max_width=text_width self.toolTipLabel.rect.width=max_width+self.toolTipLabel.margin.x+self.toolTipLabel.margin.width self.toolTipLabel.visible=False def setText(self,text): """ Truncates text if larger than allowed maximum length given by user. """ self.text = text if len(self.text)>self.maxlength: self.text=self.text[0:self.maxlength] def redraw(self): """ Redraws TextBox. """ # If not visible nothing to do. if not self.visible: return # Draw background and border if needed pygame.draw.rect(self.pyscreen, self.backcolor, self.rect.tuple(), 0) if self.drawBorder: pygame.draw.rect(self.pyscreen, self.bordercolor, self.rect.tuple(), self.borderwidth) # Draw text # If all selected we need to draw with hightlighted background if self.allSelected: textsurface = self.font.render(self.text, True, defHighEditorForeground) hrect=GRect(self.rect.x + self.margin.x-1, self.rect.y + self.margin.y, textsurface.get_rect()[2]+1,textsurface.get_rect()[3]) if self.rect.x+self.margin.x+hrect.width>self.rect.right-1: hrect.width=hrect.width-(self.rect.x+self.margin.x+hrect.width-self.rect.right)-1 pygame.draw.rect(self.pyscreen, defHighEditorBackground, hrect.tuple(), 0) # Else with normal background else: textsurface = self.font.render(self.text, True, self.textcolor) # And the text self.pyscreen.blit(textsurface, (self.rect.x + self.margin.x, self.rect.y + self.margin.y),(0,0,self.rect.width-2*self.margin.x,self.rect.height-2*self.margin.y)) # If editing also draw the cursor if self.cursorActive: text_width, text_height = self.font.size(self.text[0:self.cursorChar]) if self.rect.x+self.margin.x+text_width<self.rect.x+self.rect.width: # we don't want to put cursor outside box pygame.draw.rect(self.pyscreen, self.textcolor, (self.rect.x+self.margin.x+text_width,self.rect.y+self.margin.y,2,self.rect.height-2*self.margin.y-1), 0) prevClick=0 def handleMouseUp(self,pos,button): """ Set cursor / edit of TextBox """ # If not left button nothing to do if not button == 1: return # If not left button nothing to do if not self.editable: return # Check if clicked within textbox gpos=GPoint.fromTuple(pos) if gpos.inGRect(self.rect): # Register if we doubleclick and if so set allSelected to True (we want to select all the text) if (time.time()-self.prevClick)<0.3: #print ("Double click") self.allSelected=True else: self.allSelected=False #print("Single click") self.prevClick = time.time() # We are handling this so clear queue for others pygame.event.clear() # Set cursorActive for redraw and handleKeydown self.cursorActive=True # determine where we need to put cursor by iterating over each char relx=pos[0]-self.rect.x rely = pos[1] - self.rect.y self.cursorChar=0 for i in range(0,len(self.text)): text_width, text_height = self.font.size(self.text[0:i]) if relx>(text_width+self.margin.x): self.cursorChar=self.cursorChar+1 else: self.cursorActive = False self.allSelected=False def handleMouseDown(self,pos,button): return def handleMouseMove(self,pos): return def handleToolTips(self,pos): """ Returns label control with tooltip if hovered long enough. """ # if user did not set a tooltip nothing to do if self.toolTipLabel==None: return None #Check if mouse above control, if not exit gpos=GPoint.fromTuple(pos) if not gpos.inGRect(self.rect): self.firstHoverTime=0 self.firstHoverPos=GPoint(0,0) self.toolTipLabel.visible = False return None #Check if first time hovering, if so set timer if self.firstHoverTime==0: self.firstHoverPos=gpos self.firstHoverTime=time.time() #Check if moved since hovering, if so reset counter and exit dif=self.firstHoverPos-gpos if math.sqrt(dif.x*dif.x+dif.y*dif.y)>5: self.firstHoverPos=GPoint(0,0) self.firstHoverTime=0 self.toolTipLabel.visible = False return None #Check if hovered enough time and return tooltip timeHovered=time.time()-self.firstHoverTime if timeHovered>1.5 and not self.toolTipLabel.text=="" : self.toolTipLabel.visible=True self.toolTipLabel.rect.x = gpos.x self.toolTipLabel.rect.y = gpos.y #check if tooltip overflow right edge of pyscreen if self.toolTipLabel.rect.right>self.pyscreen.get_size()[0]: self.toolTipLabel.rect.x=gpos.x-self.toolTipLabel.rect.width return self.toolTipLabel def handleKeyDown(self,key,unicode): """ Handles keypresses and determine which keys are valid for TextBox input type """ # If not editable nothing to do if not self.editable: return # Check if textbox was clicked and in editmode if self.cursorActive: # We are handling this so clear queue for others pygame.event.clear() # If all is selected each key will clear textbox if self.allSelected: self.text = "" self.allSelected = False # Process navigation (left,right) and modify (del, backspace) keys if key == K_BACKSPACE: if self.cursorChar == 0: return self.text = self.text[0:self.cursorChar - 1] + self.text[self.cursorChar:] self.cursorChar = self.cursorChar - 1 if self.cursorChar < 0: self.cursorChar = 0 return if key == K_DELETE: if self.cursorChar==len(self.text): return self.text = self.text[0:self.cursorChar] + self.text[self.cursorChar + 1:] return if key == K_LEFT: self.cursorChar = self.cursorChar - 1 if self.cursorChar < 0: self.cursorChar = 0 return if key == K_RIGHT: self.cursorChar = self.cursorChar + 1 if self.cursorChar > len(self.text): self.cursorChar = len(self.text) return # On enter/return call use function with current text and the (unmodified) metadata we received when initialized if key == K_KP_ENTER or key == K_RETURN: if not self.onEnter == None: self.onEnter(self, self.text,self.linkedData) return# we don't want to add return char (chr 13) to text string # Remap keys of numpad to numerical keys isNumlockOn=(pygame.key.get_mods() & pygame.KMOD_NUM) ==4096 #print ("isNumlockOn: ",isNumlockOn) if isNumlockOn: if key in range(K_KP0,K_KP9+1): key=K_0+(key-K_KP0) if key == K_KP_PERIOD: key = K_PERIOD # Check for valid input depending on input type the user selected when initialized if not self.inputType==self.TEXT and (pygame.key.get_mods() & pygame.KMOD_SHIFT): return #shift (uppercase and specials chars only allowed in text if self.inputType==self.INT and key==K_PERIOD: return #float/period not allowed if int if self.inputType==self.INT or self.inputType==self.FLOAT: if key not in range(K_0,K_COLON) and not key==K_PERIOD: return #only numbers/period allowed for int/float if self.inputType==self.HEX and (key not in range (K_0,K_9) and key not in range(K_a,K_f)): return if self.inputType==self.FLOAT and (key==K_KP_PERIOD or key==K_PERIOD) and "." in self.text:return # only allow one . in a float # Process the input which made it through the validation block above if len(self.text)<self.maxlength: if self.inputType==self.HEX: unicode=unicode.upper() #if hex we want uppercase characters self.text=self.text[0:self.cursorChar]+unicode+self.text[self.cursorChar:] self.cursorChar=self.cursorChar+1