Exemplo n.º 1
0
    def testSignalMapper(self):
        checkboxMapper = QSignalMapper()
        box = QCheckBox('check me')
        box.stateChanged.connect(checkboxMapper.map)

        checkboxMapper.setMapping(box, box.text())
        checkboxMapper.mapped[str].connect(self.cb_changed)
        self._changed = False
        box.setChecked(True)
        self.assert_(self._changed)
Exemplo n.º 2
0
    def testSignalMapper(self):
        checkboxMapper = QSignalMapper()
        box = QCheckBox('check me')
        box.stateChanged.connect(checkboxMapper.map)

        checkboxMapper.setMapping(box, box.text())
        checkboxMapper.mapped[str].connect(self.cb_changed)
        self._changed = False
        box.setChecked(True)
        self.assert_(self._changed)
Exemplo n.º 3
0
class RadioEditor ( BaseEditor ):
    """ Enumeration editor, used for the "custom" style, that displays radio
        buttons.
    """

    #-- Public Methods ---------------------------------------------------------

    def init ( self, parent ):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        super( RadioEditor, self ).init( parent )

        # The control is a grid layout:
        self.control = layout = QGridLayout()
        layout.setSpacing( 0 )
        layout.setMargin( 0 )

        self._mapper = QSignalMapper()
        QObject.connect( self._mapper, SIGNAL( 'mapped(QWidget *)' ),
                         self.update_object )

        self.rebuild_editor()


    def update_object ( self, rb ):
        """ Handles the user clicking one of the custom radio buttons.
        """
        try:
            self.value = rb.value
        except:
            pass


    def update_editor ( self ):
        """ Updates the editor when the object facet changes externally to the
            editor.
        """
        value = self.value
        for i in range( self.control.count() ):
            rb = self.control.itemAt( i ).widget()
            rb.setChecked( rb.value == value )


    def rebuild_editor ( self ):
        """ Rebuilds the contents of the editor whenever the original factory
            object's **values** facet changes.
        """
        # Clear any existing content:
        ### self.clear_layout()

        # Get the current facet value:
        cur_name = self.str_value

        # Create a sizer to manage the radio buttons:
        names   = self.names
        mapping = self.mapping
        n       = len( names )
        cols    = self.factory.cols
        rows    = (n + cols - 1) / cols
        incr    = [ n / cols ] * cols
        rem     = n % cols
        for i in range( cols ):
            incr[i] += (rem > i)

        incr[-1] = -( reduce( lambda x, y: x + y, incr[:-1], 0 ) - 1 )

        # Add the set of all possible choices:
        index = 0

        for i in range( rows ):
            for j in range( cols ):
                if n > 0:
                    name = label = names[ index ]
                    label = self.string_value( label, capitalize )
                    rb = QRadioButton( label )
                    rb.value = mapping[ name ]

                    rb.setChecked( name == cur_name )

                    QObject.connect( rb, SIGNAL( 'clicked()' ), self._mapper,
                                     SLOT( 'map()' ) )
                    self._mapper.setMapping( rb, rb )

                    self.set_tooltip( rb )
                    self.control.addWidget( rb, i, j )

                    index += incr[j]
                    n     -= 1
