Пример #1
0
class Ui_TrainPanel(object):
    def setupUi(self, TrainPanel):
        self.parent = TrainPanel
        self.mainLayout = QHBoxLayout(TrainPanel)

        self.bigFont = QLabel().font()
        self.bigFont.setPointSize(13)

        self.actionsLayout = QHBoxLayout(TrainPanel)
        # self.checkRecording = QCheckBox(TrainPanel)
        self.calibrateButton = QPushButton("Calibrate", parent=TrainPanel)
        self.calibrateButton.setFont(self.bigFont)
        self.calibrateButton.setStyleSheet(CustomQStyles.buttonStyle)
        self.calibrateButton.setMinimumHeight(50)
        self.calibrateButton.setMaximumWidth(170)
        self.calibrateButton.setMinimumWidth(120)

        self.sessionButton = QPushButton("Session", parent=TrainPanel)
        self.sessionButton.setFont(self.bigFont)
        self.sessionButton.setStyleSheet(CustomQStyles.outlineButtonStyle)
        self.sessionButton.setMinimumHeight(50)
        self.sessionButton.setMaximumWidth(170)
        self.sessionButton.setMinimumWidth(120)

        self.listFiles = QListWidget(TrainPanel)
        self.listFiles.setFont(self.bigFont)
        self.label = QLabel('or select', parent=TrainPanel)
        self.subjectLayout = QVBoxLayout(TrainPanel)
        print("init")

        self.box1 = QGroupBox(parent=TrainPanel)
        self.box2 = QGroupBox(parent=TrainPanel)
        self.wizard = CalibrateWizard(parent=TrainPanel)

        self.setPatientsBox(TrainPanel)  # right panel
        self.setActionsBox(TrainPanel)  # left panel

        self.mainLayout.addWidget(self.box1, stretch=2)
        self.mainLayout.addWidget(self.box2, stretch=3)
        # self.mainLayout.setAlignment(self.box2, Qt.Alignment.AlignCenter)

    # Display progress bar, checkbox - to record new gestures or not, start train
    def setActionsBox(self, TrainPanel):
        hLayout = QVBoxLayout()
        hLayout.addWidget(self.calibrateButton)
        hLayout.addWidget(self.sessionButton)
        hLayout.setAlignment(self.calibrateButton, Qt.Alignment.AlignCenter)
        hLayout.setAlignment(self.sessionButton, Qt.Alignment.AlignCenter)
        self.box2.setLayout(hLayout)

    # Display list of subjects, or new subject
    def setPatientsBox(self, TrainPanel):

        # Button to create new patient
        self.addPatient = QPushButton('Create Patient')
        self.addPatient.setFixedSize(120, 35)
        self.addPatient.setStyleSheet(CustomQStyles.outlineButtonStyle)

        self.label.setSizePolicy(QSizePolicy.Policy.Expanding,
                                 QSizePolicy.Policy.Expanding)
        self.label.setAlignment(QtCore.Qt.Alignment.AlignCenter)

        self.loadPatientList()

        self.listFiles.setStyleSheet(CustomQStyles.listStyle)
        self.listFiles.setFocusPolicy(Qt.FocusPolicy.NoFocus)

        self.subjectLayout.addWidget(self.addPatient)
        self.subjectLayout.setAlignment(self.addPatient,
                                        Qt.Alignment.AlignCenter)
        self.subjectLayout.addWidget(self.label)
        self.subjectLayout.addWidget(self.listFiles)

        self.box1.setLayout(self.subjectLayout)

    def loadPatientList(self):
        self.listFiles.clear()
        self.parent.patients.clear()
        files = [
            f for f in listdir(PATIENTS_PATH) if isfile(join(PATIENTS_PATH, f))
        ]
        for x, ind in zip(files, range(0, len(files))):
            item = QListWidgetItem(x.split('.')[0])
            item.setTextAlignment(Qt.Alignment.AlignHCenter)
            self.listFiles.addItem(item)
            with open(PATIENTS_PATH + x, 'r') as f:
                person_dict = json.load(f)
                patient = Patient(person_dict['Name'], person_dict['Age'],
                                  person_dict['Exercises'])
                self.parent.patients.append(patient)

        print(self.parent.patients)

    def deleteItemsOfLayout(self, layout):
        if layout is not None:
            while layout.count():
                item = layout.takeAt(0)
                widget = item.widget()
                if widget is not None:
                    widget.setParent(None)
                else:
                    self.deleteItemsOfLayout(item.layout())
