Example #1
0
class FlatStem(MeristemDisplay, QGraphicsView):
    """A QTGraphics canvas to display a 2D (rolled out) view of a meristem."""

    def __init__(self, *args, **kwargs):
        self.scene = QGraphicsScene()
        super(FlatStem, self).__init__(self.scene, *args, **kwargs)

    def thin_line(self, colour=None):
        """Return a pen that draws a thin line in the given colour."""
        pen = QPen(colour) if colour else QPen()
        pen.setWidth(0)
        return pen

    def shadow_if_needed(self, bud):
        """Check if the bud crosses over the side bars, and if so draw a shadow bud on the opposite side."""
        angle = bud.angle2x(bud.angle + math.radians(self.viewing_angle[0]))
        if abs(angle) < math.pi * bud.radius - bud.scale:
            return

        if angle > 0:
            angle = angle - bud.scale - 2 * math.pi * bud.radius
        else:
            angle = angle - bud.scale + 2 * math.pi * bud.radius

        return self.scene.addEllipse(
            angle, -bud.height - bud.scale,
            bud.scale * 2, bud.scale * 2,
            self.thin_line(), QBrush(qtpy.QtGui.QColor(*(bud.html_colour + [100])))
        )

    def make_item(self, bud):
        """Add the given bud to the scene as a ball."""
        item = self.scene.addEllipse(
            bud.angle2x(bud.angle + math.radians(self.viewing_angle[0])) - bud.scale, -bud.height - bud.scale,
            bud.scale * 2, bud.scale * 2,
            self.thin_line(), QBrush(qtpy.QtGui.QColor(*bud.html_colour))
        )
        return item

    def set_scale(self):
        """Scale the view in accordance with the zoom level."""
        self.resetTransform()
        size = self.size()
        dist = math.sqrt(size.width() * size.height())/50.0
        zoom = math.pi * 300/(dist + self.zoom + 11)
        self.scale(zoom, zoom)

    def draw_side_bars(self):
        """Draw the bounding lines of the meristem."""
        if not self.displayables:
            return

        side_bar = self.thin_line(Qt.blue)
        top = max([b.radius for b in self.displayables if b.height < b.scale] or [0])
        for height in range(int(round(self.objects.height))):
            bottom = top
            top = max([b.radius for b in self.displayables if abs(b.height - height) < b.scale] or [0])

            bot_side_bar_pos = math.pi * bottom
            top_side_bar_pos = math.pi * top
            self.scene.addLine(-bot_side_bar_pos, -height + 0.5, -top_side_bar_pos, -height - 0.5, side_bar)
            self.scene.addLine(bot_side_bar_pos, -height + 0.5, top_side_bar_pos, -height - 0.5, side_bar)

    def redraw(self):
        """Redraw all objects on the scene."""
        for item in self.scene.items():
            self.scene.removeItem(item)

        # Draw all buds
        for bud in self.displayables:
            self.make_item(bud)
            self.shadow_if_needed(bud)

        self.draw_side_bars()

        self.set_scale()

    def select(self, event):
        """Select the item that is under the cursor (if enabled)."""
        if not self.can_select:
            return

        scene_pos = self.mapToScene(QPoint(event.x(), event.y()))
        x, y = scene_pos.x(), -scene_pos.y()
        offsets = (math.radians(self.viewing_angle[0]), 0)
        if self.objects.bounds_test(x, y, offsets) > 0:
            self.objects.select()

        # signal all and any slots that something new was selected
        self._signal_selected()