Exemplo n.º 4
0
class DocumentCollectionWidget(QWidget):
    """ Emitted whenever a document is added or removed from the list. """
    documents_list_changed = Signal()

    # def model_data(self):
    #     r = DocumentsModel()
    #     r.documents_changed = self.documents_changed
    #     r.documents_to_remove = self.documents_to_remove
    #     r.documents = self.model.export_objects()
    #     return r

    def set_model_data(self, model_data):
        mainlog.debug("set_model_data : setting on {}".format(model_data))
        if model_data:
            mainlog.debug(
                "we're in for {} documents, structured as {} (id:{})".format(
                    len(model_data.documents), type(model_data.documents),
                    id(model_data.documents)))
            self.model.load_documents(model_data.documents)
        else:
            self.model.clear()

    def documents_ids(self):
        return [d.document_id for d in self.model.export_objects()]

    # WARNING : This is overriden in TemplatesCollectionWidget !!!
    @Slot(QModelIndex, QModelIndex)
    def _data_edited(self, top_left, bottom_right):
        if self.track_edits:
            mainlog.debug("Row edited {} {}".format(top_left.row(),
                                                    bottom_right.row()))
            for i in range(top_left.row(), bottom_right.row() + 1):
                ndx = self.model.index(i, 1)
                name = self.model.index(i, 0).data(Qt.UserRole)

                doc_id = self.model.object_at(i).document_id

                try:
                    # self.documents_service.update_name_and_description(doc_id, name, ndx.data())
                    self.documents_list_changed.emit()
                except ServerException as ex:

                    # Undo is tricky here. Indeed, at this point the model *is* changed
                    # to a value that is rejected (see exception). So if we'd like to
                    # undo that, we'll have to change the model back to its original value
                    # which will in turn trigger a "data edited". So that's not the way
                    # to do it... I guess the undo should be handled at the delegate level.

                    showServerErrorBox(ex)
                    return

    def set_model(self, model: DocumentsModel):
        """

        :param model:
        :return:
        """

        # -TRACE-
        # assert (model is not None) and isinstance(model, DocumentsModel), "Expecting DocumentsModel, but had {}".format(type(model))

        self.model = model

        # return # -TRACE- passees if return here

        # Replaced model won't be deleted (see Qt's doc). The view does not take ownership of the model unless it is the model's parent object because the model may be shared between many different views.

        # -TRACE- Using standarditem model makes things work
        # Using a DocumentsModel, event if not initiliazed crashes

        self.view.setModel(model)  # TRACE- Crashes
        # self.view.setModel(QStandardItemModel()) # TRACE- Passes

        # return # -TRACE- crashesif return here

        if isinstance(model, DocumentsModel) and not self.model.__connected__:
            # Defensive : not sure how pyside handles re-re-re-reconnect
            self.model.__connected__ == True

            self.model.modelReset.connect(self.model_reset)
            self.model.rowsInserted.connect(self.rows_inserted)
            self.model.rowsAboutToBeRemoved.connect(
                self.rows_about_to_be_deleted)
            self.model.dataChanged.connect(self._data_edited)

        # If the model is already populated when set here, then
        # we must add the buttons...

        if self.model.rowCount() > 0:
            self.model_reset()

        # The first delegate support undo operation (well, it should)
        # The last delegate is not a delegate because we use a "table widget".

        # self.view.setItemDelegateForColumn(0, self.delegate)
        for i in range(len(self.prototype)):
            self.view.setItemDelegateForColumn(i, self.prototype[i].delegate())

    def set_documents_service(self, doc_service):
        """ Used by tests
        :param doc_service:
        :return:
        """

        assert doc_service
        self.documents_service = doc_service

    def set_used_categories(self, categories):
        """ Define the category that will be used for all documents that
        will be added.

        :param cat: None/[] == we don't use categories at all,  an array (order is important) of categories.
        :return:
        """

        self._used_categories = categories or []

        if self._used_categories:
            self.view.set_drop_zones(
                [c.full_name for c in self._used_categories],
                [c.document_category_id for c in self._used_categories])

        else:
            self.view.set_drop_zones([], [])

        mainlog.debug("set_used_categories")
        if self._used_categories:
            # print("*********")
            # print(self._used_categories)
            self.prototype['document_category_id'].set_categories(
                self._used_categories)

    def __init__(self,
                 parent=None,
                 doc_service=None,
                 show_description=False,
                 used_category_short_name=[],
                 no_header=False,
                 prototype=None):
        super(DocumentCollectionWidget, self).__init__(parent)

        # -TRACE- Crashing code seems after this
        # return

        self.view = AnimatedTableView(
            self, drop_event_handler=self.animated_drop_event_handler)
        # self.view = QTableView() # -TRACE- prevents crash
        # Editing is started by single clicking on a selected cell or by using F2
        # Double click is disabled for editing => it will just open the file
        self.view.setEditTriggers(QAbstractItemView.SelectedClicked
                                  | QAbstractItemView.AnyKeyPressed
                                  | QAbstractItemView.EditKeyPressed)
        self.view.verticalHeader().setVisible(False)
        self.view.horizontalHeader().setVisible(False)
        self.view.setMinimumSize(250, 100)
        self.view.setShowGrid(True)
        self.view.doubleClicked.connect(self._doubleClicked)

        #return # -TRACE- Crashing code seems before this

        if prototype:
            self.prototype = prototype
        else:
            # I tie the prototype to the instance because I will change some
            # of its delegate => I cannot share prototypes across instances...

            self.prototype = PrototypeArray([
                DocumentCategoryPrototype(
                    'document_category_id', _("Cat."),
                    editable=False),  # Will be configured at instance lvel
                FilenamePrototype('filename', _("Filename")),
                EmptyPrototype(_("Actions"))
            ])

        if doc_service:
            self.documents_service = doc_service
        else:
            self.documents_service = documents_service

        self.button_data = dict()
        self.button_id_counter = 1

        self.show_description = show_description
        self.documents_changed = False
        self.track_edits = True
        self.documents_to_remove = []

        # [] == we don't want to use categories at all (so no drop zones)
        if used_category_short_name != None and used_category_short_name != []:
            mainlog.debug("__init__ 1")
            categories = [
                self.documents_service.find_category_by_short_name(
                    used_category_short_name)
            ]
        elif used_category_short_name == []:  # Use all categories
            mainlog.debug("__init__ doc collection widget 2")
            categories = self.documents_service.categories()
        else:  # None => use no categories at all
            mainlog.debug("__init__ 3")
            categories = None

        mainlog.debug(categories)
        self.set_used_categories(categories)

        # self.delegate = FilenameDelegate()

        self.prototype['filename'].edit_next_item = False

        # self.delegate.edit_next_item = False

        mainlog.debug(self.prototype)

        # return # -TRACE- Test passes or crashes

        # I don't use the factory because I want this to work
        # also when inherited
        dummy_model = DocumentsModel(self.prototype)

        # dummy_model = QStandardItemModel()
        self.set_model(dummy_model)  # Doesn't prevent crash

        # return # -TRACE- Test crashes if it reaches here

        hb = QHBoxLayout()
        if no_header:
            hb.addWidget(QLabel(_("Documents")))
        else:
            hb.addWidget(QLabel(u"<h3>{}</h3>".format(_("Documents"))))

        pb = make_tool_button("appbar.page.upload.png", "upload_button")
        pb.clicked.connect(self.add_document_dialog)
        pb.setObjectName("upload_button")
        hb.addWidget(pb)

        # pb = make_tool_button("appbar.book.hardcover.open.png", "add_template_button")
        # pb.clicked.connect(self.add_template_dialog)
        # pb.setToolTip(_("Add documents from the template library"))
        # hb.addWidget(pb)

        # return # -TRACE- Test crashes

        # -TRACE- return Crash after this

        l = QVBoxLayout()
        l.setContentsMargins(0, 0, 0, 0)
        l.addLayout(hb)
        l.addWidget(
            self.view)  # -TRACE- Commenting this makes the crash disappear
        self.setLayout(l)

        # -TRACE- Crash before this

        self.signal_mapper_open_button = QSignalMapper()
        self.signal_mapper_open_button.mapped.connect(self.open_clicked)
        self.signal_mapper_close_button = QSignalMapper()
        self.signal_mapper_close_button.mapped.connect(
            self.delete_remote_clicked)
        self.signal_mapper_save_a_copy_button = QSignalMapper()
        self.signal_mapper_save_a_copy_button.mapped.connect(
            self.save_a_copy_clicked)

        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.view.customContextMenuRequested.connect(self.popup_menu)

    @Slot()
    def model_reset(self):
        mainlog.debug("model reset")
        for row in range(self.model.rowCount()):
            self._set_widget_buttons(row)

        # Make the buttons as small as possible
        self.view.resizeColumnToContents(self.model.columnCount() - 1)
        self.view.resizeRowsToContents()

        # Pack the categories
        self.view.resizeColumnToContents(
            self.prototype.index_of('document_category_id'))

        self.view.horizontalHeader().setResizeMode(
            self.prototype.index_of('filename'), QHeaderView.Stretch)
        if self.show_description:
            self.view.horizontalHeader().setResizeMode(
                self.prototype.index_of('description'), QHeaderView.Stretch)
        # self.view.resizeColumnToContents(3)
        # self.view.resizeColumnToContents(2)

    @Slot(QModelIndex, int, int)
    def rows_inserted(self, parent, first, last):
        mainlog.debug("rows_inserted {} {} / row_count={}/{}".format(
            first, last, self.model.rowCount(), self.model.columnCount()))
        for row in range(first, last + 1):
            self._set_widget_buttons(row)

        self.documents_list_changed.emit()

    @Slot(QModelIndex, int, int)
    def rows_about_to_be_deleted(self, parent, first, last):
        for row in range(first, last + 1):
            doc_id = self.model.object_at(row).document_id

            for k, v in self.button_data.items():
                if v == doc_id:
                    del self.button_data[k]
                    break

        self.documents_list_changed.emit()

    def _set_widget_buttons(self, row):
        # mainlog.debug("_set_widget_buttons({})".format(row))

        doc = self.model.object_at(row)

        p_download = make_tool_button(
            "appbar.page.download.png",
            "download{}".format(self.button_id_counter))
        p_download.clicked.connect(self.signal_mapper_save_a_copy_button.map)
        self.signal_mapper_save_a_copy_button.setMapping(
            p_download, self.button_id_counter)

        # FIXME Json calls should produce rela objects and not tuples or Frozen,...
        has_reference = False
        try:
            has_reference = doc.reference
        except:
            pass

        # FIXME HAchkish this is really valid for templates, and not super-really-valid for mere documents
        if not has_reference:
            p_delete = make_tool_button("appbar.page.delete.png")
            p_delete.clicked.connect(self.signal_mapper_close_button.map)
            self.signal_mapper_close_button.setMapping(p_delete,
                                                       self.button_id_counter)

        # I've already tested several things here
        # use a qss style, setting strecth, borders, contentsmargins, etc.
        # But for some reasons, when I test the widget in a dialog
        # the space between buttons is wide. If I run the widget in Horse
        # (that is, in normal conditions) then the display works as exepcted
        # (buttons close together and not wide apart)

        z = QWidget()
        layout = QHBoxLayout()
        layout.setSpacing(0)
        # layout.setStretch(0,0)
        # layout.setStretch(1,0)
        layout.setContentsMargins(0, 0, 0, 0)
        # layout.addWidget(p_open)

        p_download.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(p_download)

        if not has_reference:
            p_delete.setContentsMargins(0, 0, 0, 0)
            layout.addWidget(p_delete)
        # z.setMinimumHeight(64)

        z.setLayout(layout)

        # QTableView takes ownership of the widget (see Qt doc.)
        self.view.setIndexWidget(
            self.model.index(row,
                             self.model.columnCount() - 1), z)

        #doc.button_id = self.button_id

        # A bit complicated, but that's the simplest way
        # to tie an in-table button to some data
        self.button_data[self.button_id_counter] = (doc.document_id)
        self.button_id_counter += 1

    def _test_file_access(self, full_path_client):
        import os.path

        file_size = 0
        # Test the file access
        try:
            t = open(full_path_client, 'rb')
            t.close()

            file_size = os.path.getsize(full_path_client)
            if file_size == 0:
                raise Exception(_("The file is empty"))

            return True
        except Exception as exc:
            showErrorBox(_("Can't open the file located at {}").format(
                full_path_client),
                         ex=exc,
                         object_name="drag_drop_error")
            return False

    @Slot(str, int)
    def _add_file_from_drop_zone(self, full_path_client: str, drop_zone: int):
        mainlog.debug(
            "_add_file_from_drop_zone.Drop zone {}".format(drop_zone))

        if drop_zone != -1:
            self._add_file(
                full_path_client,
                self._used_categories[drop_zone].document_category_id)
        else:
            self._add_file(full_path_client)

    # WARNING : This is overrided in TemplatesCollectionWidget !!!
    def _add_file(self, full_path_client: str, document_category_id=None):
        mainlog.debug(
            "document widget _add_file categ={}".format(document_category_id))
        if self._test_file_access(full_path_client):
            progress_bar = make_progress(_("Uploading"), 100)

            def progress_tracker(percent):
                progress_bar.setValue(int(percent))

            try:
                doc_id = upload_document(full_path_client, progress_tracker)
            except Exception as exc:
                progress_bar.close()
                showErrorBox(_(
                    "There was a problem while uploading the file to the server"
                ),
                             ex=exc,
                             object_name="file_upload_error")
                return

            d = self.documents_service.find_by_id(doc_id)
            doc = Document()
            doc.document_id = d.document_id
            doc.file_size = d.file_size
            doc.description = d.description
            doc.filename = d.filename
            doc.server_location = d.server_location
            doc.upload_date = d.upload_date

            if document_category_id:
                doc.document_category_id = document_category_id

            self.model.append_objects([doc])

    # WARNING : This is overrided in TemplatesCollectionWidget !!!
    def animated_drop_event_handler(self, e, selected_drop_zone):
        mainlog.debug(
            "DocumentCollectionWidget.animated_drop_event_handler : _selected drop zone {}"
            .format(selected_drop_zone))

        # No replace, just add

        for url in e.mimeData().urls():
            mainlog.debug("DropEvent -> {}".format(url.toString()))
            if platform.system() == "Windows":
                full_path_client = url.toString().replace('file:///', '')
            else:
                full_path_client = url.toString().replace('file://', '')

            self._add_file_from_drop_zone(full_path_client, selected_drop_zone)

    def _download_on_button_id(self, button_id, destination):
        """ Download a file.
        Returns the path to file or None if nothing was downloaded
        """

        # mainlog.debug("_download_on_button_id() : button_id={}".format(button_id))
        doc_id = self.button_data[button_id]

        # if os.path.isabs(full_path_client):
        #     # The file was uploaded during this GUI session. Therefore we
        #     # still know where we picked it from (FIXME unless someone has
        #     # removed it...)
        #
        #     return full_path_client
        # else:

        progress_bar = make_progress(_("Downloading"), 100)

        def progress_tracker(percent):
            progress_bar.setValue(int(percent))

        try:
            path = download_document(doc_id, progress_tracker, destination)
            return path
        except Exception as exc:
            progress_bar.close()
            showErrorBox(_(
                "There was a problem while downloading the file from the server"
            ),
                         ex=exc,
                         object_name="file_upload_error")
            return None

        progress_bar.close()

    @Slot(int)
    def _doubleClicked(self, ndx):
        if ndx.isValid() and ndx.row() >= 0:  # and ndx.column() == 0:
            # mainlog.debug("_doubleClicked {}".format(ndx.row()))
            # table_button_id = self.model.data( self.model.index(ndx.row(),0), Qt.UserRole + 1)

            doc_id = self.model.object_at(ndx.row()).document_id

            for k, v in self.button_data.items():
                if v == doc_id:
                    self.open_clicked(k)
                    break

    def _button_id_to_doc(self, button_id):
        doc_id = self.button_data[button_id]
        for doc in self.model.export_objects():
            if doc.document_id == doc_id:
                return doc

        raise Exception("Unable to locate doc_id {}".format(doc_id))

    def _save_file_dialog(self, proposed_filename):
        """ Return the selected file path or None if none was selected (bvb
        the user has hit Cancel

        This method exists to allow the test to avoid the file dialog.
        Automating test of QFileDialog is super complex.

        :param proposed_filename:
        :return:
        """
        return QFileDialog.getSaveFileName(self, _('Save a document'),
                                           proposed_filename, "")[0]

    def _open_file_dialog(self):
        # This method exists to allow the test to avoid the file dialog.
        # Automating test of QFileDialog is super complex.

        mainlog.debug("__open_file_dialog")
        return QFileDialog.getOpenFileName(self, _('Add a document'), "",
                                           "")[0]

    @Slot()
    def save_a_copy_clicked(self, button_id):
        # self.model.object_at()
        #
        # doc_id, full_path_client, file_size, description = self.button_data[button_id]
        # doc = documents_service.find_by_id(doc_id)

        doc = self._button_id_to_doc(button_id)

        full_path_client = self._save_file_dialog(doc.filename)

        # The user might hit "cancel" !
        if full_path_client:
            new_path = self._download_on_button_id(button_id, full_path_client)

            # mainlog.debug(u"{} <- {}".format(new_path,full_path_client))
            if new_path and new_path != full_path_client:
                os.rename(new_path, full_path_client)

    @Slot()
    def add_template_dialog(self):
        dialog = TemplateSelectDialog(None)
        dialog.refresh_templates_list()
        dialog.exec_()

        if dialog.template_id:
            try:
                for tpl_id in dialog.template_id:
                    doc_id = instanciate_template(tpl_id)
                    doc = self.documents_service.find_by_id(doc_id)
                    self._add_one_document(doc.filename, doc.document_id,
                                           doc.file_size, doc.description)
                self._mark_changed()
            except Exception as exc:
                showErrorBox(_(
                    "There was a problem while uploading the template to the server"
                ),
                             ex=exc,
                             object_name="template_upload_error")

    @Slot()
    def add_document_dialog(self):
        mainlog.debug("add_document_dialog")
        full_path_client = self._open_file_dialog()
        # The user might hit "cancel" !
        if full_path_client:
            self._add_file(full_path_client)
            # self.documents_changed = true

    @Slot()
    def open_clicked(self, button_id):
        filepath = self._download_on_button_id(button_id, None)
        if filepath:
            try:
                if sys.platform.startswith('darwin'):
                    subprocess.call(('open', filepath))
                elif os.name == 'nt':
                    os.startfile(filepath)
                elif os.name == 'posix':
                    subprocess.call(('xdg-open', filepath))
            except Exception as ex:
                showErrorBox(_("Can't open file"),
                             _("I'm unable to open the file"), ex)

    # WARNING : This is overriden in TemplatesCollectionWidget !!!
    @Slot()
    def delete_remote_clicked(self, button_id):
        doc_id = self.button_data[button_id]
        doc = self.model.locate_object(lambda obj: obj.document_id == doc_id)

        if confirmationBox(
                _("Deleting a document"),
                _("Are you sure you want to remove {}").format(doc.filename)):
            try:
                self.model.delete_document(doc)
            except Exception as ex:
                showErrorBox(_("There was an error while deleting a document"),
                             ex=ex)

    @Slot(str)
    def file_modified_slot(self, path):
        pass
        # print("file_modified_slot")

    def _mark_changed(self):
        """ That's rather primitive. Indeed if a new document
        is added and then removed, we can say that the list has
        *not* changed. But with mark_changed on add file, we'll
        never be able to see that...
        """

        self.documents_changed = True
        self.documents_list_changed.emit()

    @Slot(QPoint)
    def popup_menu(self, position):

        selected_row = self.view.rowAt(position.y())

        if selected_row >= 0 and self._used_categories and len(
                self._used_categories) > 1:
            category_menu = QMenu(_("Categories"))
            selected_doc = self.model.object_at(selected_row)

            category_actions = []
            for category in self._used_categories:
                a = QAction(category.full_name, category_menu)
                a.setData(category)

                a.setEnabled(selected_doc.document_category_id !=
                             category.document_category_id)

                category_menu.addAction(a)
                category_actions.append(a)

            action = category_menu.exec_(QCursor.pos())

            if action:
                new_category = action.data()

                if selected_doc.document_category_id != new_category.document_category_id:
                    selected_doc.document_category_id = new_category.document_category_id
                    self.model.signal_object_change(selected_doc)
