def paintEvent(self, event): layer = BeeApp().master.getLayerById(self.windowid, self.layerkey) # just to make sure nothing goes wrong if not layer: return window = BeeApp().master.getWindowById(self.windowid) # get how much we need to scale down both dimensions maximagedimension = max(layer.image.width(), layer.image.height()) if maximagedimension == 0: return scalefactor = self.width() / float(maximagedimension) # get dimensions of the image if we keep the aspect ratio and put it in the preview widget scalewidth = layer.image.width() * scalefactor scaleheight = layer.image.height() * scalefactor xoffset = (self.width() - scalewidth) / 2 yoffset = (self.height() - scaleheight) / 2 scaledimage = qtcore.QRectF(xoffset, yoffset, scalewidth, scaleheight) backdrop = qtgui.QImage(scalewidth, scaleheight, qtgui.QImage.Format_ARGB32_Premultiplied) backdrop.fill(window.backdropcolor) painter = qtgui.QPainter() painter.begin(self) painter.drawImage(scaledimage, backdrop) # have preview reflect the opacity of the layer painter.setOpacity(layer.getOpacity()) painter.drawImage(scaledimage, layer.image, qtcore.QRectF(layer.image.rect())) painter.end()
def cut(self, path, imagelock=None): pathrectf = path.boundingRect() pathrect = pathrectf.toAlignedRect() if not imagelock: imagelock = qtcore.QWriteLocker(self.imagelock) oldareaimage = self.getImageCopy(imagelock, pathrect) win = BeeApp().master.getWindowById(self.windowid) if win.ownedByMe(self.owner): self.copy(path, imagelock) # erase from image painter = qtgui.QPainter() painter.begin(self.image) painter.setClipPath(path) painter.setCompositionMode(qtgui.QPainter.CompositionMode_Clear) painter.drawImage(self.image.rect(), self.image) painter.end() imagelock.unlock() win.view.updateView(pathrectf) BeeApp().master.refreshLayerThumb(self.windowid, self.key) command = CutCommand(self.key, oldareaimage, path) win = BeeApp().master.getWindowById(self.windowid) win.addCommandToHistory(command, self.owner)
def on_visibility_box_toggled(self, state): layer = BeeApp().master.getLayerById(self.windowid, self.layerkey) window = layer.getWindow() # change visibility layer.setVisible(state) # recomposite whole image window.reCompositeImage()
def compositeFromCorner(self, image, x, y, compmode, clippath=None, lock=None, refreshimage=True, opacity=1): """ Method of BeeTemporaryLayerPIL """ x = int(x) y = int(y) # print "compositing image onto pil temp layer:" # printPILImage(image) # print "calling compositeFromCorner with args:",x,y if not lock: lock = qtcore.QWriteLocker(self.imagelock) width, height = image.size rect = qtcore.QRect(x, y, width, height) # self.pilimage.paste(image,box=(x,y),mask=image) PILcomposite(self.pilimage, image, x, y, ImageCombineTypes.lightest) # updatedsection=self.pilimage.crop((x,y,x+image.size[0],y+image.size[1])) # print "new section looks like:", x,y,image.size # printPILImage(updatedsection) dirtyregion = qtgui.QRegion(rect) win = BeeApp().master.getWindowById(self.windowid) sizelock = qtcore.QReadLocker(win.docsizelock) # not every type of window actually has a full image representation so just calculate what the image rectangle would be imagerect = qtcore.QRect(0, 0, win.docwidth, win.docheight) if refreshimage: dirtyregion = dirtyregion.intersect(qtgui.QRegion(imagerect)) lock.unlock() win.reCompositeImage(dirtyregion.boundingRect())
def on_network_control_button_pressed(self): layer = BeeApp().master.getLayerById(self.windowid, self.layerkey) win = layer.getWindow() proplock = qtcore.QReadLocker(layer.propertieslock) if layer.type == LayerTypes.floating: parent = layer.layerparent parent.anchor(layer) # the layer is owned locally so change it to be owned by no one elif win.ownedByMe(layer.owner): # print_debug("adding give up layer to queue for layer key: %d" % layer.key) if len(layer.childItems()): result = qtgui.QMessageBox.warning( win, "Floating layers can not be given up", "You are attempting to give up ownership of layer that has floting layers, if you continue the floating layers will be destroyed. To avoid having them destroyed please anchor them or move them to other layers.", "Continue", "Cancel", ) if result: return win.addGiveUpLayerToQueue(layer.key) # if the layer is owned by nobody then request it elif win.ownedByNobody(layer.owner): win.addRequestLayerToQueue(layer.key)
def compositeFromCorner(self,image,x,y,compmode,clippath=None,lock=None,refreshimage=True,opacity=1): x=int(x) y=int(y) #print "calling compositeFromCorner with args:",x,y if not lock: lock=qtcore.QWriteLocker(self.imagelock) width=image.size().width() height=image.size().height() rect=qtcore.QRect(x,y,width,height) painter=qtgui.QPainter() painter.begin(self.image) if clippath: painter.setClipPath(clippath) #print "inside compositeFromCorner" painter.setCompositionMode(compmode) painter.setOpacity(opacity) #painter.setRenderHint(qtgui.QPainter.HighQualityAntialiasing) painter.drawImage(rect,image) painter.end() dirtyregion=qtgui.QRegion(rect) win=BeeApp().master.getWindowById(self.windowid) sizelock=qtcore.QReadLocker(win.docsizelock) # not every type of window actually has a full image representation so just calculate what the image rectangle would be imagerect=qtcore.QRect(0,0,win.docwidth,win.docheight) if refreshimage: dirtyregion=dirtyregion.intersect(qtgui.QRegion(imagerect)) lock.unlock() win.reCompositeImage(dirtyregion.boundingRect())
def paste(self, image, x, y): win = BeeApp().master.getWindowById(self.windowid) newkey = win.nextFloatingLayerKey() newlayer = FloatingSelection(image, newkey, self, self.windowid) newlayer.setPos(qtcore.QPointF(x, y)) BeeApp().master.requestLayerListRefresh() return newkey
def cursorMoveEvent(self,x,y,modkeys,pointertype=4,pressure=1,subx=0,suby=0): window=BeeApp().master.getWindowById(self.windowid) x=x+subx y=y+suby x,y=self.viewCoordsToImage(x,y) #print "cursor move with pressure:", pressure #print "translates to image coords:",x,y window.penMotion(x,y,pressure,modkeys)
def on_blend_mode_box_activated(self, value): # we only want the event with the string if not type(value) is qtcore.QString: return newmode = BlendTranslations.nameToMode(value) if newmode != None: layer = BeeApp().master.getLayerById(self.windowid, self.layerkey) if layer: win = layer.getWindow() win.addBlendModeChangeToQueue(layer.key, newmode)
def cursorPressEvent(self,x,y,modkeys,pointertype=4,pressure=1,subx=0,suby=0): window=BeeApp().master.getWindowById(self.windowid) # if the window has no layers in it's layers list then just return if not window.layers: return self.setCursor(BeeApp().master.getCurToolDesc().getDownCursor()) x=x+subx y=y+suby x,y=self.viewCoordsToImage(x,y) window.penDown(x,y,pressure,modkeys)
def anchor(self,child): win=BeeApp().master.getWindowById(self.windowid) win.addAnchorToQueue(self.key,child) win.scene.removeItem(child) newactive=win.setValidActiveLayer() if newactive: win.master.updateLayerHighlight(win,newactive) win.requestLayerListRefresh()
def __init__(self,queue,windowid,type=ThreadTypes.user,master=None): qtcore.QThread.__init__(self) self.queue=queue self.windowid=windowid self.type=type if not master: self.master=BeeApp().master else: self.master=master # this will be keyed on a layer key, value will be the tool # object so it retains information throughout the stroke self.inprocesstools={}
# Beedraw/Hive network capable client and server allowing collaboration on a single image # Copyright (C) 2009 Thomas Becker # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from beemaster import BeeMasterWindow from beeapp import BeeApp import sys import PyQt4.QtGui as qtgui if __name__ == "__main__": beeapp = BeeApp(sys.argv) #app = qtgui.QApplication(sys.argv) #beeapp.app=app beeapp.master = BeeMasterWindow() #app.setMainWidget(beeMasterWindow.window) beeapp.app.exec_()
class DrawingThread(qtcore.QThread): def __init__(self,queue,windowid,type=ThreadTypes.user,master=None): qtcore.QThread.__init__(self) self.queue=queue self.windowid=windowid self.type=type if not master: self.master=BeeApp().master else: self.master=master # this will be keyed on a layer key, value will be the tool # object so it retains information throughout the stroke self.inprocesstools={} #print "starting thread with type:", type def addExitEventToQueue(self): self.queue.put((DrawingCommandTypes.quit,)) def run(self): self.windowtype=self.master.getWindowById(self.windowid).type while 1: #print "Drawing thread ready to get commands from queue:", self.queue command=self.queue.get() #print "got command from queue:", command, self.type type=command[0] if type==DrawingCommandTypes.quit: return elif type==DrawingCommandTypes.history: self.processHistoryCommand(command) elif type==DrawingCommandTypes.layer: self.processLayerCommand(command) elif type==DrawingCommandTypes.alllayer: if self.type==ThreadTypes.user and self.windowtype==WindowTypes.networkclient: self.requestAllLayerCommand(command) else: self.processAllLayerCommand(command) elif type==DrawingCommandTypes.networkcontrol: self.processNetworkCommand(command) elif type==DrawingCommandTypes.localonly: self.processLocalOnlyCommand(command) def processLocalOnlyCommand(self,command): subtype=command[1] window=self.master.getWindowById(self.windowid) if subtype==LocalOnlyCommandTypes.selection: selectionop=command[2] newpath=command[3] window.changeSelection(selectionop,newpath) elif subtype==LocalOnlyCommandTypes.floatingmove: layerkey=command[2] oldparentkey=command[3] newparentkey=command[4] historycommand=FloatingChangeParentCommand(layerkey,oldparentkey,newparentkey) window.addCommandToHistory(historycommand,-1) def processHistoryCommand(self,command): window=self.master.getWindowById(self.windowid) subtype=command[1] undotype=UndoCommandTypes.none if subtype==HistoryCommandTypes.undo: undotype=window.undo(command[2]) elif subtype==HistoryCommandTypes.redo: undotype=window.redo(command[2]) else: print_debug("unknown processHistoryCommand subtype: %d" % subtype) if undotype==UndoCommandTypes.remote or undotype==UndoCommandTypes.notinnetwork: window.logCommand(command,self.type) def processLayerCommand(self,command): window=self.master.getWindowById(self.windowid) subtype=command[1] layerkey=command[2] layer=window.getLayerForKey(layerkey) if not layer: print_debug("ERROR: Can't process Layer command: %s Layer not found" % str(command)) return if subtype==LayerCommandTypes.alpha: layer.changeOpacity(command[3]) window.scene.update() elif subtype==LayerCommandTypes.alphadone: self.master.refreshLayerThumb(self.windowid,layerkey) opacity=layer.getOpacity() loggingcommand=(DrawingCommandTypes.layer,LayerCommandTypes.alpha,layerkey,opacity) window.logCommand(loggingcommand,self.type) elif subtype==LayerCommandTypes.mode: layer.setOptions(compmode=command[3]) layer.update() window.logCommand(command,self.type) elif subtype==LayerCommandTypes.cut: selection=command[3] rect=selection.boundingRect().toAlignedRect() imagelock=qtcore.QWriteLocker(layer.imagelock) layer.cut(selection,imagelock=imagelock) newimage=layer.getImageCopy(lock=imagelock,subregion=rect) rawcommand=(DrawingCommandTypes.layer,LayerCommandTypes.rawevent,layerkey,rect.x(),rect.y(),newimage,None,qtgui.QPainter.CompositionMode_Source) window.logCommand(rawcommand,self.type) elif subtype==LayerCommandTypes.copy: selection=command[3] layer.copy(selection) elif subtype==LayerCommandTypes.paste: image=self.master.getClipBoardImage() window=self.master.getWindowById(self.windowid) if image: x=command[3] y=command[4] newkey=layer.paste(image,x,y) oldpath,newpath=window.changeSelection(SelectionModTypes.clear,history=False) historycommand=PasteCommand(newkey,oldpath,newpath) window.addCommandToHistory(historycommand,layer.owner) elif subtype==LayerCommandTypes.pendown: x=command[3] y=command[4] pressure=command[5] tool=command[6] # make sure we can find the layer and either it's a locally owned layer or a source that can draw on non-local layers if window.ownedByMe(layer.owner) or self.type!=ThreadTypes.user or ( layer.type==LayerTypes.floating and layer.owner<0 and tool.allowedonfloating ): self.inprocesstools[int(layerkey)]=tool tool.penDown(x,y,pressure) else: print_debug("WARNING: no valid layer selected, remote id: %d" % window.remoteid) elif subtype==LayerCommandTypes.penmotion: #print "Pen motion event:", command x=command[3] y=command[4] pressure=command[5] #print "drawing thread pen motion (x,y,pressure)", x,y,pressure if int(layerkey) in self.inprocesstools: tool=self.inprocesstools[int(layerkey)] tool.penMotion(x,y,pressure) elif subtype==LayerCommandTypes.penup: x=command[3] y=command[4] if int(layerkey) in self.inprocesstools: tool=self.inprocesstools[int(layerkey)] tool.penUp(x,y) # send to server and log file if needed window.logStroke(tool,int(layerkey),self.type) tool.cleanUp() del self.inprocesstools[int(layerkey)] elif subtype==LayerCommandTypes.penenter: if int(layerkey) in self.inprocesstools: self.inprocesstools[int(layerkey)].penEnter() elif subtype==LayerCommandTypes.penleave: if int(layerkey) in self.inprocesstools: self.inprocesstools[int(layerkey)].penLeave() elif subtype==LayerCommandTypes.rawevent or subtype==LayerCommandTypes.anchor: x=command[3] y=command[4] image=command[5] clippath=command[6] compmode=command[7] layerimagelock=qtcore.QWriteLocker(layer.imagelock) # determine bounding area for event dirtyrect=qtcore.QRect(x,y,image.width(),image.height()) dirtyrect=rectIntersectBoundingRect(dirtyrect,layer.image.rect()) if clippath: dirtyrect=rectIntersectBoundingRect(dirtyrect,clippath.boundingRect().toAlignedRect()) # set up history event oldimage=layer.image.copy(dirtyrect) layer.compositeFromCorner(image,x,y,compmode,clippath,lock=layerimagelock) if subtype==LayerCommandTypes.rawevent: historycommand=DrawingCommand(layerkey,oldimage,dirtyrect) else: floating=command[8] historycommand=AnchorCommand(layerkey,oldimage,dirtyrect,floating) window.logCommand(command,self.type) # add to undo/redo history window.addCommandToHistory(historycommand,layer.owner) else: print_debug("unknown processLayerCommand subtype: %d" % subtype) def processAllLayerCommand(self,command): window=self.master.getWindowById(self.windowid) subtype=command[1] if subtype==AllLayerCommandTypes.resize: window.adjustCanvasSize(command[2],command[3],command[4],command[5]) elif subtype==AllLayerCommandTypes.scale: window.scaleCanvas(command[2],command[3]) elif subtype==AllLayerCommandTypes.layerup: window.layerUp(command[2]) elif subtype==AllLayerCommandTypes.layerdown: window.layerDown(command[2]) elif subtype==AllLayerCommandTypes.deletelayer: window.removeLayerByKey(command[2]) elif subtype==AllLayerCommandTypes.flatten: window.flattenImage() elif subtype==AllLayerCommandTypes.insertlayer: # in this case we want to fill out the details ourselves key = command[2] index = command[3] image = command[4] owner = command[5] if self.type==ThreadTypes.server and owner != 0: window.insertLayer(key,index,image=image,owner=owner) else: if window.ownedByMe(owner): window.insertLayer(key,index,image=image,owner=owner) else: window.insertLayer(key,index,type=LayerTypes.network,image=image,owner=owner) window.logCommand(command,self.type) def requestAllLayerCommand(self,command): self.sendToServer(command) def processNetworkCommand(self,command): window=self.master.getWindowById(self.windowid) subtype=command[1] owner=command[2] if subtype==NetworkControlCommandTypes.resyncstart: width=command[3] height=command[4] window.clearAllLayers() window.setCanvasSize(width,height,history=False) window.setRemoteId(owner) elif subtype==NetworkControlCommandTypes.giveuplayer: layer=window.getLayerForKey(command[3]) if not layer: return if layer.deleteChildren(): window.master.refreshLayersList() layer.changeOwner(0) window.logCommand(command,self.type) elif subtype==NetworkControlCommandTypes.layerowner: layer=window.getLayerForKey(command[3]) if not layer: return layer.changeOwner(command[2]) window.logCommand(command,self.type) elif subtype==NetworkControlCommandTypes.requestlayer: layer=window.getLayerForKey(command[3]) if not layer: return window.logCommand(command,self.type) def sendToServer(self,command): window=self.master.getWindowById(self.windowid) if command[0]==DrawingCommandTypes.alllayer and command[1]==AllLayerCommandTypes.insertlayer: command=(command[0],command[1],command[2],command[3],command[4],window.remoteid) window.remoteoutputqueue.put(command)
# Copyright (C) 2009 Thomas Becker # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from hivemaster import HiveMasterWindow import sys import PyQt4.QtGui as qtgui from beeapp import BeeApp import pdb if __name__ == "__main__": beeapp = BeeApp(sys.argv) #app = qtgui.QApplication(sys.argv) #beeapp.app = app beeapp.master = HiveMasterWindow() beeapp.app.exec_()
def cursorReleaseEvent(self,x,y,modkeys,pressure=1,subx=0,suby=0): #print "cursorReleaseEvent:",x,y window=BeeApp().master.getWindowById(self.windowid) self.setCursor(window.master.getCurToolDesc().getCursor()) x,y=self.viewCoordsToImage(x,y) window.penUp(x,y,modkeys)
def updateValuesFromLayer(self, winlock=None, proplock=None, layerslock=None): win = BeeApp().master.getWindowById(self.windowid, winlock) if not win: return layer = win.getLayerForKey(self.layerkey, layerslock) if not layer: print_debug("WARNING: updateValueFromLayer could not find layer with key %s" % self.layerkey) return if not proplock: proplock = qtcore.QReadLocker(layer.propertieslock) # update visibility box self.ui.visibility_box.setChecked(layer.isVisible()) # update opacity slider # self.ui.opacity_box.setValue(layer.opacity()) self.ui.opacity_slider.setValue(int(layer.opacity() * 100)) # update name displayname = layer.name if layer.type == LayerTypes.animation: displayname += " (Animation)" elif layer.type == LayerTypes.network: displayname += " (Network)" self.ui.layer_name_label.setText(displayname) # update blend mode box self.ui.blend_mode_box.setCurrentIndex( self.ui.blend_mode_box.findText(BlendTranslations.modeToName(layer.compmode)) ) netbuttonstate = False netbuttontext = "" # only need text on the button if it's a network or floating layer if win.type == WindowTypes.networkclient: if win.ownedByNobody(layer.getOwner(proplock)): netbuttontext = "Claim Ownership" netbuttonstate = True elif win.ownedByMe(layer.getOwner(proplock)): netbuttontext = "Give Up Ownership" netbuttonstate = True if layer.type == LayerTypes.floating: netbuttontext = "Anchor On Layer" netbuttonstate = True # disable controls if client shouldn't be able to control them if win.type == WindowTypes.networkclient: if win.ownedByMe(layer.getOwner(proplock)) or layer.type == LayerTypes.floating: self.ui.opacity_slider.setEnabled(True) self.ui.blend_mode_box.setEnabled(True) else: self.ui.opacity_slider.setDisabled(True) self.ui.blend_mode_box.setDisabled(True) self.ui.network_control_button.setText(netbuttontext) self.ui.network_control_button.setEnabled(netbuttonstate)
def run(self): window=BeeApp().master.getWindowById(self.windowid) # if we failed to get a socket then destroy the window and exit if not self.socket: print_debug("failed to get socket connection") window.closeDrawingWindow() # get ready for next contact from server self.parser=XmlToQueueEventsConverter(None,window,0,type=ThreadTypes.network) #qtcore.QObject.connect(self.socket, qtcore.SIGNAL("readyRead()"), self.readyRead) #qtcore.QObject.connect(self.socket, qtcore.SIGNAL("disconnected()"), self.disconnected) sendingthread=NetworkWriterThread(self.windowid,self.socket) window.sendingthread=sendingthread sendingthread.start() # enter read loop, read till socket closes while 1: #print_debug("Ready to read from server") data=self.socket.read(1024) if not data: window.setDisconnectMessage("Server has closed connection") break #print_debug("got animation data from socket: %s" % data) self.parser.xml.addData(data) error=self.parser.read() #error=QXmlStreamReader.NoError # if there was an error and it wasn't a premature end of document error then we can't recover and need to disconnect if error!=QXmlStreamReader.PrematureEndOfDocumentError and error!=QXmlStreamReader.NoError: window.setDisconnectMessage("Error in XML stream") window.addExitEventToQueue(source=ThreadTypes.network) break # this should be run when the socket is disconnected and the buffer is empty window.disconnected() self.parser=None
def on_opacity_slider_sliderMoved(self, value): # there are two events, one with a float and one with a string, we only need one win = BeeApp().master.getWindowById(self.windowid) win.addOpacityChangeToQueue(self.layerkey, value / 100.0)
def on_opacity_slider_sliderReleased(self): win = BeeApp().master.getWindowById(self.windowid) win.addOpacityDoneToQueue(self.layerkey)
def mousePressEvent(self, event): layer = BeeApp().master.getLayerById(self.windowid, self.layerkey) win = BeeApp().master.getWindowById(self.windowid) if layer: win.setActiveLayer(layer.key)