class NodeThumbnailRenderReply(QNetworkReply): def __init__(self, parent, request): QNetworkReply.__init__(self, parent) self.qbuffer = None self.connect(self, SIGNAL('abouteToClose()'), self.__close) self.byteArray = QByteArray() self.qbuffer = QBuffer(self.byteArray) self.node = vfs().getnode(str(request.url().path().toUtf8())) self.thumbnailer = Thumbnailer() self.connect(self.thumbnailer, SIGNAL("ThumbnailUpdate"), self.updateThumbnail) self.setRequest(request) self.setOperation(QNetworkAccessManager.GetOperation) mime = "image/jpg" self.setHeader(QNetworkRequest.ContentTypeHeader, QVariant(mime)) self.open() self.setUrl(request.url()) self.connect(parent, SIGNAL("ready"), self.ready) self.ready() def ready(self): if self.node.dataType().find('video') != -1: pixmap = self.thumbnailer.generate(self.node, iconSize=128, frames=10) else: pixmap = self.thumbnailer.generate(self.node, iconSize=256, frames=10) if pixmap: self.updateThumbnail(self.node, pixmap) def updateThumbnail(self, node, pixmap): if pixmap == None: pixmap = QPixmap(":file_broken.png") pixmap.save(self.qbuffer, 'JPG') self.qbuffer.seek(0) QTimer.singleShot(0, self, SIGNAL("readyRead()")) QTimer.singleShot(0, self, SIGNAL("finished()")) def abort(self): self.close() def __del__(self): self.thumbnailer.unregister() def open(self, mode=None): try: self.qbuffer.open(QIODevice.ReadWrite | QIODevice.Unbuffered) self.setOpenMode(QIODevice.ReadWrite | QIODevice.Unbuffered) return True except (AttributeError, IOError): return False def seek(self, pos): if self.qbuffer: return self.qbuffer.seek(pos) return False def __close(self): if self.qbuffer: self.qbuffer.close() self.qbuffer = None return True def readData(self, size): if self.qbuffer: return self.qbuffer.read(size) return "" def pos(self): if self.qbuffer: return self.qbuffer.pos() return 0 def isSequential(self): if self.qbuffer: return self.qbuffer.isSequential() return False def size(self): return self.qbuffer.size() def reset(self): if self.qbuffer: self.qbuffer.seek(0) return True return False def atEnd(self): if self.qbuffer: return self.qbuffer.atEnd() return False
class NodeListFragment(ReportPageFragment): DefaultHeader = [ { 'title': 'name', 'callback': lambda Node: Node.name() }, { 'title': 'size', 'callback': lambda Node: Node.size() }, { 'title': 'tags', 'callback': lambda Node: getTags(Node) }, ] ListView = 0 GalleryView = 1 DetailedAttributes = None HeaderAttributes = None HeaderAttributesName = None def __init__(self, title, nodes, thead, view): ReportPageFragment.__init__(self, title) self.nodes = nodes self.thead = thead self.view = view self.extract = Extract() self.filepath = None self.thumbpath = None if QApplication.instance(): self.gui = True else: self.gui = False #XXX header don't support '.' in name def addNode(self, node): self.nodes.append(node) def elements(self): return self.nodes def dumpsJSON(self): thead = [] for head in self.thead: thead.append(head['title']) if NodeListFragment.HeaderAttributes: for attribute in NodeListFragment.HeaderAttributes: thead.append(attribute) buff = '{"title": "' + self.title + '",' + '"widget": "node_list", "thead":' buff += json.dumps(thead) buff += ', "view" : ' + str(self.view) + ', "data" : [' for node in self.nodes: try: rowjson = {} rowjson['widget'] = 'node' for head in self.thead: cb = head['callback'](node) if type(cb) == str: cb = cb.decode('utf-8', 'replace').encode('utf-8') rowjson[head['title']] = cb if self.gui: #Need qt gui for QPixmap or will crash in console #print 'self as GUI !', QApplication.instance() self.thumbnailer = Thumbnailer() if self.thumbnailer.isThumbnailable(node): rowjson[ "thumb"] = "dff-node-thumbnail://" + node.absolute( ).decode('utf-8', 'replace').encode('utf-8') self.thumbnailer.unregister() rowjson['row_details'] = { 'widget': 'node_attribute', 'data': self.attributesToMap(node.attributes()) } buff += json.dumps(rowjson) buff += "," except UnicodeError as e: print "Can't dump node " + str( node.absolute()) + " : " + str(e) buff += "]}" return buff def writeSize(self, exportContent): size = 0 if exportContent: for node in self.nodes: size += node.size() return size def writeJSON__(self, fd, page_path, report_path, exportContent=True): self.filepath = page_path + "/" + 'files' self.thumbpath = page_path + "/" + 'thumbs' buff = '{"title": "' + self.title + '",' + '"widget": "node_list", "thead":' fd.write(buff) thead = [] for head in self.thead: thead.append(head['title']) #XXX header don't support '.' in name if NodeListFragment.HeaderAttributesName: for name in NodeListFragment.HeaderAttributesName: thead.append(name) json.dump(thead, fd) bdata = ', "view" : ' + str(self.view) + ', "data" : [' fd.write(bdata) self.notifyWrite(ReportPageFragment.EventWriteElements, len(self.nodes)) for node in self.nodes: self.notifyWrite(ReportPageFragment.EventWriteElementStart, node.absolute()) try: filename = None if exportContent: filename = self.exportNode(node, self.filepath, report_path) self.nodeToJSON(node, filename, fd, report_path, page_path, True) fd.write(',\n') except Exception as e: print "Can't write node " + str( node.absolute()) + " : " + str(e) self.notifyWrite(ReportPageFragment.EventWriteElementFinish, node.absolute()) fd.write("]}") def attributesToMap(self, attributes): attributesMap = {} for key, variantMap in attributes.iteritems(): vmap = self.recurseVariant(variantMap, {}, '') if len(vmap): attributesMap[key] = vmap attributesMap = OrderedDict( sorted(attributesMap.items(), key=lambda t: t[0])) return attributesMap def recurseVariant(self, variant, varMap, keyPath): if isinstance(variant, VMap): for key, vvar in variant.iteritems(): if len(keyPath): self.recurseVariant(vvar, varMap, keyPath + '.' + str(key)) else: self.recurseVariant(vvar, varMap, str(key)) if isinstance(variant, VList): l = [] for i in range(len(variant)): self.recurseVariant(variant[i], varMap, keyPath + ' (' + str(i) + ')') if isinstance(variant, Variant) or isinstance(variant, RCVariant): val = variant.value() if isinstance(val, VMap) or isinstance(val, VList): self.recurseVariant(val, varMap, keyPath) else: if isinstance(val, DateTime): try: val = str(val) except: val = "Invalid" if type(val) == str: val = val.decode('utf-8', 'replace').encode('utf-8') translated = False if NodeListFragment.DetailedAttributes: for ( finalName, attributeName ) in NodeListFragment.DetailedAttributes: #HeaderAttributes depend of this because it's attribute filled in the table will use the result of this function called by attributesMap if there is no detailed attributes there will no translation and the header can't be filled if keyPath == attributeName: varMap[finalName] = val if not translated: varMap[keyPath] = val varMap = OrderedDict(sorted(varMap.items(), key=lambda t: t[0])) return varMap def findInAttributesMap(self, attribute, attributesMap): for module in attributesMap: moduleAttributes = attributesMap.get(module) if moduleAttributes: result = moduleAttributes.get(attribute) if result: return result return "" def nodeToJSON(self, node, filename, fd, report_path, page_path, thumb=False): rowjson = {} rowjson['widget'] = 'node' attrMap = self.attributesToMap(node.attributes()) for head in self.thead: cb = head['callback'](node) if type(cb) == str: cb = cb.decode('utf-8', 'replace').encode('utf-8') rowjson[head['title']] = cb if NodeListFragment.HeaderAttributes: for (name, attribute) in NodeListFragment.HeaderAttributes: result = self.findInAttributesMap(name, attrMap) rowjson[name] = result if filename: rowjson["file"] = self.filepath + "/" + filename if thumb and self.gui: if self.exportThumbnail(report_path, self.thumbpath, filename + '.jpg', node): rowjson["thumb"] = self.thumbpath + "/" + filename + '.jpg' rowjson['row_details'] = {'widget': 'node_attribute', 'data': attrMap} try: json.dump(rowjson, fd) except UnicodeError as e: print 'report.fragment.nodeToJSON failed ' + str(e) print rowjson def exportThumbnail(self, report_path, thumbpath, name, node, size=None): self.thumbnailer = Thumbnailer() if self.thumbnailer.isThumbnailable(node): pixmap = self.thumbnailer.generate(node, iconSize=256, frames=10, blocking=True) self.thumbnailer.unregister() if pixmap: try: exportPath = os.path.join(report_path, os.path.join(thumbpath, name)) array = QByteArray() qfile = QBuffer(array) qfile.open(QIODevice.ReadWrite) pixmap.save(qfile, 'JPG') qfile.seek(0) with open(exportPath, 'wb') as f: f.write(qfile.read(qfile.size())) qfile.close() return True except Exception as e: qfile.close() return False else: return False def exportNode(self, node, path, report_path): abspath = os.path.join(report_path, path) try: local_path = self.extract.extractFile(node, abspath) except Exception as e: pass if local_path: return os.path.basename(local_path) else: return None
class ThumbnailVideoView(QWidget, Script): IconSize = 256 Frames = 10 def __init__(self): Script.__init__(self, "thumbnailvideo") self.icon = None self.vfs = vfs.vfs() def start(self, args): try: self.preview = args["preview"].value() except IndexError: self.preview = False try: self.node = args["file"].value() except KeyError: pass def g_display(self): QWidget.__init__(self) self.copyMenu = CopyMenu(self) self.copySelectionMenu = CopySelectionMenu(self) self.rubberBand = None self.hlayout = QVBoxLayout() self.setLayout(self.hlayout) self.menuLayout = QHBoxLayout() self.hlayout.addLayout(self.menuLayout) self.frameLayout = QFormLayout() self.menuLayout.addLayout(self.frameLayout) self.frameNumberEdit = QLineEdit(str(self.Frames)) self.frameNumberEdit.setFixedWidth(40) self.frameNumberEdit.setValidator(QIntValidator(0, 256)) self.frameLayout.addRow("Number of frame: ", self.frameNumberEdit) self.connect(self.frameNumberEdit, SIGNAL("textChanged(QString)"), self.setFrameNumber) self.iconLayout = QFormLayout() self.menuLayout.addLayout(self.iconLayout) self.iconSizeEdit = QLineEdit(str(self.IconSize)) self.iconSizeEdit.setFixedWidth(40) self.iconSizeEdit.setValidator(QIntValidator(0, 512)) self.iconLayout.addRow("Size: ", self.iconSizeEdit) self.connect(self.iconSizeEdit, SIGNAL("textChanged(QString)"), self.setIconSize) self.refreshButton = QPushButton("Refresh") self.menuLayout.addWidget(self.refreshButton) self.connect(self.refreshButton, SIGNAL("clicked()"), self.generateThumbnail) self.scrollArea = QScrollArea() self.hlayout.addWidget(self.scrollArea) self.generateThumbnail() def mousePressEvent(self, event): self.dragPosition = event.pos() if not self.rubberBand: self.rubberBand = QRubberBand(QRubberBand.Rectangle, self) self.rubberBand.setGeometry(QRect(self.dragPosition, QSize())) self.rubberBand.show() def mouseMoveEvent(self, event): self.rubberBand.setGeometry( QRect(self.dragPosition, event.pos()).normalized()) def mouseReleaseEvent(self, event): if not self.rubberBand.size().isEmpty(): rect = QRect(self.rubberBand.pos(), self.rubberBand.size()) rect.moveLeft(rect.left() - (self.width() - self.thumbLabel.pixmap().width()) / 2.0) rect.moveTop(rect.top() - (self.height() - self.thumbLabel.pixmap().height()) / 2.0) self.currentSelection = rect self.copySelectionMenu.popup(QCursor.pos()) else: self.copyMenu.popup(QCursor.pos()) self.rubberBand.hide() def copySelectionToClipboard(self): QApplication.clipboard().setPixmap(self.thumbLabel.pixmap().copy( self.currentSelection)) def copyPixmapToClipboard(self): QApplication.clipboard().setPixmap(self.thumbLabel.pixmap()) def setIconSize(self, size): ThumbnailVideoView.IconSize = int(size) def setFrameNumber(self, number): ThumbnailVideoView.Frames = int(number) def generateThumbnail(self): self.thumbnailer = Thumbnailer() self.connect(self.thumbnailer, SIGNAL("ThumbnailUpdate"), self.updateThumbnail) pixmap = self.thumbnailer.generate(self.node, iconSize=self.IconSize, frames=self.Frames) if pixmap: self.updateThumbnail(self.node, pixmap) def updateThumbnail(self, node, pixmap): if pixmap: self.thumbLabel = QLabel() self.thumbLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.thumbLabel.setWordWrap(True) self.thumbLabel.setPixmap(pixmap) self.scrollArea.setWidget(self.thumbLabel) else: self.thumbLabel.setText("Can't render, video is corrupted.") self.thumbLabel.setAlignment(Qt.AlignCenter) self.thumbnailer.unregister() def updateWidget(self): pass