Exemplo n.º 1
0
class VideoFrame(QWidget):

    def __init__(self, *args, **kwargs):
        super(VideoFrame, self).__init__(*args, **kwargs)
        self.layout = QVBoxLayout()
        self.img_list = []

        # STREAM 1 LABEL AREA
        self.label = QLabel('Nothing to show right now.', self)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setGeometry(30, 20, 750, 360)
        self.layout.addWidget(self.label)

        # PLAY BUTTON
        self.play = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'play.png'))
        self.play.setIcon(QIcon(icon))
        self.play.setGeometry(320, 370, 50, 30)
        self.play.clicked.connect(self.timerEvent)
        self.layout.addWidget(self.play)

        # PAUSE BUTTON
        self.pause = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'pause.png'))
        self.pause.setIcon(QIcon(icon))
        self.pause.setGeometry(400, 370, 50, 30)
        self.pause.clicked.connect(self.pauseTimer)
        self.layout.addWidget(self.pause)

        # FAST-FORWARD
        self.ff = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'ff.png'))
        self.ff.setIcon(QIcon(icon))
        self.ff.setGeometry(560, 370, 50, 30)
        self.ff.clicked.connect(self.fastForward)
        self.layout.addWidget(self.ff)

        # SLOWDOWN
        self.fr = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'fr.png'))
        self.fr.setIcon(QIcon(icon))
        self.fr.setGeometry(240, 370, 50, 30)
        self.fr.clicked.connect(self.slowdown)
        self.layout.addWidget(self.fr)

        self.stop = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'stop.png'))
        self.stop.setIcon(QIcon(icon))
        self.stop.setGeometry(480, 370, 50, 30)
        self.stop.clicked.connect(self.stopTimer)
        self.layout.addWidget(self.stop)

        # SLIDER 1
        self.slider = QSlider(Qt.Horizontal, self)
        self.layout.addWidget(self.slider)
        self.slider.setGeometry(10, 400, 825, 20)
        self.slider.valueChanged.connect(self.showImage)

        # RANGE SLIDER
        self.range_slider = QRangeSlider(self)
        self.range_slider.setFixedHeight(30)
        self.range_slider.setFixedWidth(825)
        self.range_slider.move(10, 420)
        self.range_slider.endValueChanged.connect(self.boundEnd)
        self.range_slider.startValueChanged.connect(self.boundStart)

        self.timer = QBasicTimer() # FIXME consider something with less lag
        self.startIdx = 0
        self.idx = 0
        self.delay = 1000
        self.skip = 1

        self.calibrate_mode = False
        self.alpha = None
        self.beta = None
        self.t = None
        self.z = None

    def size(self):
        return len(self.img_list)

    def mousePressEvent(self, event):
        print('Calibration: ' + str(self.calibrate_mode))
        if self.calibrate_mode is True:
            c_x, c_y = (event.x(), event.y())
            self.alpha, self.beta = alpha_beta(c_x, c_y)
            self.t = lp_projection_calibration(self.alpha, self.beta)
        elif self.t is not None:
            c_x, c_y = (event.x(), event.y())
            self.alpha, self.beta = alpha_beta(c_x, c_y)
            self.z = lp_projection(self.alpha, self.beta, self.t)
            print('Height (inches): ' + str(self.z))

    def setCalibrateMode(self):
        self.calibrate_mode = not self.calibrate_mode

    def boundEnd(self):
        self.endIdx = self.range_slider.end()

    def boundStart(self):
        self.startIdx = self.range_slider.start()
        self.idx = self.startIdx

    def setSkipRate(self, skip_rate):
        self.skip = skip_rate

    def setFrameRate(self, delay):
        self.delay = delay

    def setImgList(self, img_list):
        self.img_list = img_list
        self.endIdx = len(self.img_list)-1
        self.slider.setRange(0, len(self.img_list)-1)
        self.range_slider.setMax(len(self.img_list)-1)
        pixmap = QPixmap(self.img_list[self.idx]).scaled(600, 800, Qt.KeepAspectRatio, Qt.FastTransformation)
        #self.pixmap_list = [QPixmap(self.img_list[i]).scaled(600, 800, Qt.KeepAspectRatio, Qt.FastTransformation) for i in range(0, len(self.img_list))]
        self.label.setPixmap(pixmap)

    def showImage(self):
        if len(self.img_list) == 0:
            return
        self.idx = self.slider.value()
        pixmap = QPixmap(self.img_list[self.idx]).scaled(600, 800, Qt.KeepAspectRatio, Qt.FastTransformation)
        self.label.setPixmap(pixmap)

    def showImage(self, idx):
        if len(self.img_list) == 0 or idx > len(self.img_list)-1:
            return
        self.idx = idx
        self.slider.setValue(idx)
        pixmap = QPixmap(self.img_list[idx]).scaled(600, 800, Qt.KeepAspectRatio, Qt.FastTransformation)
        self.label.setPixmap(pixmap)

    def timerEvent(self, e=None):
        if len(self.img_list) == 0:
            return
        if self.idx > self.endIdx:
            self.timer.stop()
            self.idx = self.startIdx
            return
        self.timer.start(self.delay, self)
        self.showImage(self.idx)
        #self.label.setPixmap(self.pixmap_list[self.idx])
        self.slider.setValue(self.idx)
        self.idx += self.skip

    def pauseTimer(self):
        self.timer.stop()

    def stopTimer(self):
        self.timer.stop()
        self.startIdx = self.range_slider.start()
        self.idx = self.startIdx

    def fastForward(self):
        if len(self.img_list) == 0:
            return
        self.delay /= 2

    def slowdown(self):
        if len(self.img_list) == 0:
            return
        self.delay *= 2
