def __init__(self): # set the parent for all windows here, currently none self.topwinparent=None qtgui.QMainWindow.__init__(self) AbstractBeeMaster.__init__(self) settings=qtcore.QSettings("BeeDraw","BeeDraw") # set default config values self.config['username']=settings.value("username").toString() self.config['server']=settings.value("server","localhost").toString() self.config['port'],ok=settings.value("port",8333).toInt() self.config['autolog']=settings.value("autolog",False).toBool() self.config['autosave']=settings.value("autosave",False).toBool() self.config['debug']=settings.value("debug",False).toBool() self.config['maxundo'],ok=settings.value("maxundo",30).toInt() # read tool options from file if needed toolconfigfilename=os.path.join("config","tooloptions.xml") toolconfigfile=qtcore.QFile(toolconfigfilename) if toolconfigfile.exists(): if toolconfigfile.open(qtcore.QIODevice.ReadOnly): parser=BeeToolConfigParser(toolconfigfile) parser.loadToToolBox(self.toolbox) # setup interface according to designer code self.ui=Ui_BeeMasterMdiSpec() self.ui.setupUi(self) self.show() # list to hold drawing windows and lock for it self.curwindow=None self.drawingwindows=[] self.drawingwindowslock=qtcore.QReadWriteLock() self.curpointertype=-1 self.curtoolname=self.toolbox.getCurToolDesc().name self.clipboardimage=None self.clipboardlock=qtcore.QReadWriteLock() self.pointertoolmap={} # set some initial default values for tool pointers self.pointertoolmap[1]="brush" self.pointertoolmap[3]="eraser" # setup foreground and background swatches #self.ui.FGSwatch=FGSwatch(self,replacingwidget=self.ui.FGSwatch) #self.setFGColor(qtgui.QColor(0,0,0)) #self.ui.BGSwatch=BGSwatch(self,replacingwidget=self.ui.BGSwatch) #self.setBGColor(qtgui.QColor(255,255,255)) # vars for dialog windows that there should only be one of each self.layerswindow=BeeLayersWindow(self) # keep track of current ID so each window gets a unique ID self.nextwindowid=0 # setup window with tool options self.tooloptionswindow=ToolOptionsWindow(self) self.tooloptionswindow.updateCurrentTool() self.initializedwindows=True # default foreground color to black and background color to white self.fgcolor=qtgui.QColor(0,0,0) self.bgcolor=qtgui.QColor(255,255,255) # setup window with colors self.palettewindow=PaletteWindow(self) self.toolselectwindow=ToolSelectionWindow(self) self.setCorner(qtcore.Qt.TopLeftCorner,qtcore.Qt.LeftDockWidgetArea) self.setCorner(qtcore.Qt.TopRightCorner,qtcore.Qt.RightDockWidgetArea) self.setCorner(qtcore.Qt.BottomLeftCorner,qtcore.Qt.LeftDockWidgetArea) self.setCorner(qtcore.Qt.BottomRightCorner,qtcore.Qt.RightDockWidgetArea) # by default have the windows docked in the main window self.addDockWidget(qtcore.Qt.LeftDockWidgetArea,self.toolselectwindow) self.addDockWidget(qtcore.Qt.LeftDockWidgetArea,self.palettewindow) self.addDockWidget(qtcore.Qt.RightDockWidgetArea,self.tooloptionswindow) self.addDockWidget(qtcore.Qt.RightDockWidgetArea,self.layerswindow) # restore settings self.restoreGeometry(settings.value("geometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) # set menu settings according to the new restored settings if not self.layerswindow.isVisible(): self.uncheckWindowLayerBox() else: self.checkWindowLayerBox() if not self.palettewindow.isVisible(): self.uncheckWindowPaletteBox() else: self.checkWindowPaletteBox() if not self.toolselectwindow.isVisible(): self.uncheckWindowToolSelectBox() else: self.checkWindowToolSelectBox() if not self.tooloptionswindow.isVisible(): self.uncheckWindowToolOptionsBox() else: self.checkWindowToolOptionsBox()
class BeeMasterWindow(qtgui.QMainWindow,object,AbstractBeeMaster): def __init__(self): # set the parent for all windows here, currently none self.topwinparent=None qtgui.QMainWindow.__init__(self) AbstractBeeMaster.__init__(self) settings=qtcore.QSettings("BeeDraw","BeeDraw") # set default config values self.config['username']=settings.value("username").toString() self.config['server']=settings.value("server","localhost").toString() self.config['port'],ok=settings.value("port",8333).toInt() self.config['autolog']=settings.value("autolog",False).toBool() self.config['autosave']=settings.value("autosave",False).toBool() self.config['debug']=settings.value("debug",False).toBool() self.config['maxundo'],ok=settings.value("maxundo",30).toInt() # read tool options from file if needed toolconfigfilename=os.path.join("config","tooloptions.xml") toolconfigfile=qtcore.QFile(toolconfigfilename) if toolconfigfile.exists(): if toolconfigfile.open(qtcore.QIODevice.ReadOnly): parser=BeeToolConfigParser(toolconfigfile) parser.loadToToolBox(self.toolbox) # setup interface according to designer code self.ui=Ui_BeeMasterMdiSpec() self.ui.setupUi(self) self.show() # list to hold drawing windows and lock for it self.curwindow=None self.drawingwindows=[] self.drawingwindowslock=qtcore.QReadWriteLock() self.curpointertype=-1 self.curtoolname=self.toolbox.getCurToolDesc().name self.clipboardimage=None self.clipboardlock=qtcore.QReadWriteLock() self.pointertoolmap={} # set some initial default values for tool pointers self.pointertoolmap[1]="brush" self.pointertoolmap[3]="eraser" # setup foreground and background swatches #self.ui.FGSwatch=FGSwatch(self,replacingwidget=self.ui.FGSwatch) #self.setFGColor(qtgui.QColor(0,0,0)) #self.ui.BGSwatch=BGSwatch(self,replacingwidget=self.ui.BGSwatch) #self.setBGColor(qtgui.QColor(255,255,255)) # vars for dialog windows that there should only be one of each self.layerswindow=BeeLayersWindow(self) # keep track of current ID so each window gets a unique ID self.nextwindowid=0 # setup window with tool options self.tooloptionswindow=ToolOptionsWindow(self) self.tooloptionswindow.updateCurrentTool() self.initializedwindows=True # default foreground color to black and background color to white self.fgcolor=qtgui.QColor(0,0,0) self.bgcolor=qtgui.QColor(255,255,255) # setup window with colors self.palettewindow=PaletteWindow(self) self.toolselectwindow=ToolSelectionWindow(self) self.setCorner(qtcore.Qt.TopLeftCorner,qtcore.Qt.LeftDockWidgetArea) self.setCorner(qtcore.Qt.TopRightCorner,qtcore.Qt.RightDockWidgetArea) self.setCorner(qtcore.Qt.BottomLeftCorner,qtcore.Qt.LeftDockWidgetArea) self.setCorner(qtcore.Qt.BottomRightCorner,qtcore.Qt.RightDockWidgetArea) # by default have the windows docked in the main window self.addDockWidget(qtcore.Qt.LeftDockWidgetArea,self.toolselectwindow) self.addDockWidget(qtcore.Qt.LeftDockWidgetArea,self.palettewindow) self.addDockWidget(qtcore.Qt.RightDockWidgetArea,self.tooloptionswindow) self.addDockWidget(qtcore.Qt.RightDockWidgetArea,self.layerswindow) # restore settings self.restoreGeometry(settings.value("geometry").toByteArray()) self.restoreState(settings.value("windowState").toByteArray()) # set menu settings according to the new restored settings if not self.layerswindow.isVisible(): self.uncheckWindowLayerBox() else: self.checkWindowLayerBox() if not self.palettewindow.isVisible(): self.uncheckWindowPaletteBox() else: self.checkWindowPaletteBox() if not self.toolselectwindow.isVisible(): self.uncheckWindowToolSelectBox() else: self.checkWindowToolSelectBox() if not self.tooloptionswindow.isVisible(): self.uncheckWindowToolOptionsBox() else: self.checkWindowToolOptionsBox() # Palette Menu Actions: def on_Palette_Configure_triggered(self,accept=True): if not accept: return self.palettewindow.on_Palette_Configure_triggered() def on_Palette_save_triggered(self,accept=True): if not accept: return self.palettewindow.on_Palette_save_triggered() def on_Palette_save_default_triggered(self,accept=True): if not accept: return self.palettewindow.on_Palette_save_default_triggered() def on_Palette_load_default_triggered(self,accept=True): if not accept: return self.palettewindow.on_Palette_load_default_triggered() def on_Palette_load_triggered(self,accept=True): if not accept: return self.palettewindow.on_Palette_load_triggered def keyEvent(self,event): if event.key() in (qtcore.Qt.Key_Shift,qtcore.Qt.Key_Control,qtcore.Qt.Key_Alt,qtcore.Qt.Key_Meta): self.newModKeys(event.modifiers()) def keyReleaseEvent(self,event): self.keyEvent(event) def keyPressEvent(self,event): self.keyEvent(event) curwin=self.getCurWindow() if curwin: curwin.keyPressEvent(event) def getCurWindow(self,lock=None): if not lock: lock=qtcore.QReadLocker(self.drawingwindowslock) return self.curwindow def setCurWindow(self,window,lock=None): if not lock: lock=qtcore.QWriteLocker(self.drawingwindowslock) if self.curwindow!=window: self.curwindow=window self.refreshLayersList(winlock=lock) def setClipBoardImage(self,image): lock=qtcore.QWriteLocker(self.clipboardlock) self.clipboardimage=image def getClipBoardImage(self): lock=qtcore.QReadLocker(self.clipboardlock) if self.clipboardimage: return self.clipboardimage.copy() return None # I don't think these currently need to have mutexes since they are only manipulated in the gui thread, but if they ever do it can be done here def setFGColor(self,color): self.fgcolor=color self.palettewindow.ui.FGSwatch.updateColor(color) def setBGColor(self,color): self.bgcolor=color self.palettewindow.ui.BGSwatch.updateColor(color) def getFGColor(self): return self.fgcolor def getBGColor(self): return self.bgcolor def isWindowRegistered(self,window,winlock): if not winlock: winlock=qtcore.QReadLocker(self.drawingwindowslock) if window in self.drawingwindows: return True return False def registerWindow(self,window): lock=qtcore.QWriteLocker(self.drawingwindowslock) self.drawingwindows.append(window) self.setCurWindow(window,lock) action=WindowSelectionAction(self,self.ui.menu_Window_Drawing_Windows,window.id) self.ui.menu_Window_Drawing_Windows.addAction(action) window.menufocusaction=action def unregisterWindow(self,window): lock=qtcore.QWriteLocker(self.drawingwindowslock) self.drawingwindows.remove(window) self.ui.menu_Window_Drawing_Windows.removeAction(window.menufocusaction) # if the window we're deleting was the active window if self.curwindow==window: # if there is at least one other window make that the current window if self.drawingwindows: self.curwindow=self.drawingwindows[0] else: self.curwindow=None self.requestLayerListRefresh() #self.ui.mdiArea.removeSubWindow(window) def requestLayerListRefresh(self): event=qtcore.QEvent(BeeCustomEventTypes.refreshlayerslist) BeeApp().app.postEvent(self,event) def getNextWindowId(self): self.nextwindowid+=1 return self.nextwindowid def getWindowById(self,id,lock=None): if not lock: lock=qtcore.QReadLocker(self.drawingwindowslock) for win in self.drawingwindows: if win.id==id: return win print_debug("WARNING: Couldn't find window with ID: %d" % id) return None def getLayerById(self,win_id,layer_id,winlock=None): win=self.getWindowById(win_id) if win: return win.getLayerForKey(layer_id,winlock) else: print_debug("WARNING: can't find layer with id: %d in window: %d" % (layer_id,win_id)) return None def removeWindow(self,window): """remove a drawing window from the list of current windows""" lock=qtcore.QWriteLocker(self.drawingwindowslock) try: self.drawingwindows.remove(window) except: pass if self.curwindow==window: self.curwindow=None def getCurToolInst(self,window): """return new instance of current tool type, with layer set to current layer""" curtool=self.getCurToolDesc() return curtool.setupTool(window,window.getCurLayerKey()) def getCurToolDesc(self): """return pointer to the currently selected tool description object""" return self.toolbox.getCurToolDesc() def changeCurToolByName(self,name): if self.toolbox.setCurToolByName(name): self.curtoolname=name self.tooloptionswindow.updateCurrentTool() self.toolbox.getCurToolDesc().pressToolButton() else: print_debug("Unknown tool name: %s" % name) def newModKeys(self,modkeys): tooldesc=self.toolbox.getCurToolDesc() if tooldesc: tooldesc.newModKeys(modkeys) def pointerTypeCheck(self,pointertype): if pointertype!=self.curpointertype: #print "changing pointer type to:", pointertype self.pointertoolmap[self.curpointertype]=self.curtoolname if pointertype in self.pointertoolmap: self.changeCurToolByName(self.pointertoolmap[pointertype]) self.curpointertype=pointertype def on_tool_changed(self,index): self.toolbox.setCurToolIndex(index) lock=qtcore.QReadLocker(self.drawingwindowslock) for win in self.drawingwindows: win.view.setCursor(self.toolbox.getCurToolDesc().getCursor()) def on_tooloptionsbutton_pressed(self): self.getCurToolDesc().runOptionsDialog(self) def on_Tool_save_default_triggered(self,accept=True): if not accept: return filename=os.path.join("config","tooloptions.xml") outfile=qtcore.QFile(filename,self) if outfile.open(qtcore.QIODevice.Truncate|qtcore.QIODevice.WriteOnly): writer=BeeToolConfigWriter(outfile) writer.startLog() for tool in self.toolbox.toolslist: writer.logToolConfig(tool.name,tool.options) writer.endLog() outfile.close() def on_backgroundbutton_pressed(self): self.ui.BGSwatch.changeColorDialog() def on_foregroundbutton_pressed(self): self.ui.FGSwatch.changeColorDialog() def on_action_File_Play_triggered(self,accept=True): if not accept: return filename=str(qtgui.QFileDialog.getOpenFileName(self,"Select log file to play","","Sketch logfiles (*.slg)")) if filename: self.playFile(filename) def playFile(self,filename): window=self.ui.mdiArea.addSubWindow(AnimationDrawingWindow(self,filename)) result=qtcore.QObject.connect(window,qtcore.SIGNAL("windowStateChanged(Qt::WindowStates,Qt::WindowStates)"),window.widget().mdiWinStateChange) window.widget().setFileName(filename) window.show() def on_action_File_Open_triggered(self,accept=True): if not accept: return formats=getSupportedReadFileFormats() filterstring=qtcore.QString("Images (") for f in formats: filterstring.append(" *.") filterstring.append(f) filterstring.append(" *.bee)") filename=qtgui.QFileDialog.getOpenFileName(self,"Choose File To Open","",filterstring) if filename: self.openFile(filename) def openFile(self,filename): # create a drawing window to start with # if we are saving my custom format if filename.endsWith(".bee"): self.playFile(filename) else: reader=qtgui.QImageReader(filename) image=reader.read() window=self.ui.mdiArea.addSubWindow(BeeDrawingWindow(self,image.width(),image.height(),False,maxundo=self.config["maxundo"])) result=qtcore.QObject.connect(window,qtcore.SIGNAL("windowStateChanged(Qt::WindowStates,Qt::WindowStates)"),window.widget().mdiWinStateChange) window.widget().loadLayer(image) window.widget().setFileName(filename) window.show() def on_action_File_New_triggered(self,accept=True): if not accept: return dialog=qtgui.QDialog(self) dialogui=Ui_canvas_size_dialog() dialogui.setupUi(dialog) dialog.exec_() if dialog.result(): width=dialogui.width_box.value() height=dialogui.height_box.value() widget=BeeDrawingWindow(self,width=width,height=height,maxundo=self.config["maxundo"]) window=self.ui.mdiArea.addSubWindow(widget) result=qtcore.QObject.connect(window,qtcore.SIGNAL("windowStateChanged(Qt::WindowStates,Qt::WindowStates)"),window.widget().mdiWinStateChange) window.show() def on_action_Edit_Configure_triggered(self,accept=True): if not accept: return dialog=qtgui.QDialog(self) dialogui=Ui_BeeMasterOptions() dialogui.setupUi(dialog) # put current values into GUI lock=qtcore.QWriteLocker(self.configlock) dialogui.history_size_box.setValue(self.config['maxundo']) if self.config['debug']: dialogui.debug_checkBox.setCheckState(qtcore.Qt.Checked) if self.config['autolog']: dialogui.autolog_checkBox.setCheckState(qtcore.Qt.Checked) if self.config['autosave']: dialogui.autosave_checkBox.setCheckState(qtcore.Qt.Checked) dialogui.username_entry.setText(self.config['username']) dialogui.server_entry.setText(self.config['server']) dialogui.port_spinBox.setValue(self.config['port']) ok=dialog.exec_() if not ok: return # get values out of GUI self.config['username']=dialogui.username_entry.text() self.config['server']=dialogui.server_entry.text() self.config['maxundo']=dialogui.history_size_box.value() self.config['port']=dialogui.port_spinBox.value() if dialogui.debug_checkBox.isChecked(): self.config['debug']=True else: self.config['debug']=False BeeApp().debug_flags[DebugFlags.allon]=self.config['debug'] if dialogui.autolog_checkBox.isChecked(): self.config['autolog']=True else: self.config['autolog']=False if dialogui.autosave_checkBox.isChecked(): self.config['autosave']=True else: self.config['autosave']=False settings=qtcore.QSettings("BeeDraw","BeeDraw") settings.setValue("username",self.config['username']) settings.setValue("maxundo",self.config['maxundo']) settings.setValue("debug",self.config['debug']) settings.setValue("autolog",self.config['autolog']) settings.setValue("autosave",self.config['autosave']) settings.setValue("server",self.config['server']) settings.setValue("port",self.config['port']) def on_action_Help_About_triggered(self,accept=True): if not accept: return dialog=qtgui.QDialog(self) dialogui=Ui_About_Dialog() dialogui.setupUi(dialog) dialog.exec_() def on_action_File_Connect_triggered(self,accept=True): if not accept: return # launch dialog dialog=qtgui.QDialog(self) dialogui=Ui_ConnectionInfoDialog() dialogui.setupUi(dialog) # set default options dialogui.usernamefield.setText(self.getConfigOption('username',"")) ok=dialog.exec_() if not ok: return hostname=dialogui.hostnamefield.text() port=dialogui.portbox.value() username=dialogui.usernamefield.text() password=dialogui.passwordfield.text() # error if username is blank if username=="": qtgui.QMessageBox.warning(self,"No username","You must enter a username") return socket=self.getServerConnection(username,password,hostname,port) if socket: window=self.ui.mdiArea.addSubWindow(NetworkClientDrawingWindow(self,socket,maxundo=self.config["maxundo"])) result=qtcore.QObject.connect(window,qtcore.SIGNAL("windowStateChanged(Qt::WindowStates,Qt::WindowStates)"),window.widget().mdiWinStateChange) window.show() # connect to host and authticate def getServerConnection(self,username,password,host,port): #socket=BeeSocket(BeeSocketTypes.qt,qtnet.QTcpSocket()) socket=BeeSocket(BeeSocketTypes.python,pysocket()) print_debug("waiting for socket connection:") connected=socket.connect(host,port) print_debug("finished waiting for socket connection") # return error if we couldn't get a connection after 30 seconds if not connected: qtgui.QMessageBox.warning(self,"Failed to connect to server",socket.errorString()) return None #authrequest=qtcore.QByteArray() authrequest="%s\n%s\n%d\n" % (username,password,PROTOCOL_VERSION) # send authtication info socket.write(authrequest) responsestring=qtcore.QString() # wait for response while responsestring.count('\n')<2 and len(responsestring)<500: data=socket.read(512) if data: print_debug("got authentication answer: %s" % qtcore.QString(data)) responsestring.append(data) # if error exit else: qtgui.QMessageBox.warning(self,"Connection Error","server dropped connection") return None # if we get here we have a response that probably wasn't a disconnect responselist=responsestring.split('\n') if len(responselist)>1: answer="%s" % responselist[0] message="%s" % responselist[1] else: answer="Failure" message="Unknown Status" if answer=="Success": return socket socket.disconnect() qtgui.QMessageBox.warning(self,"Server Refused Connection",message) return None def uncheckWindowLayerBox(self): self.ui.Window_Layers.setChecked(False) def checkWindowLayerBox(self): self.ui.Window_Layers.setChecked(True) def on_Window_Layers_triggered(self,state=None): if state==None: return if state: self.layerswindow.show() self.refreshLayersList() else: self.layerswindow.hide() def uncheckWindowPaletteBox(self): self.ui.Window_Palette.setChecked(False) def checkWindowPaletteBox(self): self.ui.Window_Palette.setChecked(True) def on_Window_Palette_triggered(self,state=None): if state==None: return if state: self.palettewindow.show() else: self.palettewindow.hide() def uncheckWindowToolOptionsBox(self): self.ui.Window_Tool_Options.setChecked(False) def checkWindowToolOptionsBox(self): self.ui.Window_Tool_Options.setChecked(True) def on_Window_Tool_Options_triggered(self,state=None): if state==None: return if state: self.tooloptionswindow.show() else: self.tooloptionswindow.hide() def uncheckWindowToolSelectBox(self): self.ui.Window_Tool_Selection.setChecked(False) def checkWindowToolSelectBox(self): self.ui.Window_Tool_Selection.setChecked(True) def on_Window_Tool_Selection_triggered(self,state=None): if state==None: return if state: self.toolselectwindow.show() else: self.toolselectwindow.hide() # destroy all subwindows def cleanUp(self): BeeApp().app.closingState() # copy list of windows otherwise destroying the windows as we iterate through will skip some lock=qtcore.QWriteLocker(self.drawingwindowslock) tmplist=self.drawingwindows[:] # sending close will cause the windows to remove themselves from the window list for window in tmplist: window.close() self.layerswindow.close() self.layerswindow=None self.palettewindow.close() self.palettewindow=None self.tooloptionswindow.close() self.tooloptionswindow=None def closeEvent(self,event): result=qtgui.QMessageBox.question(self,"Ready to Quit?","Are you sure you'd like to exit the whole application?\nAll unsaved changes will be lost.",qtgui.QMessageBox.Ok,qtgui.QMessageBox.Cancel) if result==qtgui.QMessageBox.Ok: settings=qtcore.QSettings("BeeDraw","BeeDraw") settings.setValue("geometry",self.saveGeometry()) settings.setValue("windowState",self.saveState()) self.cleanUp() qtgui.QMainWindow.closeEvent(self,event) else: event.ignore() def refreshLayersList(self,winlock=None,layerslock=None): if not winlock: winlock=qtcore.QReadLocker(self.drawingwindowslock) if self.layerswindow: if self.curwindow: if not layerslock: layerslock=qtcore.QReadLocker(self.curwindow.layerslistlock) self.layerswindow.refreshLayersList(self.curwindow,self.curwindow.curlayerkey,winlock,layerslock) else: self.layerswindow.refreshLayersList(None,None) # function for a window to take the focus from other windows def takeFocus(self,window): self.setCurWindow(window) def updateLayerHighlight(self,win,key,lock=None): if self.layerswindow and key: self.layerswindow.refreshLayerHighlight(win,key,lock) # refresh thumbnail of layer with inidcated key def refreshLayerThumb(self,windowid,key=None): if self.curwindow and self.curwindow.id==windowid: self.layerswindow.refreshLayerThumb(key) # handle the custom event I created to trigger refreshing the list of layers def customEvent(self,event): if event.type()==BeeCustomEventTypes.refreshlayerslist: self.refreshLayersList() elif event.type()==BeeCustomEventTypes.displaymessage: self.displayMessage(event.boxtype,event.title,event.message) def displayMessage(self,boxtype,title,message): if boxtype==BeeDisplayMessageTypes.warning: qtgui.QMessageBox.warning(self,title,message) elif boxtype==BeeDisplayMessageTypes.error: qtgui.QMessageBox.critical(self,title,message) else: print_debug("ERROR unknown box type in displayMessage")