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)
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 __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)
def __init__(self, main_widget_or_layout, side_widgets, parent=None): super(VerticalSideBarLayout, self).__init__(parent) self.stars = [] self.signal_mapper_tab_changed = QSignalMapper() self.signal_mapper_tab_changed.mapped.connect(self._tab_changed) self.side_widgets = side_widgets self.stack = QStackedLayout(self) for w in side_widgets: self.stack.addWidget(w) self.stack.setCurrentIndex(-1) # I need a widget so that I can show/hide it. # (we can't do that with a layout) self.stack_widget = QWidget(self) self.stack_widget.setLayout(self.stack) layout = QHBoxLayout() if isinstance(main_widget_or_layout, QWidget): layout.addWidget(main_widget_or_layout) else: layout.addLayout(main_widget_or_layout) # The panel layout is made of # stack of widgets, vertical separator, buttons for widget selection panel_layout = QHBoxLayout() panel_layout.addWidget(self.stack_widget) self.separator = VerticalSeparator(self) panel_layout.addWidget(self.separator) self.buttons_layout = self._make_tabs_button( side_widgets, ["appbar.cabinet.files.png", "thumb-up-3x.png"]) # ,"comments.png" panel_layout.addLayout(self.buttons_layout) # The panel layout is wrapped into an inline sub frame isf = InlineSubFrame(panel_layout, parent=self) isf.setObjectName("HorseSubFrame") # ""HorseTightFrame") isf.layout().setContentsMargins(2, 2, 2, 2) # isf.setObjectName("HorseTightFrame") layout.addWidget(isf) # layout.setStretch(0,3) # layout.setStretch(1,2) self.setLayout(layout) self._hide_content_pane()
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()
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
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)
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)
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
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)
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)
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
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)