class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(300, 200)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0,0,0,0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.reload() def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None ): print 'RELOAD' try: fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) with open(fp,'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() except Exception as e: print e LOG.error('File path is not valid: {}]n,()'.format(fp),e) def line_up(self): print 'up' def line_down(self): print 'down' def selectionChanged(self,old, new): cmd = self.getSelected() self.MDILine.setText(cmd) def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() item = QStandardItem(cmd) self.model.appendRow(item) self.list.update() ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
def initUI(self): tableView = QTableView(self) report = self.questions.genReport() model = QStandardItemModel() model.setColumnCount(3) model.setRowCount(len(report)) model.setHorizontalHeaderLabels([ self.tr('Question'), self.tr('Your answer'), self.tr('Correct answer') ]) score = 0 for idx in range(len(report)): (question, answer, correctAnswer) = report[idx] model.setItem(idx, 0, QStandardItem(question)) model.setItem(idx, 1, QStandardItem(answer)) model.setItem(idx, 2, QStandardItem(correctAnswer)) if answer == correctAnswer: model.item(idx, 1).setBackground(QBrush(QColor(0, 255, 0))) score += 1 else: model.item(idx, 1).setBackground(QBrush(QColor(255, 0, 0))) tableView.setModel(model) layout = QVBoxLayout() layout.addWidget(self.getScoreWidget(int(score * 100 / len(report)))) layout.addWidget(tableView) self.setLayout(layout) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setSizePolicy(sizePolicy)
class TagChooser(QListView): changed = Signal() def __init__(self, *args, **kwargs): super(TagChooser,self).__init__(*args, **kwargs) self.db = None self.filter = u'' self.data = QStandardItemModel(self) self.proxy = QSortFilterProxyModel(self) self.proxy.setSourceModel(self.data) self.setModel(self.proxy) self.data.itemChanged.connect(self.changed) self.setContextMenuPolicy(Qt.ActionsContextMenu) act = QAction('&Refresh tags', self) act.triggered.connect(self.refreshTags) self.addAction(act) def setDb(self, db): self.db = db for t in sorted(self.db.list_tags()): item = QStandardItem(t) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Unchecked) self.data.appendRow(item) def setTags(self, tags): for i in range(self.data.rowCount()): item = self.data.item(i) if item.text() in tags: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) def selectedTags(self): tags = [] for i in range(self.data.rowCount()): item = self.data.item(i) if item.checkState() == Qt.Checked: tags.append(item.text()) return tags def matchingFiles(self): tags = self.selectedTags() if not tags: return [] res = list(self.db.find_files_by_tags(tags)) res.sort() return res @Slot() def refreshTags(self): selected = self.selectedTags() self.setDb(self.db) self.setTags(selected)
class LayerList(QListView): def __init__(self): super(LayerList, self).__init__() self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) self.setDefaultDropAction(Qt.MoveAction) self.setDragDropOverwriteMode(False) self.setAcceptDrops(True) self.setDropIndicatorShown(True) self.setDragEnabled(True) self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.setTabKeyNavigation(True) self.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.setMaximumSize(QtCore.QSize(1000, 1000)) self.model = QStandardItemModel(self) self.setModel(self.model) self.image_layers = None # embed() def dragLeaveEvent(self, event): selectedIdx = self.selectedIndexes()[0].row() print('remove item ', self.model.item(selectedIdx).whatsThis()) self.image_layers[self.model.item(selectedIdx).whatsThis()].destroy() del self.image_layers[self.model.item(selectedIdx).whatsThis()] self.model.removeRow(selectedIdx)
def friend_list(self): if hasattr(self, 'bot'): self.my_friend = self.bot.friends() model_fri = QStandardItemModel() model_fri.setHorizontalHeaderLabels( ['Friends', 'Nick_Name', 'Sex', 'Province', 'City']) for i, fri in enumerate(self.my_friend): model_fri.setItem(i, 0, QStandardItem(str(emojize(fri.name)))) model_fri.setItem(i, 1, QStandardItem(str(emojize(fri.nick_name)))) if fri.sex == wxpy.MALE: sex = 'Male' color = '#5CACEE' elif fri.sex == wxpy.FEMALE: sex = 'Female' color = '#EE82EE' else: sex = 'Intersex' color = '#FFFFFF' model_fri.setItem(i, 2, QStandardItem(str(sex))) model_fri.item(i, 2).setBackground(QBrush(QColor(color))) model_fri.setItem(i, 3, QStandardItem(str(fri.province))) model_fri.setItem(i, 4, QStandardItem(str(fri.city))) # print(fri.sex) self.friend_tableView.setModel(model_fri)
class _MetadataGroupBox(QGroupBox): def __init__(self, parent: QWidget) -> None: super().__init__("Meta data:", parent) self.model = QStandardItemModel(self) self.model.setHorizontalHeaderLabels(["Key", "Value"]) self._table_view = QTableView(self) self._table_view.setModel(self.model) self._table_view.setTabKeyNavigation(False) self._table_view.horizontalHeader().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._table_view.horizontalHeader().setSectionResizeMode( 1, QHeaderView.Stretch) self._table_view.verticalHeader().setSectionsMovable(True) self._table_view.verticalHeader().setDragEnabled(True) self._table_view.verticalHeader().setDragDropMode( QAbstractItemView.InternalMove) self._table_view.verticalHeader().setDefaultSectionSize( int(self._table_view.fontMetrics().height() * 1.8)) strip = QWidget(self) add_row_button = QPushButton("Add new row", strip) del_rows_button = QPushButton("Remove selected rows", strip) strip_layout = QHBoxLayout(strip) strip_layout.setContentsMargins(0, 0, 0, 0) strip_layout.addWidget(add_row_button) strip_layout.addWidget(del_rows_button) strip_layout.addStretch() add_row_button.clicked.connect(self._on_add_button_click) del_rows_button.clicked.connect(self._on_delete_rows_button_click) layout = QVBoxLayout(self) layout.addWidget(self._table_view) layout.addWidget(strip) def get_data(self) -> dict[str, str]: metadata: dict[str, str] = OrderedDict() for y in range(self.model.rowCount()): visual_y = self._table_view.verticalHeader().visualIndex(y) key = self.model.item(visual_y, 0).text().strip() value = self.model.item(visual_y, 1).text().strip() if key and value: metadata[key] = value return metadata def _on_add_button_click(self) -> None: self.model.appendRow([QStandardItem(""), QStandardItem("")]) def _on_delete_rows_button_click(self) -> None: rows = set( index.row() for index in self._table_view.selectionModel().selectedIndexes()) for row in sorted(rows, reverse=True): self.model.removeRow(row)
def RefreshAllHookList(): if mainWindowHelper.tbvAllHooks.model() is QStandardItemModel: mainWindowHelper.tbvAllHooks.model().destroyed() model_allHooks = QStandardItemModel() columns = ["name", "inj addr", "source addr", "inj size", "source size"] model_allHooks.setColumnCount(len(columns)) for idx in range(len(columns)): model_allHooks.setHeaderData(idx, Qt.Horizontal, columns[idx]) all_hooks: set[str] = set() for inj_hook_name in inj_hooks: all_hooks.add(inj_hooks[inj_hook_name].name) for file in file_list: for hook_name in file_hooks[file]: all_hooks.add(file_hooks[file][hook_name].name) idx = 0 for hook_name in all_hooks: inj_hook = inj_hooks.get(hook_name) source_hook = FindHook(hook_name) if source_hook != None: if IsShowSame() == False: if inj_hook == source_hook: continue if IsShowAnnotated() == False and source_hook.annotated: continue model_allHooks.setItem(idx, 0, QStandardItem(hook_name)) if source_hook != None: if source_hook.annotated: model_allHooks.item(idx, 0).setBackground(QColorConstants.Green) if inj_hook == source_hook: model_allHooks.item(idx, 0).setBackground(QColorConstants.Gray) if inj_hook != None: model_allHooks.setItem(idx, 1, QStandardItem(inj_hook.address)) model_allHooks.setItem(idx, 3, QStandardItem(inj_hook.size)) if source_hook != None: model_allHooks.setItem(idx, 2, QStandardItem(source_hook.address)) model_allHooks.setItem(idx, 4, QStandardItem(source_hook.size)) idx = idx + 1 mainWindowHelper.tbvAllHooks.setModel(model_allHooks) mainWindowHelper.tbvAllHooks.verticalHeader().setVisible(False) mainWindowHelper.tbvAllHooks.setColumnWidth(0, 400) mainWindowHelper.tbvAllHooks.setColumnWidth(1, 80) mainWindowHelper.tbvAllHooks.setColumnWidth(2, 80) mainWindowHelper.tbvAllHooks.setColumnWidth(3, 60) mainWindowHelper.tbvAllHooks.setColumnWidth(4, 80)
def _refresh_attribute_table(self): attribute_list = self._get_attribute_list() model = QStandardItemModel() for index, data in enumerate(attribute_list): item = QStandardItem(str(data[1])) model.setItem(index, 0, item) model.item(index, 0).setTextAlignment(Qt.AlignCenter) self.attribute_table.setModel(model)
class Table(QWidget): def __init__(self, parent=None): super(Table, self).__init__(parent) # 设置标题与初始大小 self.setWindowTitle('QTableView表格视图的例子') self.resize(500, 300) # 设置数据层次结构,4行4列,貌似没有影响 self.model = QStandardItemModel() # 设置水平方向四个头标签文本内容 self.model.setHorizontalHeaderLabels( ['标题1', '标题2', '标题3', '标题4', '标题5']) for row in range(20): for column in range(4): item = QStandardItem('row %s,column %s' % (row, column)) # 设置每个位置的文本值 self.model.setItem(row, column, item) # 添加单行数据 self.model.appendRow([ QStandardItem('row %s,column %s' % (11, 11)), QStandardItem('row %s,column %s' % (11, 11)), QStandardItem('row %s,column %s' % (11, 11)), QStandardItem('row %s,column %s' % (11, 11)), ]) # 实例化表格视图,设置模型为自定义的模型 self.tableView = QTableView() self.tableView.setModel(self.model) # 水平方向标签拓展剩下的窗口部分,填满表格 self.tableView.horizontalHeader().setStretchLastSection(True) # 水平方向,配置列的宽度修改方式 self.tableView.horizontalHeader().setSectionResizeMode( QHeaderView.Interactive) # 设置不可编辑 self.tableView.setEditTriggers(QTableView.NoEditTriggers) # 设置行选中 self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows) # 设置只能选中一行 self.tableView.setSelectionMode(QAbstractItemView.SingleSelection) # 设置字体颜色 self.model.item(1, 1).setForeground(QBrush(QColor(255, 0, 0))) # 设置单元格字体 self.model.item(1, 1).setFont(QFont('Times', 10, QFont.Black)) # 设置列是否隐藏 self.tableView.setColumnHidden(4, False) # 设置列宽 self.tableView.setColumnWidth(0, 20) self.tableView.setItemDelegateForColumn( 4, MyButtonDelegate(self.tableView)) # 设置布局 layout = QVBoxLayout() layout.addWidget(self.tableView) self.setLayout(layout)
class LaunchBarView: def __init__(self, root, controller): root = root.findChild(QWidget, 'launchbar') self.controller = controller self.selected_wad_name = root.findChild(QLabel, 'launchbar_selected_wad') self.selected_wad_name.setText('No wad selected') self.iwad_selector_model = QStandardItemModel() for iwad in controller.models.iwads.all(): self.append_iwad(iwad) self.iwad_selector = root.findChild(QComboBox, 'launchbar_iwad_selector') self.iwad_selector.setModel(self.iwad_selector_model) self.iwad_selector.currentIndexChanged.connect(self.select_iwad) self.source_port_selector_model = QStandardItemModel() for source_port in controller.models.source_ports.all(): self.append_source_port(source_port) self.source_port_selector = root.findChild( QComboBox, 'launchbar_source_port_selector') self.source_port_selector.setModel(self.source_port_selector_model) self.source_port_selector.currentIndexChanged.connect( self.select_source_port) launch_wad_button = root.findChild(QPushButton, 'launchbar_launch_button') launch_wad_button.clicked.connect(self.controller.launch_wad_press) def append_iwad(self, data): item = make_iwad_item(data) self.iwad_selector_model.appendRow(item) def select_iwad(self, index): item = self.iwad_selector_model.item(index) self.controller.select_iwad(item.data(ID_ROLE)) def append_source_port(self, data): item = make_source_port_item(data) self.source_port_selector_model.appendRow(item) def select_source_port(self, index): item = self.source_port_selector_model.item(index) self.controller.select_source_port(item.data(ID_ROLE)) def update_selected_wad(self, wad): if wad == None: self.selected_wad_name.setText('No wad selected') else: self.selected_wad_name.setText( (wad.title or wad.name) + ' ({})'.format(os.path.basename(wad.file_path)))
class Children_Form(QWidget): def __init__(self, year, client, salehoue, parent=None): super(Children_Form, self).__init__(parent) self.ui = ChildrenForm() self.ui.setupUi(self) self.setWindowModality(Qt.ApplicationModal) self.setWindowFlag(Qt.Window) self.ui.label.setText(year) self.ui.label_2.setText(client) self.ui.label_3.setText(salehoue) self.list_view = self.ui.listView surcharges = ["drugi reklamodawca", "duo spot", "wyłączność w bloku"] self.model = QStandardItemModel(self.list_view) self.add_surcharges(surcharges) self.ui.pushButton.clicked.connect( lambda: self.add_surcharges([self.ui.lineEdit.text()])) self.ui.pushButton_remove.clicked.connect(self.remove_surcharge) self.ui.buttonBox.accepted.connect(self.send_surcharge) self.partner = parent #self.show() def add_surcharges(self, surcharges): for surcharge in surcharges: item = QStandardItem(surcharge) item.setCheckable(True) self.model.appendRow(item) self.list_view.setModel(self.model) def send_surcharge(self): print("send_surcharge") i = 0 items = [] while self.model.item(i): if self.model.item(i).checkState(): items.append(self.model.item(i).text()) i += 1 print(type(self.partner)) self.partner.add_schurges(items) self.close() def remove_surcharge(self): i = 0 while self.model.item(i): if self.model.item(i).checkState(): self.model.removeRow(i) i += 1
class AddBookWindow(QWidget): def __init__(self, parent=None): super(AddBookWindow, self).__init__(parent=parent) self.ui = uic.loadUi('Views/addBookUi.ui', self) self.ui.addBookButton.clicked.connect(self.addBook) self.loadAuthors() self.loadGenres() def loadAuthors(self): self.authorModel = QStandardItemModel(self.ui.authorListView) authors = Authors() for author in authors: item = QStandardItem(author.JoinName()) item.setCheckable(True) self.authorModel.appendRow(item) self.ui.authorListView.setModel(self.authorModel) def loadGenres(self): self.genreModel = QStandardItemModel(self.ui.genreListView) genres = Genres() for genre in genres: item = QStandardItem(genre.Name()) item.setCheckable(True) self.genreModel.appendRow(item) self.ui.genreListView.setModel(self.genreModel) def addBook(self): authors = [] genres = [] for i in range(0, self.authorModel.rowCount()): item = self.authorModel.item(i) if item.checkState(): authors.append(Author(*item.text().split(" "))) for i in range(0, self.genreModel.rowCount()): item = self.genreModel.item(i) if item.checkState(): genres.append(Genre(item.text())) title = self.ui.bookTitleLineEdit.text() year = self.ui.yearSpinBox.value() mark = self.ui.markSpinBox.value() book = Book(title, year, mark, authors, genres) book.save() self.close()
class TicketsView(QtWidgets.QMainWindow): """Window used to view all tickets""" def __init__(self): super().__init__() uic.loadUi('src/Ui/view_window.ui', self) self.setWindowTitle("View Tickets") # Create the model self.model = None self.create_model() # Setup the view self.table_view.setAlternatingRowColors(True) self.table_view.verticalHeader().setVisible(False) self.table_view.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Stretch) # Connect to list signals raffle.signals.data_changed.connect(self.create_model) self.show() def create_model(self): """Creates the model to be used by the view""" # Create the model self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels( ('Ticket Number', 'Ticket Names', 'Number Drawn')) for i, ticket in enumerate(raffle.tickets): row_items = [ make_item(i + 1), make_item(ticket.name), make_item(ticket.number_drawn if ticket.is_drawn() else '') ] for j, item in enumerate(row_items): self.model.setItem(i, j, item) # Connect to signals ticket.signals.data_changed.connect(self.refresh) self.table_view.setModel(self.model) def refresh(self) -> None: """Runs through cells and updates them based on current data""" for i, ticket in enumerate(raffle.tickets): self.model.item(i, 1).setText(ticket.name) self.model.item(i, 2).setText( str(ticket.number_drawn) if ticket.is_drawn() else '')
def eventFilter(self, obj: QObject, event: Union[QDropEvent, QKeyEvent, QEvent]): if event.type() == QEvent.KeyPress and obj == self.lstUsedColumns: if event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace: self.remove_selected_columns_colors() event.accept() elif event.type() == QEvent.DragEnter and obj == event.source().viewport(): event.setDropAction(Qt.IgnoreAction) # Do not allow internal drag'n'drop elif event.type() == QEvent.Drop and obj == self.lstUsedColumns.viewport(): if event.source() == self.lstColors: data = event.mimeData() if data.hasFormat('application/x-qabstractitemmodeldatalist'): first_item = self.lstUsedColumns.itemAt(event.pos()) if first_item is not None: model = QStandardItemModel() model.dropMimeData(data, Qt.CopyAction, 0, 0, QModelIndex()) for i in range(model.rowCount()): color = model.item(i, 0).data(Qt.BackgroundRole).color() polygon_id = model.item(i, 0).data(BaseColorMappingDialog.PolygonRole) brushstyle = model.item(i, 0).data(BaseColorMappingDialog.BrushStyleRole) item = self.lstUsedColumns.item(self.lstUsedColumns.row(first_item) + i) if item: item.setBackground(color) item.setData(BaseColorMappingDialog.PolygonRole, polygon_id) item.setData(BaseColorMappingDialog.BrushStyleRole, brushstyle) event.accept() elif event.source() == self.lstColumns: data = event.mimeData() if data.hasFormat('application/x-qabstractitemmodeldatalist'): event.setDropAction(Qt.MoveAction) model = QStandardItemModel() model.dropMimeData(data, Qt.CopyAction, 0, 0, QModelIndex()) for i in range(model.rowCount()): item = model.item(i, 0) new_item = ColumnListWidgetItem(item.data(Qt.DisplayRole), column=item.data(ColumnRole)) bg = item.data(Qt.BackgroundRole) polygon_id = item.data(BaseColorMappingDialog.PolygonRole) brushstyle = item.data(BaseColorMappingDialog.BrushStyleRole) if bg is not None and bg.color().isValid(): new_item.setBackground(bg.color()) new_item.setData(BaseColorMappingDialog.PolygonRole, polygon_id) new_item.setData(BaseColorMappingDialog.BrushStyleRole, brushstyle) self.lstUsedColumns.addItem(new_item) self.lstUsedColumns.sortItems() event.accept() return super().eventFilter(obj, event)
class ConfigTableViewWidget(QDialog, Ui_ConfigTableViewWidget): """ Class documentation goes here. """ def __init__(self, availableScalars, scalarsToShow, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super().__init__(parent) self.setupUi(self) self.ValueListModel = QStandardItemModel() for value in availableScalars: item = QStandardItem(value) check = Qt.Checked if value in scalarsToShow else Qt.Unchecked item.setCheckState(check) item.setCheckable(True) self.ValueListModel.appendRow(item) self.ValueListView.setModel(self.ValueListModel) def setVisibleValues(self): setVisible = [] for row in range(self.ValueListModel.rowCount()): item = self.ValueListModel.item(row) if item.checkState() == Qt.Checked: setVisible.append(item.text()) return setVisible
def getSelectedItemsLabels(self, model: QStandardItemModel): labels = [] for row in range(model.rowCount()): item = model.item(row) if item.checkState() == Qt.Checked: labels.append(item.text()) return labels
class ListAuthorWindow(QWidget): def __init__(self, parent=None): super(ListAuthorWindow, self).__init__(parent=parent) self.ui = uic.loadUi('Views/listAuthorUi.ui', self) self.ui.authorBookButton.clicked.connect(self.authorBook) self.loadAuthors() def loadAuthors(self): self.authorModel = QStandardItemModel(self.ui.authorListView) authors = Authors() for author in authors: item = QStandardItem(author.JoinName()) item.setCheckable(True) self.authorModel.appendRow(item) self.ui.authorListView.setModel(self.authorModel) def authorBook(self): for i in range(0, self.authorModel.rowCount()): item = self.authorModel.item(i) if item.checkState(): books = Books(1, (Author(*item.text().split(" ")))) form = ResultListBookWindow(books) form.show()
def change_item_color(model: QStandardItemModel, item_text: str, color: Color): for i in range(model.rowCount()): item: QStandardItem = model.item(i, 0) if item_text == item.text(): color: QColor = QColor(Color.to_q_color(color)) brush: QBrush = QBrush(color) item.setForeground(brush)
class TagChooser(QListView): changed = Signal() def __init__(self, db): super(TagChooser, self).__init__() self.db = db self.filter = u'' self.data = QStandardItemModel() self.proxy = QSortFilterProxyModel() self.proxy.setSourceModel(self.data) self.setModel(self.proxy) self.data.itemChanged.connect(self.changed) for t in sorted(self.db.list_tags()): item = QStandardItem(t) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Unchecked) self.data.appendRow(item) def setTags(self, tags): for i in range(self.data.rowCount()): item = self.data.item(i) if item.text() in tags: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) def selectedTags(self): tags = [] for i in range(self.data.rowCount()): item = self.data.item(i) if item.checkState() == Qt.Checked: tags.append(item.text()) return tags def matchingFiles(self): tags = self.selectedTags() if not tags: return [] res = list(self.db.find_files_by_tags(tags)) res.sort() return res
class TagChooser(QListView): changed = Signal() def __init__(self, db): super(TagChooser,self).__init__() self.db = db self.filter = u'' self.data = QStandardItemModel() self.proxy = QSortFilterProxyModel() self.proxy.setSourceModel(self.data) self.setModel(self.proxy) self.data.itemChanged.connect(self.changed) for t in sorted(self.db.list_tags()): item = QStandardItem(t) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Unchecked) self.data.appendRow(item) def setTags(self, tags): for i in range(self.data.rowCount()): item = self.data.item(i) if item.text() in tags: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) def selectedTags(self): tags = [] for i in range(self.data.rowCount()): item = self.data.item(i) if item.checkState() == Qt.Checked: tags.append(item.text()) return tags def matchingFiles(self): tags = self.selectedTags() if not tags: return [] res = list(self.db.find_files_by_tags(tags)) res.sort() return res
class PrizesView(QtWidgets.QMainWindow): """Window used to view all tickets""" def __init__(self): super().__init__() uic.loadUi('src/Ui/view_window.ui', self) self.setWindowTitle("View Prizes") # Create the model self.model = None self.create_model() # Setup the view self.table_view.setAlternatingRowColors(True) self.table_view.verticalHeader().setVisible(False) self.table_view.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Stretch) # Connect to list signals raffle.signals.data_changed.connect(self.create_model) self.show() def create_model(self): """Creates the model for the view""" # Create the model self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels( ('Prize Number', 'Prize Description')) for i, prize in enumerate(raffle.prizes): row_items = [make_item(prize.number), make_item(prize.description)] for j, item in enumerate(row_items): self.model.setItem(i, j, item) # Connect to signals prize.signals.data_changed.connect(self.refresh) self.table_view.setModel(self.model) def refresh(self) -> None: """Runs through cells and updates them based on current data""" for i, prize in enumerate(raffle.prizes): self.model.item(i, 0).setText(str(prize.number)) self.model.item(i, 1).setText(prize.description)
class Example(QWidget): def __init__(self): super().__init__() self.setGeometry(300, 300, 350, 250) self.setWindowTitle("Actresses") self.initData() self.initUI() def initData(self): self.model = QStandardItemModel() labels = ("Name", "Place", "Born") self.model.setHorizontalHeaderLabels(labels) for i in range(len(data)): name = QStandardItem(data[i][0]) place = QStandardItem(data[i][1]) born = QStandardItem(data[i][2]) self.model.appendRow((name, place, born)) def initUI(self): tv = QTreeView(self) tv.setRootIsDecorated(False) tv.setModel(self.model) behavior = QAbstractItemView.SelectRows tv.setSelectionBehavior(behavior) self.label = QLabel(self) layout = QVBoxLayout() layout.addWidget(tv) layout.addWidget(self.label) self.setLayout(layout) tv.clicked.connect(self.onClicked) def onClicked(self, idx): row = idx.row() cols = self.model.columnCount() data = [] for col in range(cols): item = self.model.item(row, col) data.append(item.text()) self.label.setText((", ".join(data)))
class CueListWidget(QListView, Publisher): def __init__(self): QListView.__init__(self) Publisher.__init__(self) self.role = 'view' self.initUI() def initUI(self): self.setMinimumWidth(150) self.item_model = QStandardItemModel(self) self.setModel(self.item_model) self.setFont(UIFonts.cuelist_font) self.set_cues(['']) self.selectionModel().currentChanged.connect(self.update_cue_pointer) self.itemDelegate().closeEditor.connect(self.name_changed) self.lock = False self.pressed = False def set_cues(self, cues): self.cues = cues self.item_model.clear() for cue in self.cues: item = QStandardItem(cue) self.item_model.appendRow(item) def set_current_cue(self, index): self.lock = True index = self.item_model.index(index, 0) self.setCurrentIndex(index) self.lock = False def mouseMoveEvent(self, event): self.pressed = True QListView.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): self.pressed = False QListView.mouseReleaseEvent(self, event) def update_cue_pointer(self, a, b): if self.lock: return if self.pressed: self.changed('move_cue', self.currentIndex().row()) else: self.changed('cue_pointer', self.currentIndex().row()) def name_changed(self): index = self.currentIndex().row() item = self.item_model.item(index, 0) name = item.text() self.changed('cue_name', name)
class MassTabSelectorGUI(QDockWidget): """ classdocs """ masstabViewRaisedSignal = pyqtSignal(object) """ constructor """ def __init__(self, parent=None): super(MassTabSelectorGUI, self).__init__(parent) self.ui = Ui_DockWidget_MassTabSelector() self.ui.setupUi(self) def setup(self): self.__connect_events() def __connect_events(self): self.model = QStandardItemModel() self.mass_list = [] for i in range(20): mass = 290.0 + i self.mass_list.append(str(mass)) for i in range(10): mass = 599.0 + i self.mass_list.append(str(mass)) for mass in self.mass_list: item = QStandardItem(mass) item.setCheckable(True) item.setEditable(True) self.model.appendRow(item) self.view = self.ui.listView_Mass self.view.setModel(self.model) # changes in one item, don't know which one self.model.itemChanged.connect(self.change_list) # changes in button self.ui.pushButton_ChangeList.clicked.connect(self.emit_list_signal) def change_list(self): log.debug("event from %s", self.sender()) self.oneIsChecked = False self.mass_list = [] count = self.model.rowCount() for i in range(count): checked = self.model.item(i).checkState() if checked: mass_name = self.model.data(self.model.index(i, 0)) self.mass_list.append(mass_name) self.oneIsChecked = True def emit_list_signal(self): log.debug("event from %s", self.sender()) self.change_list() if self.oneIsChecked: self.masstabViewRaisedSignal.emit(self.mass_list)
class AttributeChooser(QDialog): def __init__(self, cols_, parent): super(AttributeChooser, self).__init__(parent) self.cols = cols_ self.init_ui(self.create_checkbox_list()) def create_checkbox_list(self): self.model = QStandardItemModel() for col in self.cols: item = QStandardItem(col) item.setCheckable(True) self.model.appendRow(item) return self.model def init_ui(self, model): layout = QVBoxLayout() view = QListView(self) view.setModel(model) button = QPushButton("Ok") button.clicked.connect(self.set_new_cols) layout.addWidget(view) layout.addWidget(button) self.setLayout(layout) def set_new_cols(self): i = 0 choosed_cols = ['class'] while self.model.item(i): if self.model.item(i).checkState(): choosed_cols.append(self.cols[i]) i += 1 self.cols = choosed_cols self.close() def get_data(self): self.exec_() return self.cols
class ListTab(QWidget): def __init__(self, data_manager, parent=None): super(ListTab, self).__init__(parent) self.data_manager = data_manager self.layout = QVBoxLayout() self.form_layout = QFormLayout() self.grid_layout = QGridLayout() self.package_label = QListView() self.package_label.setEditTriggers(QAbstractItemView.NoEditTriggers) self.model = QStandardItemModel(self.package_label) self.grid_layout.addWidget(self.package_label, 0, 0, 0, 0) def configure_layout(self): self.layout.addLayout(self.form_layout) self.layout.addLayout(self.grid_layout) self.setLayout(self.layout) def create_button(self, text, listener=None): new_button = QPushButton(text) new_button.setFixedWidth(new_button.sizeHint().width()) if listener: new_button.clicked.connect(listener) return new_button def add_form_element(self, text, widget=None): self.form_layout.addRow(text, QLabel("") if widget is None else widget) def select_all(self): self.change_state(Qt.Checked) def unselect_all(self): self.change_state(Qt.Unchecked) def change_state(self, state): for index in range(self.model.rowCount()): item = self.model.item(index) item.setCheckState(state) def add_select_buttons(self): select_all_button = self.create_button("Select all", self.select_all) unselect_all_button = self.create_button("Unselect all", self.unselect_all) self.add_form_element("", select_all_button) self.add_form_element("", unselect_all_button)
def dropEvent(self, event): ''' docstring: 拖动放事件 ''' if event.mimeData().hasFormat( 'application/x-qabstractitemmodeldatalist'): data = event.mimeData() source_item = QStandardItemModel() source_item.dropMimeData(data, Qt.CopyAction, 0, 0, QModelIndex()) nodename = source_item.item(0, 0).text() # 根据名字生成所需节点 if nodename == 'AS': num = len(self.scene.ASinfo) node = Node(nodetype=0, nodename='AS' + str(num + 1)) self.scene.belongAS[node.id] = node self.scene.ASinfo[node.id] = [node] elif nodename == 'RM': node = Node(nodetype=1) elif nodename == 'BR': node = Node(nodetype=2) elif nodename == 'router': node = Node(nodetype=3) elif nodename == 'switch': node = Node(nodetype=4) elif nodename == 'PC': if self.scene.node_me: return node = Node(nodetype=5, nodename='Me') self.node_me = node pos = self.view.mapToScene(event.pos()) item = self.view.getItemAtClick(event) if isinstance(item, Text): if item.parent: item = item.parent if item and not item.myType: # print(item.myType, item.name) self.scene.belongAS[node.id] = item self.scene.ASinfo[item.id].append(node) item.modifyCount(1) elif node.myType: self.scene.waitlist.append(node) self.scene.addItem(node) node.setPos(pos) if self.labelenable: node.label.show() else: node.label.hide()
class ScriptsTable(DwarfListView): """ ScriptsListView """ onScriptSelected = pyqtSignal(str, name='onScriptSelected') def __init__(self, parent=None): super(ScriptsTable, self).__init__(parent=parent) self._scripts_model = QStandardItemModel(0, 6) self._scripts_model.setHeaderData(0, Qt.Horizontal, 'Name') self._scripts_model.setHeaderData(1, Qt.Horizontal, 'Author') self._scripts_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._scripts_model.setHeaderData(2, Qt.Horizontal, 'A') self._scripts_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._scripts_model.setHeaderData(3, Qt.Horizontal, 'I') self._scripts_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._scripts_model.setHeaderData(4, Qt.Horizontal, 'W') self._scripts_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._scripts_model.setHeaderData(5, Qt.Horizontal, 'Description') self.setModel(self._scripts_model) self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.header().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.header().setSectionResizeMode(2, QHeaderView.ResizeToContents) self.header().setSectionResizeMode(3, QHeaderView.ResizeToContents) self.header().setSectionResizeMode(4, QHeaderView.ResizeToContents) self.doubleClicked.connect(self._item_doubleclicked) def _item_doubleclicked(self, item): row = item.row() script_name = self._scripts_model.item(row, 0).text() self.onScriptSelected.emit(script_name) def add_item(self, data): """ Add Item """ self._scripts_model.appendRow(data)
def _init_model(self, keyboard_shortcuts): model = QStandardItemModel(len(keyboard_shortcuts), 3) model.setHorizontalHeaderLabels(["Id", "Name", "Keyboard Shortcut"]) for index, keyboard_shortcut in enumerate(keyboard_shortcuts): model.setItem(index, 0, QStandardItem(keyboard_shortcut.id())) model.setItem( index, 1, QStandardItem(keyboard_shortcut.name(remove_anchors=True))) model.item( index, 1).setFlags(model.item(index, 1).flags() ^ Qt.ItemIsEditable) model.setItem(index, 2, QStandardItem(keyboard_shortcut.key())) model.item( index, 2).setFlags(model.item(index, 2).flags() ^ Qt.ItemIsEditable) self.setModel(model) self.setColumnHidden(0, True)
def dropEvent(self, event): if event.mimeData().hasFormat('application/x-qabstractitemmodeldatalist'): data = event.mimeData() source_item = QStandardItemModel() source_item.dropMimeData(data, Qt.CopyAction, 0, 0, QModelIndex()) Instruction = source_item.item(0, 0).text() if event.source() != self: event.setDropAction(Qt.CopyAction) TempItem = QListWidgetItem() TempItem.setText(Instruction) TempItem.setTextAlignment(Qt.AlignCenter) # TempItem.setData() self.addItem(TempItem) self.AddNewItem(Instruction) else: event.setDropAction(Qt.MoveAction) PrevIndex = self.selectedIndexes()[0].row() super(NewListWidget, self).dropEvent(event) CurrentIndex = self.selectedIndexes()[0].row() self.ItemSwap(PrevIndex, CurrentIndex) self.UpdateIndex() else: event.ignore()
class EditStudent(Ui_dialog): def __init__(self, session, student=None): super().__init__() self.session = session if student is None: self.student = Student() else: self.student = student self.subjects_model = QStandardItemModel() for i in session.query(Subject).all(): it = QStandardItem() it.setCheckable(True) if i in self.student.subjects: it.setCheckState(2) it.setText(i.name) it.setData(i) self.subjects_model.appendRow(it) def setupUi(self, dialog): super().setupUi(dialog) self.student_name.setText(self.student.name) self.subject_list.setModel(self.subjects_model) self.buttonBox.accepted.connect(self.add_student) def add_student(self): self.student.name = self.student_name.text() self.student.subjects = [] for i in range(self.subjects_model.rowCount()): subject = self.subjects_model.item(i) if subject.checkState(): self.student.subjects.append(subject.data()) self.session.add(self.student)
class GUI(QDialog): def __init__(self): super(GUI, self).__init__() dirname = os.path.dirname(os.path.abspath(__file__)) uic.loadUi(os.path.join(dirname, 'DeleteRows.ui'), self) # buttons self.btnPopulate.clicked.connect(self.populate) self.btnDelete.clicked.connect(self.delete) self.btnExit.clicked.connect(QApplication.quit) # table model self.header = ['col1', 'col2', 'col3'] self.QSModel = QStandardItemModel() self.QSModel.setColumnCount(3) self.QSModel.setHorizontalHeaderLabels(self.header) self.tableView.setModel(self.QSModel) self.tableView.verticalHeader().setSectionResizeMode( QHeaderView.ResizeToContents) def populate(self): row = self.QSModel.rowCount() for x in range(7): self.QSModel.insertRow(row) self.QSModel.setData(self.QSModel.index(row, 0), 'data' + str(x)) self.QSModel.item(row, 0).setEditable(True) self.QSModel.setData(self.QSModel.index(row, 1), 'data' + str(x)) self.QSModel.item(row, 1).setEditable(True) self.QSModel.setData(self.QSModel.index(row, 2), 'data' + str(x)) self.QSModel.item(row, 1).setEditable(True) def delete(self): if self.tableView.selectionModel().hasSelection(): indexes = [ QPersistentModelIndex(index) for index in self.tableView.selectionModel().selectedRows() ] for index in indexes: print('Deleting row %d...' % index.row()) self.QSModel.removeRow(index.row()) else: print('No row selected!')
class AttrsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.attrView.setModel(self.model) self.window.ui.attrView.doubleClicked.connect(self.edit_attr) self.model.itemChanged.connect(self.edit_attr_finished) self.window.ui.attrView.header().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_attrs) self.window.ui.treeView.clicked.connect(self.show_attrs) self.window.ui.attrRefreshButton.clicked.connect(self.show_attrs) # Context menu self.window.ui.attrView.setContextMenuPolicy(Qt.CustomContextMenu) self.window.ui.attrView.customContextMenuRequested.connect(self.showContextMenu) copyaction = QAction("&Copy Value", self.model) copyaction.triggered.connect(self._copy_value) self._contextMenu = QMenu() self._contextMenu.addAction(copyaction) def _check_edit(self, item): """ filter only element we want to edit. take either idx eller item as argument """ if item.column() != 1: return False name_item = self.model.item(item.row(), 0) if name_item.text() != "Value": return False return True def edit_attr(self, idx): if not self._check_edit(idx): return attritem = self.model.item(idx.row(), 0) if attritem.text() == "Value": self.window.ui.attrView.edit(idx) def edit_attr_finished(self, item): if not self._check_edit(item): return try: var = item.data() val = item.text() var = string_to_variant(val, var.VariantType) self.current_node.set_value(var) except Exception as ex: self.window.show_error(ex) raise finally: dv = self.current_node.get_data_value() item.setText(variant_to_string(dv.Value)) name_item = self.model.item(item.row(), 0) name_item.child(0, 1).setText(val_to_string(dv.ServerTimestamp)) name_item.child(1, 1).setText(val_to_string(dv.SourceTimestamp)) def showContextMenu(self, position): item = self.get_current_item() if item: self._contextMenu.exec_(self.window.ui.attrView.mapToGlobal(position)) def get_current_item(self, col_idx=0): idx = self.window.ui.attrView.currentIndex() return self.model.item(idx.row(), col_idx) def _copy_value(self, position): it = self.get_current_item(1) if it: QApplication.clipboard().setText(it.text()) def clear(self): self.model.clear() def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None self.current_node = self.window.get_current_node(idx) self.model.clear() if self.current_node: self._show_attrs(self.current_node) self.window.ui.attrView.expandAll() def _show_attrs(self, node): try: attrs = self.uaclient.get_all_attrs(node) except Exception as ex: self.window.show_error(ex) raise self.model.setHorizontalHeaderLabels(['Attribute', 'Value', 'DataType']) for name, dv in attrs: if name == "DataType": if isinstance(dv.Value.Value.Identifier, int) and dv.Value.Value.Identifier < 63: string = ua.DataType_to_VariantType(dv.Value.Value).name elif dv.Value.Value.Identifier in ua.ObjectIdNames: string = ua.ObjectIdNames[dv.Value.Value.Identifier] else: string = dv.Value.Value.to_string() elif name in ("AccessLevel", "UserAccessLevel"): string = ",".join([e.name for e in ua.int_to_AccessLevel(dv.Value.Value)]) elif name in ("WriteMask", "UserWriteMask"): string = ",".join([e.name for e in ua.int_to_WriteMask(dv.Value.Value)]) elif name in ("EventNotifier"): string = ",".join([e.name for e in ua.int_to_EventNotifier(dv.Value.Value)]) else: string = variant_to_string(dv.Value) name_item = QStandardItem(name) vitem = QStandardItem(string) vitem.setData(dv.Value) self.model.appendRow([name_item, vitem, QStandardItem(dv.Value.VariantType.name)]) if name == "Value": string = val_to_string(dv.ServerTimestamp) name_item.appendRow([QStandardItem("Server Timestamp"), QStandardItem(string), QStandardItem(ua.VariantType.DateTime.name)]) string = val_to_string(dv.SourceTimestamp) name_item.appendRow([QStandardItem("Source Timestamp"), QStandardItem(string), QStandardItem(ua.VariantType.DateTime.name)])
class LinesDialog(QDialog): lines = pyqtSignal(list) def __element_item(element): item = QStandardItem("{} ({})".format(element[2], element[1]) if element[1] else element[2] ) item.setData({'z': element[0], 'code': element[1], 'name': element[2]}) return item def __init__(self, database, settings, plot_widget, axes = None, enable_picker = True, selection_mode = 'multi'): super(LinesDialog, self).__init__() self.axes = axes if axes else plot_widget.axes self.database = database self.plot_widget = plot_widget self.settings = settings self.ui = Ui_LinesDialog() self.ui.setupUi(self) self.restoreGeometry(self.settings.value('pick_lines_geometry', QByteArray())) self.model = QStandardItemModel() self.elements_model = QStandardItemModel() self.ui.lines.setModel(self.model) self.ui.elements.setModel(self.elements_model) c = self.database.cursor() self.elements_model.appendRow(LinesDialog.__element_item([0, '', 'All'])) elements = c.execute("SELECT z, code, name FROM elements ORDER BY z ASC") for element in elements: self.elements_model.appendRow(LinesDialog.__element_item(element)) self.ui.elements.currentTextChanged.connect(lambda t: self.populate()) self.ui.lambda_from.editingFinished.connect(self.populate) self.ui.name.editingFinished.connect(self.populate) self.ui.sp_types.toggled.connect(self.populate) self.ui.lambda_to.editingFinished.connect(self.populate) self.accepted.connect(self.collect_selected_lines) self.populate() self.ui.pick_wavelengths.setEnabled(enable_picker) self.ui.pick_wavelengths.clicked.connect(self.pick_wavelengths_clicked) self.ui.lines.setSelectionMode({'multi':QTableView.MultiSelection, 'single':QTableView.SingleSelection}[selection_mode]) def set_picker_enabled(self,enabled): self.ui.pick_wavelengths.setEnabled(enabled) def pick_wavelengths_clicked(self): self.plot_widget.add_span_selector("pick_lines_lambda", self.picked_wavelengths, axes=self.axes, direction='horizontal') self.lower() def closeEvent(self, ev): self.settings.setValue('pick_lines_geometry', self.saveGeometry()) QDialog.closeEvent(self, ev) def picked_wavelengths(self, start, end): self.raise_() self.ui.lambda_from.setValue(start) self.ui.lambda_to.setValue(end) self.populate() def collect_selected_lines(self): selected_rows = self.ui.lines.selectionModel().selectedRows() if selected_rows: self.lines.emit([self.model.itemFromIndex(i).data() for i in selected_rows]) def populate(self): self.model.clear() self.model.setHorizontalHeaderLabels(['Lambda', 'Element', 'Atomic number', 'Ionization', 'Stellar spectral types']) c = self.database.cursor() query = "SELECT lambda, Element, Z, Ion, SpTypes from spectral_lines WHERE {} ORDER BY lambda ASC;" conditions = ['(1 = 1)'] element = self.elements_model.item(self.ui.elements.currentIndex()).data() if element['z']: conditions.append("(Z = {})".format(element['z'])) if self.ui.lambda_from.value() > 0: conditions.append("(Lambda >= {})".format(self.ui.lambda_from.value())) if self.ui.lambda_to.value() > 0: conditions.append("(Lambda <= {})".format(self.ui.lambda_to.value())) if self.ui.name.text(): conditions.append("(Element like '%{}%')".format(self.ui.name.text())) if self.ui.sp_types.isChecked(): conditions.append("(SpTypes <> '')") for row in c.execute(query.format(" AND ".join(conditions))): first_item = QStandardItem("{}".format(row[0])) first_item.setData({'lambda': row[0], 'name': row[1], 'z': row[2]}) self.model.appendRow( [ first_item, QStandardItem(row[1]), QStandardItem("{}".format(row[2])), QStandardItem("{}".format(row[3])), QStandardItem(row[4]) ]) def keyPressEvent(self, evt): if evt.key() == Qt.Key_Enter or evt.key() == Qt.Key_Return: return QDialog.keyPressEvent(self.evt)
class SvgTool(QMainWindow): class PresetMode(object): def __init__(self, id, name, baseWidth, baseHeight, isMaintainRatio, iosSizeList, androidSizeList, isMultiplier): self.id = id self.name = name self.baseWidth = baseWidth self.baseHeight = baseHeight self.isMaintainRatio = isMaintainRatio self.iosSizeList = iosSizeList self.androidSizeList = androidSizeList self.isMultiplier = isMultiplier iosSizeList = [] androidSizeList = [] isMultiplier = True inputFiles = [] modes = [ PresetMode(0, "Custom", "", "", True, [1, 2, 3], [1, 1.5, 2, 3], True), PresetMode(1, "iOS Launcher Icon", 0, 0, True, [29, 40, 50, 57, 58, 72, 76, 80, 87, 100, 114, 120, 144, 152, 167, 180], [], False), PresetMode(2, "Android Launcher Icon", 48, 48, True, [], [1, 1.5, 2, 3], True), PresetMode(3, "Store Icon", 0, 0, True, [1024], [512], False), PresetMode(4, "Button Icon", 16, 16, True, [1, 2, 3], [1, 1.5, 2, 3], True), PresetMode(5, "Big Button Icon", 24, 24, True, [1, 2, 3], [1, 1.5, 2, 3], True) ] def __init__(self, parent=None): super(SvgTool, self).__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.listViewFilesModel = QStandardItemModel(self.ui.listViewFiles) self.ui.btnCancel.setEnabled(False) self.ui.btnConvert.setEnabled(False) self.ui.labelImage.setPixmap(QPixmap("flandre.png")) self.ui.btnInputDir.clicked.connect(self.onBtnInputDir) self.ui.btnOutputDir.clicked.connect(self.onBtnOutputDir) self.ui.lineFilter.textChanged.connect(self.onLineFilterTextChanged) self.ui.lineWidth.textChanged.connect(self.onLineWidthChanged) self.ui.lineHeight.textChanged.connect(self.onLineHeightChanged) self.ui.comboBoxMode.currentIndexChanged.connect(self.onModeChanged) self.ui.checkBoxRatio.stateChanged.connect(self.onCheckBoxRatioChanged) self.ui.btnConvert.clicked.connect(self.onBtnConvert) self.ui.btnCancel.clicked.connect(self.onBtnCancel) self.ui.btnRefresh.clicked.connect(self.refreshInputDirectory) self.ui.btnSelectAll.clicked.connect(self.selectAll) self.ui.btnSelectNone.clicked.connect(self.selectNone) self.listViewFilesModel.itemChanged.connect(self.listViewChanged) self.populateModes() def onLineFilterTextChanged(self): self.refreshInputDirectory() def onBtnInputDir(self): file = str(QFileDialog.getExistingDirectory(self.ui.centralwidget, "Select input directory")) if file: self.ui.lineInputDir.setText(file) self.refreshInputDirectory() if not self.ui.lineOutputDir.text(): self.ui.lineOutputDir.setText(file) def onBtnOutputDir(self): file = str(QFileDialog.getExistingDirectory(self.ui.centralwidget, "Select output directory")) if file: self.ui.lineOutputDir.setText(file) def onBtnConvert(self): try: if self.isMultiplier: w = int(self.ui.lineWidth.text()) h = int(self.ui.lineHeight.text()) if w <= 0 or h <= 0: errormsg = QtWidgets.QMessageBox(self.ui.centralwidget) errormsg.setIcon(QtWidgets.QMessageBox.Critical) errormsg.setWindowTitle("Error") errormsg.setText("Flandre says:") errormsg.setInformativeText("\"Width and Height must be greater than 0, you moron!\"") errormsg.show() return except ValueError: errormsg = QtWidgets.QMessageBox(self.ui.centralwidget) errormsg.setIcon(QtWidgets.QMessageBox.Critical) errormsg.setWindowTitle("Error") errormsg.setText("Flandre says:") errormsg.setInformativeText("\"Width and Height must be numbers, you moron!\"") errormsg.show() return self.setUiInProgress(True) self.convertProgress = SvgConversion( self.inputFiles, self.ui.checkBoxAndroid.isChecked(), self.ui.checkBoxIos.isChecked(), self.androidSizeList, self.iosSizeList, self.ui.lineInputDir.text(), self.ui.lineOutputDir.text(), float(self.ui.lineWidth.text()), float(self.ui.lineHeight.text()), self.isMultiplier, self.ui.checkBoxXcassets.isChecked(), self.ui.checkBoxVS.isChecked() ) self.convertProgress.sigSetProgress.connect(self.setProgress) self.convertProgress.sigSetProgressTotal.connect(self.setProgressTotal) self.convertProgress.sigSetStatusMessage.connect(self.setStatusMessage) self.convertProgress.sigSetUiInProgress.connect(self.setUiInProgress) self.convertProgress.start() def onBtnCancel(self): self.ui.btnCancel.setEnabled(False) self.convertProgress.cancel() def onModeChanged(self): index = self.ui.comboBoxMode.currentIndex() for mode in self.modes: if mode.id == index: self.ui.checkBoxRatio.setChecked(mode.isMaintainRatio) self.ui.lineWidth.setText(str(mode.baseWidth)) self.ui.lineHeight.setText(str(mode.baseHeight)) self.isMultiplier = mode.isMultiplier self.iosSizeList = mode.iosSizeList self.androidSizeList = mode.androidSizeList self.ui.checkBoxAndroid.setEnabled(len(mode.androidSizeList) != 0) self.ui.checkBoxAndroid.setChecked(len(mode.androidSizeList) != 0) self.ui.checkBoxIos.setEnabled(len(mode.iosSizeList) != 0) self.ui.checkBoxIos.setChecked(len(mode.iosSizeList) != 0) self.ui.checkBoxRatio.setEnabled(self.isMultiplier) self.ui.lineWidth.setEnabled(self.isMultiplier) self.ui.lineHeight.setEnabled(self.isMultiplier) break def onLineWidthChanged(self): if self.ui.checkBoxRatio.isChecked(): self.ui.lineHeight.setText(self.ui.lineWidth.text()) def onLineHeightChanged(self): if self.ui.checkBoxRatio.isChecked(): self.ui.lineWidth.setText(self.ui.lineHeight.text()) def onCheckBoxRatioChanged(self): if self.ui.checkBoxRatio.isChecked(): self.ui.lineHeight.setText(self.ui.lineWidth.text()) def setProgress(self, progress): self.ui.progressBar.setValue(progress) def setProgressTotal(self, progressTotal): self.ui.progressBarTotal.setValue(progressTotal) def setStatusMessage(self, statusMessage): self.ui.statusbar.showMessage(statusMessage) def selectAll(self): for index in range(self.listViewFilesModel.rowCount()): item = self.listViewFilesModel.item(index) item.setCheckState(QtCore.Qt.Checked) def selectNone(self): for index in range(self.listViewFilesModel.rowCount()): item = self.listViewFilesModel.item(index) item.setCheckState(QtCore.Qt.Unchecked) def listViewChanged(self, item_changed): selected = 0 self.inputFiles = [] for index in range(self.listViewFilesModel.rowCount()): item = self.listViewFilesModel.item(index) if item.checkState() == QtCore.Qt.Checked: self.inputFiles.append(item.text()) selected += 1 self.ui.btnConvert.setEnabled(selected != 0) if selected == 1: self.ui.labelSelected.setText("{0} svg file selected".format(selected)) else: self.ui.labelSelected.setText("{0} svg files selected".format(selected)) def refreshInputDirectory(self): if not str(self.ui.lineInputDir.text()): return self.listViewFilesModel = QStandardItemModel(self.ui.listViewFiles) self.listViewFilesModel.itemChanged.connect(self.listViewChanged) self.inputFiles = [] for file in os.listdir(self.ui.lineInputDir.text()): if self.ui.lineFilter.text().upper() in file.upper(): if file.endswith(".svg"): item = QStandardItem(file) item.setCheckable(True) item.setCheckState(QtCore.Qt.Unchecked) self.listViewFilesModel.appendRow(item) self.ui.listViewFiles.setModel(self.listViewFilesModel) if self.listViewFilesModel.rowCount() == 1: self.ui.labelFilter.setText("{0} svg file found".format(self.listViewFilesModel.rowCount())) else: self.ui.labelFilter.setText("{0} svg files found".format(self.listViewFilesModel.rowCount())) self.ui.labelSelected.setText("0 svg files selected") self.ui.btnConvert.setEnabled(False) def setUiInProgress(self, state): self.ui.btnCancel.setEnabled(state) self.ui.btnConvert.setEnabled(not state) self.ui.btnInputDir.setEnabled(not state) self.ui.btnOutputDir.setEnabled(not state) self.ui.btnSelectAll.setEnabled(not state) self.ui.btnSelectNone.setEnabled(not state) self.ui.btnRefresh.setEnabled(not state) if self.isMultiplier: self.ui.checkBoxRatio.setEnabled(not state) self.ui.lineWidth.setEnabled(not state) self.ui.lineHeight.setEnabled(not state) self.ui.listViewFiles.setEnabled(not state) self.ui.lineFilter.setEnabled(not state) self.ui.checkBoxRatio.setEnabled(not state) self.ui.checkBoxAndroid.setEnabled(not state) self.ui.checkBoxIos.setEnabled(not state) self.ui.checkBoxXcassets.setEnabled(not state) self.ui.checkBoxVS.setEnabled(not state) self.ui.comboBoxMode.setEnabled(not state) if len(self.iosSizeList) == 0: self.ui.checkBoxIos.setEnabled(False) if len(self.androidSizeList) == 0: self.ui.checkBoxAndroid.setEnabled(False) def populateModes(self): for mode in self.modes: self.ui.comboBoxMode.addItem(mode.name, mode.id)
class FittingAdvancedDlg(QDialog, Ui_Dialog): ''' The tree should provide any fittable parameter of any layers layer 1 value *what to change* - thickness layer.thickness - srough_thickness (if model is on) if layer.srough: layer.sroughThickness - haze reflection (if model is on) if layer.srough: layer.sroughHazeR - haze transmission (if model is on) if layer.srough: layer.sroughHazeT - constant n (if source is constant) if layer.criSource=='constant': layer.criConstant[0] - constant k (if source is constant) if layer.criSource=='constant': layer.criConstant[1] - grading (constant) (if source is constant) if layer.criSource=='graded' && layer.criGrading['mode']=='constant': layer.criGrading['value'] + dielectric function (if source) if layer.criSource=='dielectric function' - e0 layer.dielectricFunction['e0'] + oscillator 0 for each oscillator: layer.dielectricFunction['oscillators'][#of Osci]['name'] : [{'name': 'Lorentz', 'values': [1, 3, 0.2], 'active': True}], - value 1 layer.dielectricFunction['oscillators'][#of Osci]['value'][0] - value 2 ... + oscillator 1 - value 1 .. - collection function (constant) (if selected) if layer.collection['source'] == 'from collection function' && layer.collection['mode'] == 'constant': layer.collection['value'] + diff length model if layer.collection['source'] == 'from diffusion length' - space charge region width layer.collection['SCRwidth'] - diff length layer.collection['diffLength'] - surface rec. velocity layer.collection['recVel'] layer 2 ... ''' def __init__(self, stack, references, stackname, settings, getcri, parent=None): """ Constructor @param parent reference to the parent widget @type QWidget """ super().__init__(parent) self.setupUi(self) self.setAttribute(Qt.WA_DeleteOnClose) self.originalStack = stack self.stack = copy.deepcopy(stack) self.references = references self.StackName = stackname self.settings = settings self.getCRI = getcri self.referenceDataSelected = {} self.referenceList = [] for key, value in self.references.items(): if value[0]: self.referenceList.append([key, False]) # name and selection for fitting self.referenceListModel = QStandardItemModel() self.referenceListModel.itemChanged.connect(self.updateReferences) self.referenceListView.setModel(self.referenceListModel) self.fillReferences() self.parameterModel = QStandardItemModel() #TreeOfParametersModel() self.parameterTreeView.setModel(self.parameterModel) self.parameterModel.setHorizontalHeaderLabels(['Parameter', 'Value']) self.root = self.parameterModel.invisibleRootItem() self.fillParameterTree() # configurations self.noOfFitIterations = 100 methods = ['Nelder-Mead', 'Powell', 'CG', 'BFGS', 'L-BFGS-B', 'TNC', 'COBYLA', 'SLSQP'] self.tolerance = 1e-3 # with Jacobian necessary: 'Newton-CG', 'dogleg', 'trust-ncg' self.configuration = dict([ ('method', methods[0]), ('noOfIterations', self.noOfFitIterations), ('tolerance', self.tolerance), ('plotInBetween', True) ]) self.methodCB.addItems(methods) self.noOfIterationsSB.setValue(self.noOfFitIterations) self.toleranceSB.setValue(self.tolerance) try: self.runOptics() except: self.done(0) QMessageBox.warning(self, "error", 'Error - could not calculate current stack.\nPlease check stack definition until simple simulation is running.', QMessageBox.StandardButtons(QMessageBox.Close)) def fillReferences(self): for value in self.referenceList: item = QStandardItem(value[0]) check = Qt.Checked if value[1] else Qt.Unchecked item.setCheckState(check) item.setCheckable(True) self.referenceListModel.appendRow(item) def updateReferences(self): self.referenceDataSelected = {} for row in range(self.referenceListModel.rowCount()): item = self.referenceListModel.item(row) if item.checkState() == Qt.Checked: self.referenceList[row][1] = True self.referenceDataSelected[item.text()] = self.referenceData[item.text()] else: self.referenceList[row][1] = False self.updatePlot() #print(len(self.referenceDataSelected)) #print('{} {}'.format(row, self.references[row])) def updatePlot(self): plotDict = {} for ref in self.referenceList: if ref[1]: if ref[0] == 'R reference': plotDict['R reference'] = self.referenceData['R reference'] plotDict['R'] = self.optics.RspectrumSystem if ref[0] == 'T reference': plotDict['T reference'] = self.referenceData['T reference'] plotDict['T'] = self.optics.TspectrumSystem if ref[0] == 'EQE reference': plotDict['EQE reference'] = self.referenceData['EQE reference'] plotDict['EQE'] = self.optics.EQE if ref[0] == 'psi reference': plotDict['psi reference'] = self.referenceData['psi reference'] plotDict['psi'] = self.optics.psi if ref[0] == 'delta reference': plotDict['delta reference'] = self.referenceData['delta reference'] plotDict['delta'] = self.optics.delta x = self.optics.wavelength if plotDict: self.plotView.showCurves(x, plotDict, 'wavelength (nm)', 'value [a.u.]') else: self.plotView.ax.clear() self.plotView.canvas.draw() def fillParameterTree(self): rows = self.parameterModel.rowCount() if rows: while self.parameterModel.rowCount() > 0: self.parameterModel.removeRow(0) self.parameterList = [] parameterStr = 'self.stack[' for i, layer in enumerate(self.stack): parameterStr += '{}].'.format(i) layerBranch = QStandardItem(layer.name) self.root.appendRow(layerBranch) self.addTreeEntry(layerBranch, 'thickness (nm)', layer.thickness, self.changeThickness) if layer.srough: self.addTreeEntry(layerBranch, 'thickness roughness layer (nm)', layer.sroughThickness, self.changeSroughThickness) self.addTreeEntry(layerBranch, 'Haze R', layer.sroughHazeR, self.changeSroughHazeR) self.addTreeEntry(layerBranch, 'Haze T', layer.sroughHazeT, self.changeSroughHazeT) if layer.criSource=='constant': self.addTreeEntry(layerBranch, 'constant n', layer.criConstant[0], self.changeConstantn) self.addTreeEntry(layerBranch, 'constant k', layer.criConstant[1], self.changeConstantk) if layer.criSource=='graded' and layer.criGrading['mode']=='constant': self.addTreeEntry(layerBranch, 'constant grading', layer.criGrading['value'], self.changeConstantGrading) if layer.criSource=='dielectric function': criBranch = QStandardItem('dielectric function parameters') layerBranch.appendRow(criBranch) self.addTreeEntry(criBranch, 'e0', layer.dielectricFunction['e0'], self.changeConstante, level = 2) for idx, oscillator in enumerate(layer.dielectricFunction['oscillators']): osciBranch = QStandardItem('{} {}'.format(idx, oscillator['name'])) criBranch.appendRow(osciBranch) parameterNames = MODELS[oscillator['name']]['parameter'] for i, value in enumerate(oscillator['values']): self.addTreeEntry(osciBranch, parameterNames[i], value, self.changeOscillator, level = 3) if layer.collection['source'] == 'from collection function' and layer.collection['mode'] == 'constant': self.addTreeEntry(layerBranch, 'constant collection efficiency', layer.collection['value'], self.changeConstantCollection) if layer.collection['source'] == 'from diffusion length': collectionBranch = QStandardItem('collection model') layerBranch.appendRow(collectionBranch) self.addTreeEntry(collectionBranch, 'space charge region width (nm)', layer.collection['SCRwidth'], self.changeSCR, level = 2) self.addTreeEntry(collectionBranch, 'diffusion length (nm)', layer.collection['diffLength'], self.changeDiffL, level = 2) self.addTreeEntry(collectionBranch, 'recombination velocity (cm/s)', layer.collection['recVel'], self.changerecVel, level = 2) self.parameterTreeView.setColumnWidth(1, 80) self.parameterTreeView.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.parameterTreeView.expandAll() def addTreeEntry(self, parent, name, parameter, func, level = 1): child = QStandardItem(name) child.setCheckable(True) value = QStandardItem(str(parameter)) parent.appendRow([child, value]) if level == 3: osciNum = parent.parent().rowCount() - 2 itemNum = parent.rowCount() - 1 self.parameterList.append([parameter, func, osciNum, itemNum]) else: self.parameterList.append([parameter, func]) def getSelectedValues(self): selected = [] # name, layer number, initial value, function to change i = 0 for row in range(self.parameterModel.rowCount()): layer = self.parameterModel.item(row) for row1 in range(layer.rowCount()): child1 = layer.child(row1) if not child1.hasChildren(): if child1.checkState() == Qt.Checked: selected.append([child1.text(), row, self.parameterList[i][0], self.parameterList[i][1]]) i += 1 else: for row2 in range(child1.rowCount()): child2 = child1.child(row2) if not child2.hasChildren(): if child2.checkState() == Qt.Checked: selected.append([child2.text(), row, self.parameterList[i][0], self.parameterList[i][1]]) i += 1 else: for row3 in range(child2.rowCount()): child3 = child2.child(row3) if child3.checkState() == Qt.Checked: selected.append([child3.text(), row, self.parameterList[i][0], self.parameterList[i][1], self.parameterList[i][2], self.parameterList[i][3]]) i += 1 return selected @pyqtSlot() def on_testPB_clicked(self): selected = self.getSelectedValues() str = '' #print(selected) for item in selected: str += '{} {}\n'.format(item[0], item[1]) self.resultTextEdit.setText(str) self.plot() if selected: layer = selected[0][1] func = selected[0][2] value = 250 func(layer, value) #print(self.parameterList) #self.parameterList[0][0] = 300 #print('stack {}'.format(self.stack[0].thickness)) def runOptics(self): input = LayerStack(self.StackName, self.stack, self.settings, self.getCRI) self.optics = Optics(self.StackName, input, self.references, self.settings) self.optics.calcStack() self.optics.createReferenceCurves() self.optics.calcFieldIntensity() self.optics.calcAbsorption() self.optics.calcQE() self.optics.calcEllipsometry() #get reference data self.referenceData = {} for ref in self.referenceList: if ref[0] == 'R reference': self.referenceData['R reference'] = self.optics.R_reference elif ref[0] == 'T reference': self.referenceData['T reference'] = self.optics.T_reference elif ref[0] == 'EQE reference': self.referenceData['EQE reference'] = self.optics.EQE_reference elif ref[0] == 'psi reference': self.referenceData['psi reference'] = self.optics.psi_reference elif ref[0] == 'delta reference': self.referenceData['delta reference'] = self.optics.delta_reference @pyqtSlot() def on_startFittingPB_clicked(self): if not self.referenceDataSelected: QMessageBox.warning(self, "error", 'Error - no reference curve selected!\n', QMessageBox.StandardButtons(QMessageBox.Close)) return parameterList = self.getSelectedValues() if not parameterList: QMessageBox.warning(self, "error", 'Error - no fitting parameter selected!\n', QMessageBox.StandardButtons(QMessageBox.Close)) return self.resultTextEdit.setPlainText("") start_time = time.time() #get initial deviation #get initial values parameters = [] for item in parameterList: parameters.append(item[2]) # run fitting try: self.resultTextEdit.setText("busy fitting ...") minResult = minimize(self.minimizeFunction, parameters, args = (parameterList), method= self.configuration['method'], tol=self.configuration['tolerance'], options = {'maxiter' : self.configuration['noOfIterations']}) #, tol=1e-6 except RuntimeError as e: QMessageBox.warning(self, "fitting error", 'Error - curve fitting failed!\n{}'.format(e.args[0]), QMessageBox.StandardButtons(QMessageBox.Close)) return # run all optics to get other curves when references are changed self.runOptics() # to make sure at least the last result is plotted self.updatePlot() duration = time.time() - start_time message = "The optimized parameters are:\n" for i, item in enumerate(parameterList): message += "{} of {} --> {}\n".format(item[0], self.stack[item[1]].name, minResult.x[i]) message += "Details: {}\nNumber of Iterations: {}\nNumber of function calls: {}\nChi-square: {:7.4f}\nFitting time: {:7.2f} seconds".format( minResult.message, minResult.nit, minResult.nfev, minResult.fun, duration) self.resultTextEdit.setText(message) self.fillParameterTree() #logging.info('\n' + 50 * '#' + '\n' + message + '\n do final calculation ...\n' + 50 * '#') def minimizeFunction(self, values, parameterList): #set the parameters for i, value in enumerate(values): layer = parameterList[i][1] func = parameterList[i][3] if func == self.changeOscillator: osciNum = parameterList[i][4] itemNum = parameterList[i][5] func(layer, osciNum, itemNum, value) else: func(layer, value) input = LayerStack(self.StackName, self.stack, self.settings, self.getCRI) self.optics = Optics(self.StackName, input, self.references, self.settings) self.optics.calcStack() #print(len(self.referenceDataSelected)) errorArray = np.zeros(len(self.optics.wavelength)) for key, exp in self.referenceDataSelected.items(): if key == 'R reference': model = self.optics.RspectrumSystem errorArray += ((model - exp)/exp)**2 elif key == 'T reference': model = self.optics.TspectrumSystem errorArray += ((model - exp)/exp)**2 elif key == 'EQE reference': self.optics.calcFieldIntensity() self.optics.calcAbsorption() self.optics.calcQE() model = self.optics.EQE errorArray += ((model - exp)/exp)**2 elif key == 'psi reference': self.optics.calcEllipsometry() model = self.optics.psi errorArray += ((model - exp)/exp)**2 elif key == 'delta reference': self.optics.calcEllipsometry() model = self.optics.delta errorArray += ((model - exp)/exp)**2 #N = len(errorArray) #M = len(parameterList) if self.configuration['plotInBetween']: self.updatePlot() return np.sum(errorArray) # chi² or MSE = 1/(2N-M) * chi² @pyqtSlot(str) def on_methodCB_currentIndexChanged(self, p0): self.configuration['method'] = p0 @pyqtSlot(int) def on_noOfIterationsSB_valueChanged(self, p0): self.configuration['noOfIterations'] = p0 def changeThickness(self, layer, value): self.stack[layer].thickness = int(value) def changeSroughThickness(self, layer, value): self.stack[layer].sroughThickness = int(value) def changeSroughHazeR(self, layer, value): self.stack[layer].sroughHazeR = value def changeSroughHazeT(self, layer, value): self.stack[layer].sroughHazeT = value def changeConstantn(self, layer, value): self.stack[layer].criConstant[0] = value def changeConstantk(self, layer, value): self.stack[layer].criConstant[1] = value def changeConstantGrading(self, layer, value): self.stack[layer].criGrading['value'] = value def changeConstante(self, layer, value): self.stack[layer].dielectricFunction['e0'] = value e1, e2, self.stack[layer].dielectricFunction, eV = calcOsciFunction(self.stack[layer].dielectricFunction) def changeConstantCollection(self, layer, value): self.stack[layer].collection['value'] = value self.stack[layer].makeXcollection() def changeSCR(self, layer, value): self.stack[layer].collection['SCRwidth'] = int(value) self.stack[layer].makeXcollection() def changeDiffL(self, layer, value): self.stack[layer].collection['diffLength'] = int(value) self.stack[layer].makeXcollection() def changerecVel(self, layer, value): self.stack[layer].collection['recVel'] = value self.stack[layer].makeXcollection() def changeOscillator(self, layer, osciNum, itemNum, value): self.stack[layer].dielectricFunction['oscillators'][osciNum]['values'][itemNum] = value e1, e2, self.stack[layer].dielectricFunction, eV = calcOsciFunction(self.stack[layer].dielectricFunction) @pyqtSlot() def on_reloadPB_clicked(self): self.stack = self.originalStack self.fillParameterTree() @pyqtSlot(float) def on_toleranceSB_valueChanged(self, p0): self.configuration['tolerance'] = p0 @pyqtSlot(bool) def on_plotInBetweenCB_toggled(self, checked): self.configuration['plotInBetween'] = checked
class keepColumn(QDialog): def __init__(self, parent=None): self.parent = parent self.parent.statusbar.showMessage("Keep Columns started...") QWidget.__init__(self,parent) self.ui = gui.keepColumnUi() self.ui.setupUi(self) self.columns = [] self.batch_files = filedialog.askopenfilenames(parent=root, title='Choose the file(s) you want to modify') if len(self.batch_files) == 0: self.close() return self.parent.statusbar.showMessage("Checking column validity...") #extract columns for item in self.batch_files: self.columns.append(helpers.extract_columns(item)) #check to see if all columns are equal in all the datasets if not helpers.are_columns_same(self.columns): if not helpers.columns_not_equal_message(self): self.close() return #list of items to check from self.model = QStandardItemModel() try: for col in self.columns[0]: item = QStandardItem(col) item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setData(QVariant(Qt.Unchecked), Qt.CheckStateRole) self.model.appendRow(item) except IndexError: pass self.list = QListView(self) self.list.setModel(self.model) self.list.setGeometry(10, 60, 380, 430) self.ui.removeBtn.clicked.connect(self.keep) self.ui.closeBtn.clicked.connect(self.close) self.parent.statusbar.showMessage("Welcome back!") self.show() def close(self): self.parent.statusbar.showMessage("Welcome back!") self.parent.ui.logOutput.append("") self.done(55) def keep(self): self.parent.statusbar.showMessage("Keep Columns in process...") self.columns_to_keep = [] i = 0 while self.model.item(i): if self.model.item(i).checkState() == 2: self.columns_to_keep.append(self.model.item(i).text()) i += 1 query = "Are you sure you want to proceed?" reply = QMessageBox.question(self, 'Message',query, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.No: self.columns_to_keep = [] return self.parent.statusbar.showMessage("Processing...") self.parent.ui.logOutput.append("KEPT:") print("KEPT:") #per file first retrieve data then filter columnarly for file in self.batch_files: list_colindx = [] datablob = helpers.clean_header(file) for item in self.columns_to_keep: list_colindx.append(datablob[0].index(item)) for i in range(len(datablob)): datablob[i][:] = [x for i, x in enumerate(datablob[i]) if i in list_colindx] split_name = file.split('.') now = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())) output_file = split_name[0] + "_keep_columns_" + now + "." + split_name[1] helpers.write_out_to_file(output_file,datablob) self.parent.ui.logOutput.append(" " + str(file.split('/')[-1])) print(" " + str(file.split('/')[-1])) helpers.job_complete_message(self) self.parent.statusbar.showMessage("Welcome Back!") self.close()
class DataChangeUI(object): def __init__(self, window, uaclient, hub_manager): self.window = window self.uaclient = uaclient # FIXME IoT stuff self.hub_manager = hub_manager self._subhandler = DataChangeHandler(self.hub_manager) self._subscribed_nodes = [] self.model = QStandardItemModel() self.window.ui.subView.setModel(self.model) self.window.ui.subView.horizontalHeader().setSectionResizeMode(1) self.window.ui.actionSubscribeDataChange.triggered.connect(self._subscribe) self.window.ui.actionUnsubscribeDataChange.triggered.connect(self._unsubscribe) # populate contextual menu self.window.ui.treeView.addAction(self.window.ui.actionSubscribeDataChange) self.window.ui.treeView.addAction(self.window.ui.actionUnsubscribeDataChange) # handle subscriptions self._subhandler.data_change_fired.connect(self._update_subscription_model, type=Qt.QueuedConnection) def clear(self): self._subscribed_nodes = [] self.model.clear() def _subscribe(self): node = self.window.get_current_node() if node is None: return if node in self._subscribed_nodes: print("allready subscribed to node: ", node) return self.model.setHorizontalHeaderLabels(["DisplayName", "Value", "Timestamp"]) row = [QStandardItem(node.display_name), QStandardItem("No Data yet"), QStandardItem("")] row[0].setData(node) self.model.appendRow(row) self._subscribed_nodes.append(node) self.window.ui.subDockWidget.raise_() try: self.uaclient.subscribe_datachange(node, self._subhandler) except Exception as ex: self.window.show_error(ex) idx = self.model.indexFromItem(row[0]) self.model.takeRow(idx.row()) def _unsubscribe(self): node = self.window.get_current_node() if node is None: return self.uaclient.unsubscribe_datachange(node) self._subscribed_nodes.remove(node) i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: self.model.removeRow(i) i += 1 def _update_subscription_model(self, node, value, timestamp): i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: it = self.model.item(i, 1) it.setText(value) it_ts = self.model.item(i, 2) it_ts.setText(timestamp) i += 1
class StackImages(QWidget): def __init__(self, fits_file, settings): QWidget.__init__(self) self.fits_file = fits_file self.ui = Ui_StackImages() self.ui.setupUi(self) self.settings = settings self.degrees = 0. # TODO self.files_model = QStandardItemModel() self.files_model.setHorizontalHeaderLabels(['File', 'Quality', 'Align']) self.ui.files.setModel(self.files_model) self.__add_file_to_model(fits_file) self.plot = QtCommons.nestWidget(self.ui.plot, QImPlotWidget(self.__files_data()[0]['data'], cmap='gray')) self.__set_ref(0) self.toolbar = QToolBar() self.add = self.toolbar.addAction('Add', lambda: open_files_sticky('Open FITS Images',FITS_IMG_EXTS, self.__open, settings, IMPORT_IMG, parent=self )) self.remove = self.toolbar.addAction('Remove', self.__remove_selected_rows) self.reference_action = self.toolbar.addAction('Reference', lambda: self.__set_ref(self.ui.files.selectionModel().selectedRows()[0].row() ) ) self.toolbar.addAction('Select alignment region', lambda: self.plot.add_rectangle_selector('select_align', self.__alignment_region_selected)) self.toolbar.addAction('Rotate', lambda: self.rotate_dialog.show() ) self.ui.files.selectionModel().selectionChanged.connect(lambda sel, unsel: self.__selection_changed() ) self.ui.files.clicked.connect(lambda index: self.__draw_image(index.row())) #self.accepted.connect(self.stack) self.__selection_changed() def __selection_changed(self): sel = len(self.ui.files.selectionModel().selectedRows()) self.remove.setEnabled(sel) self.reference_action.setEnabled(sel == 1) def __draw_image(self,index): image_view = self.plot.axes_image image_view.set_data(self.files_model.item(index).data()['data']) image_view.figure.canvas.draw() def __open(self, files): existing_files = [d['file'] for d in self.__files_data()] progress = QProgressDialog("Loading files", None, 0, len(files), self); progress.setWindowModality(Qt.WindowModal); progress.show() for index, file in enumerate(files): progress.setValue(index+1) QApplication.instance().processEvents() if file not in existing_files: self.__add_file(fits.open(file)) def __row_index(self, data): return [i for i, d in enumerate(self.__files_data()) if d['file'] == data['file']][0] def __add_file_to_model(self, fits_file): item = QStandardItem(os.path.basename(fits_file.filename())) data = fits_file[0].data data = scipy.ndimage.interpolation.rotate(data, self.degrees, reshape=True, order=5, mode='constant') spatial = data.sum(1) profile = data.sum(0) roots = UnivariateSpline(range(0, len(spatial)), spatial-np.max(spatial)/2, s=0.2, k=3).roots() quality = roots[1]-roots[0] item.setData({'file': fits_file.filename(), 'fits': fits_file, 'data': data, 'spatial': spatial, 'profile': profile, 'quality': quality}) offset = QStandardItem('N/A') # TODO quality_item = QStandardItem("") self.files_model.appendRow([item, quality_item, offset]) return item def __add_file(self, fits_file): item = self.__add_file_to_model(fits_file) if self.files_model.rowCount() == 1: self.__set_ref(0) else: self.align(item.data()) self.__update_qualities() def __update_qualities(self): qualities = [d['quality'] for d in self.__files_data()] self.qualities = (min(qualities), max(qualities)) for index in range(0, self.files_model.rowCount()): self.files_model.item(index, 1).setText("{}%".format(self.__quality_percent(self.files_model.item(index).data()['quality']))) def __quality_percent(self, quality): return 100. - (100. * (quality-self.qualities[0]) / (self.qualities[1]-self.qualities[0])) def align(self, data): if data['file'] == self.reference['file']: self.__update_offset(data, (0, 0)) return offset_range = lambda n: range(1-int(n), int(n)-1) offsets = lambda name, indexes: [ (pearsonr(self.reference[name][indexes[0]:indexes[1]], data[name][indexes[0]-offset:indexes[1]-offset] )[0], offset) for offset in offset_range(indexes[0]) ] x_offset = sorted(offsets('profile', self.reference_indexes['h']), key=lambda x: x[0])[-1] y_offset = sorted(offsets('spatial', self.reference_indexes['v']), key=lambda y: y[0])[-1] self.__update_offset(data, (x_offset[1], y_offset[1])) def __update_offset(self, data, offset): row = self.__row_index(data) self.files_model.item(row, 2).setText('{}, {}'.format(offset[0], offset[1])) data.update({'offset': {'x': offset[0], 'y': offset[1]}}) self.files_model.item(row).setData(data) def __files_data(self): return [self.files_model.item(i).data() for i in range(0, self.files_model.rowCount())] def __remove_selected_rows(self): for row in sorted([r.row() for r in self.ui.files.selectionModel().selectedRows()], reverse=True): self.files_model.removeRows(row, 1) if self.files_model.rowCount() == 0: return if len([f for f in self.__files_data() if f['file'] == self.reference['file']]) == 0: self.__set_ref(0) def __set_ref(self, index): self.reference = self.files_model.item(index).data() self.rotate_dialog = RotateImageDialog(self.fits_file, 0) self.rotate_dialog.rotated.connect(self.__rotated) indexes = lambda data: (int(len(data)/4), int(len(data)/4*3)) self.__set_reference_indexes(indexes(self.reference['profile']), indexes(self.reference['spatial']) ) #self.reference_indexes = { 'h': indexes(self.reference['profile']), 'v': indexes(self.reference['spatial']) } for data in self.__files_data() : self.align(data) def __rotated(self): self.degrees = self.rotate_dialog.degrees() for index in range(0, self.files_model.rowCount()): self.files_model.removeRow(index) self.__add_file(self.fits_file) self.__draw_image(0) def __alignment_region_selected(self, eclick, erelease): self.__set_reference_indexes((eclick.xdata, erelease.xdata), (eclick.ydata, erelease.ydata)) def __set_reference_indexes(self, x, y): self.reference_indexes = { 'h': x, 'v': y } self.__draw_reference_rect() def __draw_reference_rect(self): self.plot.rm_element('reference_indexes') x, y = self.reference_indexes['h'], self.reference_indexes['v'] rect = Rectangle((x[0], y[0]), x[1]-x[0], y[1]-y[0], fill=True, alpha=0.3, color='green') self.plot.figure.axes[0].add_artist(rect) self.plot.add_element(rect, 'reference_indexes') self.plot.figure.canvas.draw() def stack(self): dataset = self.__files_data() median = MedianStacker(dataset).median() self.fits_file[0].data = median
class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(200, 150)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0,0,0,0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down STATUS.connect('reload-mdi-history', self.reload) # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) try: open(self.fp, 'r') except: open(self.fp, 'a+') LOG.debug('MDI History file created: {}'.format(self.fp)) self.reload() self.select_row('last') def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None ): self.model.clear() try: with open(self.fp,'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() if self.MDILine.hasFocus(): self.select_row('last') except: LOG.debug('File path is not valid: {}'.format(fp)) def selectionChanged(self,old, new): cmd = self.getSelected() self.MDILine.setText(cmd) selectionModel = self.list.selectionModel() if selectionModel.hasSelection(): self.row = selectionModel.currentIndex().row() def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() self.select_row('down') def select_row(self, style): selectionModel = self.list.selectionModel() parent = QModelIndex() self.rows = self.model.rowCount(parent) - 1 if style == 'last': self.row = self.rows elif style == 'up': if self.row > 0: self.row -= 1 else: self.row = self.rows elif style == 'down': if self.row < self.rows: self.row += 1 else: self.row = 0 else: return top = self.model.index(self.row, 0, parent) bottom = self.model.index(self.row, 0, parent) selectionModel.setCurrentIndex(top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) def line_up(self): self.select_row('up') def line_down(self): self.select_row('down') ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
class MassTabSelectorGUI(QDockWidget): """ classdocs """ masstabViewRaisedSignal = pyqtSignal(object) """ constructor """ def __init__(self, parent=None): super(MassTabSelectorGUI, self).__init__(parent) self.ui = Ui_DockWidget_MassTabSelector() self.ui.setupUi(self) def setup(self, analysis): self.ana = analysis self.__connect_events() def __connect_events(self): self.model = QStandardItemModel() self.mass_list = [] for i in range(10): mass = 184 + i self.mass_list.append(str(mass)) for i in range(10): mass = 209 + i self.mass_list.append(str(mass)) for i in range(10): mass = 273.3 + i self.mass_list.append(str(mass)) for i in range(10): mass = 294 + i self.mass_list.append(str(mass)) for mass in self.mass_list: item = QStandardItem(mass) item.setCheckable(True) item.setEditable(True) self.model.appendRow(item) self.view = self.ui.listView_Mass self.view.setModel(self.model) # changes in one item, don't know which one self.model.itemChanged.connect(self.change_list) # changes in button self.ui.pushButton_ChangeList.clicked.connect(self.emit_list_signal) # get peaks found and update automatically the mass list self.ana.masstabSelectorRaisedSignal.connect(self.update_list_view) def update_list_view(self, xind): self.mass_list = [] for i in range(len(xind)): m = "{:.1f}".format(float(xind[i])) self.mass_list.append(str(m)) self.model.clear() self.model = QStandardItemModel() for mass in self.mass_list: item = QStandardItem(mass) item.setCheckable(True) item.setEditable(True) item.setCheckState(Qt.Checked) self.model.appendRow(item) self.view = self.ui.listView_Mass self.view.setModel(self.model) # changes in one item, don't know which one self.model.itemChanged.connect(self.change_list) def change_list(self): log.debug("event from %s", self.sender()) self.oneIsChecked = False self.mass_list = [] count = self.model.rowCount() for i in range(count): checked = self.model.item(i).checkState() if checked: mass_name = self.model.data(self.model.index(i, 0)) self.mass_list.append(mass_name) self.oneIsChecked = True def emit_list_signal(self): log.debug("event from %s", self.sender()) self.change_list() if self.oneIsChecked: self.masstabViewRaisedSignal.emit(self.mass_list)
class SetupDialog(QDialog, Ui_SetupDialog): """ Function and Event handling class for the Ui_SetupDialog. """ def __init__(self, parent, host_infos): QDialog.__init__(self, parent, get_modeless_dialog_flags()) self._gui_logger = GUILogger("GUILogger", logging.INFO) self._gui_job = None EventLogger.add_logger(self._gui_logger) self.data_logger_thread = None self.tab_debug_warning = False self.device_dialog = None self.last_host_index = -1 self.setupUi(self) self.model_data = QStandardItemModel(self) self.model_data.setHorizontalHeaderLabels(['Time', 'Name', 'UID', 'Var', 'Raw', 'Unit']) self.table_data.setModel(self.model_data) self.table_data.setColumnWidth(0, 160) self.table_data.setColumnWidth(1, 170) self.table_data.setColumnWidth(2, 50) self.table_data.setColumnWidth(3, 110) self.table_data.setColumnWidth(4, 70) self.table_data.setColumnWidth(5, 100) self.model_devices = QStandardItemModel(self) self.model_devices.setHorizontalHeaderLabels(['Device', 'Value']) self.tree_devices.setModel(self.model_devices) self.tree_devices.setColumnWidth(0, 300) self.signal_initialization() self.check_authentication.stateChanged.connect(self.authentication_state_changed) self.label_secret.hide() self.edit_secret.hide() self.edit_secret.setEchoMode(QLineEdit.Password) self.check_secret_show.hide() self.check_secret_show.stateChanged.connect(self.secret_show_state_changed) self.btn_start_logging.setIcon(QIcon(load_pixmap('data_logger/start-icon.png'))) self.example_timestamp = time.time() self.edit_csv_file_name.setText(os.path.join(get_home_path(), 'logger_data_{0}.csv'.format(int(self.example_timestamp)))) self.edit_log_file_name.setText(os.path.join(get_home_path(), 'logger_debug_{0}.log'.format(int(self.example_timestamp)))) self.combo_data_time_format.addItem(utils.timestamp_to_de(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS)', 'de') self.combo_data_time_format.addItem(utils.timestamp_to_de_msec(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS,000)', 'de-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) self.combo_data_time_format.addItem(utils.timestamp_to_us(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS)', 'us') self.combo_data_time_format.addItem(utils.timestamp_to_us_msec(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS.000)', 'us-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) self.combo_data_time_format.addItem(utils.timestamp_to_iso(self.example_timestamp) + ' (ISO 8601)', 'iso') self.combo_data_time_format.addItem(utils.timestamp_to_iso_msec(self.example_timestamp) + ' (ISO 8601 + Milliseconds)', 'iso-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) self.combo_data_time_format.addItem(utils.timestamp_to_unix(self.example_timestamp) + ' (Unix)', 'unix') self.combo_data_time_format.addItem(utils.timestamp_to_unix_msec(self.example_timestamp) + ' (Unix + Milliseconds)', 'unix-msec') self.combo_data_time_format.insertSeparator(self.combo_data_time_format.count()) t = utils.timestamp_to_strftime(self.example_timestamp, self.edit_data_time_format_strftime.text()) if len(t) == 0: t = '<empty>' self.combo_data_time_format.addItem((t + ' (strftime)'), 'strftime') self.combo_debug_time_format.addItem(utils.timestamp_to_de(self.example_timestamp) + ' (DD.MM.YYYY HH:MM:SS)', 'de') self.combo_debug_time_format.addItem(utils.timestamp_to_us(self.example_timestamp) + ' (MM/DD/YYYY HH:MM:SS)', 'us') self.combo_debug_time_format.addItem(utils.timestamp_to_iso(self.example_timestamp) + ' (ISO 8601)', 'iso') self.combo_debug_time_format.addItem(utils.timestamp_to_unix(self.example_timestamp) + ' (Unix)', 'unix') self.combo_log_level.addItem('Debug', 'debug') self.combo_log_level.addItem('Info', 'info') self.combo_log_level.addItem('Warning', 'warning') self.combo_log_level.addItem('Error', 'error') self.combo_log_level.addItem('Critical', 'critical') self.combo_log_level.setCurrentIndex(0) # debug self.combo_debug_level.addItem('Debug', logging.DEBUG) self.combo_debug_level.addItem('Info', logging.INFO) self.combo_debug_level.addItem('Warning', logging.WARNING) self.combo_debug_level.addItem('Error', logging.ERROR) self.combo_debug_level.addItem('Critical', logging.CRITICAL) self.combo_debug_level.setCurrentIndex(1) # info for host_info in host_infos: self.combo_host.addItem(host_info.host, (host_info.port, host_info.use_authentication, host_info.secret)) self._host_index_changed(0) self.update_ui_state() def update_ui_state(self): index = self.combo_data_time_format.currentIndex() if index > 0: time_format = self.combo_data_time_format.itemData(index) else: time_format = 'unknown' data_to_csv_file = self.check_data_to_csv_file.isChecked() debug_to_log_file = self.check_debug_to_log_file.isChecked() self.edit_data_time_format_strftime.setVisible(time_format == 'strftime') self.label_data_time_format_strftime_help.setVisible(time_format == 'strftime') self.label_csv_file_name.setVisible(data_to_csv_file) self.edit_csv_file_name.setVisible(data_to_csv_file) self.btn_browse_csv_file_name.setVisible(data_to_csv_file) self.label_log_file_name.setVisible(debug_to_log_file) self.edit_log_file_name.setVisible(debug_to_log_file) self.btn_browse_log_file_name.setVisible(debug_to_log_file) self.label_log_level.setVisible(debug_to_log_file) self.combo_log_level.setVisible(debug_to_log_file) def signal_initialization(self): """ Init of all important Signals and connections. """ # Buttons self.btn_start_logging.clicked.connect(self.btn_start_logging_clicked) self.btn_save_config.clicked.connect(self.btn_save_config_clicked) self.btn_load_config.clicked.connect(self.btn_load_config_clicked) self.combo_data_time_format.currentIndexChanged.connect(self.update_ui_state) self.edit_data_time_format_strftime.textChanged.connect(self.edit_data_time_format_strftime_changed) self.check_data_to_csv_file.stateChanged.connect(self.update_ui_state) self.check_debug_to_log_file.stateChanged.connect(self.update_ui_state) self.btn_browse_csv_file_name.clicked.connect(self.btn_browse_csv_file_name_clicked) self.btn_browse_log_file_name.clicked.connect(self.btn_browse_log_file_name_clicked) self.btn_clear_debug.clicked.connect(self.btn_clear_debug_clicked) self.combo_debug_level.currentIndexChanged.connect(self.combo_debug_level_changed) self.btn_add_device.clicked.connect(self.btn_add_device_clicked) self.btn_remove_device.clicked.connect(self.btn_remove_device_clicked) self.btn_remove_all_devices.clicked.connect(self.btn_remove_all_devices_clicked) self.tab_widget.currentChanged.connect(self.tab_reset_warning) self.btn_clear_data.clicked.connect(self.btn_clear_data_clicked) self._gui_logger.newEventMessage.connect(self.add_debug_message) self._gui_logger.newEventTabHighlight.connect(self.highlight_debug_tab) self.combo_host.currentIndexChanged.connect(self._host_index_changed) def btn_start_logging_clicked(self): """ Start/Stop of the logging process """ if (self.data_logger_thread is not None) and (not self.data_logger_thread.stopped): self.btn_start_logging.clicked.disconnect() self.data_logger_thread.stop() self._reset_stop() elif self.data_logger_thread is None: from brickv.data_logger import main self._gui_job = GuiDataJob(name="GuiData-Writer") self._gui_job.signalNewData.connect(self.table_add_row) self.data_logger_thread = main.main(None, GuiConfigHandler.create_config(self), self._gui_job, None, None, None) if self.data_logger_thread is not None: self.btn_start_logging.setText("Stop Logging") self.btn_start_logging.setIcon(QIcon(load_pixmap('data_logger/stop-icon.png'))) self.tab_devices.setEnabled(False) self.tab_setup.setEnabled(False) self.tab_widget.setCurrentIndex(self.tab_widget.indexOf(self.tab_data)) self.tab_reset_warning() def _reset_stop(self): self.tab_devices.setEnabled(True) self.tab_setup.setEnabled(True) self.btn_start_logging.setText("Start Logging") self.btn_start_logging.setIcon(QIcon(load_pixmap('data_logger/start-icon.png'))) self._gui_job.signalNewData.disconnect(self.table_add_row) self.data_logger_thread = None self._gui_job = None self.btn_start_logging.clicked.connect(self.btn_start_logging_clicked) def authentication_state_changed(self, state): visible = state == Qt.Checked self.label_secret.setVisible(visible) self.edit_secret.setVisible(visible) self.check_secret_show.setVisible(visible) def secret_show_state_changed(self, state): if state == Qt.Checked: self.edit_secret.setEchoMode(QLineEdit.Normal) else: self.edit_secret.setEchoMode(QLineEdit.Password) def edit_data_time_format_strftime_changed(self): index = self.combo_data_time_format.findData('strftime') if index < 0: return t = utils.timestamp_to_strftime(self.example_timestamp, self.edit_data_time_format_strftime.text()) if len(t) == 0: t = '<empty>' self.combo_data_time_format.setItemText(index, (t + ' (strftime)')) if self.edit_data_time_format_strftime.isVisible(): self.edit_data_time_format_strftime.setFocus() def btn_save_config_clicked(self): filename = get_save_file_name(self, 'Save Config', get_home_path(), 'JSON Files (*.json)') if len(filename) == 0: return if not filename.lower().endswith('.json'): filename += '.json' config = GuiConfigHandler.create_config(self) if not save_config(config, filename): QMessageBox.warning(self, 'Save Config', 'Could not save config to file! See Debug tab for details.', QMessageBox.Ok) def btn_load_config_clicked(self): filename = get_open_file_name(self, 'Load Config', get_home_path(), 'JSON Files (*.json)') if len(filename) == 0: return config = load_and_validate_config(filename) if config == None: QMessageBox.warning(self, 'Load Config', 'Could not load config from file! See Debug tab for details.', QMessageBox.Ok) return self.update_setup_tab(config) self.update_devices_tab(config) def btn_browse_csv_file_name_clicked(self): if len(self.edit_csv_file_name.text()) > 0: last_dir = os.path.dirname(os.path.realpath(self.edit_csv_file_name.text())) else: last_dir = get_home_path() filename = get_save_file_name(self, 'Choose CSV File', last_dir, "CSV Files (*.csv)") if len(filename) > 0: if not filename.lower().endswith('.csv'): filename += '.csv' self.edit_csv_file_name.setText(filename) def btn_browse_log_file_name_clicked(self): if len(self.edit_log_file_name.text()) > 0: last_dir = os.path.dirname(os.path.realpath(self.edit_log_file_name.text())) else: last_dir = get_home_path() filename = get_save_file_name(self, 'Choose Log File', last_dir, "Log Files (*.log)") if len(filename) > 0: if not filename.lower().endswith('.log'): filename += '.log' self.edit_log_file_name.setText(filename) def btn_add_device_clicked(self): """ Opens the DeviceDialog in Add-Mode. """ if self.device_dialog is None: self.device_dialog = DeviceDialog(self) self.device_dialog.btn_refresh_clicked() self.device_dialog.show() def btn_remove_device_clicked(self): selection = self.tree_devices.selectionModel().selectedIndexes() while len(selection) > 0: index = selection[0] while index.parent() != self.model_devices.invisibleRootItem().index(): index = index.parent() self.model_devices.removeRows(index.row(), 1) # get new selection, because row removal might invalid indices selection = self.tree_devices.selectionModel().selectedIndexes() def btn_remove_all_devices_clicked(self): self.model_devices.removeRows(0, self.model_devices.rowCount()) def btn_clear_data_clicked(self): self.model_data.removeRows(0, self.model_data.rowCount()) def tab_reset_warning(self): """ Resets the Warning @ the debug tab. """ if not self.tab_debug_warning or self.tab_widget.currentIndex() != self.tab_widget.indexOf(self.tab_debug): return self.tab_debug_warning = False self.tab_set(self.tab_widget.indexOf(self.tab_debug), self.palette().color(QPalette.WindowText), None) def combo_debug_level_changed(self): """ Changes the log level dynamically. """ self._gui_logger.level = self.combo_debug_level.itemData(self.combo_debug_level.currentIndex()) def tab_set(self, tab_index, color, icon=None): """ Sets the font Color and an icon, if given, at a specific tab. """ self.tab_widget.tabBar().setTabTextColor(tab_index, color) if icon is not None: self.tab_widget.setTabIcon(tab_index, QIcon(icon)) else: self.tab_widget.setTabIcon(tab_index, QIcon()) def _host_index_changed(self, i): if self.last_host_index >= 0: self.combo_host.setItemData(self.last_host_index, (self.spin_port.value(), self.check_authentication.isChecked(), self.edit_secret.text())) self.last_host_index = i if i < 0: return host_info = self.combo_host.itemData(i) self.spin_port.setValue(host_info[0]) self.check_authentication.setChecked(host_info[1]) self.edit_secret.setText(host_info[2]) def update_setup_tab(self, config): EventLogger.debug('Updating setup tab from config') name = config['hosts']['default']['name'] port = config['hosts']['default']['port'] secret = config['hosts']['default']['secret'] i = self.combo_host.findText(name) if i >= 0: self.combo_host.setCurrentIndex(i) else: self.combo_host.insertItem(0, name, (port, secret != None, secret)) self.combo_host.setCurrentIndex(0) self.spin_port.setValue(port) self.check_authentication.setChecked(secret != None) self.edit_secret.setText(secret if secret != None else '') self.combo_data_time_format.setCurrentIndex(max(self.combo_data_time_format.findData(config['data']['time_format']), 0)) self.edit_data_time_format_strftime.setText(config['data']['time_format_strftime']) self.check_data_to_csv_file.setChecked(config['data']['csv']['enabled']) self.edit_csv_file_name.setText(config['data']['csv']['file_name']) self.combo_debug_time_format.setCurrentIndex(max(self.combo_debug_time_format.findData(config['debug']['time_format']), 0)) self.check_debug_to_log_file.setChecked(config['debug']['log']['enabled']) self.edit_log_file_name.setText(config['debug']['log']['file_name']) self.combo_log_level.setCurrentIndex(max(self.combo_debug_time_format.findData(config['debug']['log']['level']), 0)) def update_devices_tab(self, config): EventLogger.debug('Updating devices tab from config') self.model_devices.removeRows(0, self.model_data.rowCount()) for device in config['devices']: self.add_device_to_tree(device) def add_device_to_tree(self, device): # check if device is already added if len(device['uid']) > 0: for row in range(self.model_devices.rowCount()): existing_name = self.model_devices.item(row, 0).text() exisitng_uid = self.tree_devices.indexWidget(self.model_devices.item(row, 1).index()).text() if device['name'] == existing_name and device['uid'] == exisitng_uid: EventLogger.info('Ignoring duplicate device "{0}" with UID "{1}"' .format(device['name'], device['uid'])) return # add device name_item = QStandardItem(device['name']) uid_item = QStandardItem('') self.model_devices.appendRow([name_item, uid_item]) edit_uid = QLineEdit() edit_uid.setPlaceholderText('Enter UID') edit_uid.setValidator(QRegExpValidator(QRegExp('^[{0}]{{1,6}}$'.format(BASE58)))) # FIXME: use stricter logic edit_uid.setText(device['uid']) self.tree_devices.setIndexWidget(uid_item.index(), edit_uid) value_specs = device_specs[device['name']]['values'] parent_item = QStandardItem('Values') name_item.appendRow([parent_item, QStandardItem('')]) self.tree_devices.expand(parent_item.index()) # add values for value_spec in value_specs: value_name_item = QStandardItem(value_spec['name']) value_interval_item = QStandardItem('') parent_item.appendRow([value_name_item, value_interval_item]) spinbox_interval = IntervalWidget() spinbox_interval.set_interval(device['values'][value_spec['name']]['interval']) self.tree_devices.setIndexWidget(value_interval_item.index(), spinbox_interval) if value_spec['subvalues'] != None: for subvalue_name in value_spec['subvalues']: subvalue_name_item = QStandardItem(subvalue_name) subvalue_check_item = QStandardItem('') value_name_item.appendRow([subvalue_name_item, subvalue_check_item]) check_subvalue = QCheckBox() check_subvalue.setChecked(device['values'][value_spec['name']]['subvalues'][subvalue_name]) self.tree_devices.setIndexWidget(subvalue_check_item.index(), check_subvalue) self.tree_devices.expand(name_item.index()) # add options option_specs = device_specs[device['name']]['options'] if option_specs != None: parent_item = QStandardItem('Options') name_item.appendRow([parent_item, QStandardItem('')]) for option_spec in option_specs: option_name_item = QStandardItem(option_spec['name']) option_widget_item = QStandardItem('') parent_item.appendRow([option_name_item, option_widget_item]) if option_spec['type'] == 'choice': widget_option_value = QComboBox() for option_value_spec in option_spec['values']: widget_option_value.addItem(option_value_spec[0], option_value_spec[1]) widget_option_value.setCurrentIndex(widget_option_value.findText(device['options'][option_spec['name']]['value'])) elif option_spec['type'] == 'int': widget_option_value = QSpinBox() widget_option_value.setRange(option_spec['minimum'], option_spec['maximum']) widget_option_value.setSuffix(option_spec['suffix']) widget_option_value.setValue(device['options'][option_spec['name']]['value']) elif option_spec['type'] == 'bool': widget_option_value = QCheckBox() widget_option_value.setChecked(device['options'][option_spec['name']]['value']) self.tree_devices.setIndexWidget(option_widget_item.index(), widget_option_value) def add_debug_message(self, message): self.text_debug.append(message) while self.text_debug.document().blockCount() > 1000: cursor = QTextCursor(self.text_debug.document().begin()) cursor.select(QTextCursor.BlockUnderCursor) cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor) cursor.removeSelectedText() if self.checkbox_debug_auto_scroll.isChecked(): self.text_debug.verticalScrollBar().setValue(self.text_debug.verticalScrollBar().maximum()) def btn_clear_debug_clicked(self): self.text_debug.clear() def highlight_debug_tab(self): """ SIGNAL function: Highlight the debug tab when an error occurs. """ if not self.tab_debug_warning and self.tab_widget.currentIndex() != self.tab_widget.indexOf(self.tab_debug): self.tab_debug_warning = True self.tab_set(self.tab_widget.indexOf(self.tab_debug), QColor(255, 0, 0), get_resources_path("warning-icon-16.png")) def table_add_row(self, csv_data): """ SIGNAL function: Adds new CSV Data into the Table. """ rows = self.model_data.rowCount() while rows >= 1000: self.model_data.removeRow(0) rows = self.model_data.rowCount() row_number = None if rows > 0: try: row_number = int(self.model_data.headerData(rows - 1, Qt.Vertical)) except ValueError: pass self.model_data.appendRow([QStandardItem(csv_data.timestamp), QStandardItem(csv_data.name), QStandardItem(csv_data.uid), QStandardItem(csv_data.var_name), QStandardItem(str(csv_data.raw_data)), QStandardItem(csv_data.var_unit)]) if row_number != None: self.model_data.setHeaderData(rows, Qt.Vertical, str(row_number + 1)) if self.checkbox_data_auto_scroll.isChecked(): self.table_data.scrollToBottom()
class CreateDialogCodeDialog(QDialog, Ui_CreateDialogCodeDialog): """ Class implementing a dialog to generate code for a Qt4/Qt5 dialog. """ DialogClasses = { "QDialog", "QWidget", "QMainWindow", "QWizard", "QWizardPage", "QDockWidget", "QFrame", "QGroupBox", "QScrollArea", "QMdiArea", "QTabWidget", "QToolBox", "QStackedWidget" } Separator = 25 * "=" def __init__(self, formName, project, parent=None): """ Constructor @param formName name of the file containing the form (string) @param project reference to the project object @param parent parent widget if the dialog (QWidget) """ super(CreateDialogCodeDialog, self).__init__(parent) self.setupUi(self) self.okButton = self.buttonBox.button(QDialogButtonBox.Ok) self.slotsView.header().hide() self.project = project self.formFile = formName filename, ext = os.path.splitext(self.formFile) self.srcFile = '{0}{1}'.format( filename, self.project.getDefaultSourceExtension()) self.slotsModel = QStandardItemModel() self.proxyModel = QSortFilterProxyModel() self.proxyModel.setDynamicSortFilter(True) self.proxyModel.setSourceModel(self.slotsModel) self.slotsView.setModel(self.proxyModel) # initialize some member variables self.__initError = False self.__module = None if os.path.exists(self.srcFile): vm = e5App().getObject("ViewManager") ed = vm.getOpenEditor(self.srcFile) if ed and not vm.checkDirty(ed): self.__initError = True return try: splitExt = os.path.splitext(self.srcFile) if len(splitExt) == 2: exts = [splitExt[1]] else: exts = None from Utilities import ModuleParser self.__module = ModuleParser.readModule( self.srcFile, extensions=exts, caching=False) except ImportError: pass if self.__module is not None: self.filenameEdit.setText(self.srcFile) classesList = [] vagueClassesList = [] for cls in list(self.__module.classes.values()): if not set(cls.super).isdisjoint( CreateDialogCodeDialog.DialogClasses): classesList.append(cls.name) else: vagueClassesList.append(cls.name) classesList.sort() self.classNameCombo.addItems(classesList) if vagueClassesList: if classesList: self.classNameCombo.addItem( CreateDialogCodeDialog.Separator) self.classNameCombo.addItems(sorted(vagueClassesList)) if os.path.exists(self.srcFile) and \ self.__module is not None and \ self.classNameCombo.count() == 0: self.__initError = True E5MessageBox.critical( self, self.tr("Create Dialog Code"), self.tr( """The file <b>{0}</b> exists but does not contain""" """ any classes.""").format(self.srcFile)) self.okButton.setEnabled(self.classNameCombo.count() > 0) self.__updateSlotsModel() def initError(self): """ Public method to determine, if there was an initialzation error. @return flag indicating an initialzation error (boolean) """ return self.__initError def __objectName(self): """ Private method to get the object name of the dialog. @return object name (string) """ try: dlg = uic.loadUi( self.formFile, package=self.project.getProjectPath()) return dlg.objectName() except (AttributeError, ImportError, xml.etree.ElementTree.ParseError) as err: E5MessageBox.critical( self, self.tr("uic error"), self.tr( """<p>There was an error loading the form <b>{0}</b>""" """.</p><p>{1}</p>""").format(self.formFile, str(err))) return "" def __className(self): """ Private method to get the class name of the dialog. @return class name (sting) """ try: dlg = uic.loadUi( self.formFile, package=self.project.getProjectPath()) return dlg.metaObject().className() except (AttributeError, ImportError, xml.etree.ElementTree.ParseError) as err: E5MessageBox.critical( self, self.tr("uic error"), self.tr( """<p>There was an error loading the form <b>{0}</b>""" """.</p><p>{1}</p>""").format(self.formFile, str(err))) return "" def __signatures(self): """ Private slot to get the signatures. @return list of signatures (list of strings) """ if self.__module is None: return [] signatures = [] clsName = self.classNameCombo.currentText() if clsName: cls = self.__module.classes[clsName] for meth in list(cls.methods.values()): if meth.name.startswith("on_"): if meth.pyqtSignature is not None: sig = ", ".join( [bytes(QMetaObject.normalizedType(t)).decode() for t in meth.pyqtSignature.split(",")]) signatures.append("{0}({1})".format(meth.name, sig)) else: signatures.append(meth.name) return signatures def __mapType(self, type_): """ Private method to map a type as reported by Qt's meta object to the correct Python type. @param type_ type as reported by Qt (QByteArray) @return mapped Python type (string) """ mapped = bytes(type_).decode() if self.project.getProjectLanguage() != "Python2" or \ self.project.getProjectType == "PySide": # 1. check for const mapped = mapped.replace("const ", "") # 2. check for * mapped = mapped.replace("*", "") # 3. replace QString and QStringList mapped = mapped.replace("QStringList", "list")\ .replace("QString", "str") # 4. replace double by float mapped = mapped.replace("double", "float") return mapped def __updateSlotsModel(self): """ Private slot to update the slots tree display. """ self.filterEdit.clear() try: dlg = uic.loadUi( self.formFile, package=self.project.getProjectPath()) objects = dlg.findChildren(QWidget) + dlg.findChildren(QAction) signatureList = self.__signatures() self.slotsModel.clear() self.slotsModel.setHorizontalHeaderLabels([""]) for obj in objects: name = obj.objectName() if not name or name.startswith("qt_"): # ignore un-named or internal objects continue metaObject = obj.metaObject() className = metaObject.className() itm = QStandardItem("{0} ({1})".format(name, className)) self.slotsModel.appendRow(itm) for index in range(metaObject.methodCount()): metaMethod = metaObject.method(index) if metaMethod.methodType() == QMetaMethod.Signal: if qVersion() >= "5.0.0": itm2 = QStandardItem("on_{0}_{1}".format( name, bytes(metaMethod.methodSignature()).decode())) else: itm2 = QStandardItem("on_{0}_{1}".format( name, metaMethod.signature())) itm.appendRow(itm2) if self.__module is not None: if qVersion() >= "5.0.0": method = "on_{0}_{1}".format( name, bytes(metaMethod.methodSignature()) .decode().split("(")[0]) else: method = "on_{0}_{1}".format( name, metaMethod.signature().split("(")[0]) method2 = "{0}({1})".format( method, ", ".join( [self.__mapType(t) for t in metaMethod.parameterTypes()])) if method2 in signatureList or \ method in signatureList: itm2.setFlags(Qt.ItemFlags(Qt.ItemIsEnabled)) itm2.setCheckState(Qt.Checked) itm2.setForeground(QBrush(Qt.blue)) continue returnType = self.__mapType( metaMethod.typeName().encode()) if returnType == 'void': returnType = "" parameterTypesList = [ self.__mapType(t) for t in metaMethod.parameterTypes()] pyqtSignature = ", ".join(parameterTypesList) parameterNames = metaMethod.parameterNames() if parameterNames: for index in range(len(parameterNames)): if not parameterNames[index]: parameterNames[index] = \ QByteArray("p{0:d}".format(index) .encode("utf-8")) parameterNamesList = [bytes(n).decode() for n in parameterNames] methNamesSig = ", ".join(parameterNamesList) if methNamesSig: if qVersion() >= "5.0.0": pythonSignature = \ "on_{0}_{1}(self, {2})".format( name, bytes(metaMethod.methodSignature()) .decode().split("(")[0], methNamesSig) else: pythonSignature = \ "on_{0}_{1}(self, {2})".format( name, metaMethod.signature().split("(")[0], methNamesSig) else: if qVersion() >= "5.0.0": pythonSignature = "on_{0}_{1}(self)".format( name, bytes(metaMethod.methodSignature()) .decode().split("(")[0]) else: pythonSignature = "on_{0}_{1}(self)".format( name, metaMethod.signature().split("(")[0]) itm2.setData(pyqtSignature, pyqtSignatureRole) itm2.setData(pythonSignature, pythonSignatureRole) itm2.setData(returnType, returnTypeRole) itm2.setData(parameterTypesList, parameterTypesListRole) itm2.setData(parameterNamesList, parameterNamesListRole) itm2.setFlags(Qt.ItemFlags( Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsSelectable) ) itm2.setCheckState(Qt.Unchecked) self.slotsView.sortByColumn(0, Qt.AscendingOrder) except (AttributeError, ImportError, xml.etree.ElementTree.ParseError) as err: E5MessageBox.critical( self, self.tr("uic error"), self.tr( """<p>There was an error loading the form <b>{0}</b>""" """.</p><p>{1}</p>""").format(self.formFile, str(err))) def __generateCode(self): """ Private slot to generate the code as requested by the user. """ # first decide on extension if self.filenameEdit.text().endswith(".py") or \ self.filenameEdit.text().endswith(".pyw"): self.__generatePythonCode() elif self.filenameEdit.text().endswith(".rb"): pass # second decide on project language elif self.project.getProjectLanguage() in ["Python2", "Python3"]: self.__generatePythonCode() elif self.project.getProjectLanguage() == "Ruby": pass else: # assume Python (our global default) self.__generatePythonCode() def __generatePythonCode(self): """ Private slot to generate Python code as requested by the user. """ # init some variables sourceImpl = [] appendAtIndex = -1 indentStr = " " slotsCode = [] if self.__module is None: # new file try: if self.project.getProjectLanguage() == "Python2": if self.project.getProjectType() == "PySide": tmplName = os.path.join( getConfig('ericCodeTemplatesDir'), "impl_pyside.py2.tmpl") elif self.project.getProjectType() == "PyQt5": tmplName = os.path.join( getConfig('ericCodeTemplatesDir'), "impl_pyqt5.py2.tmpl") else: tmplName = os.path.join( getConfig('ericCodeTemplatesDir'), "impl_pyqt.py2.tmpl") else: if self.project.getProjectType() == "PySide": tmplName = os.path.join( getConfig('ericCodeTemplatesDir'), "impl_pyside.py.tmpl") elif self.project.getProjectType() in [ "PyQt5", "E6Plugin"]: tmplName = os.path.join( getConfig('ericCodeTemplatesDir'), "impl_pyqt5.py.tmpl") else: tmplName = os.path.join( getConfig('ericCodeTemplatesDir'), "impl_pyqt.py.tmpl") tmplFile = open(tmplName, 'r', encoding="utf-8") template = tmplFile.read() tmplFile.close() except IOError as why: E5MessageBox.critical( self, self.tr("Code Generation"), self.tr( """<p>Could not open the code template file""" """ "{0}".</p><p>Reason: {1}</p>""") .format(tmplName, str(why))) return objName = self.__objectName() if objName: template = template\ .replace( "$FORMFILE$", os.path.splitext(os.path.basename(self.formFile))[0])\ .replace("$FORMCLASS$", objName)\ .replace("$CLASSNAME$", self.classNameCombo.currentText())\ .replace("$SUPERCLASS$", self.__className()) sourceImpl = template.splitlines(True) appendAtIndex = -1 # determine indent string for line in sourceImpl: if line.lstrip().startswith("def __init__"): indentStr = line.replace(line.lstrip(), "") break else: # extend existing file try: srcFile = open(self.srcFile, 'r', encoding="utf-8") sourceImpl = srcFile.readlines() srcFile.close() if not sourceImpl[-1].endswith("\n"): sourceImpl[-1] = "{0}{1}".format(sourceImpl[-1], "\n") except IOError as why: E5MessageBox.critical( self, self.tr("Code Generation"), self.tr( """<p>Could not open the source file "{0}".</p>""" """<p>Reason: {1}</p>""") .format(self.srcFile, str(why))) return cls = self.__module.classes[self.classNameCombo.currentText()] if cls.endlineno == len(sourceImpl) or cls.endlineno == -1: appendAtIndex = -1 # delete empty lines at end while not sourceImpl[-1].strip(): del sourceImpl[-1] else: appendAtIndex = cls.endlineno - 1 while not sourceImpl[appendAtIndex].strip(): appendAtIndex -= 1 appendAtIndex += 1 # determine indent string for line in sourceImpl[cls.lineno:cls.endlineno + 1]: if line.lstrip().startswith("def __init__"): indentStr = line.replace(line.lstrip(), "") break # do the coding stuff if self.project.getProjectLanguage() == "Python2": if self.project.getProjectType() == "PySide": pyqtSignatureFormat = '@Slot({0})' elif self.project.getProjectType() == "PyQt5": pyqtSignatureFormat = '@pyqtSlot({0})' else: pyqtSignatureFormat = '@pyqtSignature("{0}")' else: if self.project.getProjectType() == "PySide": pyqtSignatureFormat = '@Slot({0})' else: pyqtSignatureFormat = '@pyqtSlot({0})' for row in range(self.slotsModel.rowCount()): topItem = self.slotsModel.item(row) for childRow in range(topItem.rowCount()): child = topItem.child(childRow) if child.checkState() and \ child.flags() & Qt.ItemFlags(Qt.ItemIsUserCheckable): slotsCode.append('{0}\n'.format(indentStr)) slotsCode.append('{0}{1}\n'.format( indentStr, pyqtSignatureFormat.format( child.data(pyqtSignatureRole)))) slotsCode.append('{0}def {1}:\n'.format( indentStr, child.data(pythonSignatureRole))) indentStr2 = indentStr * 2 slotsCode.append('{0}"""\n'.format(indentStr2)) slotsCode.append( '{0}Slot documentation goes here.\n'.format( indentStr2)) if child.data(returnTypeRole) or \ child.data(parameterTypesListRole): slotsCode.append('{0}\n'.format(indentStr2)) if child.data(parameterTypesListRole): for name, type_ in zip( child.data(parameterNamesListRole), child.data(parameterTypesListRole)): slotsCode.append( '{0}@param {1} DESCRIPTION\n'.format( indentStr2, name)) slotsCode.append('{0}@type {1}\n'.format( indentStr2, type_)) if child.data(returnTypeRole): slotsCode.append( '{0}@returns DESCRIPTION\n'.format( indentStr2)) slotsCode.append('{0}@rtype {1}\n'.format( indentStr2, child.data(returnTypeRole))) slotsCode.append('{0}"""\n'.format(indentStr2)) slotsCode.append('{0}# {1}: not implemented yet\n'.format( indentStr2, "TODO")) slotsCode.append('{0}raise NotImplementedError\n'.format( indentStr2)) if appendAtIndex == -1: sourceImpl.extend(slotsCode) else: sourceImpl[appendAtIndex:appendAtIndex] = slotsCode # write the new code try: if self.project.useSystemEol(): newline = None else: newline = self.project.getEolString() srcFile = open(self.filenameEdit.text(), 'w', encoding="utf-8", newline=newline) srcFile.write("".join(sourceImpl)) srcFile.close() except IOError as why: E5MessageBox.critical( self, self.tr("Code Generation"), self.tr("""<p>Could not write the source file "{0}".</p>""" """<p>Reason: {1}</p>""") .format(self.filenameEdit.text(), str(why))) return self.project.appendFile(self.filenameEdit.text()) @pyqtSlot(int) def on_classNameCombo_activated(self, index): """ Private slot to handle the activated signal of the classname combo. @param index index of the activated item (integer) """ if (self.classNameCombo.currentText() == CreateDialogCodeDialog.Separator): self.okButton.setEnabled(False) self.filterEdit.clear() self.slotsModel.clear() self.slotsModel.setHorizontalHeaderLabels([""]) else: self.okButton.setEnabled(True) self.__updateSlotsModel() def on_filterEdit_textChanged(self, text): """ Private slot called, when thext of the filter edit has changed. @param text changed text (string) """ re = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp2) self.proxyModel.setFilterRegExp(re) @pyqtSlot() def on_newButton_clicked(self): """ Private slot called to enter the data for a new dialog class. """ path, file = os.path.split(self.srcFile) objName = self.__objectName() if objName: dlg = NewDialogClassDialog(objName, file, path, self) if dlg.exec_() == QDialog.Accepted: className, fileName = dlg.getData() self.classNameCombo.clear() self.classNameCombo.addItem(className) self.srcFile = fileName self.filenameEdit.setText(self.srcFile) self.__module = None self.okButton.setEnabled(self.classNameCombo.count() > 0) def on_buttonBox_clicked(self, button): """ Private slot to handle the buttonBox clicked signal. @param button reference to the button that was clicked (QAbstractButton) """ if button == self.okButton: self.__generateCode() self.accept()
class NamespaceWidget(QObject): error = pyqtSignal(Exception) def __init__(self, view): QObject.__init__(self, view) self.view = view self.model = QStandardItemModel() self.view.setModel(self.model) delegate = MyDelegate(self.view, self) delegate.error.connect(self.error.emit) self.view.setItemDelegate(delegate) self.node = None self.view.header().setSectionResizeMode(1) self.addNamespaceAction = QAction("Add Namespace", self.model) self.addNamespaceAction.triggered.connect(self.add_namespace) self.removeNamespaceAction = QAction("Remove Namespace", self.model) self.removeNamespaceAction.triggered.connect(self.remove_namespace) self.view.setContextMenuPolicy(Qt.CustomContextMenu) self.view.customContextMenuRequested.connect(self.showContextMenu) self._contextMenu = QMenu() self._contextMenu.addAction(self.addNamespaceAction) self._contextMenu.addAction(self.removeNamespaceAction) @trycatchslot def add_namespace(self): uries = self.node.get_value() newidx = len(uries) it = self.model.item(0, 0) uri_it = QStandardItem("") it.appendRow([QStandardItem(), QStandardItem(str(newidx)), uri_it]) idx = self.model.indexFromItem(uri_it) self.view.edit(idx) @trycatchslot def remove_namespace(self): idx = self.view.currentIndex() if not idx.isValid() or idx == self.model.item(0, 0): logger.warning("No valid item selected to remove") idx = idx.sibling(idx.row(), 2) item = self.model.itemFromIndex(idx) uri = item.text() uries = self.node.get_value() uries.remove(uri) logger.info("Writting namespace array: %s", uries) self.node.set_value(uries) self.reload() def set_node(self, node): self.model.clear() self.node = node self.show_array() def reload(self): self.set_node(self.node) def show_array(self): self.model.setHorizontalHeaderLabels(['Browse Name', 'Index', 'Value']) name_item = QStandardItem(self.node.get_browse_name().Name) self.model.appendRow([name_item, QStandardItem(""), QStandardItem()]) it = self.model.item(0, 0) val = self.node.get_value() for idx, url in enumerate(val): it.appendRow([QStandardItem(), QStandardItem(str(idx)), QStandardItem(url)]) self.view.expandAll() def clear(self): self.model.clear() def showContextMenu(self, position): self.removeNamespaceAction.setEnabled(False) idx = self.view.currentIndex() if not idx.isValid(): return if idx.parent().isValid() and idx.row() >= 1: self.removeNamespaceAction.setEnabled(True) self._contextMenu.exec_(self.view.viewport().mapToGlobal(position))
class OWCSVFileImport(widget.OWWidget): name = "CSV File Import" description = "Import a data table from a CSV formatted file." icon = "icons/CSVFile.svg" outputs = [ widget.OutputSignal( name="Data", type=Orange.data.Table, doc="Loaded data set."), widget.OutputSignal( name="Data Frame", type=pd.DataFrame, doc="" ) ] class Error(widget.OWWidget.Error): error = widget.Msg( "Unexpected error" ) encoding_error = widget.Msg( "Encoding error\n" "The file might be encoded in an unsupported encoding or it " "might be binary" ) #: Paths and options of files accessed in a 'session' _session_items = settings.Setting( [], schema_only=True) # type: List[Tuple[str, dict]] #: Saved dialog state (last directory and selected filter) dialog_state = settings.Setting({ "directory": "", "filter": "" }) # type: Dict[str, str] MaxHistorySize = 50 want_main_area = False buttons_area_orientation = None def __init__(self, *args, **kwargs): super().__init__(self, *args, **kwargs) self.__committimer = QTimer(self, singleShot=True) self.__committimer.timeout.connect(self.commit) self.__executor = qconcurrent.ThreadExecutor() self.__watcher = None # type: Optional[qconcurrent.FutureWatcher] self.controlArea.layout().setSpacing(-1) # reset spacing grid = QGridLayout() grid.addWidget(QLabel("File:", self), 0, 0, 1, 1) self.import_items_model = QStandardItemModel(self) self.recent_combo = QComboBox( self, objectName="recent-combo", toolTip="Recent files.", sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon, minimumContentsLength=16, ) self.recent_combo.setModel(self.import_items_model) self.recent_combo.activated.connect(self.activate_recent) self.recent_combo.setSizePolicy( QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) self.browse_button = QPushButton( "…", icon=self.style().standardIcon(QStyle.SP_DirOpenIcon), toolTip="Browse filesystem", autoDefault=False, ) self.browse_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.browse_button.clicked.connect(self.browse) grid.addWidget(self.recent_combo, 0, 1, 1, 1) grid.addWidget(self.browse_button, 0, 2, 1, 1) self.controlArea.layout().addLayout(grid) ########### # Info text ########### box = gui.widgetBox(self.controlArea, "Info", addSpace=False) self.summary_text = QTextBrowser( verticalScrollBarPolicy=Qt.ScrollBarAsNeeded, readOnly=True, ) self.summary_text.viewport().setBackgroundRole(QPalette.NoRole) self.summary_text.setFrameStyle(QTextBrowser.NoFrame) self.summary_text.setMinimumHeight(self.fontMetrics().ascent() * 2 + 4) self.summary_text.viewport().setAutoFillBackground(False) box.layout().addWidget(self.summary_text) button_box = QDialogButtonBox( orientation=Qt.Horizontal, standardButtons=QDialogButtonBox.Cancel | QDialogButtonBox.Retry ) self.load_button = b = button_box.button(QDialogButtonBox.Retry) b.setText("Load") b.clicked.connect(self.__committimer.start) b.setEnabled(False) b.setDefault(True) self.cancel_button = b = button_box.button(QDialogButtonBox.Cancel) b.clicked.connect(self.cancel) b.setEnabled(False) b.setAutoDefault(False) self.import_options_button = QPushButton( "Import Options…", enabled=False, autoDefault=False, clicked=self._activate_import_dialog ) self.recent_combo.currentIndexChanged.connect( lambda idx: self.import_options_button.setEnabled(idx != -1) or self.load_button.setEnabled(idx != -1) ) button_box.addButton( self.import_options_button, QDialogButtonBox.ActionRole ) button_box.setStyleSheet( "button-layout: {:d};".format(QDialogButtonBox.MacLayout) ) self.controlArea.layout().addWidget(button_box) self._restoreState() if self.current_item() is not None: self._invalidate() self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) @Slot(int) def activate_recent(self, index): """ Activate an item from the recent list. """ if 0 <= index < self.import_items_model.rowCount(): item = self.import_items_model.item(index) assert item is not None path = item.data(ImportItem.PathRole) opts = item.data(ImportItem.OptionsRole) if not isinstance(opts, Options): opts = None self.set_selected_file(path, opts) else: self.recent_combo.setCurrentIndex(-1) @Slot() def browse(self): """ Open a file dialog and select a user specified file. """ formats = [ "Text - comma separated (*.csv, *)", "Text - tab separated (*.tsv, *)", "Text - all files (*)" ] dlg = QFileDialog( self, windowTitle="Open Data File", acceptMode=QFileDialog.AcceptOpen, fileMode=QFileDialog.ExistingFile ) dlg.setNameFilters(formats) state = self.dialog_state lastdir = state.get("directory", "") lastfilter = state.get("filter", "") if lastdir and os.path.isdir(lastdir): dlg.setDirectory(lastdir) if lastfilter: dlg.selectNameFilter(lastfilter) status = dlg.exec_() dlg.deleteLater() if status == QFileDialog.Accepted: self.dialog_state["directory"] = dlg.directory().absolutePath() self.dialog_state["filter"] = dlg.selectedNameFilter() selected_filter = dlg.selectedNameFilter() path = dlg.selectedFiles()[0] # pre-flight check; try to determine the nature of the file mtype = _mime_type_for_path(path) if not mtype.inherits("text/plain"): mb = QMessageBox( parent=self, windowTitle="", icon=QMessageBox.Question, text="The '{basename}' may be a binary file.\n" "Are you sure you want to continue?".format( basename=os.path.basename(path)), standardButtons=QMessageBox.Cancel | QMessageBox.Yes ) mb.setWindowModality(Qt.WindowModal) if mb.exec() == QMessageBox.Cancel: return # initialize dialect based on selected extension if selected_filter in formats[:-1]: filter_idx = formats.index(selected_filter) if filter_idx == 0: dialect = csv.excel() elif filter_idx == 1: dialect = csv.excel_tab() else: dialect = csv.excel_tab() header = True else: try: dialect, header = sniff_csv_with_path(path) except Exception: dialect, header = csv.excel(), True options = None # Search for path in history. # If found use the stored params to initialize the import dialog items = self.itemsFromSettings() idx = index_where(items, lambda t: samepath(t[0], path)) if idx is not None: _, options_ = items[idx] if options_ is not None: options = options_ if options is None: if not header: rowspec = [] else: rowspec = [(range(0, 1), RowSpec.Header)] options = Options( encoding="utf-8", dialect=dialect, rowspec=rowspec) dlg = CSVImportDialog( self, windowTitle="Import Options", sizeGripEnabled=True) dlg.setWindowModality(Qt.WindowModal) dlg.setPath(path) dlg.setOptions(options) status = dlg.exec_() dlg.deleteLater() if status == QDialog.Accepted: self.set_selected_file(path, dlg.options()) def current_item(self): # type: () -> Optional[ImportItem] """ Return the current selected item (file) or None if there is no current item. """ idx = self.recent_combo.currentIndex() if idx == -1: return None item = self.recent_combo.model().item(idx) # type: QStandardItem if isinstance(item, ImportItem): return item else: return None def _activate_import_dialog(self): """Activate the Import Options dialog for the current item.""" item = self.current_item() assert item is not None dlg = CSVImportDialog( self, windowTitle="Import Options", sizeGripEnabled=True, ) dlg.setWindowModality(Qt.WindowModal) dlg.setAttribute(Qt.WA_DeleteOnClose) settings = QSettings() qualname = qname(type(self)) settings.beginGroup(qualname) size = settings.value("size", QSize(), type=QSize) # type: QSize if size.isValid(): dlg.resize(size) path = item.data(ImportItem.PathRole) options = item.data(ImportItem.OptionsRole) dlg.setPath(path) # Set path before options so column types can if isinstance(options, Options): dlg.setOptions(options) def update(): newoptions = dlg.options() item.setData(newoptions, ImportItem.OptionsRole) # update the stored item self._add_recent(path, newoptions) if newoptions != options: self._invalidate() dlg.accepted.connect(update) def store_size(): settings.setValue("size", dlg.size()) dlg.finished.connect(store_size) dlg.show() def set_selected_file(self, filename, options=None): """ Set the current selected filename path. """ self._add_recent(filename, options) self._invalidate() #: Saved options for a filename SCHEMA = { "path": str, # Local filesystem path "options": str, # json encoded 'Options' } @classmethod def _local_settings(cls): # type: () -> QSettings """Return a QSettings instance with local persistent settings.""" filename = "{}.ini".format(qname(cls)) fname = os.path.join(settings.widget_settings_dir(), filename) return QSettings(fname, QSettings.IniFormat) def _add_recent(self, filename, options=None): # type: (str, Optional[Options]) -> None """ Add filename to the list of recent files. """ model = self.import_items_model index = index_where( (model.index(i, 0).data(ImportItem.PathRole) for i in range(model.rowCount())), lambda path: isinstance(path, str) and samepath(path, filename) ) if index is not None: item, *_ = model.takeRow(index) else: item = ImportItem.fromPath(filename) model.insertRow(0, item) if options is not None: item.setOptions(options) self.recent_combo.setCurrentIndex(0) # store items to local persistent settings s = self._local_settings() arr = QSettings_readArray(s, "recent", OWCSVFileImport.SCHEMA) item = {"path": filename} if options is not None: item["options"] = json.dumps(options.as_dict()) arr = [item for item in arr if item.get("path") != filename] arr.append(item) QSettings_writeArray(s, "recent", arr) # update workflow session items items = self._session_items[:] idx = index_where(items, lambda t: samepath(t[0], filename)) if idx is not None: del items[idx] items.insert(0, (filename, options.as_dict())) self._session_items = items[:OWCSVFileImport.MaxHistorySize] def _invalidate(self): # Invalidate the current output and schedule a new commit call. # (NOTE: The widget enters a blocking state) self.__committimer.start() if self.__watcher is not None: self.__cancel_task() self.setBlocking(True) def commit(self): """ Commit the current state and submit the load task for execution. Note ---- Any existing pending task is canceled. """ self.__committimer.stop() if self.__watcher is not None: self.__cancel_task() self.error() item = self.current_item() if item is None: return path = item.data(ImportItem.PathRole) opts = item.data(ImportItem.OptionsRole) if not isinstance(opts, Options): return task = state = TaskState() state.future = ... state.watcher = qconcurrent.FutureWatcher() state.progressChanged.connect(self.__set_read_progress, Qt.QueuedConnection) def progress_(i, j): task.emitProgressChangedOrCancel(i, j) task.future = self.__executor.submit( clear_stack_on_cancel(load_csv), path, opts, progress_, ) task.watcher.setFuture(task.future) w = task.watcher w.done.connect(self.__handle_result) w.progress = state self.__watcher = w self.__set_running_state() @Slot('qint64', 'qint64') def __set_read_progress(self, read, count): if count > 0: self.progressBarSet(100 * read / count) def __cancel_task(self): # Cancel and dispose of the current task assert self.__watcher is not None w = self.__watcher w.future().cancel() w.progress.cancel = True w.done.disconnect(self.__handle_result) w.progress.progressChanged.disconnect(self.__set_read_progress) w.progress.deleteLater() # wait until completion futures.wait([w.future()]) self.__watcher = None def cancel(self): """ Cancel current pending or executing task. """ if self.__watcher is not None: self.__cancel_task() self.__clear_running_state() self.setStatusMessage("Cancelled") self.summary_text.setText( "<div>Cancelled<br/><small>Press 'Reload' to try again</small></div>" ) def __set_running_state(self): self.progressBarInit() self.setBlocking(True) self.setStatusMessage("Running") self.cancel_button.setEnabled(True) self.load_button.setText("Restart") path = self.current_item().path() self.Error.clear() self.summary_text.setText( "<div>Loading: <i>{}</i><br/>".format(prettyfypath(path)) ) def __clear_running_state(self, ): self.progressBarFinished() self.setStatusMessage("") self.setBlocking(False) self.cancel_button.setEnabled(False) self.load_button.setText("Reload") def __set_error_state(self, err): self.Error.clear() if isinstance(err, UnicodeDecodeError): self.Error.encoding_error(exc_info=err) else: self.Error.error(exc_info=err) path = self.current_item().path() basename = os.path.basename(path) if isinstance(err, UnicodeDecodeError): text = ( "<div><i>{basename}</i> was not loaded due to a text encoding " "error. The file might be saved in an unknown or invalid " "encoding, or it might be a binary file.</div>" ).format( basename=escape(basename) ) else: text = ( "<div><i>{basename}</i> was not loaded due to an error:" "<p style='white-space: pre;'>{err}</p>" ).format( basename=escape(basename), err="".join(traceback.format_exception_only(type(err), err)) ) self.summary_text.setText(text) def __clear_error_state(self): self.Error.error.clear() self.summary_text.setText("") def onDeleteWidget(self): """Reimplemented.""" if self.__watcher is not None: self.__cancel_task() self.__executor.shutdown() super().onDeleteWidget() @Slot(object) def __handle_result(self, f): # type: (qconcurrent.Future[pd.DataFrame]) -> None assert f.done() assert f is self.__watcher.future() self.__watcher = None self.__clear_running_state() try: df = f.result() assert isinstance(df, pd.DataFrame) except pd.errors.EmptyDataError: df = pd.DataFrame({}) except Exception as e: # pylint: disable=broad-except self.__set_error_state(e) df = None else: self.__clear_error_state() if df is not None: table = pandas_to_table(df) else: table = None self.send("Data Frame", df) self.send('Data', table) self._update_status_messages(table) def _update_status_messages(self, data): if data is None: return def pluralize(seq): return "s" if len(seq) != 1 else "" summary = ("{n_instances} row{plural_1}, " "{n_features} feature{plural_2}, " "{n_meta} meta{plural_3}").format( n_instances=len(data), plural_1=pluralize(data), n_features=len(data.domain.attributes), plural_2=pluralize(data.domain.attributes), n_meta=len(data.domain.metas), plural_3=pluralize(data.domain.metas)) self.summary_text.setText(summary) def itemsFromSettings(self): # type: () -> List[Tuple[str, Options]] """ Return items from local history. """ s = self._local_settings() items_ = QSettings_readArray(s, "recent", OWCSVFileImport.SCHEMA) items = [] # type: List[Tuple[str, Options]] for item in items_: path = item.get("path", "") if not path: continue opts_json = item.get("options", "") try: opts = Options.from_dict(json.loads(opts_json)) except (csv.Error, LookupError, TypeError, json.JSONDecodeError): _log.error("Could not reconstruct options for '%s'", path, exc_info=True) pass else: items.append((path, opts)) return items[::-1] def _restoreState(self): # Restore the state. Merge session (workflow) items with the # local history. model = self.import_items_model # local history items = self.itemsFromSettings() # stored session items sitems = [] for p, m in self._session_items: try: item_ = (p, Options.from_dict(m)) except (csv.Error, LookupError) as e: # Is it better to fail then to lose a item slot? _log.error("Failed to restore '%s'", p, exc_info=True) else: sitems.append(item_) items = sitems + items items = unique(items, key=lambda t: pathnormalize(t[0])) curr = self.recent_combo.currentIndex() if curr != -1: currentpath = self.recent_combo.currentData(ImportItem.PathRole) else: currentpath = None for path, options in items: item = ImportItem.fromPath(path) item.setOptions(options) model.appendRow(item) if currentpath is not None: idx = self.recent_combo.findData(currentpath, ImportItem.PathRole) if idx != -1: self.recent_combo.setCurrentIndex(idx)
class AttrsWidget(QObject): error = pyqtSignal(str) def __init__(self, view): QObject.__init__(self, view) self.view = view delegate = MyDelegate(self.view, self) self.view.setItemDelegate(delegate) self.model = QStandardItemModel() self.view.setModel(self.model) self.current_node = None self.model.itemChanged.connect(self._item_changed) self.view.header().setSectionResizeMode(1) # Context menu self.view.setContextMenuPolicy(Qt.CustomContextMenu) self.view.customContextMenuRequested.connect(self.showContextMenu) copyaction = QAction("&Copy Value", self.model) copyaction.triggered.connect(self._copy_value) self._contextMenu = QMenu() self._contextMenu.addAction(copyaction) def _item_changed(self, item): attr, dv = item.data(Qt.UserRole) print("Item changed", attr, dv) try: self.current_node.set_attribute(attr, dv) except Exception as ex: self.error.emit(ex) raise finally: #self.reload() pass def showContextMenu(self, position): item = self.get_current_item() if item: self._contextMenu.exec_(self.view.mapToGlobal(position)) def get_current_item(self, col_idx=0): idx = self.view.currentIndex() return self.model.item(idx.row(), col_idx) def _copy_value(self, position): it = self.get_current_item(1) if it: QApplication.clipboard().setText(it.text()) def clear(self): self.model.clear() def reload(self): self.show_attrs(self.current_node) def show_attrs(self, node): self.current_node = node self.model.clear() if self.current_node: self._show_attrs() self.view.expandAll() def _show_attrs(self): attrs = self.get_all_attrs() self.model.setHorizontalHeaderLabels(['Attribute', 'Value', 'DataType']) for attr, dv in attrs: if attr == ua.AttributeIds.DataType: string = data_type_to_string(dv) elif attr in (ua.AttributeIds.AccessLevel, ua.AttributeIds.UserAccessLevel, ua.AttributeIds.WriteMask, ua.AttributeIds.UserWriteMask, ua.AttributeIds.EventNotifier): attr_name = attr.name if attr_name.startswith("User"): attr_name = attr_name[4:] attr_enum = getattr(ua, attr_name) string = ", ".join([e.name for e in attr_enum.parse_bitfield(dv.Value.Value)]) else: string = variant_to_string(dv.Value) name_item = QStandardItem(attr.name) vitem = QStandardItem(string) vitem.setData((attr, dv), Qt.UserRole) self.model.appendRow([name_item, vitem, QStandardItem(dv.Value.VariantType.name)]) # special case for Value, we want to show timestamps if attr == ua.AttributeIds.Value: string = val_to_string(dv.ServerTimestamp) name_item.appendRow([QStandardItem("Server Timestamp"), QStandardItem(string), QStandardItem(ua.VariantType.DateTime.name)]) string = val_to_string(dv.SourceTimestamp) name_item.appendRow([QStandardItem("Source Timestamp"), QStandardItem(string), QStandardItem(ua.VariantType.DateTime.name)]) def get_all_attrs(self): attrs = [attr for attr in ua.AttributeIds] try: dvs = self.current_node.get_attributes(attrs) except Exception as ex: self.error.emit(ex) raise res = [] for idx, dv in enumerate(dvs): if dv.StatusCode.is_good(): res.append((attrs[idx], dv)) res.sort() return res
class ReferenceSpectraDialog(QDialog): fits_picked = pyqtSignal(str) def __init__(self, database, main_spectrum = None): super(ReferenceSpectraDialog, self).__init__() self.main_spectrum = main_spectrum self.ui = Ui_ReferenceSpectraDialog() self.ui.setupUi(self) self.reference_catalogues = ReferenceCatalogues(database) self.full_model = QStandardItemModel() self.catalogues_model = QStandardItemModel() self.ui.catalogue.setModel(self.catalogues_model) self.ui.catalogue.currentTextChanged.connect(lambda txt: self.populate()) for catname, cat in self.reference_catalogues.catalogues.items(): row = QStandardItem(catname) row.setData(cat) self.catalogues_model.appendRow(row) self.model = QSortFilterProxyModel() self.model.setSourceModel(self.full_model) self.model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.model.setFilterKeyColumn(0) self.ui.entries.setModel(self.model) self.ui.type_filter.currentTextChanged.connect(lambda txt: self.model.setFilterWildcard("{}*".format(txt) ) ) self.ui.buttonBox.button(QDialogButtonBox.Open).setEnabled(False) self.ui.entries.selectionModel().selectionChanged.connect(lambda selected, deselected: self.ui.buttonBox.button(QDialogButtonBox.Open).setEnabled(len(selected.indexes()) > 0) ) self.accepted.connect(self.load_fits) self.populate() def set_main_spectrum(self, spectrum): self.main_spectrum = spectrum def populate(self): self.full_model.clear() catalogue = self.catalogues_model.item(self.ui.catalogue.currentIndex()).data() self.full_model.setHorizontalHeaderLabels(['Spectral Type']) entries = self.reference_catalogues.spectra(catalogue['name']) self.ui.type_filter.clear() self.ui.type_filter.addItem('') self.ui.type_filter.addItems( sorted(set([i['sptype'] for i in entries])) ) for entry in entries: item = QStandardItem(entry['sptype']) item.setData(entry) self.full_model.appendRow(item) def load_fits(self): original_index = self.model.mapToSource(self.ui.entries.selectionModel().selectedIndexes()[0]) entry = self.full_model.item(original_index.row()).data() self.fits_picked.emit(self.reference_catalogues.fits(entry)) def setup_menu(self, toolbar, axes, settings): self.current_line = None reference_action = QtCommons.addToolbarPopup(toolbar, "Reference") reference_action.menu().addAction("Reference library", lambda: self.show()) reference_action.menu().addAction("Load from FITS file", lambda: open_file_sticky('Open Reference Profile', FITS_EXTS, lambda f: self.__open_reference(f[0], axes), settings, REFERENCE )) self.close_action = reference_action.menu().addAction("Close", lambda: self.__close_reference(axes)) self.close_action.setEnabled(False) self.fits_picked.connect(lambda f: self.__open_reference(f, axes)) self.blackbody_menu = blackbody.BlackBodyAction(lambda bb: self.blackbody(bb, axes), reference_action.menu()) return reference_action.menu() def blackbody(self, blackbody, axes): self.__open(blackbody.spectrum(), axes) def __open_reference(self, file, axes): fits_spectrum = FitsSpectrum(fits.open(file)) self.__open(fits_spectrum.spectrum, axes) def __open(self, spectrum, axes): self.__close_reference(axes) if spectrum.dispersion() < 0.4 and spectrum.dispersion() > 0: spectrum.resample(spectrum.dispersion() /0.4) if(self.main_spectrum): print("Cutting spectrum: {0}, {1}".format(self.main_spectrum.wavelengths[0], self.main_spectrum.wavelengths[-1])) spectrum.cut_lambda(self.main_spectrum.wavelengths[0], self.main_spectrum.wavelengths[-1]) spectrum.normalize_to_max() self.current_line = Line2D(spectrum.wavelengths, spectrum.fluxes, color='gray') axes.add_line(self.current_line) axes.figure.canvas.draw() self.close_action.setEnabled(True) def __close_reference(self, axes): self.close_action.setEnabled(False) if self.current_line: try: # TODO: verify self.current_line.remove() self.current_line = None axes.figure.canvas.draw() except: pass
class E5NetworkMonitor(QDialog, Ui_E5NetworkMonitor): """ Class implementing a network monitor dialog. """ _monitor = None @classmethod def instance(cls, networkAccessManager): """ Class method to get a reference to our singleton. @param networkAccessManager reference to the network access manager (QNetworkAccessManager) @return reference to the network monitor singleton (E5NetworkMonitor) """ if cls._monitor is None: cls._monitor = E5NetworkMonitor(networkAccessManager) return cls._monitor @classmethod def closeMonitor(cls): """ Class method to close the monitor dialog. """ if cls._monitor is not None: cls._monitor.close() def __init__(self, networkAccessManager, parent=None): """ Constructor @param networkAccessManager reference to the network access manager (QNetworkAccessManager) @param parent reference to the parent widget (QWidget) """ super(E5NetworkMonitor, self).__init__(parent) self.setupUi(self) self.__requestHeaders = QStandardItemModel(self) self.__requestHeaders.setHorizontalHeaderLabels( [self.tr("Name"), self.tr("Value")]) self.requestHeadersList.setModel(self.__requestHeaders) self.requestHeadersList.horizontalHeader().setStretchLastSection(True) self.requestHeadersList.doubleClicked.connect(self.__showHeaderDetails) self.__replyHeaders = QStandardItemModel(self) self.__replyHeaders.setHorizontalHeaderLabels( [self.tr("Name"), self.tr("Value")]) self.responseHeadersList.setModel(self.__replyHeaders) self.responseHeadersList.horizontalHeader().setStretchLastSection(True) self.responseHeadersList.doubleClicked.connect( self.__showHeaderDetails) self.requestsList.horizontalHeader().setStretchLastSection(True) self.requestsList.verticalHeader().setMinimumSectionSize(-1) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterKeyColumn(-1) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.removeButton.clicked.connect(self.requestsList.removeSelected) self.removeAllButton.clicked.connect(self.requestsList.removeAll) self.__model = E5RequestModel(networkAccessManager, self) self.__proxyModel.setSourceModel(self.__model) self.requestsList.setModel(self.__proxyModel) self.__proxyModel.rowsInserted.connect( self.requestsList.scrollToBottom) self.requestsList.selectionModel()\ .currentChanged[QModelIndex, QModelIndex]\ .connect(self.__currentChanged) fm = self.fontMetrics() em = fm.width("m") self.requestsList.horizontalHeader().resizeSection(0, em * 5) self.requestsList.horizontalHeader().resizeSection(1, em * 20) self.requestsList.horizontalHeader().resizeSection(3, em * 5) self.requestsList.horizontalHeader().resizeSection(4, em * 15) self.__headersDlg = None def closeEvent(self, evt): """ Protected method called upon closing the dialog. @param evt reference to the close event object (QCloseEvent) """ self.__class__._monitor = None super(E5NetworkMonitor, self).closeEvent(evt) def reject(self): """ Public slot to close the dialog with a Reject status. """ self.__class__._monitor = None super(E5NetworkMonitor, self).reject() def __currentChanged(self, current, previous): """ Private slot to handle a change of the current index. @param current new current index (QModelIndex) @param previous old current index (QModelIndex) """ self.__requestHeaders.setRowCount(0) self.__replyHeaders.setRowCount(0) if not current.isValid(): return row = self.__proxyModel.mapToSource(current).row() req = self.__model.requests[row].request for header in req.rawHeaderList(): self.__requestHeaders.insertRows(0, 1, QModelIndex()) self.__requestHeaders.setData( self.__requestHeaders.index(0, 0), str(header, "utf-8")) self.__requestHeaders.setData( self.__requestHeaders.index(0, 1), str(req.rawHeader(header), "utf-8")) self.__requestHeaders.item(0, 0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.__requestHeaders.item(0, 1).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) for header in self.__model.requests[row].replyHeaders: self.__replyHeaders.insertRows(0, 1, QModelIndex()) self.__replyHeaders.setData( self.__replyHeaders.index(0, 0), header[0]) self.__replyHeaders.setData( self.__replyHeaders.index(0, 1), header[1]) self.__replyHeaders.item(0, 0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.__replyHeaders.item(0, 1).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) def __showHeaderDetails(self, index): """ Private slot to show a dialog with the header details. @param index index of the entry to show (QModelIndex) """ if not index.isValid(): return headerList = self.sender() if headerList is None: return row = index.row() name = headerList.model().data(headerList.model().index(row, 0)) value = headerList.model().data(headerList.model().index(row, 1)) if self.__headersDlg is None: from .E5NetworkHeaderDetailsDialog import \ E5NetworkHeaderDetailsDialog self.__headersDlg = E5NetworkHeaderDetailsDialog(self) self.__headersDlg.setData(name, value) self.__headersDlg.show()
class AttrsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.attrView.setModel(self.model) self.window.ui.attrView.header().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_attrs) self.window.ui.treeView.clicked.connect(self.show_attrs) self.window.ui.attrRefreshButton.clicked.connect(self.show_attrs) # Context menu self.window.ui.attrView.setContextMenuPolicy(Qt.CustomContextMenu) self.window.ui.attrView.customContextMenuRequested.connect(self.showContextMenu) copyaction = QAction("&Copy Value", self.model) copyaction.triggered.connect(self._copy_value) self._contextMenu = QMenu() self._contextMenu.addAction(copyaction) def showContextMenu(self, position): item = self.get_current_item() if item: self._contextMenu.exec_(self.window.ui.attrView.mapToGlobal(position)) def get_current_item(self, col_idx=0): idx = self.window.ui.attrView.currentIndex() return self.model.item(idx.row(), col_idx) def _copy_value(self, position): it = self.get_current_item(1) if it: QApplication.clipboard().setText(it.text()) def clear(self): self.model.clear() def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None node = self.window.get_current_node(idx) self.model.clear() if node: self._show_attrs(node) def _show_attrs(self, node): try: attrs = self.uaclient.get_all_attrs(node) except Exception as ex: self.window.show_error(ex) raise self.model.setHorizontalHeaderLabels(['Attribute', 'Value']) for k, v in attrs.items(): if isinstance(v, (ua.NodeId)): v = str(v) elif isinstance(v, (ua.QualifiedName, ua.LocalizedText)): v = v.to_string() elif isinstance(v, Enum): v = repr(v) elif isinstance(v, ua.DataValue): v = repr(v) else: v = str(v) self.model.appendRow([QStandardItem(k), QStandardItem(v)])
class DataChangeUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self._subhandler = DataChangeHandler() self._subscribed_nodes = [] self.model = QStandardItemModel() self.window.ui.subView.setModel(self.model) self.window.ui.subView.horizontalHeader().setSectionResizeMode(1) self.window.ui.actionSubscribeDataChange.triggered.connect(self._subscribe) self.window.ui.actionUnsubscribeDataChange.triggered.connect(self._unsubscribe) # populate contextual menu self.window.ui.treeView.addAction(self.window.ui.actionSubscribeDataChange) self.window.ui.treeView.addAction(self.window.ui.actionUnsubscribeDataChange) # handle subscriptions self._subhandler.data_change_fired.connect(self._update_subscription_model, type=Qt.QueuedConnection) # accept drops self.model.canDropMimeData = self.canDropMimeData self.model.dropMimeData = self.dropMimeData def canDropMimeData(self, mdata, action, row, column, parent): return True def dropMimeData(self, mdata, action, row, column, parent): node = self.uaclient.client.get_node(mdata.text()) self._subscribe(node) return True def clear(self): self._subscribed_nodes = [] self.model.clear() def show_error(self, *args): self.window.show_error(*args) @trycatchslot def _subscribe(self, node=None): if not isinstance(node, Node): node = self.window.get_current_node() if node is None: return if node in self._subscribed_nodes: logger.warning("allready subscribed to node: %s ", node) return self.model.setHorizontalHeaderLabels(["DisplayName", "Value", "Timestamp"]) text = str(node.get_display_name().Text) row = [QStandardItem(text), QStandardItem("No Data yet"), QStandardItem("")] row[0].setData(node) self.model.appendRow(row) self._subscribed_nodes.append(node) self.window.ui.subDockWidget.raise_() try: self.uaclient.subscribe_datachange(node, self._subhandler) except Exception as ex: self.window.show_error(ex) idx = self.model.indexFromItem(row[0]) self.model.takeRow(idx.row()) raise @trycatchslot def _unsubscribe(self): node = self.window.get_current_node() if node is None: return self.uaclient.unsubscribe_datachange(node) self._subscribed_nodes.remove(node) i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: self.model.removeRow(i) i += 1 def _update_subscription_model(self, node, value, timestamp): i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: it = self.model.item(i, 1) it.setText(value) it_ts = self.model.item(i, 2) it_ts.setText(timestamp) i += 1
class HelpWebSearchWidget(E5ClearableLineEdit): """ Class implementing a web search widget for the web browser. @signal search(QUrl) emitted when the search should be done """ search = pyqtSignal(QUrl) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(HelpWebSearchWidget, self).__init__(parent) from E5Gui.E5LineEdit import E5LineEdit from E5Gui.E5LineEditButton import E5LineEditButton from .OpenSearch.OpenSearchManager import OpenSearchManager self.__mw = parent self.__openSearchManager = OpenSearchManager(self) self.__openSearchManager.currentEngineChanged.connect( self.__currentEngineChanged) self.__currentEngine = "" self.__enginesMenu = QMenu(self) self.__engineButton = E5LineEditButton(self) self.__engineButton.setMenu(self.__enginesMenu) self.addWidget(self.__engineButton, E5LineEdit.LeftSide) self.__searchButton = E5LineEditButton(self) self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png")) self.addWidget(self.__searchButton, E5LineEdit.LeftSide) self.__model = QStandardItemModel(self) self.__completer = QCompleter() self.__completer.setModel(self.__model) self.__completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.__completer.setWidget(self) self.__searchButton.clicked.connect(self.__searchButtonClicked) self.textEdited.connect(self.__textEdited) self.returnPressed.connect(self.__searchNow) self.__completer.activated[QModelIndex].connect( self.__completerActivated) self.__completer.highlighted[QModelIndex].connect( self.__completerHighlighted) self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu) self.__suggestionsItem = None self.__suggestions = [] self.__suggestTimer = None self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") self.__recentSearchesItem = None self.__recentSearches = [] self.__maxSavedSearches = 10 self.__engine = None self.__loadSearches() self.__setupCompleterMenu() self.__currentEngineChanged() def __searchNow(self): """ Private slot to perform the web search. """ searchText = self.text() if not searchText: return globalSettings = QWebSettings.globalSettings() if not globalSettings.testAttribute( QWebSettings.PrivateBrowsingEnabled): if searchText in self.__recentSearches: self.__recentSearches.remove(searchText) self.__recentSearches.insert(0, searchText) if len(self.__recentSearches) > self.__maxSavedSearches: self.__recentSearches = \ self.__recentSearches[:self.__maxSavedSearches] self.__setupCompleterMenu() url = self.__openSearchManager.currentEngine().searchUrl(searchText) self.search.emit(url) def __setupCompleterMenu(self): """ Private method to create the completer menu. """ if not self.__suggestions or \ (self.__model.rowCount() > 0 and self.__model.item(0) != self.__suggestionsItem): self.__model.clear() self.__suggestionsItem = None else: self.__model.removeRows(1, self.__model.rowCount() - 1) boldFont = QFont() boldFont.setBold(True) if self.__suggestions: if self.__model.rowCount() == 0: if not self.__suggestionsItem: self.__suggestionsItem = QStandardItem( self.tr("Suggestions")) self.__suggestionsItem.setFont(boldFont) self.__model.appendRow(self.__suggestionsItem) for suggestion in self.__suggestions: self.__model.appendRow(QStandardItem(suggestion)) if not self.__recentSearches: self.__recentSearchesItem = QStandardItem( self.tr("No Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) else: self.__recentSearchesItem = QStandardItem( self.tr("Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) for recentSearch in self.__recentSearches: self.__model.appendRow(QStandardItem(recentSearch)) view = self.__completer.popup() view.setFixedHeight(view.sizeHintForRow(0) * self.__model.rowCount() + view.frameWidth() * 2) self.__searchButton.setEnabled( bool(self.__recentSearches or self.__suggestions)) def __completerActivated(self, index): """ Private slot handling the selection of an entry from the completer. @param index index of the item (QModelIndex) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return self.__searchNow() def __completerHighlighted(self, index): """ Private slot handling the highlighting of an entry of the completer. @param index index of the item (QModelIndex) @return flah indicating a successful highlighting (boolean) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return False if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return False self.setText(index.data()) return True def __textEdited(self, txt): """ Private slot to handle changes of the search text. @param txt search text (string) """ if self.__suggestionsEnabled: if self.__suggestTimer is None: self.__suggestTimer = QTimer(self) self.__suggestTimer.setSingleShot(True) self.__suggestTimer.setInterval(200) self.__suggestTimer.timeout.connect(self.__getSuggestions) self.__suggestTimer.start() else: self.__completer.setCompletionPrefix(txt) self.__completer.complete() def __getSuggestions(self): """ Private slot to get search suggestions from the configured search engine. """ searchText = self.text() if searchText: self.__openSearchManager.currentEngine()\ .requestSuggestions(searchText) def __newSuggestions(self, suggestions): """ Private slot to receive a new list of suggestions. @param suggestions list of suggestions (list of strings) """ self.__suggestions = suggestions self.__setupCompleterMenu() self.__completer.complete() def __showEnginesMenu(self): """ Private slot to handle the display of the engines menu. """ self.__enginesMenu.clear() from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction engineNames = self.__openSearchManager.allEnginesNames() for engineName in engineNames: engine = self.__openSearchManager.engine(engineName) action = OpenSearchEngineAction(engine, self.__enginesMenu) action.setData(engineName) action.triggered.connect(self.__changeCurrentEngine) self.__enginesMenu.addAction(action) if self.__openSearchManager.currentEngineName() == engineName: action.setCheckable(True) action.setChecked(True) ct = self.__mw.currentBrowser() linkedResources = ct.linkedResources("search") if len(linkedResources) > 0: self.__enginesMenu.addSeparator() for linkedResource in linkedResources: url = QUrl(linkedResource.href) title = linkedResource.title mimetype = linkedResource.type_ if mimetype != "application/opensearchdescription+xml": continue if url.isEmpty(): continue if url.isRelative(): url = ct.url().resolved(url) if not title: if not ct.title(): title = url.host() else: title = ct.title() action = self.__enginesMenu.addAction( self.tr("Add '{0}'").format(title), self.__addEngineFromUrl) action.setData(url) action.setIcon(ct.icon()) self.__enginesMenu.addSeparator() self.__enginesMenu.addAction(self.__mw.searchEnginesAction()) if self.__recentSearches: self.__enginesMenu.addAction(self.tr("Clear Recent Searches"), self.clear) def __changeCurrentEngine(self): """ Private slot to handle the selection of a search engine. """ action = self.sender() if action is not None: name = action.data() self.__openSearchManager.setCurrentEngineName(name) def __addEngineFromUrl(self): """ Private slot to add a search engine given its URL. """ action = self.sender() if action is not None: url = action.data() if not isinstance(url, QUrl): return self.__openSearchManager.addEngine(url) def __searchButtonClicked(self): """ Private slot to show the search menu via the search button. """ self.__setupCompleterMenu() self.__completer.complete() def clear(self): """ Public method to clear all private data. """ self.__recentSearches = [] self.__setupCompleterMenu() super(HelpWebSearchWidget, self).clear() self.clearFocus() def preferencesChanged(self): """ Public method to handle the change of preferences. """ self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") if not self.__suggestionsEnabled: self.__suggestions = [] self.__setupCompleterMenu() def saveSearches(self): """ Public method to save the recently performed web searches. """ Preferences.Prefs.settings.setValue( 'Help/WebSearches', self.__recentSearches) def __loadSearches(self): """ Private method to load the recently performed web searches. """ searches = Preferences.Prefs.settings.value('Help/WebSearches') if searches is not None: self.__recentSearches = searches def openSearchManager(self): """ Public method to get a reference to the opensearch manager object. @return reference to the opensearch manager object (OpenSearchManager) """ return self.__openSearchManager def __currentEngineChanged(self): """ Private slot to track a change of the current search engine. """ if self.__openSearchManager.engineExists(self.__currentEngine): oldEngine = self.__openSearchManager.engine(self.__currentEngine) oldEngine.imageChanged.disconnect(self.__engineImageChanged) if self.__suggestionsEnabled: oldEngine.suggestions.disconnect(self.__newSuggestions) newEngine = self.__openSearchManager.currentEngine() if newEngine.networkAccessManager() is None: newEngine.setNetworkAccessManager(self.__mw.networkAccessManager()) newEngine.imageChanged.connect(self.__engineImageChanged) if self.__suggestionsEnabled: newEngine.suggestions.connect(self.__newSuggestions) self.setInactiveText(self.__openSearchManager.currentEngineName()) self.__currentEngine = self.__openSearchManager.currentEngineName() self.__engineButton.setIcon(QIcon(QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) self.__suggestions = [] self.__setupCompleterMenu() def __engineImageChanged(self): """ Private slot to handle a change of the current search engine icon. """ self.__engineButton.setIcon(QIcon(QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) def mousePressEvent(self, evt): """ Protected method called by a mouse press event. @param evt reference to the mouse event (QMouseEvent) """ if evt.button() == Qt.XButton1: self.__mw.currentBrowser().pageAction(QWebPage.Back).trigger() elif evt.button() == Qt.XButton2: self.__mw.currentBrowser().pageAction(QWebPage.Forward).trigger() else: super(HelpWebSearchWidget, self).mousePressEvent(evt)
class PlotsMath(QWidget): F_X = Qt.UserRole + 1 FITS_SPECTRUM = Qt.UserRole + 2 def __init__(self, settings, database, project=None): super(PlotsMath, self).__init__() self.ui = Ui_PlotsMath() self.ui.setupUi(self) self.settings = settings self.project=project self.plot = QtCommons.nestWidget(self.ui.plot, QMathPlotWidget()) self.reference_dialog = ReferenceSpectraDialog(database) self.reference_dialog.fits_picked.connect(self.open_fits) self.toolbar = QToolBar('Instrument Response Toolbar') open_btn = QtCommons.addToolbarPopup(self.toolbar, text="Open...", icon_file=':/new_open_20') open_file_action = open_btn.menu().addAction('FITS file') open_btn.menu().addAction('Reference library', self.reference_dialog.show) self.blackbody_menu = blackbody.BlackBodyAction(self.blackbody, open_btn.menu()) if project: save_result = QtCommons.addToolbarPopup(self.toolbar, text='Save', icon_file=':/save_20') save_result.menu().addAction('As File', lambda: QtCommons.save_file('Save Operation Result...', FITS_EXTS, lambda f: self.save(f[0]), project.path)) save_result.menu().addAction('As Instrument Response', self.save_project_instrument_response) open_file_action.triggered.connect(lambda: QtCommons.open_file('Open FITS Spectrum',FITS_EXTS, lambda f: self.open_fits(f[0]), project.path)) else: open_file_action.triggered.connect(lambda: open_file_sticky('Open FITS Spectrum',FITS_EXTS, lambda f: self.open_fits(f[0]), self.settings, CALIBRATED_PROFILE, [RAW_PROFILE])) self.toolbar.addAction(QIcon(':/save_20'), 'Save', lambda: save_file_sticky('Save Operation Result...', 'FITS file (.fit)', lambda f: self.save(f[0]), self.settings, MATH_OPERATION, [CALIBRATED_PROFILE])) self.toolbar.addAction('Set operand', self.set_operand) self.toolbar.addSeparator() self.toolbar.addAction(self.ui.actionZoom) self.ui.actionZoom.triggered.connect(self.start_zoom) self.toolbar.addAction(self.ui.actionReset_Zoom) self.ui.actionReset_Zoom.triggered.connect(self.reset_zoom) self.toolbar.addSeparator() self.operands_model = QStandardItemModel() self.ui.operands_listview.setModel(self.operands_model) remove_btn = QtCommons.addToolbarPopup(self.toolbar, text='Remove...') remove_btn.menu().addAction(self.ui.actionSelectPointsToRemove) remove_btn.menu().addAction("Before point", lambda: spectrum_trim_dialog(self.spectrum, 'before', self.plot.axes, lambda: self.draw(), self, before_removal=self.undo.save_undo)) remove_btn.menu().addAction("After point", lambda: spectrum_trim_dialog(self.spectrum, 'after', self.plot.axes, lambda: self.draw(), self, before_removal=self.undo.save_undo)) self.ui.clear_operands.clicked.connect(self.operands_model.clear) self.ui.remove_operand.clicked.connect(lambda: self.operands_model.removeRows(self.ui.operands_listview.selectionModel().selectedRows()[0].row(), 1)) self.operands_model.rowsInserted.connect(lambda: self.ui.clear_operands.setEnabled(self.operands_model.rowCount() > 0) ) self.operands_model.rowsRemoved.connect(lambda: self.ui.clear_operands.setEnabled(self.operands_model.rowCount() > 0) ) self.ui.operands_listview.selectionModel().selectionChanged.connect(lambda s, u: self.ui.remove_operand.setEnabled(len(s))) self.ui.actionSelectPointsToRemove.triggered.connect(self.pick_rm_points) self.undo = Undo(None, self.draw) self.undo.add_actions(self.toolbar) self.ui.spline_factor.valueChanged.connect(self.factor_valueChanged) self.ui.spline_degrees.valueChanged.connect(lambda v: self.draw()) self.ui.spline_factor_auto.toggled.connect(lambda v: self.draw()) self.ui.spline_factor_auto.toggled.connect(lambda v: self.ui.spline_factor.setEnabled(not v)) self.ui.execute.clicked.connect(self.execute_operation) self.plot.figure.tight_layout() def blackbody(self, blackbody): self.spectrum = blackbody.spectrum() self.spectrum_name = "Blackbody radiation for {0}".format(blackbody.kelvin) self.undo.set_spectrum(self.spectrum) self.spectrum.normalize_to_max() self.draw() def open_fits(self, filename): fits_file = fits.open(filename) fits_spectrum = FitsSpectrum(fits_file) self.spectrum_name = fits_spectrum.name() self.spectrum = fits_spectrum.spectrum self.undo.set_spectrum(self.spectrum) self.spectrum.normalize_to_max() if self.spectrum.dispersion() <0.4: print("dispersion too high ({}), reducing spectrum resolution".format(self.spectrum.dispersion())) self.spectrum.resample(self.spectrum.dispersion() / 0.4) self.draw() @pyqtSlot(float) def factor_valueChanged(self, f): self.draw() def pick_rm_points(self): self.plot.rm_element('zoom') self.plot.add_span_selector('pick_rm_points', lambda min,max: self.rm_points(min,max+1),direction='horizontal') def start_zoom(self): self.plot.rm_element('pick_rm_points') self.plot.select_zoom() def draw(self): self.ui.spline_degrees_value.setText("{}".format(self.ui.spline_degrees.value())) spline_factor = self.ui.spline_factor.value() if not self.ui.spline_factor_auto.isChecked() else None spline = UnivariateSpline(self.spectrum.wavelengths, self.spectrum.fluxes, k=self.ui.spline_degrees.value(), s=spline_factor) self.f_x = lambda x: spline(x) self.plot.plot(None, self.spectrum.wavelengths, self.spectrum.fluxes, '--', self.spectrum.wavelengths, spline(self.spectrum.wavelengths), '-') self.plot.figure.tight_layout() self.plot.figure.canvas.draw() def rm_points(self, wmin, wmax): self.undo.save_undo() x_min = self.spectrum.wavelength_index(max(self.spectrum.wavelengths[0], wmin)) x_max = self.spectrum.wavelength_index(min(self.spectrum.wavelengths[-1], wmax)) m=(self.spectrum.fluxes[x_max]-self.spectrum.fluxes[x_min])/(x_max-x_min) q = self.spectrum.fluxes[x_min] f = lambda x: x * m + q self.spectrum.fluxes[x_min:x_max] = np.fromfunction(f, self.spectrum.fluxes[x_min:x_max].shape) self.draw() def trim(self, direction): point = QInputDialog.getInt(None, 'Trim curve', 'Enter wavelength for trimming', self.spectrum.wavelengths[0] if direction == 'before' else self.spectrum.wavelengths[-1], self.spectrum.wavelengths[0], self.spectrum.wavelengths[-1]) if not point[1]: return self.undo.save_undo() if direction == 'before': self.spectrum.cut(start=self.spectrum.wavelength_index(point[0])) else: self.spectrum.cut(end=self.spectrum.wavelength_index(point[0])) self.reset_zoom() self.draw() def set_operand(self): item = QStandardItem(self.spectrum_name) item.setData(self.f_x, PlotsMath.F_X) item.setData(self.spectrum, PlotsMath.FITS_SPECTRUM) self.operands_model.appendRow(item) def execute_operation(self): max_wavelengths = lambda operands: np.arange(max([o[0].wavelengths[0] for o in operands]), min([o[0].wavelengths[-1] for o in operands])) datasets = lambda operands, wavelengths: [PlotsMath.__data(o[1], wavelengths) for o in operands] operands = [(self.operands_model.item(a).data(PlotsMath.FITS_SPECTRUM), self.operands_model.item(a).data(PlotsMath.F_X)) for a in np.arange(self.operands_model.rowCount())] def divide(operands): if len(operands) > 2: print("Division supports only 2 operands, truncating") wavelengths = max_wavelengths(operands[0:2]) datas = datasets(operands[0:2], wavelengths) return (wavelengths, datas[0]/datas[1]) def mean(operands): wavelengths = max_wavelengths(operands) mean_data = np.zeros(wavelengths.shape) for data in datasets(operands, wavelengths): mean_data += data return (wavelengths, mean_data/len(wavelengths)) operations = { 0: divide, 1: mean } try: wavelengths, data = operations[self.ui.operation_type.currentIndex()](operands) self.spectrum = Spectrum(data, wavelengths) self.spectrum.normalize_to_max() self.undo.set_spectrum(self.spectrum) self.ui.spline_degrees.setValue(5) self.ui.spline_factor.setValue(0) self.ui.spline_factor_auto.setChecked(False) self.draw() except IndexError: QMessageBox.warning(None, "Error", "Datasets are not compatible. Maybe you need to calibrate better, or use a different reference file") def save_project_instrument_response(self): name = QInputDialog.getText(self, 'Enter Name', 'Enter new instrument response name for saving') if name[1]: self.project.add_file(Project.INSTRUMENT_RESPONSES, lambda f: self.save(f), bare_name=name[0]) def save(self, filename): hdu = fits.PrimaryHDU( PlotsMath.__data(self.f_x, self.spectrum.wavelengths)) #hdu = fits.PrimaryHDU( self.spectrum.fluxes) fits_file = fits.HDUList([hdu]) hdu.header['CRPIX1'] = 1 hdu.header['CRVAL1'] = self.spectrum.wavelengths[0] hdu.header['CDELT1'] = self.spectrum.dispersion() hdu.writeto(filename, clobber=True) def reset_zoom(self): self.plot.reset_zoom(self.spectrum.wavelengths, self.spectrum.fluxes.min(), self.spectrum.fluxes.max()) def __data(f_x, xrange): return np.fromfunction(lambda x: f_x(x+xrange[0]), xrange.shape)
class CalibrateSpectrum(QWidget): def __init__(self, fits_file, settings, database, project=None): super(CalibrateSpectrum, self).__init__() self.project=project self.settings = settings self.fits_spectrum = FitsSpectrum(fits_file) self.fits_spectrum.spectrum.normalize_to_max() self.fits_file = fits_file self.ui = Ui_CalibrateSpectrum() self.ui.setupUi(self) self.toolbar = QToolBar('Calibration Toolbar') self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.ui.x_axis_pick.setMenu(QMenu()) self.ui.x_axis_pick.menu().addAction("Maximum from range").triggered.connect(lambda: self.pick_from_range('maximum')) self.ui.x_axis_pick.menu().addAction("Minimum from range").triggered.connect(lambda: self.pick_from_range('minimum')) self.ui.x_axis_pick.menu().addAction("Central value from range").triggered.connect(lambda: self.pick_from_range('central')) self.ui.wavelength_pick.clicked.connect(lambda: self.lines_dialog.show()) save_action = self.toolbar.addAction(QIcon(':/save_20'), 'Save', self.save_spectrum) self.spectrum_plot = QtCommons.nestWidget(self.ui.spectrum_plot_widget, QMathPlotWidget()) self.reference_spectra_dialog = ReferenceSpectraDialog(database) self.reference_spectra_dialog.setup_menu(self.toolbar, self.spectrum_plot.axes, settings) self.object_properties = ObjectProperties(self.fits_file, project=project) self.object_properties_dialog = ObjectPropertiesDialog(settings, self.object_properties) self.toolbar.addAction("Object properties", self.object_properties_dialog.show) self.calibration_model = QStandardItemModel() self.calibration_model.setHorizontalHeaderLabels(["x-axis", "wavelength", "error"]) self.ui.calibration_points.setModel(self.calibration_model) self.ui.calibration_points.selectionModel().selectionChanged.connect(lambda selected, deselected: self.ui.remove_calibration_point.setEnabled(len(selected.indexes()) > 0) ) self.ui.add_calibration_point.clicked.connect(self.add_calibration_point) self.ui.remove_calibration_point.setEnabled(False) self.ui.remove_calibration_point.clicked.connect(self.remove_calibration_point) if project and project.avg_dispersion(): self.ui.set_dispersion.setMenu(QMenu()) self.ui.set_dispersion.menu().addAction('From input value', self.calculate_calibration) self.ui.set_dispersion.menu().addAction('From Project', lambda: self.calculate_calibration(project.avg_dispersion())) else: self.ui.set_dispersion.clicked.connect(self.calculate_calibration) self.ui.point_is_star.toggled.connect(lambda checked: self.ui.wavelength_pick.setEnabled(not checked)) self.ui.point_is_star.toggled.connect(lambda checked: self.ui.point_wavelength.setEnabled(not checked)) self.fits_spectrum.plot_to(self.spectrum_plot.axes) self.toolbar.addSeparator() self.toolbar.addAction("Zoom", self.spectrum_plot.select_zoom) self.toolbar.addAction("Reset Zoom", lambda: self.spectrum_plot.reset_zoom(self.fits_spectrum.spectrum.wavelengths, self.fits_spectrum.spectrum.fluxes.min(), self.fits_spectrum.spectrum.fluxes.max())) self.toolbar.addSeparator() self.lines_dialog = LinesDialog(database, settings, self.spectrum_plot, enable_picker = False, selection_mode = 'single') self.lines_dialog.lines.connect(self.picked_line) hdu_calibration_points = [h for h in self.fits_file if h.name == FitsSpectrum.CALIBRATION_DATA] if len(hdu_calibration_points) > 0: for point in hdu_calibration_points[-1].data: self.add_calibration_point_data(point[0], point[1]) self.calculate_calibration() def picked_from_range(self, type, min, max): min=(self.fits_spectrum.spectrum.wavelength_index(min)) max=(self.fits_spectrum.spectrum.wavelength_index(max)) add_line = lambda x: self.spectrum_plot.add_line("x_axis_pick", self.fits_spectrum.spectrum.wavelengths[x], color='r') set_x_value = lambda x: self.ui.point_x_axis.setValue(x) if type != 'central': subplot = SelectPlottedPoints(self.fits_spectrum.spectrum.fluxes, min, max+1, self.settings, type) subplot.point.connect(add_line) subplot.point.connect(set_x_value) subplot.show() return point = min+(max-min)/2 self.ui.point_x_axis.setValue(point) set_x_value(point) add_line(point) def pick_from_range(self, type): self.spectrum_plot.add_span_selector('pick_x_axis', lambda min,max: self.picked_from_range(type, min, max),direction='horizontal') def remove_calibration_point(self): self.calibration_model.removeRow(self.ui.calibration_points.selectionModel().selectedIndexes()[0].row()) self.calculate_calibration() def add_calibration_point_data(self, x_value, wavelength): x_axis_item = QStandardItem("star" if x_value == 0 else "{}".format(x_value)) x_axis_item.setData(x_value) wavelength_item = QStandardItem("{:.2f}".format(wavelength)) wavelength_item.setData(wavelength) self.calibration_model.appendRow([x_axis_item, wavelength_item, QStandardItem("n/a")]) self.spectrum_plot.rm_element('x_axis_pick') def picked_line(self, lines): self.ui.point_wavelength.setValue(lines[0]['lambda']) def add_calibration_point(self): self.add_calibration_point_data(self.ui.point_x_axis.value(), 0 if self.ui.point_is_star.isChecked() else self.ui.point_wavelength.value()) self.calculate_calibration() def calibration_points(self): return [{'row': row, 'x': self.calibration_model.item(row, 0).data(), 'wavelength': self.calibration_model.item(row, 1).data()} for row in range(self.calibration_model.rowCount())] def calculate_calibration(self, dispersion = None): points_number = self.calibration_model.rowCount() self.ui.set_dispersion.setEnabled(points_number == 1) self.ui.dispersion.setEnabled(points_number == 1) if points_number == 0: self.fits_spectrum.reset() self.lines_dialog.set_picker_enabled(False) else: self.lines_dialog.set_picker_enabled(True) points = sorted(self.calibration_points(), key=lambda point: point['x']) self.fits_spectrum.calibrate(points, dispersion if dispersion else self.ui.dispersion.value() ) for row, value in [(p['row'], "{:.2f}".format( p['wavelength']-self.fits_spectrum.spectrum.wavelengths[p['x']])) for p in points]: self.calibration_model.item(row, 2).setText(value) self.ui.dispersion.setValue(self.fits_spectrum.spectrum.dispersion()) self.fits_spectrum.plot_to(self.spectrum_plot.axes) def save_spectrum(self): if not self.project: save_file_sticky('Save plot...', 'FITS file (.fit)', lambda f: self.save(f[0]), self.settings, CALIBRATED_PROFILE, [RAW_PROFILE]) return self.project.add_file(Project.CALIBRATED_PROFILE, object_properties = self.object_properties, on_added=self.save) def save(self, filename): self.fits_spectrum.save(filename, self.calibration_points())
class SubtitleEditor(SubTab): def __init__(self, filePath, subtitleData, parent = None): name = os.path.split(filePath)[1] super(SubtitleEditor, self).__init__(name, parent) self.__initWidgets() self.__initContextMenu() self._settings = SubSettings() self._filePath = filePath self._movieFilePath = None self._subtitleData = subtitleData self.refreshSubtitles() # Some signals self._subtitleData.fileChanged.connect(self.fileChanged) self._subtitleData.subtitlesAdded.connect(self._subtitlesAdded) self._subtitleData.subtitlesRemoved.connect(self._subtitlesRemoved) self._subtitleData.subtitlesChanged.connect(self._subtitlesChanged) self._model.itemChanged.connect(self._subtitleEdited) self.customContextMenuRequested.connect(self.showContextMenu) def __initWidgets(self): minimalSizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) # List of subtitles subListDelegate = SubListItemDelegate() self._model = QStandardItemModel(0, 3, self) self._model.setHorizontalHeaderLabels([_("Begin"), _("End"), _("Subtitle")]) self._subList = QTableView(self) self._subList.setModel(self._model) self._subList.setItemDelegateForColumn(0, subListDelegate) self._subList.setItemDelegateForColumn(1, subListDelegate) self._subList.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self._searchBar = SearchBar(self) self._searchBar.hide() # Top toolbar toolbar = QHBoxLayout() toolbar.setAlignment(Qt.AlignLeft) #toolbar.addWidget(someWidget....) toolbar.addStretch(1) # Main layout grid = QGridLayout() grid.setSpacing(10) grid.setContentsMargins(0, 3, 0, 0) grid.addLayout(toolbar, 0, 0, 1, 1) # stretch to the right grid.addWidget(self._subList, 1, 0) grid.addWidget(self._searchBar, 2, 0) self.setLayout(grid) def __initContextMenu(self): self._contextMenu = QMenu(self) self.setContextMenuPolicy(Qt.CustomContextMenu) af = ActionFactory(self) insertSub = af.create(title = _("&Insert subtitle"), icon = "list-add", connection = self.insertNewSubtitle) self._contextMenu.addAction(insertSub) insertSub = af.create(title = _("&Add subtitle"), icon = "list-add", connection = self.addNewSubtitle) self._contextMenu.addAction(insertSub) removeSub = af.create(title = _("&Remove subtitles"), icon = "list-remove", connection = self.removeSelectedSubtitles) self._contextMenu.addAction(removeSub) def _changeRowBackground(self, rowNo, bg): with DisableSignalling(self._model.itemChanged, self._subtitleEdited): for columnNo in range(self._model.columnCount()): item = self._model.item(rowNo, columnNo) item.setBackground(bg) def _changeItemData(self, item, val, role): with DisableSignalling(self._model.itemChanged, self._subtitleEdited): item.setData(val, role) def _handleIncorrectItem(self, item): with DisableSignalling(self._model.itemChanged, self._subtitleEdited): item.setData(False, CustomDataRoles.ErrorFlagRole) bg = item.background() rowNo = item.row() self._changeRowBackground(rowNo, Qt.red) QTimer.singleShot(600, lambda rowNo=rowNo, bg=bg: self._changeRowBackground(rowNo, bg)) def _subtitleEdited(self, item): modelIndex = item.index() column = modelIndex.column() subNo = modelIndex.row() errorFlag = item.data(CustomDataRoles.ErrorFlagRole) if errorFlag is True: self._handleIncorrectItem(item) else: # TODO: timeStart and timeEnd might be displayed in a frame format in a bright future. # Check it and create FrameTime properly in that case. # TODO: Maybe add column numbers to some kind of enum to avoid magic numbers? oldSubtitle = self.subtitles[subNo] newSubtitle = oldSubtitle.clone() if 0 == column: timeStart = item.data(CustomDataRoles.FrameTimeRole) newSubtitle.change(start = timeStart) elif 1 == column: timeEnd = item.data(CustomDataRoles.FrameTimeRole) newSubtitle.change(end = timeEnd) elif 2 == column: newSubtitle.change(text = item.text()) command = ChangeSubtitle(self.filePath, oldSubtitle, newSubtitle, subNo) self._subtitleData.execute(command) def _subtitlesAdded(self, path, subNos): if path != self.filePath: return subtitles = self.subtitles for subNo in subNos: row = createRow(subtitles[subNo]) self._model.insertRow(subNo, row) def _subtitlesRemoved(self, path, subNos): if path != self.filePath: return for subNo in subNos: self._model.removeRow(subNo) def _subtitlesChanged(self, path, subNos): if path != self.filePath: return for subNo in subNos: self.refreshSubtitle(subNo) def _createNewSubtitle(self, data, subNo): fps = data.fps # data is passed to avoid unnecessary copies minFrameTime = FrameTime(fps, frames = 1) # calculate correct minimum subtitle start time if subNo > 0: timeStart = data.subtitles[subNo - 1].end + minFrameTime else: timeStart = FrameTime(fps, frames = 0) # calculate correct maximum subtitle end time if subNo < data.subtitles.size(): try: timeEnd = data.subtitles[subNo].start - minFrameTime except SubException: timeEnd = FrameTime(fps, frames = 0) else: timeEnd = timeStart + FrameTime(fps, frames = 50) # add subtitle to DataModel sub = Subtitle(timeStart, timeEnd, "") command = AddSubtitle(self.filePath, subNo, sub) with DisableSignalling(self._subtitleData.subtitlesAdded, self._subtitlesAdded): self._subtitleData.execute(command) # create subtitle graphical representation in editor sub list row = createRow(sub) self._model.insertRow(subNo, row) index = self._model.index(subNo, 2) self._subList.clearSelection() self._subList.setCurrentIndex(index) self._subList.edit(index) def addNewSubtitle(self): data = self.data subNo = data.subtitles.size() indices = self._subList.selectedIndexes() if len(indices) > 0: rows = [index.row() for index in indices] subNo = max(rows) + 1 self._createNewSubtitle(data, subNo) def insertNewSubtitle(self): data = self.data subNo = 0 indices = self._subList.selectedIndexes() if len(indices) > 0: rows = [index.row() for index in indices] subNo = max(rows) self._createNewSubtitle(data, subNo) def removeSelectedSubtitles(self): indices = self._subList.selectedIndexes() if len(indices) > 0: rows = list(set([index.row() for index in indices])) command = RemoveSubtitles(self.filePath, rows) self._subtitleData.execute(command) if self._model.rowCount() > rows[-1]: self._subList.selectRow(rows[-1]) else: self._subList.selectRow(self._model.rowCount() - 1) def highlight(self): self._searchBar.show() self._searchBar.highlight() def showContextMenu(self): self._contextMenu.exec(QCursor.pos()) def changeInputEncoding(self, encoding): data = self._subtitleData.data(self.filePath) if encoding != data.inputEncoding: try: data.encode(encoding) except UnicodeDecodeError: message = QMessageBox( QMessageBox.Warning, _("Decoding error"), _("Cannot decode subtitles to '%s' encoding.\nPlease try different encoding.") % encoding, QMessageBox.Ok, self ) message.exec() except LookupError: message = QMessageBox(QMessageBox.Warning, _("Unknown encoding"), _("Unknown encoding: '%s'") % encoding, QMessageBox.Ok, self ) message.exec() else: # TODO: outputEncoding command = ChangeData(self.filePath, data, _("Input encoding: %s") % encoding) self._subtitleData.execute(command) def changeOutputEncoding(self, encoding): data = self._subtitleData.data(self.filePath) if encoding != data.outputEncoding: data.outputEncoding = encoding command = ChangeData(self.filePath, data, _("Output encoding: %s") % encoding) self._subtitleData.execute(command) def changeSubFormat(self, fmt): data = self._subtitleData.data(self.filePath) if data.outputFormat != fmt: data.outputFormat = fmt command = ChangeData(self.filePath, data) self._subtitleData.execute(command) def changeFps(self, fps): data = self.data if data.fps != fps: data.subtitles.changeFps(fps) data.fps = fps command = ChangeData(self.filePath, data, _("FPS: %s") % fps) self._subtitleData.execute(command) def changeVideoPath(self, path): data = self.data if data.videoPath != path: data.videoPath = path command = ChangeData(self.filePath, data, _("Video path: %s") % path) self._subtitleData.execute(command) def offset(self, seconds): data = self.data fps = data.subtitles.fps if fps is None: log.error(_("No FPS for '%s' (empty subtitles)." % self.filePath)) return ft = FrameTime(data.subtitles.fps, seconds=seconds) data.subtitles.offset(ft) command = ChangeData(self.filePath, data, _("Offset by: %s") % ft.toStr()) self._subtitleData.execute(command) def detectFps(self): data = self.data if data.videoPath is not None: fpsInfo = File.detectFpsFromMovie(data.videoPath) if data.videoPath != fpsInfo.videoPath or data.fps != fpsInfo.fps: data.videoPath = fpsInfo.videoPath data.subtitles.changeFps(fpsInfo.fps) data.fps = fpsInfo.fps command = ChangeData(self.filePath, data, _("Detected FPS: %s") % data.fps) self._subtitleData.execute(command) def fileChanged(self, filePath): if filePath == self._filePath: self.refreshSubtitles() def refreshSubtitle(self, subNo): sub = self.subtitles[subNo] self._model.removeRow(subNo) self._model.insertRow(subNo, createRow(sub)) def refreshSubtitles(self): self._model.removeRows(0, self._model.rowCount()) for sub in self.subtitles: self._model.appendRow(createRow(sub)) def updateTab(self): self.refreshSubtitles() def selectedSubtitles(self): rows = self.selectedRows() subtitleList = [self.subtitles[row] for row in rows] return subtitleList def selectedRows(self): indices = self._subList.selectedIndexes() # unique list rows = list(set([index.row() for index in indices])) rows.sort() return rows def selectRow(self, row): self._subList.selectRow(row) @property def filePath(self): return self._filePath @property def movieFilePath(self): return self._movieFilePath @property def data(self): return self._subtitleData.data(self.filePath) @property def subtitles(self): return self.data.subtitles @property def history(self): return self._subtitleData.history(self.filePath) @property def inputEncoding(self): return self.data.inputEncoding @property def outputEncoding(self): return self.data.outputEncoding @property def outputFormat(self): return self.data.outputFormat