Exemplo n.º 5
0
class RecorderControl(ScreenWithBackButton):
    def __init__(self, hyperdeck, atem, state, mainWindow):
        self.hyperdeck = hyperdeck
        self.atem = atem
        self.state = state
        self.mainWindow = mainWindow
        super(RecorderControl, self).__init__("Recorder", mainWindow)
        self.state.transportChange.connect(self.updateState)
        if self.hyperdeck:
            self.updateState(state.transport)

    def makeContent(self):
        layout = QGridLayout()

        self.btnGroupSDCard = QButtonGroup()

        self.sdSlotMapper = QSignalMapper()

        for i in range(2):
            btn = ExpandingButton()
            btn.setCheckable(True)
            btn.setText("SD card {}".format(i + 1))
            btn.clicked.connect(self.sdSlotMapper.map)
            self.sdSlotMapper.setMapping(btn, i + 1)
            self.btnGroupSDCard.addButton(btn, i)
            layout.addWidget(btn, 0, i)

        self.sdSlotMapper.mapped.connect(self.hyperdeck.selectSlot)

        self.btnSetPreview = ExpandingButton()
        self.btnSetPreview.setText("To preview")
        self.btnSetPreview.clicked.connect(
            lambda: self.atem.setPreview(VideoSource.INPUT_7))
        layout.addWidget(self.btnSetPreview, 0, 4)

        btnClearPeaks = ExpandingButton()
        btnClearPeaks.setText("Clear VU peaks")
        btnClearPeaks.clicked.connect(self.atem.resetAudioMixerPeaks)
        layout.addWidget(btnClearPeaks, 0, 5)

        self.btnGroupTransportMode = QButtonGroup()

        self.btnPlaybackMode = ExpandingButton()
        self.btnPlaybackMode.setCheckable(True)
        self.btnPlaybackMode.setChecked(True)
        self.btnPlaybackMode.setText("Playback mode")
        self.btnGroupTransportMode.addButton(self.btnPlaybackMode)
        self.btnPlaybackMode.clicked.connect(
            lambda: self._setRecordMode(False))
        layout.addWidget(self.btnPlaybackMode, 1, 1, 1, 2)

        self.btnRecordMode = ExpandingButton()
        self.btnRecordMode.setCheckable(True)
        self.btnRecordMode.setText("Record mode")
        self.btnGroupTransportMode.addButton(self.btnRecordMode)
        self.btnRecordMode.clicked.connect(lambda: self._setRecordMode(True))
        layout.addWidget(self.btnRecordMode, 1, 3, 1, 2)

        self.btnSkipBack = _make_button("Back", ":icons/media-skip-backward",
                                        self.hyperdeck.prev)
        layout.addWidget(self.btnSkipBack, 2, 0)

        self.btngroup = QButtonGroup()

        self.btnPlay = _make_button("Play", ":icons/media-playback-start",
                                    self.hyperdeck.play)
        self.btnPlay.setCheckable(True)
        self.btngroup.addButton(self.btnPlay)
        layout.addWidget(self.btnPlay, 2, 1)

        self.btnLoopPlay = _make_button("Loop", ":icons/media-playback-loop",
                                        lambda: self.hyperdeck.play(loop=True))
        layout.addWidget(self.btnLoopPlay, 2, 2)

        self.btnSkipForward = _make_button("Forward",
                                           ":icons/media-skip-forward",
                                           self.hyperdeck.next)
        layout.addWidget(self.btnSkipForward, 2, 3)

        self.btnStop = _make_button("Stop", ":icons/media-playback-stop",
                                    self.hyperdeck.stop)
        self.btnStop.setCheckable(True)
        self.btngroup.addButton(self.btnStop)
        layout.addWidget(self.btnStop, 2, 4)

        self.btnRecord = _make_button("Record", ":icons/media-record",
                                      self.hyperdeck.record)
        self.btnRecord.setCheckable(True)
        self.btnRecord.setEnabled(False)
        self.btngroup.addButton(self.btnRecord)
        layout.addWidget(self.btnRecord, 2, 5)

        self.clipSelectionScreen = RecorderClipSelectionScreen(
            self.hyperdeck, self.state, self.mainWindow)
        self.state.clipsListChange.connect(
            self.clipSelectionScreen.populateClipsList)
        self.state.transportChange.connect(
            self.clipSelectionScreen._updateClipSelectionFromState)

        self.btnChooseClip = ExpandingButton()
        self.btnChooseClip.setText("Select clip")
        self.btnChooseClip.clicked.connect(self._showClipSelection)
        layout.addWidget(self.btnChooseClip, 3, 1, 1, 2)

        layout.setRowStretch(0, 1)
        layout.setRowStretch(1, 1)
        layout.setRowStretch(2, 2)
        layout.setRowStretch(3, 1)

        return layout

    def updateState(self, state):
        if 'status' in state:
            self.btnRecord.setChecked(state['status'] == TransportState.RECORD)
            self.btnPlay.setChecked(state['status'] == TransportState.PLAYING)
            self.btnStop.setChecked(
                state['status'] != TransportState.RECORD
                and state['status'] != TransportState.PLAYING)
        currentSlot = state.get('active slot', 1)
        self.btnGroupSDCard.button(currentSlot - 1).setChecked(True)

    def _setRecordMode(self, isRecordMode):
        if isRecordMode:
            self.btnSkipBack.setEnabled(False)
            self.btnSkipForward.setEnabled(False)
            self.btnPlay.setEnabled(False)
            self.btnPlay.setChecked(False)
            self.btnLoopPlay.setEnabled(False)
            self.btnRecord.setEnabled(True)
            self.hyperdeck.setTransportMode(TransportMode.RECORD)
        else:
            self.btnSkipBack.setEnabled(True)
            self.btnSkipForward.setEnabled(True)
            self.btnPlay.setEnabled(True)
            self.btnLoopPlay.setEnabled(True)
            self.btnRecord.setEnabled(False)
            self.btnRecord.setChecked(False)
            self.hyperdeck.setTransportMode(TransportMode.PLAYBACK)

    def _showClipSelection(self):
        self.hyperdeck.broadcastClipsList()
        self.mainWindow.showScreen(self.clipSelectionScreen)
