Exemplo n.º 1
0
class ConfigDialog(QDialog):
    def __init__(self, parent=None):
        super(ConfigDialog, self).__init__(parent)
        self.classifyExercises = parent.classifyExercises
        self.setFixedSize(500, 400)
        self.setWindowTitle("Model Configurations")

        self.epochValue = QLabel()
        self.vbox = QVBoxLayout()
        self.label_maximum = QLabel()
        self.label_minimum = QLabel()
        self.slider_hbox = QHBoxLayout()
        self.slider_vbox = QVBoxLayout()
        self.batchSizeMenu = QComboBox()
        self.properties = QFormLayout()
        self.epochSlider = Slider(orientation=Qt.Orientations.Horizontal)

        self.trainButton = QPushButton('Train Model')
        self.resultButton = QPushButton('Show result image')
        self.progress = QProgressBar()

        self.batchSizeMenu.addItems(['2', '4', '8', '16', '32', '64', '128'])
        self.batchSizeMenu.setCurrentIndex(3)
        self.batchSizeMenu.setMaximumWidth(100)

        self.initSlider()

        self.properties.addRow('Batch Size', self.batchSizeMenu)

        self.resultButton.setEnabled(False)
        self.actionsLayout = QHBoxLayout()

        self.actionsLayout.addWidget(self.trainButton)
        self.actionsLayout.addWidget(self.resultButton)

        self.optionsLayout = QVBoxLayout()
        self.optionsLayout.addWidget(QLabel('Model properties'))
        self.optionsLayout.addLayout(self.vbox)
        self.optionsLayout.addLayout(self.properties)
        self.optionsLayout.addLayout(self.actionsLayout)
        self.progress.setAlignment(QtCore.Qt.Alignment.AlignCenter)
        self.optionsLayout.addWidget(self.progress)
        # self.options_layout.addWidget(self.label)
        # self.options_layout.addWidget(self.list_widget)

        self.setLayout(self.optionsLayout)

        self.trainThread = TrainThread(self.classifyExercises)
        self.connections()
        print("init config")

    def initSlider(self):
        self.epochValue.setAlignment(QtCore.Qt.Alignment.AlignHCenter)

        self.epochSlider.setMinimum(2)
        self.epochSlider.setMaximum(10)
        self.epochSlider.setTickInterval(1)
        # self.epochSlider.setInterval(1)
        # self.epochSlider.setValue(8)  # no idea why, but 8 is the middle somehow
        self.epochSlider.setSliderPosition(6)
        self.epochSlider.setTickPosition(QSlider.TickPosition.TicksBelow)

        self.label_minimum.setNum(self.epochSlider.minimum().real * 50)
        self.label_minimum.setAlignment(QtCore.Qt.Alignment.AlignLeft)
        self.label_maximum.setNum(self.epochSlider.maximum().real * 50)

        self.label_maximum.setAlignment(QtCore.Qt.Alignment.AlignRight)

        self.epochSlider.minimumChanged.connect(self.label_minimum.setNum)
        self.epochSlider.maximumChanged.connect(self.label_maximum.setNum)

        self.slider_hbox.addWidget(self.label_minimum,
                                   QtCore.Qt.Alignment.AlignLeft)
        self.slider_hbox.addWidget(self.epochValue)
        self.slider_hbox.addWidget(self.label_maximum,
                                   QtCore.Qt.Alignment.AlignRight)

        self.slider_vbox.addWidget(self.epochSlider)
        self.slider_vbox.addLayout(self.slider_hbox)
        # self.slider_vbox.addStretch()

        self.vbox.addLayout(self.slider_vbox)

    def connections(self):
        self.batchSizeMenu.currentIndexChanged.connect(
            self.onBatchSizeSelected)
        self.resultButton.clicked.connect(self.onResultClicked)
        self.trainButton.clicked.connect(self.onTrainClicked)
        self.epochSlider.valueChanged.connect(self.updateEpochValue)
        self.trainThread.taskFinished.connect(self.onFinished)

    def updateEpochValue(self, num):
        print(num)
        epochs = num * 50
        self.epochValue.setNum(epochs)
        self.classifyExercises.epochs = epochs

    def onBatchSizeSelected(self, ind):
        self.classifyExercises.training_batch_size = int(
            self.batchSizeMenu.currentText())

    def onResultClicked(self):
        print("open image")
        self.classifyExercises.DisplayResults()

    def onTrainClicked(self):
        if self.classifyExercises.subject is not None:
            if self.classifyExercises.DataAvailable():
                if self.resultButton.isEnabled:
                    self.resultButton.setEnabled(False)

                self.trainThread.start()
                self.progress.setRange(0, 0)
            else:
                CustomMessage.showDialog(
                    "Message", "Calibrate for patient to obtain data.",
                    QMessageBox.StandardButtons.Ok)

        else:
            CustomMessage.showDialog(
                "Message", "You must either select or enter a subject name.",
                QMessageBox.StandardButtons.Ok)
            print("Subject is none!")

    def onFinished(self):
        # Stop the progress
        self.progress.setRange(0, 1)
        self.progress.setValue(1)
        CustomMessage.showDialog("Message", "Training model finished!",
                                 QMessageBox.StandardButtons.Ok)
        self.resultButton.setEnabled(True)
