Exemple #1
0
class Window(QMainWindow):
  
  def __init__(self, conf, argv, v4l):
    '''
    Creates main window of the program.
    
    < conf: Config object
    < argv: command line options
    < v4l: v4l wrapper object
    '''
    # config and wrapper instance
    self.conf = conf
    self.v4l = v4l
    
    # thread: None for evaluating first execution
    self.params = thread.CaptureParams(self.conf)
    self.thread = None
    
    # application
    self.app = QApplication(argv)
    super(Window, self).__init__()
    
    ## title, icon and default size and position
    self.setWindowTitle('V4LCapture')
    iconpath = pkg.resource_filename('v4lcapture', 'v4lcapture.png')
    if path.isfile(iconpath):
      self.setWindowIcon(QIcon(iconpath))
    
    ## ui creation
    self._createUI()
    error.log('created main window')
    self._status(message=False)
    self._createPreview()
    self.capture()
    # route, device, preference (when thread is ready)
    self.thread.ready.connect(self._route)
    self.thread.ready.connect(self._device)
    self.thread.ready.connect(self._preferences)
    # visualize
    self.show()
  
  def _createUI(self):
    '''
    Create a menubar with file and about menu and a central widget with its
    own layout.
    Creates the preferences window opened by omonimous entry in file menu.
    '''
    # menubar
    self.menubar = self.menuBar()
    
    ## file
    file_menu = self.menubar.addMenu('&File')
    
    self.save_act = QAction(QIcon.fromTheme('document-save'),
        '&Save settings', self)
    self.save_act.setShortcut('Ctrl+S')
    self.save_act.triggered.connect(self._save_config)
    file_menu.addAction(self.save_act)
    
    self.rst_act = QAction(QIcon.fromTheme('document-revert'),
        '&Reset settings', self)
    self.rst_act.setShortcut('Ctrl+R')
    self.rst_act.triggered.connect(self._reset_config)
    file_menu.addAction(self.rst_act)
    
    sep_act1 = QAction(self)
    sep_act1.setSeparator(True)
    file_menu.addAction(sep_act1)
    
    self.enc_act = QAction(QIcon(), 'S&treaming', self)
    self.enc_act.setShortcut('Ctrl+T')
    self.enc_act.setCheckable(True)
    self.enc_act.setChecked(False)
    self.enc_act.toggled.connect(self._toggleEncoding)
    file_menu.addAction(self.enc_act)
    
    self.route_act = QAction(QIcon.fromTheme('configure'),
        'Add static rout&e', self)
    self.route_act.setShortcut('Ctrl+E')
    self.route_act.triggered.connect(self._show_route)
    file_menu.addAction(self.route_act)
    
    self.dev_act = QAction(QIcon.fromTheme('configure'),
        'Change capture &board', self)
    self.dev_act.setShortcut('Ctrl+Alt+B')
    self.dev_act.triggered.connect(self._show_device)
    file_menu.addAction(self.dev_act)
    
    self.pref_act = QAction(QIcon.fromTheme('configure'), '&Preferences', self)
    self.pref_act.setShortcut('Ctrl+Alt+P')
    self.pref_act.triggered.connect(self._show_preferences)
    file_menu.addAction(self.pref_act)
    
    sep_act2 = QAction(self)
    sep_act2.setSeparator(True)
    file_menu.addAction(sep_act2)
    
    close_act = QAction(QIcon.fromTheme('application-exit'), '&Quit', self)
    close_act.setShortcut('Ctrl+Q')
    close_act.triggered.connect(self.close)
    file_menu.addAction(close_act)
    
    ## about
    about_menu = self.menubar.addMenu('&About')
    
    wiki_act = QAction(QIcon.fromTheme('help-browser'),
        'Go to wiki...', self)
    wiki_act.triggered.connect(
      lambda:
        QDesktopServices.openUrl(QUrl('https://github.com/datasoftsrl/'
            'v4lcapture/wiki', QUrl.TolerantMode))
    )
    about_menu.addAction(wiki_act)
    
    sep_act3 = QAction(self)
    sep_act3.setSeparator(True)
    about_menu.addAction(sep_act3)
    
    ds_act = QAction(QIcon.fromTheme('applications-internet'),
        'DataSoft Srl', self)
    ds_act.triggered.connect(
      lambda:
        QDesktopServices.openUrl(QUrl('http://www.datasoftweb.com/',
            QUrl.TolerantMode))
    )
    about_menu.addAction(ds_act)
    
    qt_act = QAction(QIcon(':/qt-project.org/qmessagebox/images/qtlogo-64.png'),
        'About Qt', self)
    qt_act.triggered.connect(QApplication.aboutQt)
    about_menu.addAction(qt_act)
    
    self.sb_perm = QLabel()
    self.sb = self.statusBar()
    self.sb.setStyleSheet('''
      QStatusBar QLabel {
        margin-right: 5px;
      }
    ''')
    self.sb.addPermanentWidget(self.sb_perm)
    self.sb.setSizeGripEnabled(False)
    
    # central widget and layout
    self.central = QWidget()
    self.layout = QVBoxLayout()
    self.central.setLayout(self.layout)
    self.setCentralWidget(self.central)
  
  def _status(self, message=True):
    '''
    Sets statusbar text on streaming enabled/disabled.
    
    < message: flash a message if True
    '''
    label = 'address: <b>{}</b>'
    tooltip =  'codec: <b>{}</b>\nformat: <b>{}</b>\nsize: <b>{}</b>\n' \
        'framerate: <b>{}</b>'
    if self.params.encoding():
      (width, height) = self.v4l.getFormat()
      if message:
        self.sb.showMessage('Streaming enabled')
      self.sb_perm.setText(label.format(
        '<font color="green">{}</font>'.format(self.v4l.getFilename())
      ))
      self.sb_perm.setToolTip(
        tooltip.format(
          'H.264',
          self.v4l.getMuxer(),
          '{}x{}'.format(width, height),
          '{} fps'.format(self.v4l.getStrFps())
      ))
    else:
      if message:
        self.sb.showMessage('Streaming disabled')
      self.sb_perm.setText(label.format('n/a'))
      self.sb_perm.setToolTip(
        tooltip.format(
          'n/a',
          'n/a',
          'n/a',
          'n/a'
      ))
  
  '''
  Creates a preview of captured frames, occupying the window size.
  '''
  def _createPreview(self):
    self.preview = RgbPreview(self)
    self.layout.addWidget(self.preview)
  
  def _toggleEncoding(self):
    self.enc_act.toggled.disconnect(self._toggleEncoding)
    self.params.toggle_encoding()
    if self.params.encoding():
      error.log('streaming started at {}'.format(self.v4l.getFilename()))
    else:
      error.log('streaming stopped')
    self._status()
    self.capture()
    self.enc_act.toggled.connect(self._toggleEncoding)
  
  def _save_config(self):
    '''
    Write preferences into json object and prints a message in status bar.
    '''
    self.params.store()
    self.conf.write()
    self.sb.showMessage('Settings saved')
  
  def _reset_config(self):
    '''
    Resets prefereneces to line default and prints a message in status bar.
    '''
    self.params.default()
    self.capture()
    self.sb.showMessage('Settings reset')
  
  def _route(self):
    '''
    Create eth choosing window to add static route for streamings.
    '''
    self.route = RouteDialog(self.central)
    error.log('static route window created')
  
  def _show_route(self):
    '''
    Show eth choosing window.
    '''
    self.route_act.triggered.disconnect(self._show_route)
    self.route.show()
    self.route.setFixedSize(self.dev.sizeHint())
    self.route_act.triggered.connect(self._show_route)
  
  def _device(self):
    '''
    Create device choosing window.
    '''
    self.thread.ready.disconnect(self._device)
    self.dev = SimpleDialog(self.v4l, self.params, self.central)
    self.dev.setWindowTitle('Change capture board')
    self.dev.setWindowIcon(QIcon.fromTheme('preferences-desktop-multimedia'))
    error.log('device choosing window created')
  
  def _show_device(self):
    '''
    Show device choosing window.
    '''
    self.dev_act.triggered.disconnect(self._show_device)
    self.dev.show()
    self.dev.setFixedSize(self.dev.sizeHint())
    self.dev_act.triggered.connect(self._show_device)
  
  def _preferences(self):
    '''
    Create preference window.
    '''
    self.thread.ready.disconnect(self._preferences)
    self.pref = TabDialog(self.v4l, self.params, self.central)
    self.pref.setWindowTitle('Preferences')
    self.pref.setWindowIcon(QIcon.fromTheme('configure'))
    error.log('preferences window created')
  
  def _show_preferences(self):
    '''
    Show preference window.
    '''
    self.pref_act.triggered.disconnect(self._show_preferences)
    self.pref.show()
    self.pref.setFixedSize(self.pref.sizeHint())
    self.pref_act.triggered.connect(self._show_preferences)
  
  def _updatePreview(self, data):
    '''
    Updates the preview with a new frame. Called from capture QThread.
    
    < data: RGB data to display
    '''
    (width, height) = self.v4l.getFormat()
    if data is not None:
      self.preview.setPixmap(data, width, height)
      self.setFixedSize(self.sizeHint())
  
  def capture(self):
    '''
    Instantiate new QThread to capture frames, waiting for previous to stop.
    '''
    if self.thread is not None:
      self.thread.quit()
      self.thread.wait()
    self.thread = thread.CaptureThread(self.app, self.v4l, self.params,
        self._updatePreview)
    self.thread.start()
    # update only if initialized
    try:
      self.thread.ready.connect(self.pref.update)
      self.thread.ready.connect(lambda: self._status(message=False))
    except AttributeError:
      pass
    error.log('capture thread created and started')
  
  def closeEvent(self, event):
    '''
    Closes capture QThread when window is closed.
    Closes other windows open.
    Writes config.
    
    < event: closing event
    '''
    self.thread.quit()
    self.thread.wait()
    error.log('capture thread stopped')
    
    # close only if initialized
    try:
      self.dev.close()
      self.pref.close()
    except AttributeError:
      pass
    
    # close core log
    self.v4l.closeLog()
    error.log('core log closed')
  
  old_w = 0
  old_h = 0
  def resizeEvent(self, event):
    '''
    If current size is different from previous frame size, resize, else do nothing.
    old_w and old_h are defined outside to simulate C static keyword use.
    
    < event: resizing event
    '''
    size = self.sizeHint()
    w = size.width()
    h = size.height()
    if w != self.old_w or h != self.old_h:
      super().resizeEvent(event)
      self.old_w = w
      self.old_h = h
  
  def quit(self):
    '''
    Closes main window.
    '''
    sys.exit(self.app.exec_())