Exemplo n.º 2
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setWindowIcon(
            QIcon(os.path.join(os.path.dirname(__file__), 'icons',
                               'plant.png')))

        self.setWindowTitle("Video Comparison GUI")
        self.statusBar()
        self.img_list = None
        self.cur_video_id = 0

        self.layout = QGridLayout()
        # Main Widget Init
        self.widget = QWidget()
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)
        self.setGeometry(250, 50, 1920, 1080)

        # Open File Dialog
        openFile = QAction('Open Image Directory', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open Image Directory')
        openFile.triggered.connect(self.fileDialog)

        change_framerate = QAction('Change Framerate...', self)
        change_framerate.setStatusTip(
            'Change Playback Framerate of Video (default: 1 frame/sec)')
        change_framerate.triggered.connect(self.setFrameRate)

        change_skiprate = QAction('Change Skip Rate...', self)
        change_skiprate.setStatusTip(
            'Change Frame Skip Rate in Selected Photos (default: 1)')
        change_skiprate.triggered.connect(self.setSkipRate)

        set_v_1 = QAction('Video 1...', self)
        set_v_1.setStatusTip('Set Video 1 (Top Left Corner) Settings')
        set_v_1.triggered.connect(self.setv1)

        set_v_2 = QAction('Video 2...', self)
        set_v_2.setStatusTip('Set Video 2 (Top Right Corner) Settings')
        set_v_2.triggered.connect(self.setv2)

        set_v_3 = QAction('Video 3...', self)
        set_v_3.setStatusTip('Set Video 3 (Bottom Left Corner) Settings')
        set_v_3.triggered.connect(self.setv3)

        set_v_4 = QAction('Video 4...', self)
        set_v_4.setStatusTip('Set Video 4 (Bottom Right Corner) Settings')
        set_v_4.triggered.connect(self.setv4)

        change_calibrate = QAction('Turn Calibration On/Off...', self)
        change_calibrate.setStatusTip(
            'Calibrate auto height detection to plant pot')
        change_calibrate.triggered.connect(self.calibrate)

        export_video = QAction('Export Video...', self)
        export_video.setStatusTip(
            'Export the current video frame with current settings')
        export_video.triggered.connect(self.export)

        # Menu Bar
        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        file_menu.addAction(openFile)
        file_menu.addAction(export_video)
        edit_menu = menubar.addMenu('&Edit')
        edit_menu.addAction(change_framerate)
        edit_menu.addAction(change_skiprate)
        video_menu = menubar.addMenu('&Video')
        video_menu.addAction(set_v_1)
        video_menu.addAction(set_v_2)
        video_menu.addAction(set_v_3)
        video_menu.addAction(set_v_4)
        calibrate_menu = menubar.addMenu('&Calibrate')
        calibrate_menu.addAction(change_calibrate)

        # Video Frame Objects
        self.v1 = VideoFrame()
        self.layout.addWidget(self.v1, 0, 0, 1, 2)
        self.v2 = VideoFrame()
        self.layout.addWidget(self.v2, 0, 2, 1, 2)
        self.v3 = VideoFrame()
        self.layout.addWidget(self.v3, 1, 0, 1, 2)
        self.v4 = VideoFrame()
        self.layout.addWidget(self.v4, 1, 2, 1, 2)
        self.video_list = [self.v1, self.v2, self.v3, self.v4]

        ## STREAM 1 LABEL AREA
        #self.label = QLabel('Nothing to show right now.')
        #self.label.setAlignment(Qt.AlignCenter)
        #self.layout.addWidget(self.label)

        self.bottomLayout = QVBoxLayout()

        # GLOBAL PLAY BUTTON
        #self.play = QPushButton("Play")
        #self.play.clicked.connect(self.timerEvent)
        #self.bottomLayout.addWidget(self.play)

        # SLOWDOWN
        #self.fr = QPushButton(self)
        #icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'fr.png'))
        #self.fr.setIcon(QIcon(icon))
        #self.fr.clicked.connect(self.slowdown)
        #self.layout.addWidget(self.fr, 2, 0, 1, 1)

        # GLOBAL PLAY BUTTON
        self.play = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'play.png'))
        self.play.setIcon(QIcon(icon))
        self.play.clicked.connect(self.timerEvent)
        self.layout.addWidget(self.play, 2, 0, 1, 1)

        # GLOBAL PAUSE BUTTON
        self.pause = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'pause.png'))
        self.pause.setIcon(QIcon(icon))
        self.pause.clicked.connect(self.pauseTimer)
        self.layout.addWidget(self.pause, 2, 1, 1, 1)

        # GLOBAL FAST-FORWARD
        self.ff = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'ff.png'))
        self.ff.setIcon(QIcon(icon))
        self.ff.clicked.connect(self.fastForward)
        self.layout.addWidget(self.ff, 2, 3, 1, 1)

        # GLOBAL STOP
        self.stop = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'stop.png'))
        self.stop.setIcon(QIcon(icon))
        self.stop.clicked.connect(self.stopTimer)
        self.layout.addWidget(self.stop, 2, 2, 1, 1)

        # GLOBAL SLIDER
        self.slider = QSlider(Qt.Horizontal)
        self.bottomLayout.addWidget(self.slider)
        self.slider.valueChanged.connect(self.showImage)

        # GLOBAL RANGE SLIDER
        self.range_slider = QRangeSlider()
        self.range_slider.setFixedHeight(20)
        self.bottomLayout.addWidget(self.range_slider)
        self.range_slider.endValueChanged.connect(self.boundEnd)
        self.range_slider.startValueChanged.connect(self.boundStart)

        self.layout.addLayout(self.bottomLayout, 3, 0, 1, 4)

        self.timer = QBasicTimer()  # FIXME consider something with less lag
        self.startIdx = 0
        self.idx = 0
        self.delay = 1000
        self.skip = 1

    def fileDialog(self):
        self.img_list, _ = QFileDialog.getOpenFileNames(
            self, 'Select one or more files to open', '/home',
            'Images (*.jpg)')
        self.img_list.sort()
        if len(self.img_list) == 0:
            return
        self.video_list[self.cur_video_id].setImgList(self.img_list)
        self.endIdx = max(self.video_list, key=lambda end: end.size() - 1)
        self.endIdx = self.endIdx.size() - 1
        self.slider.setRange(0, self.endIdx)
        self.range_slider.setMax(self.endIdx)

    def timerEvent(self):
        self.v1.timerEvent()
        self.v2.timerEvent()
        self.v3.timerEvent()
        self.v4.timerEvent()
        p1 = Process(target=self.v1.timerEvent(), args=())
        p2 = Process(target=self.v2.timerEvent(), args=())
        p3 = Process(target=self.v3.timerEvent(), args=())
        p4 = Process(target=self.v4.timerEvent(), args=())
        p1.start()
        p2.start()
        p3.start()
        p4.start()
        p1.join()
        p2.join()
        p3.join()
        p4.join()
        self.slider.setValue(self.idx)
        self.idx += self.skip

    def showImage(self):
        self.idx = self.slider.value()
        for i in range(len(self.video_list)):
            self.video_list[i].showImage(self.idx)

    def setFrameRate(self):
        delay, _ = QInputDialog.getDouble(self, 'Change Output Framerate...',
                                          'New Framerate (frames/second)',
                                          1000 / self.delay, 1, 100, 1)
        self.delay = 1000.0 / delay
        self.video_list[self.cur_video_id].setFrameRate(self.delay)

    def setSkipRate(self):
        self.skip, _ = QInputDialog.getInt(self, 'Change Skip Rate...',
                                           'New Skip Rate', self.skip, 1,
                                           10000, 1)
        self.video_list[self.cur_video_id].setSkipRate(self.skip)

    def boundEnd(self):
        self.endIdx = self.range_slider.end()

    def boundStart(self):
        self.startIdx = self.range_slider.start()
        self.idx = self.startIdx

    def setv1(self):
        self.cur_video_id = 0
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def setv2(self):
        self.cur_video_id = 1
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def setv3(self):
        self.cur_video_id = 2
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def setv4(self):
        self.cur_video_id = 3
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def pauseTimer(self):
        for i in range(len(self.video_list)):
            self.video_list[i].pauseTimer()

    def stopTimer(self):
        for i in range(len(self.video_list)):
            self.video_list[i].stopTimer()

    def fastForward(self):
        for i in range(len(self.video_list)):
            self.video_list[i].fastForward()

    def slowdown(self):
        for i in range(len(self.video_list)):
            self.video_list[i].slowdown()

    def calibrate(self):
        self.video_list[self.cur_video_id].setCalibrateMode()

    def export(self):
        with open('imglist.txt', 'w') as file_list:
            for idx in range(self.video_list[self.cur_video_id].startIdx,
                             len(self.video_list[self.cur_video_id].img_list),
                             self.video_list[self.cur_video_id].skip):
                print(
                    f'file \'{self.video_list[self.cur_video_id].img_list[idx]}\'',
                    file=file_list)
        framerate = 1000 / self.video_list[self.cur_video_id].delay
        if framerate == 1:
            ff = FFmpeg(inputs={'imglist.txt': f'-f concat -safe 0 -r 1'},
                        outputs={
                            'timelapse.mp4':
                            f'-vf "fps={framerate}" -c:v libx264'
                        })
        else:
            ff = FFmpeg(inputs={'imglist.txt': f'-f concat -safe 0'},
                        outputs={
                            'timelapse.mp4':
                            f'-vf "fps={framerate}" -c:v libx264'
                        })
        print(ff.cmd)
        ff.run()
