예제 #1
1
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)
예제 #2
0
    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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
파일: Tool_wxpy.py 프로젝트: isanmu/MyTools
 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)
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
    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)
예제 #9
0
    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)
예제 #10
0
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)))
예제 #11
0
파일: main.py 프로젝트: PawelDabala/PyQT
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
예제 #12
0
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()
예제 #13
0
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 '')
예제 #14
0
    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)
예제 #15
0
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
예제 #16
0
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
예제 #17
0
 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
예제 #18
0
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()
예제 #19
0
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)
예제 #20
0
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
예제 #21
0
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
예제 #22
0
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)
예제 #23
0
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)))
예제 #24
0
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)
예제 #25
0
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)
예제 #26
0
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
예제 #27
0
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)
예제 #28
0
 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()
예제 #29
0
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)
예제 #30
0
 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)
예제 #31
0
	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()
예제 #32
0
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)
예제 #33
0
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!')
예제 #34
0
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)])
예제 #35
0
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)
예제 #36
0
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)
예제 #37
0
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()
예제 #39
0
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
예제 #40
0
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
예제 #41
0
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)
예제 #42
0
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)
예제 #43
0
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()
예제 #44
0
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()
예제 #45
0
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)
예제 #47
0
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
예제 #48
0
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
예제 #49
0
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()
예제 #50
0
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)])
예제 #51
0
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
예제 #52
0
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)
예제 #53
0
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)
예제 #54
0
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())
예제 #55
0
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