Exemplo n.º 6
0
class IPHelper(QDialog):
	def __init__(self, parent=None):
		super(IPHelper, self).__init__(parent)
		f = QFile(os.path.join(os.path.split(__file__)[0], 'iphelper.ui'))
		loadUi(f, self)
		f.close()
		self.ipAddress = None
		
		# create validators
		validator = QRegExpValidator(QRegExp('\d{,3}'))
		self.uiFirstTetTXT.setValidator(validator)
		self.uiSecondTetTXT.setValidator(validator)
		self.uiThirdTetTXT.setValidator(validator)
		self.uiFourthTetTXT.setValidator(validator)
		
		# build a map of the buttons
		self.buttons = [None]*16
		self.signalMapper = QSignalMapper(self)
		self.signalMapper.mapped.connect(self.tetMap)
		for button in self.findChildren(QPushButton):
			match = re.findall(r'^uiTrellis(\d{,2})BTN$', button.objectName())
			if match:
				i = int(match[0])
				self.buttons[i] = button
				if i >= 12:
					self.signalMapper.setMapping(button, i)
					button.clicked.connect(self.signalMapper.map)
		self.tetMap(12)

	@Slot()
	def accept(self):
		self.ipAddress = '{}.{}.{}.{}'.format(self.uiFirstTetTXT.text(), self.uiSecondTetTXT.text(), self.uiThirdTetTXT.text(), self.uiFourthTetTXT.text())
		super(IPHelper, self).accept()

	@Slot(int)
	def tetMap(self, index):
		button = self.buttons[index]
		if not button.isChecked():
			return
		for i in range(12, 16):
			b = self.buttons[i]
			if b != button:
				b.setChecked(False)
		# update the buttons to match the current value of the text
		for edit in (self.uiFirstTetTXT, self.uiSecondTetTXT, self.uiThirdTetTXT, self.uiFourthTetTXT):
			edit.setProperty('active', False)
		if index == 12:
			val = int(self.uiFourthTetTXT.text())
			self.uiFourthTetTXT.setProperty('active', True)
		elif index == 13:
			val = int(self.uiThirdTetTXT.text())
			self.uiThirdTetTXT.setProperty('active', True)
		elif index == 14:
			val = int(self.uiSecondTetTXT.text())
			self.uiSecondTetTXT.setProperty('active', True)
		elif index == 15:
			val = int(self.uiFirstTetTXT.text())
			self.uiFirstTetTXT.setProperty('active', True)
		for i in range(8):
			b = self.buttons[i]
			b.blockSignals(True)
			b.setChecked(2**i & val)
			b.blockSignals(False)
		# force a refresh of the styleSheet
		self.setStyleSheet(self.styleSheet())

	@Slot()
	def buttonPressed(self):
		total = 0
		for i in range(8):
			if self.buttons[i].isChecked():
				total += 2**i
		total = unicode(total)
		if self.uiTrellis12BTN.isChecked():
			self.uiFourthTetTXT.setText(total)
		elif self.uiTrellis13BTN.isChecked():
			self.uiThirdTetTXT.setText(total)
		elif self.uiTrellis14BTN.isChecked():
			self.uiSecondTetTXT.setText(total)
		elif self.uiTrellis15BTN.isChecked():
			self.uiFirstTetTXT.setText(total)