Example #2
0
class MyForm(QMainWindow):
    def __init__(self, width, height, pixel_ratio, path):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # overwrite dimesions specified in slideShow3.py, as they are specific to MacBookPro display, and QTDesigner
        # has its own idea on what they should be.  This code should work on any size display
        self.resize(width, height)
        self.ui.graphicsView.setGeometry(QtCore.QRect(0, 0, width, height))
        self.ui.menubar.setGeometry(QtCore.QRect(0, 0, width, 0))

        self.width = width
        self.height = height
        self.pixel_ratio = pixel_ratio
        self.path = path
        self.imageFiles = []
        self.slideIndex = -1
        self.random_index_number = 0
        self.random = ""
        self.imageFiles, self.random_index, self.path, self.max_index = self.getImageNames2(
        )
        self.helpFile = os.path.join(
            os.path.dirname(os.path.realpath(sys.argv[0])), "instructions.png")
        #print(self.helpFile)
        self.scene = QGraphicsScene(self)
        #self.scene.setAlignment(QtCore.Qt.AlignCenter)
        self.ui.actionDir.triggered.connect(self.openFileNameDialog)
        self.ui.actionStart_Slide_Show.triggered.connect(self.slide_show)
        self.ui.actionRandom_Slide_Show.triggered.connect(
            self.random_slide_show)
        eventFilter = MouseEventFilter(self.scene)
        self.scene.installEventFilter(eventFilter)
        self.ui.actionHelp.triggered.connect(self.helpWindow)

        #self.show()
        self.showFullScreen()

    extension = staticmethod(lambda f: f.split('.').pop().lower())
    filename = staticmethod(lambda f: f.split('/').pop())

    def openFileNameDialog(self):
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        self.fileName, _ = QFileDialog.getOpenFileName(
            self,
            "QFileDialog.getOpenFileName()",
            "",
            "All Files (*);;Python Files (*.py)",
            options=options)
        if self.fileName:
            self.path = os.path.dirname(self.fileName)
            self.imageFiles = []
            self.random_index = []
            self.max_index = []
            self.imageFiles, self.random_index, self.path, self.max_index = self.getImageNames2(
            )
            self.slideIndex = self.imageFiles.index(self.fileName) - 1

    def getImageNames2(self):
        "get the names of all images on disc or from the web (which are cached locally)"

        if not self.path:
            self.path = os.getcwd()
        if self.path[-1] != '/':
            self.path += '/'
        try:
            os.listdir(self.path)
        except:
            error_dialog = QtWidgets.QErrorMessage()
            error_dialog.showMessage(
                'Error in path' + self.path
            )  # https://stackoverflow.com/questions/40227047/python-pyqt5-how-to-show-an-error-message-with-pyqt5
            return [], self.path

        for i in GlobDirectoryWalker(self.path, "*.*"):
            if os.path.isfile(i):
                if self.checkImageType(i): self.imageFiles.append(i)

        max_index = len(self.imageFiles) - 1

        self.imageFiles.sort()

        random_index = list(range(max_index + 1))
        random.shuffle(random_index)
        return self.imageFiles, random_index, self.path, max_index

    def slide(self, i):
        self.pixmap = QtGui.QPixmap()
        #self.pixmap.setAlignment(QtCore.Qt.AlignCenter)
        self.pixmap.load(self.imageFiles[i])
        self.pixmap.setDevicePixelRatio(
            self.pixel_ratio
        )  # https://stackoverflow.com/questions/50127246/pyqt-5-10-enabling-high-dpi-support-for-macos-poor-pixmap-quality
        #self.pixmap4 = self.pixmap.scaled(self.width * self.pixel_ratio, (self.height * self.pixel_ratio)-45, Qt.KeepAspectRatio)
        self.pixmap4 = self.pixmap.scaled(self.width * self.pixel_ratio,
                                          (self.height * self.pixel_ratio),
                                          Qt.KeepAspectRatio)
        try:
            self.scene.removeItem(self.item)
        except:
            print("failed to remove item")
        self.item = QGraphicsPixmapItem(self.pixmap4)
        self.scene.addItem(self.item)
        #myapp.setWindowTitle(os.path.basename(self.imageFiles[i]))
        self.setWindowTitle(os.path.basename(self.imageFiles[i]))
        self.ui.graphicsView.setScene(self.scene)

    def slide_show(self):
        self.random = 0
        self.next_slide()

    def random_slide_show(self):
        self.random = 1
        self.next_slide()

    def next_slide(self):
        if self.random == 0:
            self.increment_slide()
        else:
            self.random_next()

    def prev_slide(self):
        if self.random == 0:
            self.decrement_slide()
        else:
            self.random_prev()

    def random_next(self):
        "display the next random slide"
        self.random_index_number += 1
        try:
            self.slideIndex = self.random_index[self.random_index_number]
            self.slide(self.slideIndex)
        except IndexError:
            self.random_index_number = 0
            self.slideIndex = self.random_index[self.random_index_number]
            self.slide(self.slideIndex)
        return False

    def random_prev(self):
        "display the previous random slide"
        self.random_index_number -= 1
        #self.ImageWindow.clear()
        try:
            self.slideIndex = self.random_index[self.random_index_number]
            self.slide(self.slideIndex)
        except IndexError:
            self.random_index_number = self.max_index
            self.slideIndex = self.random_index[self.random_index_number]
            self.slide(self.slideIndex)
        return False

    def increment_slide(self):
        "display a higher slide"
        print("in increment_slide")
        self.slideIndex += 1
        if self.slideIndex > self.max_index:
            self.slideIndex = 0
            print('Max index hit')
        self.slide(self.slideIndex)
        return False

    def decrement_slide(self):
        "display a lower slide"
        self.slideIndex -= 1
        if self.slideIndex < 0:
            self.slideIndex = self.max_index
        self.slide(self.slideIndex)
        return False

    def checkImageType(self, f):
        "check to see if we have an file with an image extension"
        ext = self.extension(f)
        chk = [
            i for i in ['jpg', 'gif', 'ppm', 'tif', 'png', 'jpeg'] if i == ext
        ]
        if chk == []: return False
        return True

    def helpWindow(self):
        self.pixmap = QtGui.QPixmap()
        #self.pixmap.setAlignment(QtCore.Qt.AlignCenter)
        self.pixmap.load(self.helpFile)
        self.pixmap.setDevicePixelRatio(
            self.pixel_ratio
        )  # https://stackoverflow.com/questions/50127246/pyqt-5-10-enabling-high-dpi-support-for-macos-poor-pixmap-quality
        #self.pixmap4 = self.pixmap.scaled(self.width * self.pixel_ratio, (self.height * self.pixel_ratio)-45, Qt.KeepAspectRatio)
        self.pixmap4 = self.pixmap.scaled(self.width * self.pixel_ratio,
                                          (self.height * self.pixel_ratio),
                                          Qt.KeepAspectRatio)
        try:
            self.scene.removeItem(self.item)
        except:
            print("failed to remove item")
        self.item = QGraphicsPixmapItem(self.pixmap4)
        self.scene.addItem(self.item)
        #myapp.setWindowTitle(os.path.basename("Instructions"))
        self.setWindowTitle(os.path.basename("Instructions"))
        self.ui.graphicsView.setScene(self.scene)

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Escape:
            self.Quit()
        if e.key() == Qt.Key_Q:
            self.Quit()
        if e.key() == Qt.Key_Space:
            self.next_slide()
        if e.key() == Qt.Key_N:
            self.random_next()
        if e.key() == Qt.Key_P:
            self.random_prev()
        if e.key() == Qt.Key_Comma:
            self.decrement_slide()
        if e.key() == Qt.Key_Period:
            self.increment_slide()
        if e.key() == Qt.Key_H:
            self.helpWindow = self.helpWindow()
        if e.key() == Qt.Key_BracketLeft:
            self.slideIndex = self.decrement_slide()

    def mousePressEvent(self, e):
        if e.button() == QtCore.Qt.LeftButton:
            print("trapped left mouse click")
            self.next_slide()
        if e.button() == QtCore.Qt.RightButton:
            print("trapped right mouse click")
            self.prev_slide()

    def Quit(self):
        sys.exit(app.exec_())