Aluode/PerceptionLabPortable
0
1import os2import re3 4from ..GraphicsScene import GraphicsScene5from ..Qt import QtCore, QtWidgets6from ..widgets.FileDialog import FileDialog7 8LastExportDirectory = None9 10 11class Exporter(object):12 """13 Abstract class used for exporting graphics to file / printer / whatever.14 """ 15 allowCopy = False # subclasses set this to True if they can use the copy buffer16 Exporters = []17 18 @classmethod19 def register(cls):20 """21 Used to register Exporter classes to appear in the export dialog.22 """23 Exporter.Exporters.append(cls)24 25 def __init__(self, item):26 """27 Initialize with the item to be exported.28 Can be an individual graphics item or a scene.29 """30 object.__init__(self)31 self.item = item32 33 def parameters(self):34 """Return the parameters used to configure this exporter."""35 raise Exception("Abstract method must be overridden in subclass.")36 37 def export(self, fileName=None, toBytes=False, copy=False):38 """39 If *fileName* is None, pop-up a file dialog.40 If *toBytes* is True, return a bytes object rather than writing to file.41 If *copy* is True, export to the copy buffer rather than writing to file.42 """43 raise Exception("Abstract method must be overridden in subclass.")44 45 def fileSaveDialog(self, filter=None, opts=None):46 ## Show a file dialog, call self.export(fileName) when finished.47 if opts is None:48 opts = {}49 self.fileDialog = FileDialog()50 self.fileDialog.setFileMode(QtWidgets.QFileDialog.FileMode.AnyFile)51 self.fileDialog.setAcceptMode(QtWidgets.QFileDialog.AcceptMode.AcceptSave)52 if filter is not None:53 if isinstance(filter, str):54 self.fileDialog.setNameFilter(filter)55 elif isinstance(filter, list):56 self.fileDialog.setNameFilters(filter)57 global LastExportDirectory58 exportDir = LastExportDirectory59 if exportDir is not None:60 self.fileDialog.setDirectory(exportDir)61 self.fileDialog.show()62 self.fileDialog.opts = opts63 self.fileDialog.fileSelected.connect(self.fileSaveFinished)64 return65 66 def fileSaveFinished(self, fileName):67 global LastExportDirectory68 LastExportDirectory = os.path.split(fileName)[0]69 70 ## If file name does not match selected extension, append it now71 ext = os.path.splitext(fileName)[1].lower().lstrip('.')72 selectedExt = re.search(r'\*\.(\w+)\b', self.fileDialog.selectedNameFilter())73 if selectedExt is not None:74 selectedExt = selectedExt.groups()[0].lower()75 if ext != selectedExt:76 fileName = fileName + '.' + selectedExt.lstrip('.')77 78 self.export(fileName=fileName, **self.fileDialog.opts)79 80 def getScene(self):81 if isinstance(self.item, GraphicsScene):82 return self.item83 else:84 return self.item.scene()85 86 def getSourceRect(self):87 if isinstance(self.item, GraphicsScene):88 w = self.item.getViewWidget()89 return w.viewportTransform().inverted()[0].mapRect(w.rect())90 else:91 return self.item.sceneBoundingRect()92 93 def getTargetRect(self): 94 if isinstance(self.item, GraphicsScene):95 return self.item.getViewWidget().rect()96 else:97 return self.item.mapRectToDevice(self.item.boundingRect())98 99 def setExportMode(self, export, opts=None):100 """101 Call setExportMode(export, opts) on all items that will 102 be painted during the export. This informs the item103 that it is about to be painted for export, allowing it to 104 alter its appearance temporarily105 106 107 *export* - bool; must be True before exporting and False afterward108 *opts* - dict; common parameters are 'antialias' and 'background'109 """110 if opts is None:111 opts = {}112 for item in self.getPaintItems():113 if hasattr(item, 'setExportMode'):114 item.setExportMode(export, opts)115 116 def getPaintItems(self, root=None):117 """Return a list of all items that should be painted in the correct order."""118 if root is None:119 root = self.item120 preItems = []121 postItems = []122 if isinstance(root, QtWidgets.QGraphicsScene):123 childs = [i for i in root.items() if i.parentItem() is None]124 rootItem = []125 else:126 childs = root.childItems()127 rootItem = [root]128 childs.sort(key=lambda a: a.zValue())129 while len(childs) > 0:130 ch = childs.pop(0)131 tree = self.getPaintItems(ch)132 if (ch.flags() & ch.GraphicsItemFlag.ItemStacksBehindParent) or \133 (ch.zValue() < 0 and (ch.flags() & ch.GraphicsItemFlag.ItemNegativeZStacksBehindParent)):134 preItems.extend(tree)135 else:136 postItems.extend(tree)137 138 return preItems + rootItem + postItems139 140 def render(self, painter, targetRect, sourceRect, item=None):141 self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))142 