Exemplo n.º 2
0
class B23Download(QWidget):
    def __init__(self):
        super(B23Download, self).__init__()
        # setup some flags
        self.is_fetching = False
        self.is_downloading = False

        # default output path
        basepath = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(basepath, "videos")
        self.output_path = path

        # setup some window specific things
        self.setWindowTitle("Bilibili Favorite Downloader")
        self.setWindowIcon(QIcon("images/icon_bilibili.ico"))
        self.setFixedSize(705, 343)

        # parent layout
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(15, 15, 15, 10)
        self.setLayout(main_layout)

        # top bar layout
        top_layout = QHBoxLayout()

        # detail section
        mid_main_layout = QHBoxLayout()
        mid_right_layout = QVBoxLayout()

        # download section
        bottom_main_layout = QHBoxLayout()
        bottom_right_layout = QVBoxLayout()

        # output path link button
        self.output_btn = QPushButton("📂  Output Path")
        self.output_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.output_btn.setToolTip(self.output_path)
        self.output_btn.clicked.connect(self.set_output_path)

        # status bar
        self.status_bar = QStatusBar()

        # message box
        self.message_box = QMessageBox()

        # setting up widgets
        self.url_edit = QLineEdit()
        self.url_edit.setPlaceholderText("🔍 Enter or paste favorite URL...")
        self.get_btn = QPushButton("Get")
        self.get_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.get_btn.clicked.connect(self.get_details)

        # thumbnail
        pixmap = QPixmap("images/placeholder.png")
        self.thumb = QLabel()
        self.thumb.setFixedSize(250, 141)
        self.thumb.setScaledContents(True)
        self.thumb.setPixmap(pixmap)

        # detail widgets
        self.title = QLabel("Title: ")
        self.author = QLabel("Author: ")
        self.length = QLabel("Videos: ")
        self.publish_date = QLabel("Published: ")

        # progress bar
        self.progress_bar = QProgressBar()

        # download options
        self.download_btn = QPushButton(" Download Videos ")
        self.download_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download_btn.clicked.connect(self.get_content)
        self.download_btn.setEnabled(False)
        self.download_btn.setShortcut("Ctrl+Return")
        self.download_btn.setMinimumWidth(200)

        # add widgets and layouts
        top_layout.addWidget(self.url_edit)
        top_layout.addWidget(self.get_btn)

        # detail section
        mid_right_layout.addWidget(self.title)
        mid_right_layout.addWidget(self.author)
        mid_right_layout.addWidget(self.length)
        mid_right_layout.addWidget(self.publish_date)
        mid_main_layout.addWidget(self.thumb)
        mid_main_layout.addSpacing(20)
        mid_main_layout.addLayout(mid_right_layout)

        # download section
        bottom_right_layout.addWidget(self.download_btn)
        bottom_main_layout.addWidget(self.progress_bar)
        bottom_main_layout.addSpacing(10)
        bottom_main_layout.addLayout(bottom_right_layout)

        # status bar
        self.status_bar.setSizeGripEnabled(False)
        self.status_bar.addPermanentWidget(self.output_btn)

        # add content to parent layout
        main_layout.addLayout(top_layout)
        main_layout.addSpacing(20)
        main_layout.addLayout(mid_main_layout)
        main_layout.addSpacing(5)
        main_layout.addLayout(bottom_main_layout)
        main_layout.addWidget(self.status_bar)

    # set output path slot
    def set_output_path(self):
        # update the output path
        path = str(
            QFileDialog.getExistingDirectory(self, "Select Output Directory"))
        if path:
            self.output_path = path
            # update tooltip
            self.output_btn.setToolTip(path)

    # get button slot
    def get_details(self):
        text = self.url_edit.text().strip()

        if not text:
            return

        if text.find("fid") < 0:
            self.message_box.warning(
                self,
                "Error",
                ("Input a correct favorite URL!\n"
                 "For example: https://space.bilibili.com/xxx/favlist?fid=xxx..."
                 ),
            )
            return

        if self.get_btn.text() == "Get":
            self.get_btn.setText("Stop")
            # indicate progress bar as busy
            self.progress_bar.setRange(0, 0)
            # set fetching flag
            self.is_fetching = True
            # setup a worker thread to keep UI responsive
            self.media_id = text.split("fid=")[-1].split("&")[0]
            self.worker = WorkerThread(self.media_id)
            self.worker.start()
            # catch the finished signal
            self.worker.finished.connect(self.finished_slot)
            # catch the response signal
            self.worker.worker_response.connect(self.response_slot)
            # catch the error signal
            self.worker.worker_err_response.connect(self.err_slot)
        elif self.get_btn.text() == "Stop":
            if self.is_fetching:
                # stop worker thread
                self.worker.terminate()
                # set back the get_btn text
                self.get_btn.setText("Get")
            elif self.is_downloading:
                # stop download thread
                self.download_thread.terminate()
                # show the warning message_box
                self.message_box.information(
                    self,
                    "Interrupted",
                    "Download interrupted!\nThe process was aborted while the file was being downloaded... ",
                )
                # reset progress bar
                self.progress_bar.reset()

    # download options slot
    def get_content(self):
        if self.is_fetching:
            # show the warning message
            self.message_box.critical(
                self,
                "Error",
                "Please wait!\nWait while the details are being fetched... ",
            )
        else:
            # disable the download options
            self.download_btn.setDisabled(True)
            # set downloading flag
            self.is_downloading = True
            # set button to stop
            self.get_btn.setText("Stop")
            self.download_thread = DownloadThread(
                self.media_id,
                self.media_counts,
                self.first_page_medias,
                self.output_path,
            )
            # start the thread
            self.download_thread.start()
            # catch the finished signal
            self.download_thread.finished.connect(self.download_finished_slot)
            # catch the response signal
            self.download_thread.download_response.connect(
                self.download_response_slot)
            # catch the complete signal
            self.download_thread.download_complete.connect(
                self.download_complete_slot)
            # catch the error signal
            self.download_thread.download_err.connect(self.download_err_slot)

    # handling enter key for get/stop button
    def keyPressEvent(self, event):
        self.url_edit.setFocus()
        if (event.key() == Qt.Key.Key_Enter.value
                or event.key() == Qt.Key.Key_Return.value):
            self.get_details()

    # finished slot
    def finished_slot(self):
        # remove progress bar busy indication
        self.progress_bar.setRange(0, 100)
        # unset fetching flag
        self.is_fetching = False

    # response slot
    def response_slot(self, res):
        # set back the button text
        self.get_btn.setText("Get")
        # set the actual thumbnail of requested video
        self.thumb.setPixmap(res.thumb_img)
        # slice the title if it is more than the limit
        if len(res.title) > 50:
            self.title.setText(f"Title: {res.title[:50]}...")
        else:
            self.title.setText(f"Title: {res.title}")
        # cache first page medias
        self.first_page_medias = res.medias
        self.media_counts = res.media_counts
        # set leftover details
        self.author.setText(f"Author: {res.author}")
        self.length.setText(f"Videos: {res.media_counts}")
        self.publish_date.setText(
            f'Published: {time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(res.publish_date))}'
        )
        self.download_btn.setDisabled(False)

    # error slot
    def err_slot(self):
        # show the warning message
        self.message_box.warning(
            self,
            "Warning",
            "Something went wrong!\nProbably a broken link or some restricted content... ",
        )
        # set back the button text
        self.get_btn.setText("Get")

    # download finished slot
    def download_finished_slot(self):
        # set back the button text
        self.get_btn.setText("Get")
        # now enable the download options
        self.download_btn.setDisabled(False)
        # unset downloading flag
        self.is_downloading = False
        # reset pogress bar
        self.progress_bar.reset()

    # download response slot
    def download_response_slot(self, per):
        # update progress bar
        self.progress_bar.setValue(per)
        # adjust the font color to maintain the contrast
        if per > 52:
            self.progress_bar.setStyleSheet("QProgressBar { color: #fff }")
        else:
            self.progress_bar.setStyleSheet("QProgressBar { color: #000 }")

    # download complete slot
    def download_complete_slot(self, location):
        # use native separators
        location = QDir.toNativeSeparators(location)
        # show the success message
        if (self.message_box.information(
                self,
                "Downloaded",
                f"Download complete!\nFile was successfully downloaded to :\n{location}\n\nOpen the downloaded file now ?",
                QMessageBox.StandardButtons.Open,
                QMessageBox.StandardButtons.Cancel,
        ) is QMessageBox.StandardButtons.Open):
            subprocess.Popen(f"explorer /select,{location}")

    # download error slot
    def download_err_slot(self):
        # show the error message
        self.message_box.critical(
            self,
            "Error",
            "Error!\nSomething unusual happened and was unable to download...",
        )