Exemple #2
0
class Window(QMainWindow):
    def __init__(self, conf, argv, v4l):
        '''
    Creates main window of the program.
    
    < conf: Config object
    < argv: command line options
    < v4l: v4l wrapper object
    '''
        # config and wrapper instance
        self.conf = conf
        self.v4l = v4l

        # thread: None for evaluating first execution
        self.params = thread.CaptureParams(self.conf)
        self.thread = None

        # application
        self.app = QApplication(argv)
        super(Window, self).__init__()

        ## title, icon and default size and position
        self.setWindowTitle('V4LCapture')
        iconpath = pkg.resource_filename('v4lcapture', 'v4lcapture.png')
        if path.isfile(iconpath):
            self.setWindowIcon(QIcon(iconpath))

        ## ui creation
        self._createUI()
        error.log('created main window')
        self._status(message=False)
        self._createPreview()
        self.capture()
        # route, device, preference (when thread is ready)
        self.thread.ready.connect(self._route)
        self.thread.ready.connect(self._device)
        self.thread.ready.connect(self._preferences)
        # visualize
        self.show()

    def _createUI(self):
        '''
    Create a menubar with file and about menu and a central widget with its
    own layout.
    Creates the preferences window opened by omonimous entry in file menu.
    '''
        # menubar
        self.menubar = self.menuBar()

        ## file
        file_menu = self.menubar.addMenu('&File')

        self.save_act = QAction(QIcon.fromTheme('document-save'),
                                '&Save settings', self)
        self.save_act.setShortcut('Ctrl+S')
        self.save_act.triggered.connect(self._save_config)
        file_menu.addAction(self.save_act)

        self.rst_act = QAction(QIcon.fromTheme('document-revert'),
                               '&Reset settings', self)
        self.rst_act.setShortcut('Ctrl+R')
        self.rst_act.triggered.connect(self._reset_config)
        file_menu.addAction(self.rst_act)

        sep_act1 = QAction(self)
        sep_act1.setSeparator(True)
        file_menu.addAction(sep_act1)

        self.enc_act = QAction(QIcon(), 'S&treaming', self)
        self.enc_act.setShortcut('Ctrl+T')
        self.enc_act.setCheckable(True)
        self.enc_act.setChecked(False)
        self.enc_act.toggled.connect(self._toggleEncoding)
        file_menu.addAction(self.enc_act)

        self.route_act = QAction(QIcon.fromTheme('configure'),
                                 'Add static rout&e', self)
        self.route_act.setShortcut('Ctrl+E')
        self.route_act.triggered.connect(self._show_route)
        file_menu.addAction(self.route_act)

        self.dev_act = QAction(QIcon.fromTheme('configure'),
                               'Change capture &board', self)
        self.dev_act.setShortcut('Ctrl+Alt+B')
        self.dev_act.triggered.connect(self._show_device)
        file_menu.addAction(self.dev_act)

        self.pref_act = QAction(QIcon.fromTheme('configure'), '&Preferences',
                                self)
        self.pref_act.setShortcut('Ctrl+Alt+P')
        self.pref_act.triggered.connect(self._show_preferences)
        file_menu.addAction(self.pref_act)

        sep_act2 = QAction(self)
        sep_act2.setSeparator(True)
        file_menu.addAction(sep_act2)

        close_act = QAction(QIcon.fromTheme('application-exit'), '&Quit', self)
        close_act.setShortcut('Ctrl+Q')
        close_act.triggered.connect(self.close)
        file_menu.addAction(close_act)

        ## about
        about_menu = self.menubar.addMenu('&About')

        wiki_act = QAction(QIcon.fromTheme('help-browser'), 'Go to wiki...',
                           self)
        wiki_act.triggered.connect(lambda: QDesktopServices.openUrl(
            QUrl('https://github.com/datasoftsrl/'
                 'v4lcapture/wiki', QUrl.TolerantMode)))
        about_menu.addAction(wiki_act)

        sep_act3 = QAction(self)
        sep_act3.setSeparator(True)
        about_menu.addAction(sep_act3)

        ds_act = QAction(QIcon.fromTheme('applications-internet'),
                         'DataSoft Srl', self)
        ds_act.triggered.connect(lambda: QDesktopServices.openUrl(
            QUrl('http://www.datasoftweb.com/', QUrl.TolerantMode)))
        about_menu.addAction(ds_act)

        qt_act = QAction(
            QIcon(':/qt-project.org/qmessagebox/images/qtlogo-64.png'),
            'About Qt', self)
        qt_act.triggered.connect(QApplication.aboutQt)
        about_menu.addAction(qt_act)

        self.sb_perm = QLabel()
        self.sb = self.statusBar()
        self.sb.setStyleSheet('''
      QStatusBar QLabel {
        margin-right: 5px;
      }
    ''')
        self.sb.addPermanentWidget(self.sb_perm)
        self.sb.setSizeGripEnabled(False)

        # central widget and layout
        self.central = QWidget()
        self.layout = QVBoxLayout()
        self.central.setLayout(self.layout)
        self.setCentralWidget(self.central)

    def _status(self, message=True):
        '''
    Sets statusbar text on streaming enabled/disabled.
    
    < message: flash a message if True
    '''
        label = 'address: <b>{}</b>'
        tooltip =  'codec: <b>{}</b>\nformat: <b>{}</b>\nsize: <b>{}</b>\n' \
            'framerate: <b>{}</b>'
        if self.params.encoding():
            (width, height) = self.v4l.getFormat()
            if message:
                self.sb.showMessage('Streaming enabled')
            self.sb_perm.setText(
                label.format('<font color="green">{}</font>'.format(
                    self.v4l.getFilename())))
            self.sb_perm.setToolTip(
                tooltip.format('H.264', self.v4l.getMuxer(),
                               '{}x{}'.format(width, height),
                               '{} fps'.format(self.v4l.getStrFps())))
        else:
            if message:
                self.sb.showMessage('Streaming disabled')
            self.sb_perm.setText(label.format('n/a'))
            self.sb_perm.setToolTip(tooltip.format('n/a', 'n/a', 'n/a', 'n/a'))

    '''
  Creates a preview of captured frames, occupying the window size.
  '''

    def _createPreview(self):
        self.preview = RgbPreview(self)
        self.layout.addWidget(self.preview)

    def _toggleEncoding(self):
        self.enc_act.toggled.disconnect(self._toggleEncoding)
        self.params.toggle_encoding()
        if self.params.encoding():
            error.log('streaming started at {}'.format(self.v4l.getFilename()))
        else:
            error.log('streaming stopped')
        self._status()
        self.capture()
        self.enc_act.toggled.connect(self._toggleEncoding)

    def _save_config(self):
        '''
    Write preferences into json object and prints a message in status bar.
    '''
        self.params.store()
        self.conf.write()
        self.sb.showMessage('Settings saved')

    def _reset_config(self):
        '''
    Resets prefereneces to line default and prints a message in status bar.
    '''
        self.params.default()
        self.capture()
        self.sb.showMessage('Settings reset')

    def _route(self):
        '''
    Create eth choosing window to add static route for streamings.
    '''
        self.route = RouteDialog(self.central)
        error.log('static route window created')

    def _show_route(self):
        '''
    Show eth choosing window.
    '''
        self.route_act.triggered.disconnect(self._show_route)
        self.route.show()
        self.route.setFixedSize(self.dev.sizeHint())
        self.route_act.triggered.connect(self._show_route)

    def _device(self):
        '''
    Create device choosing window.
    '''
        self.thread.ready.disconnect(self._device)
        self.dev = SimpleDialog(self.v4l, self.params, self.central)
        self.dev.setWindowTitle('Change capture board')
        self.dev.setWindowIcon(
            QIcon.fromTheme('preferences-desktop-multimedia'))
        error.log('device choosing window created')

    def _show_device(self):
        '''
    Show device choosing window.
    '''
        self.dev_act.triggered.disconnect(self._show_device)
        self.dev.show()
        self.dev.setFixedSize(self.dev.sizeHint())
        self.dev_act.triggered.connect(self._show_device)

    def _preferences(self):
        '''
    Create preference window.
    '''
        self.thread.ready.disconnect(self._preferences)
        self.pref = TabDialog(self.v4l, self.params, self.central)
        self.pref.setWindowTitle('Preferences')
        self.pref.setWindowIcon(QIcon.fromTheme('configure'))
        error.log('preferences window created')

    def _show_preferences(self):
        '''
    Show preference window.
    '''
        self.pref_act.triggered.disconnect(self._show_preferences)
        self.pref.show()
        self.pref.setFixedSize(self.pref.sizeHint())
        self.pref_act.triggered.connect(self._show_preferences)

    def _updatePreview(self, data):
        '''
    Updates the preview with a new frame. Called from capture QThread.
    
    < data: RGB data to display
    '''
        (width, height) = self.v4l.getFormat()
        if data is not None:
            self.preview.setPixmap(data, width, height)
            self.setFixedSize(self.sizeHint())

    def capture(self):
        '''
    Instantiate new QThread to capture frames, waiting for previous to stop.
    '''
        if self.thread is not None:
            self.thread.quit()
            self.thread.wait()
        self.thread = thread.CaptureThread(self.app, self.v4l, self.params,
                                           self._updatePreview)
        self.thread.start()
        # update only if initialized
        try:
            self.thread.ready.connect(self.pref.update)
            self.thread.ready.connect(lambda: self._status(message=False))
        except AttributeError:
            pass
        error.log('capture thread created and started')

    def closeEvent(self, event):
        '''
    Closes capture QThread when window is closed.
    Closes other windows open.
    Writes config.
    
    < event: closing event
    '''
        self.thread.quit()
        self.thread.wait()
        error.log('capture thread stopped')

        # close only if initialized
        try:
            self.dev.close()
            self.pref.close()
        except AttributeError:
            pass

        # close core log
        self.v4l.closeLog()
        error.log('core log closed')

    old_w = 0
    old_h = 0

    def resizeEvent(self, event):
        '''
    If current size is different from previous frame size, resize, else do nothing.
    old_w and old_h are defined outside to simulate C static keyword use.
    
    < event: resizing event
    '''
        size = self.sizeHint()
        w = size.width()
        h = size.height()
        if w != self.old_w or h != self.old_h:
            super().resizeEvent(event)
            self.old_w = w
            self.old_h = h

    def quit(self):
        '''
    Closes main window.
    '''
        sys.exit(self.app.exec_())
Exemple #3
0
 def _createPreview(self):
   self.preview = RgbPreview(self)
   self.layout.addWidget(self.preview)
Exemple #4
0
 def _createPreview(self):
     self.preview = RgbPreview(self)
     self.layout.addWidget(self.preview)