Exemplo n.º 3
0
class VideoFrame(QWidget):
    def __init__(self, *args, **kwargs):
        super(VideoFrame, self).__init__(*args, **kwargs)
        self.layout = QVBoxLayout()
        self.img_list = []

        # VIDEO LABEL AREA
        self.label = QLabel('Nothing to show right now.', self)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setGeometry(30, 20, 750, 360)
        self.layout.addWidget(self.label)

        # PLAY BUTTON
        self.play = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'play.png'))
        self.play.setIcon(QIcon(icon))
        self.play.setGeometry(320, 370, 50, 30)
        self.play.clicked.connect(self.timerEvent)
        self.layout.addWidget(self.play)

        # PAUSE BUTTON
        self.pause = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'pause.png'))
        self.pause.setIcon(QIcon(icon))
        self.pause.setGeometry(400, 370, 50, 30)
        self.pause.clicked.connect(self.pauseTimer)
        self.layout.addWidget(self.pause)

        # FAST-FORWARD
        self.ff = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'ff.png'))
        self.ff.setIcon(QIcon(icon))
        self.ff.setGeometry(480, 370, 50, 30)
        self.ff.clicked.connect(self.fastForward)
        self.layout.addWidget(self.ff)

        # SLOWDOWN
        self.fr = QPushButton(self)
        icon = QPixmap(
            os.path.join(os.path.dirname(__file__), 'icons', 'fr.png'))
        self.fr.setIcon(QIcon(icon))
        self.fr.setGeometry(240, 370, 50, 30)
        self.fr.clicked.connect(self.slowdown)
        self.layout.addWidget(self.fr)

        # SLIDER 1
        self.slider = QSlider(Qt.Horizontal, self)
        self.layout.addWidget(self.slider)
        self.slider.setGeometry(10, 400, 825, 20)
        self.slider.valueChanged.connect(self.showImage)

        # RANGE SLIDER
        self.range_slider = QRangeSlider(self)
        self.range_slider.setFixedHeight(30)
        self.range_slider.setFixedWidth(825)
        self.range_slider.move(10, 420)
        self.range_slider.endValueChanged.connect(self.boundEnd)
        self.range_slider.startValueChanged.connect(self.boundStart)

        self.timer = QBasicTimer()  # FIXME consider something with less lag
        self.startIdx = 0
        self.endIdx = 0
        self.idx = 0
        self.delay = 1000
        self.skip = 1

        self.threadpool = QThreadPool()
        self.thread = None

    def size(self):
        return len(self.img_list)

    def boundEnd(self):
        self.endIdx = self.range_slider.end()

    def boundStart(self):
        self.startIdx = self.range_slider.start()
        self.idx = self.startIdx

    def setSkipRate(self, skip_rate):
        self.skip = skip_rate
        if self.thread is not None:
            self.thread.kwargs['skip'] = self.delay / 1000

    def setFrameRate(self, delay):
        self.delay = delay
        if self.thread is not None:
            self.thread.kwargs['delay'] = self.delay / 1000

    def setImgList(self, img_list):
        self.img_list = img_list
        self.endIdx = len(self.img_list) - 1
        self.slider.setRange(0, len(self.img_list) - 1)
        self.range_slider.setMax(len(self.img_list) - 1)
        pixmap = QPixmap(self.img_list[self.idx]).scaled(
            600, 800, Qt.KeepAspectRatio, Qt.FastTransformation)
        self.label.setPixmap(pixmap)

    def showImage(self):
        if len(self.img_list) == 0:
            return
        self.idx = self.slider.value()
        pixmap = QPixmap(self.img_list[self.idx]).scaled(
            600, 800, Qt.KeepAspectRatio, Qt.FastTransformation)
        self.label.setPixmap(pixmap)

    def showImage(self, idx):
        if len(self.img_list) == 0 or idx > len(self.img_list) - 1:
            return
        self.idx = idx
        self.slider.setValue(idx)
        pixmap = QPixmap(self.img_list[idx]).scaled(600, 800,
                                                    Qt.KeepAspectRatio,
                                                    Qt.FastTransformation)
        self.label.setPixmap(pixmap)

    def timerEvent(self):
        self.thread = DelayWorker(self.startIdx,
                                  self.endIdx,
                                  delay=self.delay,
                                  skip=self.skip)
        self.thread.signals.idx.connect(self.showImage)
        self.threadpool.start(self.thread)
        # if len(self.img_list) == 0:
        #     return
        # if self.idx > self.endIdx:
        #     self.timer.stop()
        #     self.idx = self.startIdx
        #     return
        # self.timer.start(self.delay, self)
        # pixmap = QPixmap(self.img_list[self.idx]).scaled(600, 800, Qt.KeepAspectRatio, Qt.FastTransformation)
        # self.label.setPixmap(pixmap)
        # self.slider.setValue(self.idx)
        # self.idx += self.skip

    def pauseTimer(self):
        self.thread.stop = True
        self.startIdx = self.idx

    def fastForward(self):
        if len(self.img_list) == 0:
            return
        self.thread.kwargs['delay'] /= 2

    def slowdown(self):
        if len(self.img_list) == 0:
            return
        self.thread.kwargs['delay'] *= 2