Exemplo n.º 7
0
class OutputsGrid(QFrame):

    cut = Signal()
    take = Signal()
    mainToAll = Signal()
    all = Signal()
    selected = Signal(int)
    sendMain = Signal(int)

    def __init__(self, switcherState, parent=None):
        super(OutputsGrid, self).__init__(parent)

        self.signalMapper = QSignalMapper(self)
        self.longPressSignalMapper = QSignalMapper(self)

        layout = QGridLayout()

        mainMixFrame = MainMixControl()
        mainMixFrame.cut.connect(self.cut.emit)
        mainMixFrame.take.connect(self.take.emit)

        layout.addWidget(mainMixFrame, 0, 0, 1, 2)

        self.aux_buttons = []

        for idx, output in switcherState.outputs.iteritems():
            ob = OutputButton(output)
            layout.addWidget(ob, 1 + (idx / 2), idx % 2)
            ob.clicked.connect(self.signalMapper.map)
            self.signalMapper.setMapping(ob, idx)

            ob.longpress.connect(self.longPressSignalMapper.map)
            self.longPressSignalMapper.setMapping(ob, idx)
            self.aux_buttons.append(ob)

        self.signalMapper.mapped.connect(self.registerClick)
        self.longPressSignalMapper.mapped.connect(self.longPress)

        btnAll = ExpandingButton()
        btnAll.setProperty("class", "mainMix")
        btnAll.setText("Mix to all")
        btnAll.clicked.connect(self.mainToAll.emit)
        layout.addWidget(btnAll, 4, 0)

        self.btnAll = ExpandingButton()
        self.btnAll.setText("All")
        self.btnAll.clicked.connect(self.all.emit)
        layout.addWidget(self.btnAll, 4, 1)

        layout.setColumnMinimumWidth(0, 100)
        layout.setColumnMinimumWidth(1, 100)
        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(1, 1)

        layout.setRowStretch(0, 2)
        for i in range(1, 5):
            layout.setRowStretch(i, 1)

        self.setLayout(layout)

    def registerClick(self, idx):
        self.selected.emit(idx)

    def longPress(self, idx):
        self.sendMain.emit(idx)

    def setAuxesEnabled(self, enabled):
        for button in self.aux_buttons:
            button.setEnabled(enabled)
        self.btnAll.setEnabled(enabled)