Exemplo n.º 3
0
class YTdownloader(QWidget):
    def __init__(self):
        super().__init__()
        # setup some flags
        self.isFetching = False
        self.isDownloading = False

        # default output path
        self.outputPath = f'{QDir.homePath()}/videos'

        # setup some window specific things
        self.setWindowTitle('YouTube Downloader')
        self.setWindowIcon(QIcon('assets/yt-icon.ico'))
        self.setFixedSize(705, 343)

        # parent layout
        layout = QVBoxLayout()
        layout.setContentsMargins(15, 15, 15, 10)
        self.setLayout(layout)

        # top bar layout
        topBar = QHBoxLayout()

        # detail section
        detailSec = QHBoxLayout()
        metaSec = QVBoxLayout()

        # download section
        downloadSec = QHBoxLayout()
        downloadBtn = QVBoxLayout()

        # output path link button
        self.outputBtn = QPushButton('📂  Output Path')
        self.outputBtn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.outputBtn.setToolTip(self.outputPath)
        self.outputBtn.clicked.connect(self.setOutputPath)

        # status bar
        self.statusBar = QStatusBar()

        # message box
        self.message = QMessageBox()

        # setting up widgets
        self.urlBox = QLineEdit()
        self.urlBox.setFocusPolicy(Qt.FocusPolicy.ClickFocus or Qt.FocusPolicy.NoFocus)
        self.urlBox.setPlaceholderText('🔍 Enter or paste video URL...')
        self.button = QPushButton('Get')
        self.button.setDefault(True)
        self.button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.button.clicked.connect(self.getDetails)

        # thumbnail
        pixmap = QPixmap('assets\placeholder.jpg')
        self.thumb = QLabel()
        self.thumb.setFixedSize(250, 141)
        self.thumb.setScaledContents(True)
        self.thumb.setPixmap(pixmap)

        # detail widgets
        self.title = QLabel('Title: ')
        self.author = QLabel('Author: ')
        self.length = QLabel('Duration: ')
        self.publish_date = QLabel('Published: ')

        # progress bar
        self.progress_bar = QProgressBar()
        
        # download options
        self.download = QComboBox()
        self.download.setPlaceholderText('Download Video')
        self.download.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download.activated.connect(lambda: self.getContent(0))
        self.download.setEnabled(False)

        # download audio button
        self.download_audio = QPushButton('Download Audio')
        self.download_audio.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download_audio.clicked.connect(lambda: self.getContent(1))
        self.download_audio.setEnabled(False)

        # add widgets and layouts
        topBar.addWidget(self.urlBox)
        topBar.addWidget(self.button)

        # detail section
        metaSec.addWidget(self.title)
        metaSec.addWidget(self.author)
        metaSec.addWidget(self.length)
        metaSec.addWidget(self.publish_date)
        detailSec.addWidget(self.thumb)
        detailSec.addSpacing(20)
        detailSec.addLayout(metaSec)

        # download section
        downloadBtn.addWidget(self.download)
        downloadBtn.addWidget(self.download_audio)
        downloadSec.addWidget(self.progress_bar)
        downloadSec.addSpacing(10)
        downloadSec.addLayout(downloadBtn)

        # status bar
        self.statusBar.setSizeGripEnabled(False)
        self.statusBar.addPermanentWidget(self.outputBtn)

        # add content to parent layout
        layout.addLayout(topBar)
        layout.addSpacing(20)
        layout.addLayout(detailSec)
        layout.addSpacing(5)
        layout.addLayout(downloadSec)
        layout.addWidget(self.statusBar)

        # setup a connection thread to keep checking internet connectivity
        self.connection = ConnectionThread()
        self.connection.start()

        # catch the connection response signal
        self.connection.con_response.connect(self.connection_slot)

    # connection slot
    def connection_slot(self, status):
        curMsg = self.statusBar.currentMessage()
        # connection succeeded
        if status:
            if curMsg == '🔴  Disconnected':
                self.statusBar.showMessage('🟢  Connection restored!', 3000)
            elif curMsg != '🟢  Connected':
                self.statusBar.showMessage('🟢  Connected')
        # connection failed
        elif curMsg == '🟢  Connected':
            self.statusBar.showMessage('🔴  Connection interrupted!', 3000)
        elif curMsg != '🔴  Disconnected': 
            self.statusBar.showMessage('🔴  Disconnected')

    # set output path slot
    def setOutputPath(self):
        # update the output path
        path = str(QFileDialog.getExistingDirectory(self, "Select Output Directory"))
        if path:
            self.outputPath = path
            # update tooltip
            self.outputBtn.setToolTip(path)

    # get button slot
    def getDetails(self):
        curMsg = self.statusBar.currentMessage()
        if curMsg == '🔴  Disconnected' or curMsg == '🔴  Connection interrupted!':
            self.message.critical(
                self,
                'Error',
                'Connection failed!\nAre you sure you\'re connected to the internet ? '
            )
        elif self.button.text() == 'Get':
            self.button.setText('Stop')
            # indicate progress bar as busy
            self.progress_bar.setRange(0, 0)
            # set fetching flag
            self.isFetching = True
            # setup a worker thread to keep UI responsive
            self.worker = WorkerThread(self.urlBox.text())
            self.worker.start()
            # catch the finished signal
            self.worker.finished.connect(self.finished_slot)
            # catch the response signal
            self.worker.worker_response.connect(self.response_slot)
            # catch the error signal
            self.worker.worker_err_response.connect(self.err_slot)
        elif self.button.text() == 'Stop':
            if self.isFetching:
                # stop worker thread
                self.worker.terminate()
                # set back the button text
                self.button.setText('Get')
            elif self.isDownloading:
                # stop download thread
                self.download_thread.terminate()
                # show the warning message
                self.message.information(
                    self,
                    'Interrupted',
                    'Download interrupted!\nThe process was aborted while the file was being downloaded... '
                )
                # reset pogress bar
                self.progress_bar.reset()

    # download options slot
    def getContent(self, id):
        if self.isFetching:
            # show the warning message
            self.message.warning(
                self,
                'Warning',
                'Please wait!\nWait while the details are being fetched... '
            )
        else:
            # disable the download options
            self.download.setDisabled(True)
            self.download_audio.setDisabled(True)
            # set downloading flag
            self.isDownloading = True
            # set button to stop 
            self.button.setText('Stop')
            # setup download thread
            if id == 0:
                self.download_thread = DownloadThread(self.yt, self.download.currentText()[:4], self.outputPath)
            else:
                self.download_thread = DownloadThread(self.yt, 'audio', self.outputPath)
            # start the thread
            self.download_thread.start()
            # catch the finished signal
            self.download_thread.finished.connect(self.download_finished_slot)
            # catch the response signal
            self.download_thread.download_response.connect(self.download_response_slot)
            # catch the complete signal
            self.download_thread.download_complete.connect(self.download_complete_slot)
            # catch the error signal
            self.download_thread.download_err.connect(self.download_err_slot)

    # finished slot
    def finished_slot(self):
        # remove progress bar busy indication
        self.progress_bar.setRange(0, 100)
        # unset fetching flag
        self.isFetching = False

    # response slot
    def response_slot(self, res):
        # set back the button text
        self.button.setText('Get')
        # save the yt object for speeding up download
        self.yt = res[0]
        # set the actual thumbnail of requested video
        self.thumb.setPixmap(res[1])
        # slice the title if it is more than the limit
        if len(res[2]) > 50:
            self.title.setText(f'Title:  {res[2][:50]}...')
        else:
            self.title.setText(f'Title:  {res[2]}')
        # set leftover details
        self.author.setText(f'Author:  {res[3]}')
        self.length.setText(f'Duration:  {timedelta(seconds=res[4])}')
        self.publish_date.setText(f'Published:  {res[5].strftime("%d/%m/%Y")}')
        # clear any previous items if any
        self.download.clear()
        # add resolutions as items to the download button and enable them
        self.download.addItems([item for item in res[6]])
        self.download.setDisabled(False)
        self.download_audio.setDisabled(False)

    # error slot
    def err_slot(self):
        # show the warning message
        self.message.warning(
            self,
            'Warning',
            'Something went wrong!\nProbably a broken link or some restricted content... '
        )
        # set back the button text
        self.button.setText('Get')

    # download finished slot
    def download_finished_slot(self):
        # set back the button text
        self.button.setText('Get')
        # now enable the download options
        self.download.setDisabled(False)
        self.download_audio.setDisabled(False)
        # unset downloading flag
        self.isDownloading = False
        # reset pogress bar
        self.progress_bar.reset()

    # download response slot
    def download_response_slot(self, per):
        # update progress bar
        self.progress_bar.setValue(per)
        # adjust the font color to maintain the contrast
        if per > 52:
            self.progress_bar.setStyleSheet('QProgressBar { color: #fff }')
        else:
            self.progress_bar.setStyleSheet('QProgressBar { color: #000 }')
    
    # download complete slot
    def download_complete_slot(self, location):
        # use native separators
        location = QDir.toNativeSeparators(location)
        # show the success message
        if self.message.information(
            self,
            'Downloaded',
            f'Download complete!\nFile was successfully downloaded to :\n{location}\n\nOpen the downloaded file now ?',
            QMessageBox.StandardButtons.Open,
            QMessageBox.StandardButtons.Cancel
        ) is QMessageBox.StandardButtons.Open: subprocess.Popen(f'explorer /select,{location}')

    # download error slot
    def download_err_slot(self):
        # show the error message
        self.message.critical(
            self,
            'Error',
            'Error!\nSomething unusual happened and was unable to download...'
        )
