def draw_text(self, txt, txtlen, window): if ((txtlen>0) and not ((txt == self.cursor_char) and (self._cursor_visible == False))): # If there IS something to print if (self.pbuffer_painter[window.id] == None): self.brush.setColor(self.ztoq_color[self.cur_bg]) self.pbuffer_painter[window.id] = QPainter(self.pbuffer[window.id]) self.pbuffer_painter[window.id].setPen(self.ztoq_color[self.cur_fg]) self.pbuffer_painter[window.id].setBackground(self.brush) painter = self.pbuffer_painter[window.id] # @type window ZWindow if (window.cursor == None): if (window.id == 0): # Main window window.set_cursor_position(1, self.height) window.set_cursor_real_position(2, self.height*(self.linesize-1)) else: window.set_cursor_position(1, 1) window.set_cursor_real_position(2, self.linesize-1) if (txt=='\n'): if (window.cursor[1]==self.height): if (window.scrolling): self.scroll(painter) window.set_cursor_position(1, window.cursor[1]) window.set_cursor_real_position(2, window.cursor_real_pos[1]) else: window.set_cursor_position(1, window.cursor[1]+1) window.set_cursor_real_position(2, window.cursor_real_pos[1]+self.linesize) else: rect = QRectF(window.cursor_real_pos[0], window.cursor_real_pos[1], self.pbuffer[window.id].width()-window.cursor_real_pos[0], self.linesize) painter.setFont(self.font()) #painter.setRenderHint(QPainter.TextAntialiasing) if (self._input_buffer_printing == False): painter.setBackgroundMode(Qt.OpaqueMode) else: painter.setBackgroundMode(Qt.TransparentMode) bounding_rect = painter.boundingRect(rect,txt) if (rect.contains(bounding_rect)): #print rect.x(), rect.y(), rect.width(),rect.height(), txt, bounding_rect painter.drawText(bounding_rect, txt) if txt != self.cursor_char: window.set_cursor_position(window.cursor[0]+txtlen, window.cursor[1]) window.set_cursor_real_position(rect.x()+bounding_rect.width(), rect.y()) else: # There is not enough space #print "Not enough space to print:", txt self.scroll(painter) window.set_cursor_position(1, self.height) window.set_cursor_real_position(2, self.height*(self.linesize-1)) rect.setX(2) rect.setY(window.cursor_real_pos[1]) rect.setWidth(self.pbuffer[window.id].width()-window.cursor_real_pos[0]) rect.setHeight(self.linesize) bounding_rect = painter.boundingRect(rect,txt) painter.drawText(bounding_rect, txt) if txt != self.cursor_char: window.set_cursor_position(window.cursor[0]+txtlen, window.cursor[1]) window.set_cursor_real_position(rect.x()+bounding_rect.width(), rect.y())
def data2scene(self, data2scene): self._data2scene = data2scene self.scene2data, isInvertible = data2scene.inverted() assert isInvertible for patchNr in range(self._patchAccessor.patchCount): # the patch accessor uses the data coordinate system. # because the patch is drawn on the screen, its holds coordinates # corresponding to Qt's QGraphicsScene's system, which need to be # converted to scene coordinates # the image rectangle includes an overlap margin imageRectF = data2scene.mapRect( self._patchAccessor.patchRectF(patchNr, self.overlap)) # the patch rectangle has per default no overlap patchRectF = data2scene.mapRect( self._patchAccessor.patchRectF(patchNr, 0)) # add a little overlap when the overlap_draw setting is # activated if self._overlap_draw != 0: patchRectF = QRectF( patchRectF.x() - self._overlap_draw, patchRectF.y() - self._overlap_draw, patchRectF.width() + 2 * self._overlap_draw, patchRectF.height() + 2 * self._overlap_draw) patchRect = QRect(round(patchRectF.x()), round(patchRectF.y()), round(patchRectF.width()), round(patchRectF.height())) # the image rectangles of neighboring patches can overlap # slightly, to account for inaccuracies in sub-pixel # rendering of many ImagePatch objects imageRect = QRect(round(imageRectF.x()), round(imageRectF.y()), round(imageRectF.width()), round(imageRectF.height())) self.imageRectFs[patchNr] = imageRectF self.dataRectFs[patchNr] = imageRectF self.tileRectFs[patchNr] = patchRectF self.imageRects[patchNr] = imageRect self.tileRects[patchNr] = patchRect
def data2scene(self, data2scene): self._data2scene = data2scene self.scene2data, isInvertible = data2scene.inverted() assert isInvertible for patchNr in range(self._patchAccessor.patchCount): # the patch accessor uses the data coordinate system. # because the patch is drawn on the screen, its holds coordinates # corresponding to Qt's QGraphicsScene's system, which need to be # converted to scene coordinates # the image rectangle includes an overlap margin imageRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, self.overlap)) # the patch rectangle has per default no overlap patchRectF = data2scene.mapRect(self._patchAccessor.patchRectF(patchNr, 0)) # add a little overlap when the overlap_draw setting is # activated if self._overlap_draw != 0: patchRectF = QRectF(patchRectF.x() - self._overlap_draw, patchRectF.y() - self._overlap_draw, patchRectF.width() + 2 * self._overlap_draw, patchRectF.height() + 2 * self._overlap_draw) patchRect = QRect(round(patchRectF.x()), round(patchRectF.y()), round(patchRectF.width()), round(patchRectF.height())) # the image rectangles of neighboring patches can overlap # slightly, to account for inaccuracies in sub-pixel # rendering of many ImagePatch objects imageRect = QRect(round(imageRectF.x()), round(imageRectF.y()), round(imageRectF.width()), round(imageRectF.height())) self.imageRectFs[patchNr] = imageRectF self.dataRectFs[ patchNr] = imageRectF self.tileRectFs[ patchNr] = patchRectF self.imageRects[ patchNr] = imageRect self.tileRects[ patchNr] = patchRect
def relayout(self): """Approximate Fruchterman-Reingold spring layout""" nodes = list(self.nodes.values()) pos = np.array([(np.cos(i / len(nodes) * 2 * np.pi + np.pi / 4), np.sin(i / len(nodes) * 2 * np.pi + np.pi / 4)) for i in range(1, 1 + len(nodes))]) K = 1 / np.sqrt(pos.shape[0]) GRAVITY, ITERATIONS = 10, 20 TEMPERATURES = np.linspace(.3, .01, ITERATIONS) for temp in chain([.8, .5], TEMPERATURES): # Repulsive forces delta = pos[:, np.newaxis, :] - pos delta /= np.abs(delta).sum( 2)[:, :, np.newaxis]**2 # NOTE: This warning was expected delta = np.nan_to_num(delta) # Reverse the effect of zero-division disp = -delta.sum(0) * K * K # Attractive forces for edge in self.edges: n1, n2 = nodes.index(edge.source), nodes.index(edge.dest) delta = pos[n1] - pos[n2] magnitude = np.abs(delta).sum() disp[n1] -= delta * magnitude / K disp[n2] += delta * magnitude / K # Gravity; tend toward center magnitude = np.sqrt(np.sum(np.abs(pos)**2, 1)) disp -= (pos.T * K * GRAVITY * magnitude).T # Limit max displacement and reposition magnitude = np.sqrt(np.sum(np.abs(disp)**2, 1)) pos += (disp.T / magnitude).T * np.clip(np.abs(disp), 0, temp) for node, position in zip(nodes, 500 * pos): node.setPos(*position) for edge in self.edges: edge.adjust() MARGIN, rect = 10, self.scene().itemsBoundingRect() rect = QRectF(rect.x() - MARGIN, rect.y() - MARGIN, rect.width() + 2 * MARGIN, rect.height() + 2 * MARGIN) self.scene().setSceneRect(rect) self.scene().invalidate()
def relayout(self): """Approximate Fruchterman-Reingold spring layout""" nodes = list(self.nodes.values()) pos = np.array( [ (np.cos(i / len(nodes) * 2 * np.pi + np.pi / 4), np.sin(i / len(nodes) * 2 * np.pi + np.pi / 4)) for i in range(1, 1 + len(nodes)) ] ) K = 1 / np.sqrt(pos.shape[0]) GRAVITY, ITERATIONS = 10, 20 TEMPERATURES = np.linspace(0.3, 0.01, ITERATIONS) for temp in chain([0.8, 0.5], TEMPERATURES): # Repulsive forces delta = pos[:, np.newaxis, :] - pos delta /= np.abs(delta).sum(2)[:, :, np.newaxis] ** 2 # NOTE: This warning was expected delta = np.nan_to_num(delta) # Reverse the effect of zero-division disp = -delta.sum(0) * K * K # Attractive forces for edge in self.edges: n1, n2 = nodes.index(edge.source), nodes.index(edge.dest) delta = pos[n1] - pos[n2] magnitude = np.abs(delta).sum() disp[n1] -= delta * magnitude / K disp[n2] += delta * magnitude / K # Gravity; tend toward center magnitude = np.sqrt(np.sum(np.abs(pos) ** 2, 1)) disp -= (pos.T * K * GRAVITY * magnitude).T # Limit max displacement and reposition magnitude = np.sqrt(np.sum(np.abs(disp) ** 2, 1)) pos += (disp.T / magnitude).T * np.clip(np.abs(disp), 0, temp) for node, position in zip(nodes, 500 * pos): node.setPos(*position) for edge in self.edges: edge.adjust() MARGIN, rect = 10, self.scene().itemsBoundingRect() rect = QRectF(rect.x() - MARGIN, rect.y() - MARGIN, rect.width() + 2 * MARGIN, rect.height() + 2 * MARGIN) self.scene().setSceneRect(rect) self.scene().invalidate()
def overlay_for(pt1, pt2, frequency): # Construct the line-geometry, we'll use this to construct the ellipsoid line = QLineF(pt1, pt2) # Determine the radius for the ellipsoid radius = fresnel_radius(line.length(), frequency) # Draw the ellipsoid zone = QPainterPath() zone.addEllipse(QPointF(0., 0.), line.length() / 2, radius) # Rotate the ellipsoid - same angle as the line transform = QTransform() transform.rotate(-line.angle()) zone = transform.map(zone) # Center the zone over the line lc = QRectF(pt1, pt2).center() zc = zone.boundingRect().center() zone.translate(lc.x() - zc.x(), lc.y() - zc.y()) return line, zone
class XYGraphicsScene(QGraphicsScene): def __init__(self, parent): QGraphicsScene.__init__(self, parent) self.cc_x = 1 self.cc_y = 2 self.m_channels = [] self.m_mouseLock = False self.m_smooth = False self.m_smooth_x = 0 self.m_smooth_y = 0 self.setBackgroundBrush(Qt.black) cursorPen = QPen(QColor(255, 255, 255), 2) cursorBrush = QColor(255, 255, 255, 50) self.m_cursor = self.addEllipse(QRectF(-10, -10, 20, 20), cursorPen, cursorBrush) linePen = QPen(QColor(200, 200, 200, 100), 1, Qt.DashLine) self.m_lineH = self.addLine(-9999, 0, 9999, 0, linePen) self.m_lineV = self.addLine(0, -9999, 0, 9999, linePen) self.p_size = QRectF(-100, -100, 100, 100) def setControlX(self, x): self.cc_x = x def setControlY(self, y): self.cc_y = y def setChannels(self, channels): self.m_channels = channels def setPosX(self, x, forward=True): if not self.m_mouseLock: pos_x = x * (self.p_size.x() + self.p_size.width()) self.m_cursor.setPos(pos_x, self.m_cursor.y()) self.m_lineV.setX(pos_x) if forward: self.sendMIDI(pos_x / (self.p_size.x() + self.p_size.width()), None) else: self.m_smooth_x = pos_x def setPosY(self, y, forward=True): if not self.m_mouseLock: pos_y = y * (self.p_size.y() + self.p_size.height()) self.m_cursor.setPos(self.m_cursor.x(), pos_y) self.m_lineH.setY(pos_y) if forward: self.sendMIDI(None, pos_y / (self.p_size.y() + self.p_size.height())) else: self.m_smooth_y = pos_y def setSmooth(self, smooth): self.m_smooth = smooth def setSmoothValues(self, x, y): self.m_smooth_x = x * (self.p_size.x() + self.p_size.width()) self.m_smooth_y = y * (self.p_size.y() + self.p_size.height()) def handleCC(self, param, value): sendUpdate = False xp = yp = 0.0 if param == self.cc_x: sendUpdate = True xp = (float(value) / 63) - 1.0 yp = self.m_cursor.y() / (self.p_size.y() + self.p_size.height()) if xp < -1.0: xp = -1.0 elif xp > 1.0: xp = 1.0 self.setPosX(xp, False) if param == self.cc_y: sendUpdate = True xp = self.m_cursor.x() / (self.p_size.x() + self.p_size.width()) yp = (float(value) / 63) - 1.0 if yp < -1.0: yp = -1.0 elif yp > 1.0: yp = 1.0 self.setPosY(yp, False) if sendUpdate: self.emit(SIGNAL("cursorMoved(double, double)"), xp, yp) def handleMousePos(self, pos): if not self.p_size.contains(pos): if pos.x() < self.p_size.x(): pos.setX(self.p_size.x()) elif pos.x() > self.p_size.x() + self.p_size.width(): pos.setX(self.p_size.x() + self.p_size.width()) if pos.y() < self.p_size.y(): pos.setY(self.p_size.y()) elif pos.y() > self.p_size.y() + self.p_size.height(): pos.setY(self.p_size.y() + self.p_size.height()) self.m_smooth_x = pos.x() self.m_smooth_y = pos.y() if not self.m_smooth: self.m_cursor.setPos(pos) self.m_lineH.setY(pos.y()) self.m_lineV.setX(pos.x()) xp = pos.x() / (self.p_size.x() + self.p_size.width()) yp = pos.y() / (self.p_size.y() + self.p_size.height()) self.sendMIDI(xp, yp) self.emit(SIGNAL("cursorMoved(double, double)"), xp, yp) def sendMIDI(self, xp=None, yp=None): global jack_midi_out_data rate = float(0xff) / 4 if xp != None: value = int((xp * rate) + rate) for channel in self.m_channels: jack_midi_out_data.put_nowait((0xB0 + channel - 1, self.cc_x, value)) if yp != None: value = int((yp * rate) + rate) for channel in self.m_channels: jack_midi_out_data.put_nowait((0xB0 + channel - 1, self.cc_y, value)) def updateSize(self, size): self.p_size.setRect(-(size.width() / 2), -(size.height() / 2), size.width(), size.height()) def updateSmooth(self): if self.m_smooth: if self.m_cursor.x() != self.m_smooth_x or self.m_cursor.y() != self.m_smooth_y: if abs(self.m_cursor.x() - self.m_smooth_x) <= 0.001: self.m_smooth_x = self.m_cursor.x() return elif abs(self.m_cursor.y() - self.m_smooth_y) <= 0.001: self.m_smooth_y = self.m_cursor.y() return new_x = (self.m_smooth_x + self.m_cursor.x() * 3) / 4 new_y = (self.m_smooth_y + self.m_cursor.y() * 3) / 4 pos = QPointF(new_x, new_y) self.m_cursor.setPos(pos) self.m_lineH.setY(pos.y()) self.m_lineV.setX(pos.x()) xp = pos.x() / (self.p_size.x() + self.p_size.width()) yp = pos.y() / (self.p_size.y() + self.p_size.height()) self.sendMIDI(xp, yp) self.emit(SIGNAL("cursorMoved(double, double)"), xp, yp) def keyPressEvent(self, event): event.accept() def wheelEvent(self, event): event.accept() def mousePressEvent(self, event): self.m_mouseLock = True self.handleMousePos(event.scenePos()) QGraphicsScene.mousePressEvent(self, event) def mouseMoveEvent(self, event): self.handleMousePos(event.scenePos()) QGraphicsScene.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): self.m_mouseLock = False QGraphicsScene.mouseReleaseEvent(self, event)
def layout(self, scene, nodes, center=None, padX=None, padY=None, direction=None, animationGroup=None): """ Lays out the nodes for this scene based on a block layering algorithm. :param scene | <XNodeScene> nodes | [<XNode>, ..] center | <QPointF> || None padX | <int> || None padY | <int> || None direction | <Qt.Direction> animationGroup | <QAnimationGroup> || None :return {<XNode>: <QRectF>, ..} | new rects per affected node """ nodes = filter(lambda x: x is not None and x.isVisible(), nodes) # make sure we have at least 1 node, otherwise, it is already laid out if not nodes or len(nodes) == 1: return {} # calculate the default padding based on the scene if padX == None: if direction == Qt.Vertical: padX = 2 * scene.cellWidth() else: padX = 4 * scene.cellWidth() if padY == None: if direction == Qt.Vertical: padY = 4 * scene.cellHeight() else: padY = 2 * scene.cellWidth() # step 1: create a mapping of the connections connection_map = self.connectionMap(scene, nodes) # step 2: organize the nodes into layers based on their connection chain layers = self.generateLayers(scene, nodes, connection_map) layers = list(reversed(layers)) # step 3: calculate the total dimensions for the layout bounds = QRectF() # step 3.1: compare the nodes together that have common connections layer_widths = [] layer_heights = [] node_heights = {} node_widths = {} for layer_index, layer in enumerate(layers): layer_w = 0 layer_h = 0 layer_node_w = [] layer_node_h = [] self.organizeLayer(layer, connection_map) for node in layer: rect = node.rect() layer_node_w.append(rect.width()) layer_node_h.append(rect.height()) if direction == Qt.Vertical: layer_w += rect.width() layer_h = max(rect.height(), layer_h) else: layer_w = max(rect.width(), layer_w) layer_h += rect.height() # update the bounding area if direction == Qt.Vertical: layer_w += padX * 1 - len(layer) bounds.setWidth(max(layer_w, bounds.width())) bounds.setHeight(bounds.height() + layer_h) else: layer_h += padY * 1 - len(layer) bounds.setWidth(bounds.width() + layer_w) bounds.setHeight(max(layer_h, bounds.height())) node_widths[layer_index] = layer_node_w node_heights[layer_index] = layer_node_h layer_widths.append(layer_w) layer_heights.append(layer_h) if not center: center = scene.sceneRect().center() w = bounds.width() h = bounds.height() bounds.setX(center.x() - bounds.width() / 2.0) bounds.setY(center.y() - bounds.height() / 2.0) bounds.setWidth(w) bounds.setHeight(h) # step 4: assign positions for each node by layer processed_nodes = {} layer_grps = [(i, layer) for i, layer in enumerate(layers)] layer_grps.sort(key=lambda x: len(x[1])) for layer_index, layer in reversed(layer_grps): layer_width = layer_widths[layer_index] layer_height = layer_heights[layer_index] # determine the starting point for this layer if direction == Qt.Vertical: offset = layer_index * padY + sum(layer_heights[:layer_index]) point = QPointF(bounds.x(), offset + bounds.y()) else: offset = layer_index * padX + sum(layer_widths[:layer_index]) point = QPointF(offset + bounds.x(), bounds.y()) # assign node positions based on existing connections for node_index, node in enumerate(layer): max_, min_ = (None, None) inputs, outputs = connection_map[node] for connected_node in inputs + outputs: if not connected_node in processed_nodes: continue npos = processed_nodes[connected_node] nrect = connected_node.rect() rect = QRectF(npos.x(), npos.y(), nrect.width(), nrect.height()) if direction == Qt.Vertical: if min_ is None: min_ = rect.left() min_ = min(rect.left(), min_) max_ = max(rect.right(), max_) else: if min_ is None: min_ = rect.top() min_ = min(rect.top(), min_) max_ = max(rect.bottom(), max_) if direction == Qt.Vertical: off_x = 0 off_y = (layer_height - node.rect().height()) / 2.0 start_x = (bounds.width() - layer_width) start_y = 0 else: off_x = (layer_width - node.rect().width()) / 2.0 off_y = 0 start_x = 0 start_y = (bounds.height() - layer_height) # align against existing nodes if not None in (min_, max): if direction == Qt.Vertical: off_x = (max_ - min_) / 2.0 - node.rect().width() / 2.0 point_x = min_ + off_x point_y = point.y() + off_y else: off_y = (max_ - min_) / 2.0 - node.rect().height() / 2.0 point_x = point.x() + off_x point_y = min_ + off_y # otherwise, align based on its position in the layer else: if direction == Qt.Vertical: off_x = sum(node_widths[layer_index][:node_index]) off_x += node_index * padX off_x += start_x point_x = point.x() + off_x point_y = point.y() + off_y else: off_y = sum(node_heights[layer_index][:node_index]) off_y += node_index * padY off_y += start_y point_x = point.x() + off_x point_y = point.y() + off_y if not animationGroup: node.setPos(point_x, point_y) else: anim = XNodeAnimation(node, 'setPos') anim.setStartValue(node.pos()) anim.setEndValue(QPointF(point_x, point_y)) animationGroup.addAnimation(anim) processed_nodes[node] = QPointF(point_x, point_y) if self._testing: QApplication.processEvents() time.sleep(1) return processed_nodes
class SlideshowFrame(object): def __init__(self, win, rect, filename, metadata): self.win = win self.rect = rect self.filename = filename self.metadata = metadata if self.metadata: self.line1 = self.metadata['Name'] self.line2 = "%s Mission" % (self.metadata['Mission']) self.line3 = "%s" % (self.metadata['Time']) else: self.line1 = "" self.line2 = "" self.line3 = "" self.image = QGraphicsPixmapItem() self.win.scene.addItem(self.image) self.image.setTransformationMode(Qt.SmoothTransformation) self.use_two_lines = True self.fontsize1 = 32 self.fontsize2 = 26 self.fontsize3 = 24 self.font1 = QFont('Times New Roman', self.fontsize1) self.font2 = QFont('Times New Roman', self.fontsize2) self.font3 = QFont('Times New Roman', self.fontsize3) self.title1 = self.win.scene.addText(self.line1, self.font1) self.title1.setDefaultTextColor(Qt.white) self.title1.setVisible(False) self.title2 = self.win.scene.addText(self.line2, self.font2) self.title2.setDefaultTextColor(Qt.white) self.title2.setVisible(False) self.title3 = self.win.scene.addText(self.line3, self.font3) self.title3.setDefaultTextColor(Qt.white) self.title3.setVisible(False) self.reservedHeight = 128 self.padding = 20 self.hide() def move(self, x, y): self.rect = QRectF(x, y, self.rect.width(), self.rect.height()) def __rotate(self, metadata, origImgSize): # Qt only handles orientation properly from v5.5 try: # try directly to get the tag, because sometimes get_tags() returns # tags that don't actually are in the file rot = metadata['Exif.Image.Orientation'] except KeyError: # guess :-/ rot = '1' # see http://www.daveperrett.com/images/articles/2012-07-28-exif-orientation-handling-is-a-ghetto/EXIF_Orientations.jpg # we have to 'undo' the rotations, so the numbers are negative if rot == '1': rotate = 0 imgSize = origImgSize if rot == '8': rotate = -90 imgSize = QSize(origImgSize.height(), origImgSize.width()) if rot == '3': rotate = -180 imgSize = origImgSize if rot == '6': rotate = -270 imgSize = QSize(origImgSize.height(), origImgSize.width()) # undo the last rotation and apply the new one self.image.setRotation(rotate) return imgSize def __zoomFit(self, imgSize): reservedHeight = self.reservedHeight + self.padding * 2 hZoom = self.rect.width() / imgSize.width() vZoom = (self.rect.height() - reservedHeight) / imgSize.height() scale = min(hZoom, vZoom) self.image.setScale(scale) width = imgSize.width() * scale height = imgSize.height() * scale self.image.setPos( (self.rect.width() - width) / 2 + self.rect.x(), (self.rect.height() - reservedHeight - height) / 2 + self.rect.y()) def layoutText(self): reservedHeight = self.reservedHeight + self.padding vertical_spacing = (self.reservedHeight - self.title1.boundingRect().height() - self.title2.boundingRect().height() - self.title3.boundingRect().height()) / 3 x = (self.rect.width() - self.title1.boundingRect().width()) / 2 y = self.rect.height() - reservedHeight + vertical_spacing self.title1.setPos(x + self.rect.x(), y + self.rect.y()) x = (self.rect.width() - self.title2.boundingRect().width()) / 2 y = self.rect.height( ) - reservedHeight + vertical_spacing * 2 + self.title1.boundingRect( ).height() self.title2.setPos(x + self.rect.x(), y + self.rect.y()) x = (self.rect.width() - self.title3.boundingRect().width()) / 2 y = self.rect.height( ) - reservedHeight + vertical_spacing * 3 + self.title1.boundingRect( ).height() + self.title2.boundingRect().height() self.title3.setPos(x + self.rect.x(), y + self.rect.y()) def show(self): img = QPixmap(self.filename) try: metadata = GExiv2.Metadata(self.filename) except GLib.Error as e: print(repr(e)) return self.image.setPixmap(img) self.image.setScale(1.0) self.image.setRotation(0) imgSize = self.__rotate(metadata, img.size()) self.__zoomFit(imgSize) self.layoutText() self.title1.setVisible(True) self.title2.setVisible(True) self.title3.setVisible(True) self.image.setVisible(True) def hide(self): self.title1.setVisible(False) self.title2.setVisible(False) self.title3.setVisible(False) self.image.setVisible(False) self.image.setPixmap(QPixmap()) def showImage(self, file, md): self.metadata = md try: metadata = GExiv2.Metadata(file) except GLib.Error as e: print(repr(e)) return img = QPixmap(file) self.image.setPixmap(img) self.image.setScale(1.0) self.image.setRotation(0) imgSize = self.__rotate(metadata, img.size()) self.__zoomFit(imgSize) self.layoutMetadata()