Exemplo n.º 8
0
class RecorderControl(ScreenWithBackButton):
    def __init__(self, hyperdeck, atem, state, mainWindow):
        self.hyperdeck = hyperdeck
        self.atem = atem
        self.state = state
        self.mainWindow = mainWindow
        super(RecorderControl, self).__init__("Recorder", mainWindow)
        self.state.transportChange.connect(self.updateState)
        if self.hyperdeck:
            self.updateState(state.transport)

    def makeContent(self):
        layout = QGridLayout()

        self.btnGroupSDCard = QButtonGroup()

        self.sdSlotMapper = QSignalMapper()

        for i in range(2):
            btn = ExpandingButton()
            btn.setCheckable(True)
            btn.setText("SD card {}".format(i + 1))
            btn.clicked.connect(self.sdSlotMapper.map)
            self.sdSlotMapper.setMapping(btn, i + 1)
            self.btnGroupSDCard.addButton(btn, i)
            layout.addWidget(btn, 0, i)

        self.sdSlotMapper.mapped.connect(self.hyperdeck.selectSlot)

        self.btnSetPreview = ExpandingButton()
        self.btnSetPreview.setText("To preview")
        self.btnSetPreview.clicked.connect(lambda: self.atem.setPreview(VideoSource.INPUT_7))
        layout.addWidget(self.btnSetPreview, 0, 4)

        btnClearPeaks = ExpandingButton()
        btnClearPeaks.setText("Clear VU peaks")
        btnClearPeaks.clicked.connect(self.atem.resetAudioMixerPeaks)
        layout.addWidget(btnClearPeaks, 0, 5)

        self.btnGroupTransportMode = QButtonGroup()

        self.btnPlaybackMode = ExpandingButton()
        self.btnPlaybackMode.setCheckable(True)
        self.btnPlaybackMode.setChecked(True)
        self.btnPlaybackMode.setText("Playback mode")
        self.btnGroupTransportMode.addButton(self.btnPlaybackMode)
        self.btnPlaybackMode.clicked.connect(lambda: self._setRecordMode(False))
        layout.addWidget(self.btnPlaybackMode, 1, 1, 1, 2)

        self.btnRecordMode = ExpandingButton()
        self.btnRecordMode.setCheckable(True)
        self.btnRecordMode.setText("Record mode")
        self.btnGroupTransportMode.addButton(self.btnRecordMode)
        self.btnRecordMode.clicked.connect(lambda: self._setRecordMode(True))
        layout.addWidget(self.btnRecordMode, 1, 3, 1, 2)

        self.btnSkipBack = _make_button("Back", ":icons/media-skip-backward", self.hyperdeck.prev)
        layout.addWidget(self.btnSkipBack, 2, 0)

        self.btngroup = QButtonGroup()

        self.btnPlay = _make_button("Play", ":icons/media-playback-start", self.hyperdeck.play)
        self.btnPlay.setCheckable(True)
        self.btngroup.addButton(self.btnPlay)
        layout.addWidget(self.btnPlay, 2, 1)

        self.btnLoopPlay = _make_button("Loop", ":icons/media-playback-loop", lambda: self.hyperdeck.play(loop=True))
        layout.addWidget(self.btnLoopPlay, 2, 2)

        self.btnSkipForward = _make_button("Forward", ":icons/media-skip-forward", self.hyperdeck.next)
        layout.addWidget(self.btnSkipForward, 2, 3)

        self.btnStop = _make_button("Stop", ":icons/media-playback-stop", self.hyperdeck.stop)
        self.btnStop.setCheckable(True)
        self.btngroup.addButton(self.btnStop)
        layout.addWidget(self.btnStop, 2, 4)

        self.btnRecord = _make_button("Record", ":icons/media-record", self.hyperdeck.record)
        self.btnRecord.setCheckable(True)
        self.btnRecord.setEnabled(False)
        self.btngroup.addButton(self.btnRecord)
        layout.addWidget(self.btnRecord, 2, 5)

        self.clipSelectionScreen = RecorderClipSelectionScreen(self.hyperdeck, self.state, self.mainWindow)
        self.state.clipsListChange.connect(self.clipSelectionScreen.populateClipsList)
        self.state.transportChange.connect(self.clipSelectionScreen._updateClipSelectionFromState)

        self.btnChooseClip = ExpandingButton()
        self.btnChooseClip.setText("Select clip")
        self.btnChooseClip.clicked.connect(self._showClipSelection)
        layout.addWidget(self.btnChooseClip, 3, 1, 1, 2)

        layout.setRowStretch(0, 1)
        layout.setRowStretch(1, 1)
        layout.setRowStretch(2, 2)
        layout.setRowStretch(3, 1)

        return layout

    def updateState(self, state):
        if 'status' in state:
            self.btnRecord.setChecked(state['status'] == TransportState.RECORD)
            self.btnPlay.setChecked(state['status'] == TransportState.PLAYING)
            self.btnStop.setChecked(state['status'] != TransportState.RECORD and state['status'] != TransportState.PLAYING)
        currentSlot = state.get('active slot', 1)
        self.btnGroupSDCard.button(currentSlot - 1).setChecked(True)

    def _setRecordMode(self, isRecordMode):
        if isRecordMode:
            self.btnSkipBack.setEnabled(False)
            self.btnSkipForward.setEnabled(False)
            self.btnPlay.setEnabled(False)
            self.btnPlay.setChecked(False)
            self.btnLoopPlay.setEnabled(False)
            self.btnRecord.setEnabled(True)
            self.hyperdeck.setTransportMode(TransportMode.RECORD)
        else:
            self.btnSkipBack.setEnabled(True)
            self.btnSkipForward.setEnabled(True)
            self.btnPlay.setEnabled(True)
            self.btnLoopPlay.setEnabled(True)
            self.btnRecord.setEnabled(False)
            self.btnRecord.setChecked(False)
            self.hyperdeck.setTransportMode(TransportMode.PLAYBACK)

    def _showClipSelection(self):
        self.hyperdeck.broadcastClipsList()
        self.mainWindow.showScreen(self.clipSelectionScreen)