Exemplo n.º 4
0
class CalibrateWizard(QWizard):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.setWizardStyle(QWizard.WizardStyle.ModernStyle)

        # CREATE PAGE 1, LINE EDIT, TITLES
        buttons_layout = [QWizard.WizardButton.NextButton]
        self.page1 = QWizardPage()
        self.page1.setTitle('Select the exercises you wish to do later')
        self.page1.setSubTitle(
            'Below are listed all the available and selected exercises by you.'
        )
        self.listSelection = TwoListSelection()
        # listSelection.addAvailableItems(["item-{}".format(i) for i in range(5)])
        hLayout1 = QHBoxLayout(self.page1)
        hLayout1.addWidget(self.listSelection)

        # CREATE PAGE 2, LABEL, TITLES
        self.page2 = QWizardPage()
        self.page2.setFinalPage(True)
        self.setButtonLayout(buttons_layout)
        self.page2.setTitle('Calibrate every exercise')
        self.page2.setSubTitle(
            'Do every exercise once, record after pressing button.')
        self.contentLayout = QVBoxLayout(self.page2)
        self.hLayout2 = QHBoxLayout()

        # Create progress bar, buttons
        self.actionsLayout = QHBoxLayout()
        self.finishButton = QPushButton('Ready')
        self.finishButton.setStyleSheet(CustomQStyles.buttonStyle)
        self.finishButton.setFixedSize(120, 35)

        self.progress = QProgressBar()
        self.progress.setRange(0, 1)
        self.actionsLayout.addWidget(self.progress)
        self.actionsLayout.setAlignment(self.progress,
                                        Qt.Alignment.AlignBottom)
        self.actionsLayout.addWidget(self.finishButton)
        self.actionsLayout.setAlignment(self.finishButton,
                                        Qt.Alignment.AlignBottom)

        self.contentLayout.addLayout(self.hLayout2)
        self.contentLayout.addLayout(self.actionsLayout)
        self.actionsLayout.setContentsMargins(15, 35, 15, 0)
        itemsTextList = [
            str(self.listSelection.mInput.item(i).text())
            for i in range(self.listSelection.mInput.count())
        ]
        print("items:", itemsTextList)

        self.button(QWizard.WizardButton.NextButton).clicked.connect(
            self.onWizardNextButton)
        self.finishButton.clicked.connect(self.onWizardFinishButton)

        self.addPage(self.page1)
        self.addPage(self.page2)

        # Recording data
        self.buttons = []
        self.images = []
        self.labels = []
        self.exerciseLayouts = []
        self.recordReady = []
        self.recordThread = RecordThread(self.parent.classifyExercises)

        # Training recorded data
        self.trained = False
        self.trainThread = TrainThread(self.parent.classifyExercises)
        self.trainThread.taskFinished.connect(self.onTrainFinished)

    # Send list to next page
    def onWizardNextButton(self):
        self.setPage(1, self.page1)
        self.trained = False
        itemsTextList = [
            str(self.listSelection.mInput.item(i).text())
            for i in range(self.listSelection.mInput.count())
        ]
        # Update list
        if self.parent.classifyExercises is not None:
            self.parent.classifyExercises.UpdateExerciseList(itemsTextList)

        # Set elements on UI
        self.setMinimumWidth(len(itemsTextList) * 200)
        self.deleteItemsOfLayout(self.hLayout2)
        self.images.clear()
        self.labels.clear()
        self.buttons.clear()
        self.recordReady.clear()
        for x, i in zip(itemsTextList, range(len(itemsTextList))):
            self.exerciseLayouts.append(QVBoxLayout())
            self.buttons.append(QPushButton('Record'))
            self.recordReady.append(False)
            image = QLabel()
            image.setPixmap(
                QPixmap(os.getcwd() + "/resources/images/" + itemsTextList[i] +
                        ".png"))
            self.labels.append(QLabel(itemsTextList[i]))
            self.images.append(image)
            self.buttons[i].setFixedSize(100, 35)
            self.buttons[i].clicked.connect(
                functools.partial(self.onRecordExerciseButtonClicked, x, i))
            self.buttons[i].setStyleSheet(CustomQStyles.outlineButtonStyle)
            self.exerciseLayouts[i].addWidget(self.labels[i])
            self.exerciseLayouts[i].addWidget(self.images[i])
            self.exerciseLayouts[i].addWidget(self.buttons[i])
            self.exerciseLayouts[i].setAlignment(self.labels[i],
                                                 Qt.Alignment.AlignCenter)
            self.exerciseLayouts[i].setAlignment(self.images[i],
                                                 Qt.Alignment.AlignCenter)
            self.exerciseLayouts[i].setAlignment(self.buttons[i],
                                                 Qt.Alignment.AlignCenter)
            self.hLayout2.addLayout(self.exerciseLayouts[i])

    def onRecordExerciseButtonClicked(self, exercise, ind):
        print("Recording - ", exercise)
        if self.parent.classifyExercises is not None:
            self.recordThread.exercise = exercise
            self.recordThread.taskFinished.connect(
                functools.partial(self.recordFinished, exercise, ind),
                Qt.ConnectionType.SingleShotConnection)
            self.recordThread.start()
            self.recordReady[ind] = False
            self.buttons[ind].setStyleSheet(CustomQStyles.recordButtonStyle)
            self.images[ind].setPixmap(
                QPixmap(os.getcwd() + "/resources/images/" + exercise +
                        ".png"))

    def recordFinished(self, exercise, index):
        imagePath = os.getcwd() + "/resources/images/" + exercise + ".png"
        if self.recordThread.result == 0:
            imagePath = os.getcwd(
            ) + "/resources/images/" + exercise + "-fail.png"
        elif self.recordThread.result == 1:
            imagePath = os.getcwd(
            ) + "/resources/images/" + exercise + "-success.png"
            self.recordReady[index] = True
        else:
            print("None.")
        self.images[index].setPixmap(QPixmap(imagePath))
        self.buttons[index].setStyleSheet(CustomQStyles.outlineButtonStyle)
        print(self.recordReady)

    def onWizardFinishButton(self):
        if all(x == True for x in self.recordReady):
            print("All recorded!")
            if not self.trained:
                if self.parent.classifyExercises is not None:
                    self.progress.setRange(0, 0)  # indefinite progress bar
                    self.parent.classifyExercises.SaveProcessedData()
                    self.parent.classifyExercises.SavePatientData()
                    self.parent.ui.loadPatientList()
                    self.trainThread.start()

            else:
                self.close()

        else:
            print("Not all recorded!")

    def onTrainFinished(self):
        self.progress.setRange(0, 1)
        self.progress.setValue(1)
        self.trained = True
        CustomMessage.showDialog("Message", "Training model finished!",
                                 QMessageBox.StandardButtons.Ok)
        self.finishButton.setText('Finish')

    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())