class MainWindow(QMainWindow):

    def __init__(self):
        """ MainWindow Constructor """
        super().__init__()
        self.initializeUI()
        
    def initializeUI(self):
        """Set up the GUI's main window."""        
        self.setMinimumSize(700, 400)
        self.setWindowTitle("GIF and Image Viewer")

        # Set up the main window, menu, and dock widget
        self.setUpMainWindow()
        self.displayFilesDock()
        self.createActions()
        self.createMenus()
        self.createToolbar()
        self.show() # Display the main window

    def setUpMainWindow(self):
        """Set up the application's main window and widgets."""
        self.movie = QMovie() # Create movie object
        self.movie.stateChanged.connect(self.changeButtonStates)
        
        self.media_label = QLabel() # Create label to place images/GIFs on
        self.media_label.setPixmap(QPixmap("icons/image_label.png"))
        self.media_label.setFrameShape(QFrame.Shape.StyledPanel)
        self.media_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        # Prevent the label from resizing and affecting the 
        # dock widget when viewing images
        self.media_label.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Expanding)

        self.setCentralWidget(self.media_label)

    def createActions(self):
        """Create the application's menu actions."""
        # Create actions for File menu
        self.open_act = QAction("Open...", self, triggered=self.openDirectory)
        self.open_act.setShortcut(QKeySequence.StandardKey.Open) 

        self.quit_act = QAction("Quit Viewer", self, triggered=self.close)
        self.quit_act.setShortcut(QKeySequence.StandardKey.Quit) # Ctrl+Q

        # Create actions for View menu
        # Handle the visibility of the dock widget
        self.show_dock_act = self.files_dock.toggleViewAction()
        self.show_dock_act.setText("Show Media Folder") 

        # Create actions for the toolbar (These actions could also be 
        # added to the GUI's menu bar or to a context menu)
        self.play_act = QAction(QIcon("icons/play.png"), "Play", self, triggered=self.startMovie)
        self.pause_act = QAction(QIcon("icons/pause.png"), "Pause", self, triggered=self.pauseMovie)
        self.stop_act = QAction(QIcon("icons/stop.png"), "Stop/Reset", self, triggered=self.stopMovie)
        self.disableMovieButtons()

    def createMenus(self):
        """Create the application's menu."""
        # Make the toolbar appear in the main window for macOS users.
        # More information about this in Chapter 2 - Building the Foundation for GUIs
        if QSysInfo.productType() == "macos" or "osx":
            self.menuBar().setNativeMenuBar(False)

        self.file_menu = self.menuBar().addMenu("&File")
        self.file_menu.addAction(self.open_act)
        self.file_menu.addSeparator()
        self.file_menu.addAction(self.quit_act)

        self.view_menu = self.menuBar().addMenu("&View")
        self.view_menu.addAction(self.show_dock_act)      

    def createToolbar(self):
        """Create the application's toolbar for playing GIFs."""
        toolbar = self.addToolBar("GIF Controls Toolbar")
        toolbar.setIconSize(QSize(24, 24))

        # Add actions to the toolbar
        toolbar.addAction(self.play_act)
        toolbar.addAction(self.pause_act)
        toolbar.addAction(self.stop_act)

    def displayFilesDock(self):
        """Dock widget that displays the movie file location in a QLineEdit 
        widget, provides a button for opening directories with images and GIFs, 
        and shows the media from the selected folder in a QTreeWidget."""
        self.files_dock = QDockWidget()
        self.files_dock.setWindowTitle("Media Folder")
        self.files_dock.setAllowedAreas(Qt.DockWidgetArea.LeftDockWidgetArea)

        folder_label = QLabel("Media Location:")
        # The QLineEdit widget is set to read-only as a quick way to display 
        # the folder path
        self.folder_line = QLineEdit()
        self.folder_line.setMinimumWidth(100)
        self.folder_line.setReadOnly(True)

        open_button = QPushButton("Open...")
        open_button.clicked.connect(self.openDirectory)

        folder_h_box = QHBoxLayout()
        folder_h_box.addWidget(folder_label)
        folder_h_box.addWidget(self.folder_line)
        folder_h_box.addWidget(open_button)

        self.files_tree = QTreeWidget()
        self.files_tree.setHeaderLabel("Media Files")
        self.files_tree.setColumnCount(1)
        self.files_tree.itemSelectionChanged.connect(self.displayMediaFile)

        # Set up the dock's layout
        dock_v_box = QVBoxLayout()
        dock_v_box.addLayout(folder_h_box)
        dock_v_box.addWidget(self.files_tree)

        dock_container = QWidget()
        dock_container.setLayout(dock_v_box)

        self.files_dock.setWidget(dock_container)
        self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, self.files_dock)

    def openDirectory(self):
        """Open a QFileDialog for selecting a local directory. Only display 
        image and GIF files.""" 
        directory = QFileDialog.getExistingDirectory(self, "Choose Directory", "", 
            QFileDialog.Option.ShowDirsOnly) # Specify the file mode to only select directories

        if directory:
            # Display the selected folder text in the QLineEdit. There are other, and 
            # possibly better, ways to handle displaying the text, but for simplicity, 
            # this is the method used in this GUI
            self.folder_line.setText(directory)

            # Get the contents of the directory and only display files with
            # specified extensions
            file_dir = QDir(directory)
            filters = ["*.gif", "*.png", "*.jpg", "*.jpeg"]
            files_info = file_dir.entryInfoList(filters, QDir.Filter.Files)

            # Clear the contents of the QTreeWidget
            if self.files_tree.model().rowCount() > 0:         
                # NOTE: Since files_tree is connected to the itemSelectionChanged signal,
                # using the clear() method below will also cause the signal to be emitted. 
                # This causes an undesired issue because of how the items are deleted. To avoid
                # the signal being called, QObject.blockSignals() will halt files_tree from 
                # emitting signals while removing items
                self.files_tree.blockSignals(True)
                # Use the convenience method clear() provided by QTreeWidget to remove 
                # all items and selections
                self.files_tree.clear() 
                self.files_tree.blockSignals(False)

                # Reset the QLabel and its image, and disable the movie buttons (in case the 
                # last item selected was a GIF)
                self.media_label.clear()
                self.media_label.setPixmap(QPixmap("icons/image_label.png"))
                self.disableMovieButtons()

            # Create items for each file and add them to the tree
            for file in files_info:
                item = QTreeWidgetItem()
                item.setText(0, file.fileName())
                self.files_tree.addTopLevelItem(item)

    def displayMediaFile(self): 
        """Display the selected media file on the QLabel. Used instead of 
        the itemClicked signal to handle whether the user clicks on an item
        or if arrow keys are used to navigate the items in the tree."""  
        # Check the state of the movie, i.e. check if a movie is playing. If it is, 
        # stop the movie
        if self.movie.state() == QMovie.MovieState.Running:
            self.stopMovie()

        # Get the text from the QLineEdit, folder_line, and concatenate it with 
        # the selected item's text
        column = self.files_tree.currentColumn()
        media_location = self.folder_line.text() + "/" + self.files_tree.selectedItems()[0].text(column)
        
        if media_location.split(".")[1] == "gif":
            self.movie.setFileName(media_location)
            # Check if image data is valid before playing
            if self.movie.isValid(): 
                # Use setMovie() to set the label's contents as the selected GIF
                self.media_label.setMovie(self.movie)
                self.startMovie() # Call method to begin playing
        else:
            # Disable all buttons when an image is selected
            self.disableMovieButtons()

            # Set the label's pixmap (have the image fit the current size of the image label)
            self.media_label.setPixmap(QPixmap(media_location).scaled(self.media_label.size(), 
                Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))  

    def startMovie(self):
        """Start playing the movie."""
        self.movie.start() 

    def pauseMovie(self):
        """Pause the movie."""
        self.movie.setPaused(True)

    def stopMovie(self):
        """Stop playing the movie and reset the movie back to 
        the first frame."""
        self.movie.stop()
        self.movie.jumpToFrame(0)

    def changeButtonStates(self, state):
        """Slot that handles enabling/disabling buttons in the toolbar
        based on the state of QMovie."""
        if state == QMovie.MovieState.Running:
            # The animation begins playing once control returns to the event loop
            self.play_act.setEnabled(False)
            self.pause_act.setEnabled(True)
            self.stop_act.setEnabled(True)
        if state == QMovie.MovieState.Paused:
            self.play_act.setEnabled(True)
            self.pause_act.setEnabled(False)
            self.stop_act.setEnabled(False)
        if state == QMovie.MovieState.NotRunning:
            self.play_act.setEnabled(True)
            self.pause_act.setEnabled(False)
            self.stop_act.setEnabled(False)

    def disableMovieButtons(self):
        """Simple method to disable the movie buttons in the toolbar."""
        self.play_act.setEnabled(False)
        self.pause_act.setEnabled(False)
        self.stop_act.setEnabled(False)