Exemplo n.º 4
0
class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), 'icons', 'plant.png')))

        self.setWindowTitle("Video Comparison GUI")
        self.statusBar()
        self.img_list = None
        self.cur_video_id = 0

        self.layout = QGridLayout()
        # Main Widget Init
        self.widget = QWidget()
        self.widget.setLayout(self.layout)
        self.setCentralWidget(self.widget)
        self.setGeometry(250, 50, 1920, 1080)

        # Open File Dialog
        openFile = QAction('Open Image Directory', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open Image Directory')
        openFile.triggered.connect(self.fileDialog)

        change_framerate = QAction('Change Framerate...', self)
        change_framerate.setStatusTip('Change Playback Framerate of Video (default: 1 frame/sec)')
        change_framerate.triggered.connect(self.setFrameRate)

        change_skiprate = QAction('Change Skip Rate...', self)
        change_skiprate.setStatusTip('Change Frame Skip Rate in Selected Photos (default: 1)')
        change_skiprate.triggered.connect(self.setSkipRate)

        set_v_1 = QAction('Video 1...', self)
        set_v_1.setStatusTip('Set Video 1 (Top Left Corner) Settings')
        set_v_1.triggered.connect(self.setv1)

        set_v_2 = QAction('Video 2...', self)
        set_v_2.setStatusTip('Set Video 2 (Top Right Corner) Settings')
        set_v_2.triggered.connect(self.setv2)

        set_v_3 = QAction('Video 3...', self)
        set_v_3.setStatusTip('Set Video 3 (Bottom Left Corner) Settings')
        set_v_3.triggered.connect(self.setv3)

        set_v_4 = QAction('Video 4...', self)
        set_v_4.setStatusTip('Set Video 4 (Bottom Right Corner) Settings')
        set_v_4.triggered.connect(self.setv4)

        # Menu Bar
        menubar = self.menuBar()
        file_menu = menubar.addMenu('&File')
        file_menu.addAction(openFile)
        edit_menu = menubar.addMenu('&Edit')
        edit_menu.addAction(change_framerate)
        edit_menu.addAction(change_skiprate)
        video_menu = menubar.addMenu('&Video')
        video_menu.addAction(set_v_1)
        video_menu.addAction(set_v_2)
        video_menu.addAction(set_v_3)
        video_menu.addAction(set_v_4)

        # Video Frame Objects
        self.v1 = VideoFrame()
        self.layout.addWidget(self.v1, 0, 0, 1, 2)
        self.v2 = VideoFrame()
        self.layout.addWidget(self.v2, 0, 2, 1, 2)
        self.v3 = VideoFrame()
        self.layout.addWidget(self.v3, 1, 0, 1, 2)
        self.v4 = VideoFrame()
        self.layout.addWidget(self.v4, 1, 2, 1, 2)
        self.video_list = [self.v1, self.v2, self.v3, self.v4]

        ## STREAM 1 LABEL AREA
        #self.label = QLabel('Nothing to show right now.')
        #self.label.setAlignment(Qt.AlignCenter)
        #self.layout.addWidget(self.label)

        self.bottomLayout = QVBoxLayout()

        # GLOBAL PLAY BUTTON
        #self.play = QPushButton("Play")
        #self.play.clicked.connect(self.timerEvent)
        #self.bottomLayout.addWidget(self.play)

        # SLOWDOWN
        self.fr = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'fr.png'))
        self.fr.setIcon(QIcon(icon))
        self.fr.clicked.connect(self.slowdown)
        self.layout.addWidget(self.fr, 2, 0, 1, 1)

        # GLOBAL PLAY BUTTON
        self.play = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'play.png'))
        self.play.setIcon(QIcon(icon))
        self.play.clicked.connect(self.playVideos)
        self.layout.addWidget(self.play, 2, 1, 1, 1)

        # GLOBAL PAUSE BUTTON
        self.pause = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'pause.png'))
        self.pause.setIcon(QIcon(icon))
        self.pause.clicked.connect(self.pauseTimer)
        self.layout.addWidget(self.pause, 2, 2, 1, 1)

        # GLOBAL FAST-FORWARD
        self.ff = QPushButton(self)
        icon = QPixmap(os.path.join(os.path.dirname(__file__), 'icons', 'ff.png'))
        self.ff.setIcon(QIcon(icon))
        self.ff.clicked.connect(self.fastForward)
        self.layout.addWidget(self.ff, 2, 3, 1, 1)


        # GLOBAL SLIDER
        self.slider = QSlider(Qt.Horizontal)
        self.bottomLayout.addWidget(self.slider)
        self.slider.valueChanged.connect(self.showImage)

        # GLOBAL RANGE SLIDER
        self.range_slider = QRangeSlider()
        self.range_slider.setFixedHeight(20)
        self.bottomLayout.addWidget(self.range_slider)
        self.range_slider.endValueChanged.connect(self.boundEnd)
        self.range_slider.startValueChanged.connect(self.boundStart)

        self.layout.addLayout(self.bottomLayout, 3, 0, 1, 4)

        self.timer = QBasicTimer() # FIXME consider something with less lag
        self.startIdx = 0
        self.idx = 0
        self.delay = 1000
        self.skip = 1

        self.threadpool = QThreadPool()

    def fileDialog(self):
        self.img_list, _ = QFileDialog.getOpenFileNames(self,
                                                   'Select one or more files to open',
                                                   '/home', 'Images (*.jpg)')
        self.img_list.sort()
        self.video_list[self.cur_video_id].setImgList(self.img_list)
        self.endIdx = max(self.video_list, key=lambda end: end.size()-1)
        self.endIdx = self.endIdx.size()-1
        self.slider.setRange(0, self.endIdx)
        self.range_slider.setMax(self.endIdx)

    def playVideos(self):
        #v1_thread = DelayWorker(self.v1.startIdx, self.v1.endIdx,
        #                        delay=self.v1.delay, skip=self.v1.skip)
        #v1_thread.signals.idx.connect(self.v1.showImage)
        #v2_thread = DelayWorker(self.v2.startIdx, self.v2.endIdx,
        #                        delay=self.v2.delay, skip=self.v2.skip)
        #v2_thread.signals.idx.connect(self.v2.showImage)
        #v3_thread = DelayWorker(self.v3.startIdx, self.v3.endIdx,
        #                        delay=self.v3.delay, skip=self.v3.skip)
        #v3_thread.signals.idx.connect(self.v3.showImage)
        #v4_thread = DelayWorker(self.v4.startIdx, self.v4.endIdx,
        #                        delay=self.v4.delay, skip=self.v4.skip)
        #v4_thread.signals.idx.connect(self.v4.showImage)
        #self.threadpool.start(v1_thread)
        #self.threadpool.start(v2_thread)
        #self.threadpool.start(v3_thread)
        #self.threadpool.start(v4_thread)
        #self.threadpool.start(v2_thread)
        self.v1.timerEvent()
        self.v2.timerEvent()
        self.v3.timerEvent()
        self.v4.timerEvent()
        #self.slider.setValue(self.idx)
        #self.idx += self.skip

    def showImage(self):
        self.idx = self.slider.value()
        for i in range(len(self.video_list)):
            self.video_list[i].showImage(self.idx)

    def setFrameRate(self):
        delay, _ = QInputDialog.getDouble(self, 'Change Framerate...',
                                         'New Framerate (frames/second)',
                                         1000/self.delay, 1, 100, 1)
        self.delay = 1000.0/delay
        self.video_list[self.cur_video_id].setFrameRate(self.delay)

    def setSkipRate(self):
        self.skip, _ = QInputDialog.getInt(self, 'Change Skip Rate...',
                                         'New Skip Rate', self.skip, 1, 100, 1)
        self.video_list[self.cur_video_id].setSkipRate(self.skip)

    def boundEnd(self):
        self.endIdx = self.range_slider.end()

    def boundStart(self):
        self.startIdx = self.range_slider.start()
        self.idx = self.startIdx

    def setv1(self):
        self.cur_video_id = 0
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def setv2(self):
        self.cur_video_id = 1
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def setv3(self):
        self.cur_video_id = 2
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def setv4(self):
        self.cur_video_id = 3
        self.statusBar().showMessage(f'Selected Video {self.cur_video_id+1}')

    def pauseTimer(self):
        for i in range(len(self.video_list)):
            self.video_list[i].pauseTimer()

    def fastForward(self):
        for i in range(len(self.video_list)):
            self.video_list[i].fastForward()

    def slowdown(self):
        for i in range(len(self.video_list)):
            self.video_list[i].slowdown()