Example #1
0
class PrvTreeviewNest(QTreeView):
    def __init__(self):
        super(PrvTreeviewNest, self).__init__()

        loadUi('treeview_nest.ui')

        # row can be 0 even when it's more than 0.
        self._datamodel = QStandardItemModel(0, 2)
        self.setModel(self._datamodel)

        for i in range(4):
            self.add_widget(i + 1)

        self.show()

    def add_widget(self, n):
        std_item = QStandardItem('{}th item'.format(n))
        self._datamodel.setItem(n, 0, std_item)

        node_widget = QPushButton('{}th button'.format(n))
        qindex_widget = self._datamodel.index(n, 1, QModelIndex())
        self.setIndexWidget(qindex_widget, node_widget)

        if n == 2:
            std_item_child = QStandardItem('child')
            std_item.appendRow(std_item_child)

            node_widget_child = QPushButton('petit button')
            qindex_widget_child = self._datamodel.index(n, 1, QModelIndex())
            self.setIndexWidget(qindex_widget_child, node_widget_child)
Example #2
0
class PrvTreeviewNest(QTreeView):
    def __init__(self):
        super(PrvTreeviewNest, self).__init__()

        loadUi('treeview_nest.ui')

        # row can be 0 even when it's more than 0.
        self._datamodel = QStandardItemModel(0, 2)
        self.setModel(self._datamodel)

        for i in range(4):
            self.add_widget(i + 1)

        self.show()

    def add_widget(self, n):
        std_item = QStandardItem('{}th item'.format(n))
        self._datamodel.setItem(n, 0, std_item)

        node_widget = QPushButton('{}th button'.format(n))
        qindex_widget = self._datamodel.index(n, 1, QModelIndex())
        self.setIndexWidget(qindex_widget, node_widget)

        if n == 2:
            std_item_child = QStandardItem('child')
            std_item.appendRow(std_item_child)

            node_widget_child = QPushButton('petit button')
            qindex_widget_child = self._datamodel.index(n, 1, QModelIndex())
            self.setIndexWidget(qindex_widget_child, node_widget_child)
Example #3
0
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)

        model = QStandardItemModel(7, 4, self)
        for row in range(7):
            for column in range(4):
                item = QStandardItem(QString("%1").arg(row * 4 + column))
                model.setItem(row, column, item)

        self.tableView = QTableView()
        self.tableView.setModel(model)
        self.setCentralWidget(self.tableView)

        # 获取视图的项目选择模型
        selectionModel = self.tableView.selectionModel()

        # 定义左上角和右下角的索引,然后使用这两个索引创建选择
        topLeft = model.index(1, 1, QModelIndex())
        bottomRight = model.index(5, 2, QModelIndex())
        selection = QItemSelection(topLeft, bottomRight)

        # 使用指定的选择模式来选择项目
        selectionModel.select(selection, QItemSelectionModel.Select)

        self.mainToolBar.addAction(_fromUtf8("当前项目"), self.getCurrentItemData)
        self.mainToolBar.addAction(_fromUtf8("切换选择"), self.toggleSelection)
    def _show_mashups(self, mashups):
        self.switch_btn_apis.hide()
        self.switch_btn_mashups.hide()
        self.recommendLabel.hide()

        row = 0
        model = QStandardItemModel(len(mashups), 4)
        model.setColumnCount(4)
        for mashup in mashups:
            model.setData(model.index(row, 0), QVariant(get_mashup_name(mashup)))
            model.setData(model.index(row, 1), QVariant(mashup['title']))
            model.setData(model.index(row, 2), QVariant(mashup['self']))
            model.setData(model.index(row, 3), QVariant(mashup['description']))
            
            row += 1
        
        model.setHeaderData(0, Qt.Horizontal, QVariant("Workflow"))
        model.setHeaderData(1, Qt.Horizontal, QVariant("Short Description"))
        model.setHeaderData(2, Qt.Horizontal, QVariant("Provider"))
        model.setHeaderData(3, Qt.Horizontal, QVariant("Detailed Info"))

#        model.setHeaderData(0, Qt.Horizontal, QVariant("Info"))
#        model.setHeaderData(1, Qt.Horizontal, QVariant("Title"))
#        model.setHeaderData(2, Qt.Horizontal, QVariant("self"))
#        model.setHeaderData(3, Qt.Horizontal, QVariant("Description"))

        self.table.setModel(model)
        self.table.resizeColumnsToContents()
        self.add_btn.show()
    def _show_apis(self, apis, recommend=False):
        self.switch_btn_apis.hide()
        self.switch_btn_mashups.hide()
        self.recommendLabel.hide()

        row = 0
        model = QStandardItemModel(len(apis), 4)
        model.setColumnCount(4)
        for api in apis:
            model.setData(model.index(row, 0), QVariant(get_api_name(api)))
            model.setData(model.index(row, 1), QVariant(api['protocols']))
            model.setData(model.index(row, 2), QVariant(api['provider']))
            model.setData(model.index(row, 3), QVariant(api['version']))
            row += 1

        model.setHeaderData(0, Qt.Horizontal, QVariant("Module"))
        model.setHeaderData(1, Qt.Horizontal, QVariant("Protocol"))
        model.setHeaderData(2, Qt.Horizontal, QVariant("Provider"))
        model.setHeaderData(3, Qt.Horizontal, QVariant("Version"))

#        model.setHeaderData(0, Qt.Horizontal, QVariant("API"))
#        model.setHeaderData(1, Qt.Horizontal, QVariant("Protocols"))
#        model.setHeaderData(2, Qt.Horizontal, QVariant("Provider"))
#        model.setHeaderData(3, Qt.Horizontal, QVariant("Version"))

        self.table.setModel(model)
        self.table.resizeColumnsToContents()
        
        if recommend:
            self.recommendLabel.show()
        
        self.add_btn.show()
    def _show_related_mashups(self):
        self.switch_btn_apis.hide()
        self.switch_btn_mashups.hide()
        self.recommendLabel.hide()
        
        apis = []
        objs = []
        for mashup in self.related_mashups:
            apis.extend(mashup["related_mashups"])

        for api in apis:
            objs.append(api)
            mashups = self.data_source.mashups_by_api(api)
            objs.extend(mashups)

        row = 0
        model = QStandardItemModel(len(objs), 4)
        model.setColumnCount(4)
        for obj in objs:
            if obj.get('protocols'):
                model.setData(model.index(row, 0), QVariant(get_api_name(obj)))
                model.setData(model.index(row, 1), QVariant(obj['protocols']))
                model.setData(model.index(row, 2), QVariant(obj['provider']))
            else:
                model.setData(model.index(row, 3), QVariant(get_mashup_name(obj)))
            row += 1

        model.setHeaderData(0, Qt.Horizontal, QVariant("API"))
        model.setHeaderData(1, Qt.Horizontal, QVariant("Protocols"))
        model.setHeaderData(2, Qt.Horizontal, QVariant("Provider"))
        model.setHeaderData(3, Qt.Horizontal, QVariant("Mashup"))
        self.table.setModel(model)
        self.table.resizeColumnsToContents()
        self.switch_btn_apis.show()
    def init_view(self):
        self.table = QTableView(self)
        self.table.setMinimumSize(600, 600)

        manager = core.packagemanager.get_package_manager()
        package = manager.get_package("edu.cmu.sv.components", "1.0.0")
        modules = package.descriptors
        apis = []
        self.modules_to_upgrade = []
        for module in modules.values():
            api = self.data_source.api_by_id(module.name)
            if ("1.0" != module.version):
                apis.append(api)
                self.modules_to_upgrade.append(module)

        table_model = QStandardItemModel(len(apis), 4)
        row = 0
        for api in apis:
            table_model.setData(table_model.index(row, 0), QVariant(api['id']))
            table_model.setData(table_model.index(row, 1), QVariant(api['category']))
            table_model.setData(table_model.index(row, 2), QVariant(api['version']))
            table_model.setData(table_model.index(row, 3), QVariant(api['description']))
            row += 1

        table_model.setHeaderData(0, Qt.Horizontal, QVariant("Link"))
        table_model.setHeaderData(1, Qt.Horizontal, QVariant("Category"))
        table_model.setHeaderData(2, Qt.Horizontal, QVariant("Version"))
        table_model.setHeaderData(3, Qt.Horizontal, QVariant("Description"))
        self.table.setModel(table_model)


        btn = QPushButton(self)
        btn.clicked.connect(self.upgrade_api)
        btn.setText("Upgrade")
        btn.move(600, 500)
Example #8
0
    def on_analyses_viewmodel_updated(self, view_model):
        qmodel = QStandardItemModel()
        qmodel.itemChanged.connect(self.on_qmodel_itemChanged)
        qmodel.setColumnCount(5)
        qmodel.setHorizontalHeaderItem(0, QStandardItem('Ion'))
        qmodel.setHorizontalHeaderItem(1, QStandardItem('Method'))
        qmodel.setHorizontalHeaderItem(2, QStandardItem('Start'))
        qmodel.setHorizontalHeaderItem(3, QStandardItem('End'))
        qmodel.setHorizontalHeaderItem(4, QStandardItem('Reason'))

        root = qmodel.invisibleRootItem()

        for ion, analysis in view_model.analyses.items():
            ion_name = QStandardItem(ion.name)
            ion_name.setData(ion, Qt.UserRole)
            method = QStandardItem(analysis.method)
            start = QStandardItem(str(round(analysis.range.start,2)))
            end = QStandardItem(str(round(analysis.range.end,2)))
            reason = QStandardItem(analysis.reason)

            root.appendRow([ion_name, method, start, end, reason])

        self.rangedTable.setModel(qmodel)
        self.rangedTable.setItemDelegateForColumn(1, MethodsComboDelegate(view_model.methods, self.rangedTable))
        for row in range(0, qmodel.rowCount()):
            self.rangedTable.openPersistentEditor(qmodel.index(row, 1))
        self.rangedTable.setColumnWidth(1, 95)
        self.rangedTable.setContextMenuPolicy(3)
        self.rangedTable.customContextMenuRequested.connect(self._context_menu_requested)
        shortcut = QShortcut(QKeySequence('Del'), self.rangedTable, self._delete_ion,self._delete_ion, context=0)
Example #9
0
class PrvTreeviewNest(QTreeView):
    def __init__(self):
        super(PrvTreeviewNest, self).__init__()

        loadUi('/home/user/yourproject/resource/treeview_nest.ui')

        # row can be 0 even when it's more than 0.
        self._datamodel = QStandardItemModel(0, 2)
        self.setModel(self._datamodel)

        for i in range(4):
            self._add_widget(i + 1)

        self.show()

    def _add_widget(self, n):
        item_toplevel = QStandardItem('{}th item'.format(n))
        self._datamodel.setItem(n, 0, item_toplevel)

        widget_toplevel = QPushButton('{}th button'.format(n))
        qindex_toplevel = self._datamodel.index(n, 1, QModelIndex())
        self.setIndexWidget(qindex_toplevel, widget_toplevel)

        if n == 2:
            item_child_col0 = QStandardItem('child col0')
            item_child_col1 = QStandardItem('child col1')
            #item_toplevel.appendRow(item_child_col0)

            item_toplevel.insertRow(0, [item_child_col0, item_child_col1])

            widget_child = QPushButton('child widget')
            qindex_child = item_child_col1.index()
            self.setIndexWidget(qindex_child, widget_child)
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        uic.loadUi("mainwindow.ui", self)

        model = QStandardItemModel(7, 4, self)
        for row in range(7):
            for column in range(4):
                item = QStandardItem(QString("%1").arg(row * 4 + column))
                model.setItem(row, column, item)

        self.tableView = QTableView()
        self.tableView.setModel(model)
        self.setCentralWidget(self.tableView)

        # 获取视图的项目选择模型
        selectionModel = self.tableView.selectionModel()

        # 定义左上角和右下角的索引,然后使用这两个索引创建选择
        topLeft = model.index(1, 1, QModelIndex())
        bottomRight = model.index(5, 2, QModelIndex())
        selection = QItemSelection(topLeft, bottomRight)

        # 使用指定的选择模式来选择项目
        selectionModel.select(selection, QItemSelectionModel.Select)

        self.mainToolBar.addAction(_fromUtf8("当前项目"), self.getCurrentItemData)
        self.mainToolBar.addAction(_fromUtf8("切换选择"), self.toggleSelection)

        self.connect(selectionModel,SIGNAL("selectionChanged(QItemSelection,QItemSelection)"),
                     self.updateSelection)
        self.connect(selectionModel, SIGNAL("currentChanged(QModelIndex,QModelIndex)"),
                self.changeCurrent)

        # 多个视图共享选择
        self.tableView2 = QTableView()
        self.tableView2.setWindowTitle("tableView2")
        self.tableView2.resize(400, 300)
        self.tableView2.setModel(model)
        self.tableView2.setSelectionModel(selectionModel)
        self.tableView2.show()

        # 使用自定义委托
        delegate = SpinBoxDelegate(self)
        self.tableView.setItemDelegate(delegate)
    def _show_related_apis(self):
        self.switch_btn_apis.hide()
        self.switch_btn_mashups.hide()
        self.recommendLabel.hide()
        
        row = 0
        objs = []
        for mashup in self.related_mashups:
            objs.append(mashup)
            for api in mashup["related_mashups"]:
                objs.append(api)
        #Combining similarity and related.
        similar_apis = self.data_source.search_api_similarity(self.highlighted_api)
        #return str(mashup['id'])[(len("http://www.programmableweb.com/mashup/")):]
        objs.append({'id': "http://www.programmableweb.com/mashup/Using-Similarity-Metric"})
        objs.extend(similar_apis)
        #Combining similarity and related.

        #http://localhost:9000/getReputation/John%20Lions
        model = QStandardItemModel(len(objs), 6)
        for obj in objs:
            if obj.get('protocols'):
                model.setData(model.index(row, 1), QVariant(get_api_name(obj)))
                model.setData(model.index(row, 2), QVariant(obj['protocols']))
                model.setData(model.index(row, 3), QVariant(obj['provider']))
                model.setData(model.index(row, 4), QVariant(obj['version']))
                trust  = requests.get('http://localhost:9000/getReputation/Luis Ramos').content
                #model.setData(model.index(row, 5), QVariant(str(random.random())))
                model.setData(model.index(row, 5), QVariant(trust))
            else:
                model.setData(model.index(row, 0), QVariant(get_mashup_name(obj)))
            row += 1

        model.setHeaderData(0, Qt.Horizontal, QVariant("Mashup"))
        model.setHeaderData(1, Qt.Horizontal, QVariant("API"))
        model.setHeaderData(2, Qt.Horizontal, QVariant("Protocols"))
        model.setHeaderData(3, Qt.Horizontal, QVariant("Provider"))
        model.setHeaderData(4, Qt.Horizontal, QVariant("Version"))
        model.setHeaderData(5, Qt.Horizontal, QVariant("Trust"))

        self.table.setModel(model)
        self.table.resizeColumnsToContents()
        self.switch_btn_mashups.show()
    def _show_related_apis(self):
        self.switch_btn_apis.hide()
        self.switch_btn_mashups.hide()
        self.recommendLabel.hide()
        
        row = 0
        objs = []
        for mashup in self.related_mashups:
            objs.append(mashup)
            for api in mashup["related_mashups"]:
                objs.append(api)
        #Combining similarity and related.
        similar_apis = self.data_source.search_api_similarity(self.highlighted_api)
        #return str(mashup['id'])[(len("http://www.programmableweb.com/mashup/")):]
        objs.append({'id': "http://www.programmableweb.com/mashup/Using-Similarity-Metric"})
        objs.extend(similar_apis)
        #Combining similarity and related.

        model = QStandardItemModel(len(objs), 5)
        for obj in objs:
            if obj.get('protocols'):
                model.setData(model.index(row, 1), QVariant(get_api_name(obj)))
                model.setData(model.index(row, 2), QVariant(obj['protocols']))
                model.setData(model.index(row, 3), QVariant(obj['provider']))
                model.setData(model.index(row, 4), QVariant(obj['version']))
            else:
                model.setData(model.index(row, 0), QVariant(get_mashup_name(obj)))
            row += 1

        model.setHeaderData(0, Qt.Horizontal, QVariant("Mashup"))
        model.setHeaderData(1, Qt.Horizontal, QVariant("API"))
        model.setHeaderData(2, Qt.Horizontal, QVariant("Protocols"))
        model.setHeaderData(3, Qt.Horizontal, QVariant("Provider"))
        model.setHeaderData(4, Qt.Horizontal, QVariant("Version"))

        self.table.setModel(model)
        self.switch_btn_mashups.show()
Example #13
0
class Menu(TreeViewTela):
    def __init__(self, altura, parent=None):
        super().__init__(altura, parent)

        self._model = QStandardItemModel(0, 1)
        self._model.setHorizontalHeaderItem(0, QStandardItem("Menu"))

        self._itemsFuncoes = []

    def setMenu(self, menu):
        self._adicionarSubMenu(menu, self._model.invisibleRootItem())
        self.setSelecionado(self._model.index(0,0))

    def getMenuSelecionado(self):
        sel = self.getSelecionado()
        s = ''
        while sel.isValid():
            s = '/' + sel.data() + s
            sel = sel.parent()
        return s

    def getFuncaoItem(self, item):
        for i in self._itemsFuncoes:
            if i.item == item.data():
                return i.funcao

    def getFuncaoItemSelecionado(self):
        return self.getFuncaoItem(self.getSelecionado())
        
    def _adicionarSubMenu(self, subMenu, pai):
        if isinstance(subMenu, ItemFuncao):
            item = QStandardItem(subMenu.item)
            self._itemsFuncoes.append(subMenu)
            pai.appendRow(item)
        elif isinstance(subMenu, str):
            pai.appendRow(QStandardItem(subMenu))
        elif isinstance(subMenu, (tuple, list)):
            for i in subMenu:
                self._adicionarSubMenu(i, pai)
        else:
            for key, value in subMenu.items():
                p = QStandardItem(key)
                pai.appendRow(p)
                self._adicionarSubMenu(value, p)
Example #14
0
class PrvTreeviewNest(QTreeView):
    def __init__(self):
        super(PrvTreeviewNest, self).__init__()

        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_prove'),
                              'resource', 'treeview_nest.ui')
        loadUi(ui_file, self)
        #loadUi('/home/n130s/data/Dropbox/ROS/groovy_quantal/catkin_ws/src/' + \
            #'rqt_prove/resource/treeview_nest.ui')

        # row can be 0 even when it's more than 0.
        self._datamodel = QStandardItemModel(0, 2)

        self.setModel(self._datamodel)

        for i in range(4):
            self._add_widget(i + 1)

        self.show()

    def _add_widget(self, n):
        item_toplevel = QStandardItem('{}th item'.format(n))
        self._datamodel.setItem(n, 0, item_toplevel)

        widget_toplevel = QPushButton('{}th button'.format(n))
        qindex_toplevel = self._datamodel.index(n, 1, QModelIndex())
        self.setIndexWidget(qindex_toplevel, widget_toplevel)

        if n == 2:
            item_child_col0 = QStandardItem('child col0')
            item_child_col1 = QStandardItem('child col1')
            #item_toplevel.appendRow(item_child_col0)

            item_child_col2 = QStandardItem('child col2')
            item_toplevel.insertRow(0, [item_child_col0, item_child_col1])
            #item_child_col0.insertColumn(0, [item_child_col1, item_child_col2])
            #item_child_col0.appendColumn([item_child_col1])  # appends another child

            widget_child = QPushButton('child widget')
            #qindex_child = self._datamodel.index(n, 1, QModelIndex())
            qindex_child = item_child_col1.index()
            #qindex_child = item_toplevel.index(0, 1, QModelIndex())
            self.setIndexWidget(qindex_child, widget_child)
Example #15
0
class Terminals(QWidget):
    ready = pyqtSignal(bool)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.ui = uic.loadUiType('terminal.ui')[0]()
        self.ui.setupUi(self)

        self.notifier = QSystemTrayIcon(QIcon('arrow-up-icon.png'), self)
        self.notifier.show()

        self.model = None
        self.mainloop = None
        self.delegate = None

    def test_display(self):
        if self.mainloop:
            self.mainloop.test_display()

    def update_device_config(self):
        if self.mainloop:
            self.mainloop.update_config()

    def terminal_open(self, addr):
        if self.mainloop:
            self.mainloop.terminal_open(addr)

    def terminal_close(self, addr):
        if self.mainloop:
            self.mainloop.terminal_close(addr)

    def start_mainloop(self):
        if self.mainloop is None:
            self.mainloop = Mainloop(parent=self)
            self.mainloop.ready.connect(self.on_mainloop_ready)
            self.mainloop.notify.connect(lambda title, msg: self.notifier.showMessage(title, msg))
            self.mainloop.start()

    def stop_mainloop(self):
        if self.mainloop:
            self.mainloop.state.disconnect()
            self.mainloop.ready.disconnect()
            self.mainloop.notify.disconnect()

            [self.ui.terminals.closePersistentEditor(self.model.index(row, 0))
             for row in xrange(self.model.rowCount())]
            self.mainloop.stop()

            self.mainloop = None

    def update_model(self):
        self.stop_mainloop()
        self.start_mainloop()

    def on_mainloop_ready(self, ok, titles):
        if ok:
            self.model = QStandardItemModel(len(titles), 1)
            [self.model.setItem(i, QStandardItem(str(addr))) for i, addr in enumerate(titles.keys())]

            self.delegate = TerminalDelegate(self.mainloop, titles)
            self.mainloop.report.connect(self.delegate.report)
            self.mainloop.state.connect(self.delegate.state)

            self.ui.terminals.setModel(self.model)
            self.ui.terminals.setItemDelegateForColumn(0, self.delegate)
            [self.ui.terminals.openPersistentEditor(self.model.index(row, 0))
             for row in xrange(self.model.rowCount())]

            self.mainloop.db.free_places_update.connect(self.ui.free_places.setValue)
            self.mainloop.update_config()
        else:
            self.model = None
            self.mainloop = None

        self.ready.emit(ok)
Example #16
0
class OWConfusionMatrix(widget.OWWidget):
    """Confusion matrix widget"""

    name = "Confusion Matrix"
    description = "Display a confusion matrix constructed from " \
                  "the results of classifier evaluations."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = [
        "Number of instances", "Proportion of predicted",
        "Proportion of actual"
    ]

    settingsHandler = settings.ClassValuesContextHandler()

    selected_learner = settings.Setting([0], schema_only=True)
    selection = settings.ContextSetting(set())
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    UserAdviceMessages = [
        widget.Message(
            "Clicking on cells or in headers outputs the corresponding "
            "data instances", "click_cell")
    ]

    def __init__(self):
        super().__init__()

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.vBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(box,
                                        self,
                                        "selected_learner",
                                        "learners",
                                        callback=self._learner_changed)
        box = gui.vBox(self.controlArea, "Show")

        gui.comboBox(box,
                     self,
                     "selected_quantity",
                     items=self.quantities,
                     callback=self._update)

        box = gui.vBox(self.controlArea, "Select")

        gui.button(box,
                   self,
                   "Select Correct",
                   callback=self.select_correct,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Select Misclassified",
                   callback=self.select_wrong,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Clear Selection",
                   callback=self.select_none,
                   autoDefault=False)

        self.outputbox = box = gui.vBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_predictions",
                     "Predictions",
                     callback=self._invalidate)
        gui.checkBox(box,
                     self,
                     "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit", "Send Selected",
                        "Send Automatically")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.setItemDelegate(BorderedItemDelegate(Qt.white))
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        """Initial size"""
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def _init_table(self, nclasses):
        item = self._item(0, 2)
        item.setData("Predicted", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignCenter)
        item.setFlags(Qt.NoItemFlags)

        self._set_item(0, 2, item)
        item = self._item(2, 0)
        item.setData("Actual", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
        item.setFlags(Qt.NoItemFlags)
        self.tableview.setItemDelegateForColumn(0, gui.VerticalItemDelegate())
        self._set_item(2, 0, item)
        self.tableview.setSpan(0, 2, 1, nclasses)
        self.tableview.setSpan(2, 0, nclasses, 1)

        font = self.tablemodel.invisibleRootItem().font()
        bold_font = QFont(font)
        bold_font.setBold(True)

        for i in (0, 1):
            for j in (0, 1):
                item = self._item(i, j)
                item.setFlags(Qt.NoItemFlags)
                self._set_item(i, j, item)

        for p, label in enumerate(self.headers):
            for i, j in ((1, p + 2), (p + 2, 1)):
                item = self._item(i, j)
                item.setData(label, Qt.DisplayRole)
                item.setFont(bold_font)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                if p < len(self.headers) - 1:
                    item.setData("br"[j == 1], BorderRole)
                    item.setData(QColor(192, 192, 192), BorderColorRole)
                self._set_item(i, j, item)

        hor_header = self.tableview.horizontalHeader()
        if len(' '.join(self.headers)) < 120:
            hor_header.setResizeMode(QHeaderView.ResizeToContents)
        else:
            hor_header.setDefaultSectionSize(60)
        self.tablemodel.setRowCount(nclasses + 3)
        self.tablemodel.setColumnCount(nclasses + 3)

    def set_results(self, results):
        """Set the input results."""

        prev_sel_learner = self.selected_learner.copy()
        self.clear()
        self.warning()
        self.closeContext()

        data = None
        if results is not None and results.data is not None:
            data = results.data

        if data is not None and not data.domain.has_discrete_class:
            self.warning("Confusion Matrix cannot show regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is None:
            self.report_button.setDisabled(True)
        else:
            self.report_button.setDisabled(False)

            nmodels = results.predicted.shape[0]
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = [
                    "Learner #{}".format(i + 1) for i in range(nmodels)
                ]

            self._init_table(len(class_values))
            self.openContext(data.domain.class_var)
            if not prev_sel_learner or prev_sel_learner[0] >= len(
                    self.learners):
                self.selected_learner[:] = [0]
            else:
                self.selected_learner[:] = prev_sel_learner
            self._update()
            self._set_selection()
            self.unconditional_commit()

    def clear(self):
        """Reset the widget, clear controls"""
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        self.learners = []

    def select_correct(self):
        """Select the diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        """Select the off-diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        """Reset selection"""
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        """Handle cell click event"""
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def commit(self):
        """Output data instances corresponding to selected cells"""
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            learner_name = self.learners[self.selected_learner[0]]
            predicted = self.results.predicted[self.selected_learner[0]]
            selected = [
                i for i, t in enumerate(zip(actual, predicted)) if t in indices
            ]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values)
                metas = metas + (var, )

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[self.selected_learner[0],
                                                   selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [
                    Orange.data.ContinuousVariable("p({})".format(value))
                    for value in class_var.values
                ]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M, ) + tuple(extra))
            domain = Orange.data.Domain(self.data.domain.attributes,
                                        self.data.domain.class_vars, metas)
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        indices = self.tableview.selectedIndexes()
        self.selection = {(ind.row() - 2, ind.column() - 2) for ind in indices}
        self.commit()

    def _set_selection(self):
        selection = QItemSelection()
        index = self.tableview.model().index
        for row, col in self.selection:
            sel = index(row + 2, col + 2)
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def _learner_changed(self):
        self._update()
        self._set_selection()
        self.commit()

    def _update(self):
        def _isinvalid(x):
            return isnan(x) or isinf(x)

        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            cmatrix = confusion_matrix(self.results, self.selected_learner[0])
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            n = len(cmatrix)
            diag = numpy.diag_indices(n)

            colors = cmatrix.astype(numpy.double)
            colors[diag] = 0
            if self.selected_quantity == 0:
                normalized = cmatrix.astype(numpy.int)
                formatstr = "{}"
                div = numpy.array([colors.max()])
            else:
                if self.selected_quantity == 1:
                    normalized = 100 * cmatrix / colsum
                    div = colors.max(axis=0)
                else:
                    normalized = 100 * cmatrix / rowsum[:, numpy.newaxis]
                    div = colors.max(axis=1)[:, numpy.newaxis]
                formatstr = "{:2.1f} %"
            div[div == 0] = 1
            colors /= div
            colors[diag] = normalized[diag] / normalized[diag].max()

            for i in range(n):
                for j in range(n):
                    val = normalized[i, j]
                    col_val = colors[i, j]
                    item = self._item(i + 2, j + 2)
                    item.setData(
                        "NA" if _isinvalid(val) else formatstr.format(val),
                        Qt.DisplayRole)
                    bkcolor = QColor.fromHsl(
                        [0, 240][i == j], 160,
                        255 if _isinvalid(col_val) else int(255 -
                                                            30 * col_val))
                    item.setData(QBrush(bkcolor), Qt.BackgroundRole)
                    item.setData("trbl", BorderRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            bold_font = self.tablemodel.invisibleRootItem().font()
            bold_font.setBold(True)

            def _sum_item(value, border=""):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                item.setData(border, BorderRole)
                item.setData(QColor(192, 192, 192), BorderColorRole)
                return item

            for i in range(n):
                self._set_item(n + 2, i + 2, _sum_item(int(colsum[i]), "t"))
                self._set_item(i + 2, n + 2, _sum_item(int(rowsum[i]), "l"))
            self._set_item(n + 2, n + 2, _sum_item(int(rowsum.sum())))

    def send_report(self):
        """Send report"""
        if self.results is not None and self.selected_learner:
            self.report_table(
                "Confusion matrix for {} (showing {})".format(
                    self.learners[self.selected_learner[0]],
                    self.quantities[self.selected_quantity].lower()),
                self.tableview)
Example #17
0
class ResultView(QWidget):
    """This class represent a search result view.
    """

    def __init__(self, filter='', attributes=[], resultlist=[], parent=None):
        """Initialize a result view for the `SearchPlugin`.

        :param filter: the filter applied on the search
        :type filter: string
        :param attributes: a list containing the attributes used in the
         search operation. Usually extracted from the `filter`.
        :type attributes: list
        :param resultlist: a list of `SmartDataObject` from the search
         operation.
        :type resultlist: list
        :param parent: the parent for this widget.
        :type parent: QWidget
        """
        super(ResultView, self).__init__(parent)
        self.setObjectName('ResultView')
        self.layout = QtGui.QVBoxLayout(self)

        # Only display the no-result message if resultlist is empty
        if len(resultlist) == 0:
            self.retranslate(all=False)
            self.onNoResult()
            return

        # The proxy model is used for sort and filter support
        self.proxymodel = QSortFilterProxyModel(self)
        self.proxymodel.setDynamicSortFilter(True)

        self.headerdata = ['dn']
        self.headerdata.extend(attributes)
        self.resultdata = resultlist

        # FIXME: should we create a custom item model ?
        self.model = QStandardItemModel(0, len(self.headerdata), parent=self)
        #self.model = ResultItemModel(self)
        #self.model = ResultItemModel(self.headerdata, self.resultdata, self)
        self.proxymodel.setSourceModel(self.model)

        self.resultview = QTreeView(self)
        self.resultview.setUniformRowHeights(True)
        self.resultview.setRootIsDecorated(False)
        self.resultview.setAlternatingRowColors(True)
        self.resultview.setSortingEnabled(True)

        self.resultview.setModel(self.proxymodel)

        # For right-click context menu
        self.resultview.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.resultview.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.layout.addWidget(self.resultview)
        # The filter box enables the user to filter the returned search
        # results. It becomes accessible with Ctrl-F (QKeySequence.Find)
        self.filterBox = ResultFilterWidget(self.headerdata, parent=self)
        self.filterBox.setVisible(False)

        self.layout.addWidget(self.filterBox)

        # We need to call the retranslate method before populating
        # the result data
        self.retranslate()
        #self.model.populateHeader(self.headerdata)
        #self.model.populateModel(self.resultdata)
        self.setHeaderData(self.headerdata)
        self.setResultData(self.resultdata)
        self.resultview.resizeColumnToContents(0)
        self.__createContextMenu()
        self.__connectSlots()

    def __connectSlots(self):
        """Connect signal and slots.
        """
        self.resultview.customContextMenuRequested.connect(
            self.onContextMenuRequested)
        self.filterBox.inputEdit.textChanged['QString'].connect(
            self.onFilterInputChanged)
        self.filterBox.columnBox.currentIndexChanged[int].connect(
            self.onFilterColumnChanged)

    def __getVSpacer(self):
        return QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding,
                                 QtGui.QSizePolicy.Minimum)

    def __createContextMenu(self):
        """Display the context menu.
        """
        self.contextMenu = QtGui.QMenu()
        self.contextMenuView = QtGui.QAction(self)
        self.contextMenuDelete = QtGui.QAction(self)
        self.contextMenuExport = QtGui.QAction(self)
        self.contextMenu.addAction(self.contextMenuView)
        self.contextMenu.addAction(self.contextMenuDelete)
        self.contextMenu.addAction(self.contextMenuExport)

        # Connect the context menu actions to the correct slots
        self.contextMenuView.triggered.connect(self.onViewItemsSelected)
        self.contextMenuDelete.triggered.connect(self.onDeleteItemsSelected)
        self.contextMenuExport.triggered.connect(self.onExportItemsSelected)

    def onNoResult(self):
        """Adds a styled *no result* message to the main layout.
        """
        font = QtGui.QFont()
        font.setBold(True)
        sadface = QtGui.QLabel(self)
        sadface.setPixmap(pixmapFromTheme('face-sad', ':/icons/48/face-sad'))
        noresult = QtGui.QLabel(self)
        noresult.setText(self.str_NO_RESULT)
        noresult.setFont(font)
        hlayout = QtGui.QHBoxLayout()
        hlayout.addItem(self.__getVSpacer())
        hlayout.addWidget(sadface)
        hlayout.addWidget(noresult)
        hlayout.addItem(self.__getVSpacer())

        self.layout.addLayout(hlayout)

    def setHeaderData(self, data=[]):
        """Populates the ``resultview`` model with header data.

        Parameters:

        - `data`: a list with header items. Usually this is the
          attributelist from the LDAP search.
        """
        i = 0
        for header in data:
            self.model.setHeaderData(i, QtCore.Qt.Horizontal, header)
            i += 1

    def setResultData(self, data=[]):
        """Populates the ``resultview`` model with result data.

        Parameters:

        - `data`: a list containing the SmartDataObjects representing
          items in the LDAP search result.
        """
        row = 0
        for obj in data:
            self.model.insertRow(row)
            col = 0
            for attr in self.headerdata:
                if self.isDistinguishedName(attr):
                    modelData = obj.getPrettyDN()
                elif self.isObjectClass(attr):
                    modelData = ','.join(obj.getObjectClasses())
                elif obj.hasAttribute(attr):
                    if obj.isAttributeBinary(attr):
                        modelData = self.str_BINARY_DATA
                    else:
                        modelData = ','.join(obj.getAttributeValueList(attr))

                self.model.setData(self.model.index(row, col), modelData)
                col += 1

            row += 1

    def isDistinguishedName(self, attr):
        """Returns ``True`` if `attr` is a distinguished name,
        ``False`` otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'dn'

    def isObjectClass(self, attr):
        """Returns ``True`` if `attr` is an object class, ``False``
        otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'objectclass'

    def onContextMenuRequested(self, point):
        """Display the context menu
        """

        # FIXME: In order to be able to export, delete and view search
        # result entries. We should make use of the various dialogs in
        # the Browser plugin. Unitl we have refactored the design in a
        # way that allow us to use these without accessing the browser
        # modules, we simple don't provide these options yet.
        return

        self.selection = self.resultview.selectedIndexes()

        deleteSupport = True
        exportSupport = True

        rowsselected = len(self.selection) / len(self.headerdata)

        if not rowsselected > 0:
            self.contextMenu.setEnabled(False)
            self.contextMenu.exec_(self.resultview.mapToGlobal(point))
            return

        self.contextMenu.setEnabled(True)
        # Look over at Browser plugin for implementation of
        # multiselect and operation support validation
        print rowsselected

        self.contextMenuView.setEnabled(True)
        if rowsselected == 1:
            self.contextMenuView.setText(self.str_VIEW_ITEM)
        else:
            self.contextMenuView.setText(self.str_VIEW_ITEMS)

        if deleteSupport:
            self.contextMenuDelete.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuDelete.setText(self.str_DELETE_ITEM)
            else:
                self.contextMenuDelete.setText(self.str_DELETE_ITEMS)

        if exportSupport:
            self.contextMenuExport.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuExport.setText(self.str_EXPORT_ITEM)
            else:
                self.contextMenuExport.setText(self.str_EXPORT_ITEMS)

        # Finally we execute the context menu
        self.contextMenu.exec_(self.resultview.mapToGlobal(point))

    def onViewItemsSelected(self):
        """Slot for the *view* context menu action.
        """
        raise NotImplementedError(
            'Need to implement a proper model for this to be supported')

    def onDeleteItemsSelected(self):
        """Slot for the *delete* context menu action.
        """
        msg = 'Delete from the Search Plugin is not implemented jet.'
        dialog = DeleteDialog(self, msg)
        dialog.setDeleteItems([])
        dialog.exec_()

    def onExportItemsSelected(self):
        """Slot for the 'export' context menu action.
        """
        msg = 'Export from the Search Plugin is not implemented jet.'
        dialog = ExportDialog(self, msg)
        # Only for proof of concept
        dialog.setExportData([])
        dialog.exec_()

    def onFilterBoxVisibilityChanged(self, visible):
        """Slot for the QKeySequence.Find.

        - `visible`: a boolean value indicating wether or not to toggle
          the filter box widget visibility on or off.
        """
        if visible:
            self.filterBox.setVisible(True)
            self.filterBox.inputEdit.setFocus()
        else:
            # I belive it's common practise to clear the filter when
            # the filter box is closed. This is at least the way the
            # filter boxes works for most webbrowsers.
            self.filterBox.inputEdit.clear()
            self.filterBox.setVisible(False)
            self.resultview.setFocus()

    def onFilterInputChanged(self, filter=''):
        """Slot for the filter input in the result filter widget.

        We get the selected syntax from the syntax combobox
        """
        # The PyQt4 QVariant is causing some problems here, when we try
        # to use the <combobox>.itemData directly, even though the data
        # holds valid QRexExp.PatternSyntax values.
        # We therefore need to explicitly make the QVariant and integer.
        i = self.filterBox.syntaxBox.currentIndex()
        syntaxIndex = self.filterBox.syntaxBox.itemData(i).toInt()[0]
        syntax = QtCore.QRegExp.PatternSyntax(syntaxIndex)
        # As of now we do filtering in a case insensitive way, until we
        # come up with a way to introduce case sensitivity selection in a
        # UI inexpensive way. We want to keep the filter widget as clean
        # and simple as possible.
        regex = QtCore.QRegExp(filter, QtCore.Qt.CaseInsensitive, syntax)
        self.proxymodel.setFilterRegExp(regex)

    def onFilterColumnChanged(self, index):
        """Slot for the column combobox in the filter box widget.
        """
        self.proxymodel.setFilterKeyColumn(index)

    def retranslate(self, all=True):
        """For dynamic translation support.
        """
        self.str_VIEW_ITEM = QtGui.QApplication.translate(
            'ResultView', 'View Item')
        self.str_VIEW_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'View Items')
        self.str_DELETE_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Delete Item')
        self.str_DELETE_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Delete Items')
        self.str_EXPORT_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Export Item')
        self.str_EXPORT_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Export Items')
        self.str_NO_RESULT = QtGui.QApplication.translate(
            'ResultView', 'Sorry, no result to display!')
        self.str_BINARY_DATA = QtGui.QApplication.translate(
            'ResultView', 'Binary Data')
        if all:
            self.filterBox.retranslate()
Example #18
0
class FormWidget(ui_formwidget.Ui_Form, WidgetBase):
    def __init__(self, parent=None):
        super(FormWidget, self).__init__(parent)
        self.setupUi(self)
        self.form = None

        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()
        self.formlayersmodel = QgsLayerModel(watchregistry=True)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.layerCombo.setModel(self.formlayers)
        self.useablewidgets.setModel(self.possiblewidgetsmodel)
        self.fieldList.setModel(self.fieldsmodel)

        self.userwidgets.setModel(self.widgetmodel)
        self.userwidgets.selectionModel().currentChanged.connect(self.load_widget)
        self.widgetmodel.rowsRemoved.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.rowsInserted.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.modelReset.connect(self.setwidgetconfigvisiable)

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.expressionButton.clicked.connect(self.opendefaultexpression)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        self.loadwidgettypes()

        self.formLabelText.textChanged.connect(self.form_name_changed)
        self.layerCombo.currentIndexChanged.connect(self.layer_updated)

        self.fieldList.currentIndexChanged.connect(self._save_current_widget)
        self.nameText.textChanged.connect(self._save_current_widget)
        self.useablewidgets.currentIndexChanged.connect(self._save_current_widget)
        self.useablewidgets.currentIndexChanged.connect(self.swapwidgetconfig)

        menu = QMenu("Field Actions")
        action = menu.addAction("Auto add all fields")
        action.triggered.connect(self.auto_add_fields)

        self.addWidgetButton.setMenu(menu)
        self.addWidgetButton.setPopupMode(QToolButton.MenuButtonPopup)

    def layer_updated(self, index):
        if not self.selected_layer:
            return

        self.updatefields(self.selected_layer)

    def form_name_changed(self, text):
        self.form.settings['label'] = self.formLabelText.text()
        self.treenode.emitDataChanged()

    def updatewidgetname(self, index):
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def opendefaultexpression(self):
        layer = self.form.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def formtabchanged(self, index):
        def setformpreview(form):
            item = self.frame_2.layout().itemAt(0)
            if item and item.widget():
                item.widget().setParent(None)

            featureform = FeatureForm.from_form(form, form.settings, None, {})
            from roam import defaults
            defaultwidgets = form.widgetswithdefaults()
            layer = form.QGISLayer
            try:
                values = {}
                feature = layer.getFeatures().next()
                defaultvalues = defaults.default_values(defaultwidgets, feature, layer)
                values.update(defaultvalues)
                featureform.bindvalues(values)
            except StopIteration:
                pass

            self.frame_2.layout().addWidget(featureform)

        if index == 1:
            self.form.settings['widgets'] = list(self.widgetmodel.widgets())
            setformpreview(self.form)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        widgets = self.widgetmodel.widgets()
        for widget in widgets:
            yield widget['field']

    def openformfolder(self, url):
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.form.folder))

    def loadwidgettypes(self):
        self.useablewidgets.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.useablewidgets.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.useablewidgets.blockSignals(False)

    def setwidgetconfigvisiable(self, *args):
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetConfigTabs.setVisible(haswidgets)

    def newwidget(self, field=None):
        """
        Create a new widget.  The default is a list.
        """
        mapping = {QVariant.String: "Text",
                   QVariant.Int: "Number",
                   QVariant.Double: "Number(Double)",
                   QVariant.ByteArray: "Image",
                   QVariant.Date: "Date",
                   QVariant.DateTime: "Date"}
        widget = {}
        if not field:
            field = self.fieldsmodel.index(0, 0).data(Qt.UserRole)
            if not field:
                return
            widget['field'] = field.name()
        else:
            widget['field'] = field.name()

        try:
            widget['widget'] = mapping[field.type()]
        except KeyError:
            widget['widget'] = 'Text'
        # Grab the first field.

        widget['name'] = field.name().replace("_", " ").title()

        currentindex = self.userwidgets.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.userwidgets.setCurrentIndex(index)

    def auto_add_fields(self):
        used = list(self.usedfields())
        for field in self.selected_layer.pendingFields():
            if field.name() in used:
                continue

            self.newwidget(field)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def set_project(self, project, treenode):
        super(FormWidget, self).set_project(project, treenode)
        self.formlayers.setSelectLayers(self.project.selectlayers)
        form = self.treenode.form
        self.form = form
        self.setform(self.form)

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            index = self.formlayers.index(0, 0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.userwidgets.setCurrentIndex(index)
            self.load_widget(index, None)

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.current_config_widget
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def load_widget(self, index, last):
        """
        Update the UI with the config for the current selected widget.
        """
        self.fieldList.blockSignals(True)
        self.nameText.blockSignals(True)
        self.useablewidgets.blockSignals(True)

        if last:
            self._save_widget(last)

        widget = index.data(Qt.UserRole)
        if not widget:
            self.fieldList.blockSignals(False)
            self.nameText.blockSignals(False)
            self.useablewidgets.blockSignals(False)
            return

        widgettype = widget['widget']
        field = widget['field']
        required = widget.setdefault('required', False)
        savevalue = widget.setdefault('rememberlastvalue', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)

        try:
            data = readonly[0]
        except IndexError:
            data = 'never'

        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)

        if not isinstance(default, dict):
            self.defaultvalueText.setText(default)
            self.defaultvalueText.setEnabled(True)
            self.expressionButton.setEnabled(True)
        else:
            # TODO Handle the more advanced default values.
            self.defaultvalueText.setText("Advanced default set in config")
            self.defaultvalueText.setEnabled(False)
            self.expressionButton.setEnabled(False)

        self.nameText.setText(name)
        self.requiredCheck.setChecked(required)
        self.savevalueCheck.setChecked(savevalue)
        self.hiddenCheck.setChecked(hidden)

        if field is not None:
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)

        index = self.useablewidgets.findText(widgettype)
        if index > -1:
            self.useablewidgets.setCurrentIndex(index)

        config = widget.get('config', {})
        self.updatewidgetconfig(config)

        self.fieldList.blockSignals(False)
        self.nameText.blockSignals(False)
        self.useablewidgets.blockSignals(False)

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.userwidgets.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def current_config_widget(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.useablewidgets.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetconfig(self, config):
        configwidget, _, _ = self.current_config_widget
        self.setconfigwidget(configwidget, config)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_current_widget)
        except TypeError:
            pass

        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_current_widget)

    def _save_current_widget(self, *args):
        _, index = self.currentuserwidget
        self._save_widget(index)

    def _save_widget(self, index):
        widgetdata = self._get_widget_config()
        self.widgetmodel.setData(index, widgetdata, Qt.UserRole)

    def _get_widget_config(self):
        def current_field():
            row = self.fieldList.currentIndex()
            field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
            return field

        configwidget, _, widgettype = self.current_config_widget
        widget = {}
        widget['field'] = current_field()
        widget['default'] = self.defaultvalueText.text()
        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['rememberlastvalue'] = self.savevalueCheck.isChecked()
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['hidden'] = self.hiddenCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        return widget

    @property
    def selected_layer(self):
        index = self.formlayers.index(self.layerCombo.currentIndex(), 0)
        layer = index.data(Qt.UserRole)
        return layer

    def write_config(self):
        if not self.selected_layer:
            return

        self._save_current_widget()
        self.form.settings['layer'] = self.selected_layer.name()
        self.form.settings['type'] = self.formtypeCombo.currentText()
        self.form.settings['label'] = self.formLabelText.text()
        self.form.settings['widgets'] = list(self.widgetmodel.widgets())
Example #19
0
class DoProfile(QWidget):

    def __init__(self, iface, dockwidget1 , tool1 , plugin, parent = None):
        QWidget.__init__(self, parent)
        self.profiles = None        #dictionary where is saved the plotting data {"l":[l],"z":[z], "layer":layer1, "curve":curve1}
        self.xAxisSteps = None
        self.xAxisStepType = "numeric"
        self.iface = iface
        self.tool = tool1
        self.dockwidget = dockwidget1
        self.pointstoDraw = None
        self.plugin = plugin
        #init scale widgets
        self.dockwidget.sbMaxVal.setValue(0)
        self.dockwidget.sbMinVal.setValue(0)
        self.dockwidget.sbMaxVal.setEnabled(False)
        self.dockwidget.sbMinVal.setEnabled(False)
        self.dockwidget.sbMinVal.valueChanged.connect(self.reScalePlot)
        self.dockwidget.sbMaxVal.valueChanged.connect(self.reScalePlot)


    #**************************** function part *************************************************

    # remove layers which were removed from QGIS
    def removeClosedLayers(self, model1):
        qgisLayerNames = []
        for i in range(0, self.iface.mapCanvas().layerCount()):
                qgisLayerNames.append(self.iface.mapCanvas().layer(i).name())

        for i in range(0 , model1.rowCount()):
            layerName = model1.item(i,2).data(Qt.EditRole)
            if not layerName in qgisLayerNames:
                self.plugin.removeLayer(i)
                self.removeClosedLayers(model1)
                break

    def calculatePointProfile(self, point, model, library):
        self.model = model
        self.library = library
        
        statName = self.getPointProfileStatNames()[0]

        self.removeClosedLayers(model)
        if point == None:
            return
        PlottingTool().clearData(self.dockwidget, model, library)
        self.profiles = []
        
        #creating the plots of profiles
        for i in range(0 , model.rowCount()):
            self.profiles.append( {"layer": model.item(i,3).data(Qt.EditRole) } )
            self.profiles[i][statName] = []
            self.profiles[i]["l"] = []
            layer = self.profiles[i]["layer"]

            if layer:
                try:
                    ident = layer.dataProvider().identify(point, QgsRaster.IdentifyFormatValue )
                except:
                    ident = None
            else:
                ident = None
            #if ident is not None and ident.has_key(choosenBand+1):
            if ident is not None:
                self.profiles[i][statName] = ident.results().values()
                self.profiles[i]["l"] = ident.results().keys()
        
        self.setXAxisSteps()
        PlottingTool().attachCurves(self.dockwidget, self.profiles, model, library)
        PlottingTool().reScalePlot(self.dockwidget, self.profiles, model, library)
        self.setupTableTab(model)

    def getPointProfileStatNames(self):
        return ["value"]

    # The code is based on the approach of ZonalStatistics from Processing toolbox 
    def calculatePolygonProfile(self, geometry, crs, model, library):
        self.model = model
        self.library = library
        
        self.removeClosedLayers(model)
        if geometry is None or geometry.isEmpty():
            return
        
        PlottingTool().clearData(self.dockwidget, model, library)
        self.profiles = []

        #creating the plots of profiles
        for i in range(0 , model.rowCount()):
            self.profiles.append( {"layer": model.item(i,3).data(Qt.EditRole) } )
            self.profiles[i]["l"] = []
            for statistic in self.getPolygonProfileStatNames():
                self.profiles[i][statistic] = []
            
            # Get intersection between polygon geometry and raster following ZonalStatistics code
            rasterDS = gdal.Open(self.profiles[i]["layer"].source(), gdal.GA_ReadOnly)
            geoTransform = rasterDS.GetGeoTransform()
            
    
            cellXSize = abs(geoTransform[1])
            cellYSize = abs(geoTransform[5])
            rasterXSize = rasterDS.RasterXSize
            rasterYSize = rasterDS.RasterYSize
    
            rasterBBox = QgsRectangle(geoTransform[0], geoTransform[3] - cellYSize
                                      * rasterYSize, geoTransform[0] + cellXSize
                                      * rasterXSize, geoTransform[3])
            rasterGeom = QgsGeometry.fromRect(rasterBBox)
            
            memVectorDriver = ogr.GetDriverByName('Memory')
            memRasterDriver = gdal.GetDriverByName('MEM')
            
            intersectedGeom = rasterGeom.intersection(geometry)
            ogrGeom = ogr.CreateGeometryFromWkt(intersectedGeom.exportToWkt())
            
            bbox = intersectedGeom.boundingBox()

            xMin = bbox.xMinimum()
            xMax = bbox.xMaximum()
            yMin = bbox.yMinimum()
            yMax = bbox.yMaximum()

            (startColumn, startRow) = self.mapToPixel(xMin, yMax, geoTransform)
            (endColumn, endRow) = self.mapToPixel(xMax, yMin, geoTransform)

            width = endColumn - startColumn
            height = endRow - startRow

            if width == 0 or height == 0:
                return

            srcOffset = (startColumn, startRow, width, height)

            newGeoTransform = (
                geoTransform[0] + srcOffset[0] * geoTransform[1],
                geoTransform[1],
                0.0,
                geoTransform[3] + srcOffset[1] * geoTransform[5],
                0.0,
                geoTransform[5],
            )
            
            # Create a temporary vector layer in memory
            memVDS = memVectorDriver.CreateDataSource('out')
            memLayer = memVDS.CreateLayer('poly', crs, ogr.wkbPolygon)

            ft = ogr.Feature(memLayer.GetLayerDefn())
            ft.SetGeometry(ogrGeom)
            memLayer.CreateFeature(ft)
            ft.Destroy()
            
            # Rasterize it
            rasterizedDS = memRasterDriver.Create('', srcOffset[2],
                    srcOffset[3], 1, gdal.GDT_Byte)
            rasterizedDS.SetGeoTransform(newGeoTransform)
            gdal.RasterizeLayer(rasterizedDS, [1], memLayer, burn_values=[1])
            rasterizedArray = rasterizedDS.ReadAsArray()
            
            for bandNumber in range(1, rasterDS.RasterCount+1): 
                rasterBand = rasterDS.GetRasterBand(bandNumber)
                noData = rasterBand.GetNoDataValue()
                if noData is None:
                    noData = np.nan
                scale = rasterBand.GetScale()
                if scale is None:
                    scale = 1.0
                offset = rasterBand.GetOffset()
                if offset is None:
                    offset = 0.0
                srcArray = rasterBand.ReadAsArray(*srcOffset)
                srcArray = srcArray*scale+offset 
                masked = np.ma.MaskedArray(srcArray,
                            mask=np.logical_or.reduce((
                             srcArray == noData,
                             np.logical_not(rasterizedArray),
                             np.isnan(srcArray))))

                self.profiles[i]["l"].append(bandNumber)
                self.profiles[i]["count"].append(float(masked.count()))
                self.profiles[i]["max"].append(float(masked.max()))
                self.profiles[i]["mean"].append(float(masked.mean()))
                self.profiles[i]["median"].append(float(np.ma.median(masked)))
                self.profiles[i]["min"].append(float(masked.min()))
                self.profiles[i]["range"].append(float(masked.max()) - float(masked.min()))
                self.profiles[i]["std"].append(float(masked.std()))
                self.profiles[i]["sum"].append(float(masked.sum()))
                self.profiles[i]["unique"].append(np.unique(masked.compressed()).size)
                self.profiles[i]["var"].append(float(masked.var()))
                
            memVDS = None
            rasterizedDS = None
        
        rasterDS = None
        
        self.setXAxisSteps()
        PlottingTool().attachCurves(self.dockwidget, self.profiles, model, library)
        PlottingTool().reScalePlot(self.dockwidget, self.profiles, model, library)
        self.setupTableTab(model)

    def getPolygonProfileStatNames(self):
        return ["count", "max", "mean", "median", "min", "range", "std", "sum", "unique", "var"]

    def setXAxisSteps(self):
        if self.xAxisSteps == None:
            self.changeXAxisStepType("numeric")
            return
        
        elif self.xAxisSteps[0] == "Timesteps":
            self.changeXAxisStepType("numeric")
            for profile in self.profiles:
                stepsNum = len(profile["l"])
                startTime = self.xAxisSteps[1]
                step = self.xAxisSteps[2]
                stepType = self.xAxisSteps[3]
                useNetcdfTime = self.xAxisSteps[4]
                if stepType == "years":
                    stepType = "days"
                    step = step * 365
                elif stepType == "months":
                    stepType = "days"
                    step = step * 365/12

                profile["l"] = []
                if useNetcdfTime and (profile["layer"].source().startswith("NETCDF:") or
                                      profile["layer"].source().endswith(".nc")):
                    try:
                        import netCDF4
                        if profile["layer"].source().startswith("NETCDF:"):
                            filename = re.match('NETCDF:\"(.*)\":.*$', profile["layer"].source()).group(1)
                        else:
                            filename = profile["layer"].source()
                        nc = netCDF4.Dataset(filename, mode='r')
                        profile["l"] = netCDF4.num2date(nc.variables["time"][:],
                                                        units = nc.variables["time"].units,
                                                        calendar = nc.variables["time"].calendar)
                        nc.close()
                    except ImportError:
                        text = "Temporal/Spectral Profile Tool: netCDF4 module is required to read NetCDF " + \
                               "time dimension. Please use pip install netCDF4"
                        self.iface.messageBar().pushWidget(self.iface.messageBar().createMessage(text), 
                                                           QgsMessageBar.WARNING, 5)
                        profile["l"] = []
                    except KeyError:
                        text = "Temporal/Spectral Profile Tool: NetCDF file does not have " + \
                               "time dimension."
                        self.iface.messageBar().pushWidget(self.iface.messageBar().createMessage(text), 
                                                           QgsMessageBar.WARNING, 5)
                        nc.close()
                        profile["l"] = []
                if profile["l"] == []:
                    for i in range(stepsNum):
                        timedeltaParams = {stepType: step*i}
                        profile["l"].append(startTime + timedelta(**timedeltaParams))
                self.changeXAxisStepType("timedate")        
        else:
            for profile in self.profiles:
                # Truncate the profiles to the minimum of the length of each profile
                # or length of provided x-axis steps
                stepsNum = min(len(self.xAxisSteps), len(profile["l"]))
                profile["l"] = self.xAxisSteps[:stepsNum]
                for stat in profile.keys():
                    if stat == "l" or stat == "layer":
                        continue
                    profile[stat] = profile[stat][:stepsNum]
                    
                # If any x-axis step is a NaN then remove the corresponding
                # value from profile
                nans = [i for i, x in enumerate(profile["l"]) if math.isnan(x)]
                for stat in profile.keys():
                    if stat == "layer":
                        continue
                    profile[stat] = [x for i, x in enumerate(profile[stat]) if i not in nans]
            
            self.changeXAxisStepType("numeric")
            
    def changeXAxisStepType(self, newType):
        if self.xAxisStepType == newType:
            return
        else:
            self.xAxisStepType = newType
            PlottingTool().resetAxis(self.dockwidget, self.library)
    
    def mapToPixel(self, mX, mY, geoTransform):
        (pX, pY) = gdal.ApplyGeoTransform(
            gdal.InvGeoTransform(geoTransform), mX, mY)
            
        return (int(pX), int(pY))            
    
    def setupTableTab(self, model1):
        #*********************** TAble tab *************************************************
        try:                                                                    #Reinitializing the table tab
            self.VLayout = self.dockwidget.scrollAreaWidgetContents.layout()
            while 1:
                child = self.VLayout.takeAt(0)
                if not child:
                    break
                child.widget().deleteLater()
        except:
            self.VLayout = QVBoxLayout(self.dockwidget.scrollAreaWidgetContents)
            self.VLayout.setContentsMargins(9, -1, -1, -1)
        #Setup the table tab
        self.groupBox = []
        self.profilePushButton = []
        self.tableView = []
        self.verticalLayout = []
        for i in range(0 , model1.rowCount()):
            self.groupBox.append( QGroupBox(self.dockwidget.scrollAreaWidgetContents) )
            sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(self.groupBox[i].sizePolicy().hasHeightForWidth())
            self.groupBox[i].setSizePolicy(sizePolicy)
            self.groupBox[i].setMinimumSize(QSize(0, 150))
            self.groupBox[i].setMaximumSize(QSize(16777215, 350))
            self.groupBox[i].setTitle(QApplication.translate("GroupBox" + str(i), self.profiles[i]["layer"].name(), None, QApplication.UnicodeUTF8))
            self.groupBox[i].setObjectName("groupBox" + str(i))

            self.verticalLayout.append( QVBoxLayout(self.groupBox[i]) )
            self.verticalLayout[i].setObjectName("verticalLayout")
            #The table
            self.tableView.append( QTableView(self.groupBox[i]) )
            self.tableView[i].setObjectName("tableView" + str(i))
            font = QFont("Arial", 8)
            columns = len(self.profiles[i]["l"])
            rowNames = self.profiles[i].keys()
            rowNames.remove("layer") # holds the QgsMapLayer instance
            rowNames.remove("l") # holds the band number
            rows = len(rowNames)
            self.mdl = QStandardItemModel(rows+1, columns)
            self.mdl.setVerticalHeaderLabels(["band"] + rowNames)
            for j in range(columns):
                self.mdl.setData(self.mdl.index(0, j, QModelIndex()), str(self.profiles[i]["l"][j]))
                self.mdl.setData(self.mdl.index(0, j, QModelIndex()), font ,Qt.FontRole)
                for k in range(rows):
                    self.mdl.setData(self.mdl.index(k+1, j, QModelIndex()), str(self.profiles[i][rowNames[k]][j]))
                    self.mdl.setData(self.mdl.index(k+1, j, QModelIndex()), font ,Qt.FontRole)
            #self.tableView[i].setVerticalHeaderLabels(rowNames)
            self.tableView[i].verticalHeader().setDefaultSectionSize(18)
            self.tableView[i].horizontalHeader().setDefaultSectionSize(60)
            self.tableView[i].setModel(self.mdl)
            self.verticalLayout[i].addWidget(self.tableView[i])

            self.horizontalLayout = QHBoxLayout()

            #the copy to clipboard button
            self.profilePushButton.append( QPushButton(self.groupBox[i]) )
            sizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(self.profilePushButton[i].sizePolicy().hasHeightForWidth())
            self.profilePushButton[i].setSizePolicy(sizePolicy)
            self.profilePushButton[i].setText(QApplication.translate("GroupBox", "Copy to clipboard", None, QApplication.UnicodeUTF8))
            self.profilePushButton[i].setObjectName(str(i))
            self.horizontalLayout.addWidget(self.profilePushButton[i])

            self.horizontalLayout.addStretch(0)
            self.verticalLayout[i].addLayout(self.horizontalLayout)

            self.VLayout.addWidget(self.groupBox[i])
            QObject.connect(self.profilePushButton[i], SIGNAL("clicked()"), self.copyTable)

    def copyTable(self):                            #Writing the table to clipboard in excel form
        nr = int( self.sender().objectName() )
        self.clipboard = QApplication.clipboard()
        text = "band"
        rowNames = self.profiles[nr].keys()
        rowNames.remove("layer")
        rowNames.remove("l")
        for name in rowNames:
            text += "\t"+name
        text += "\n"
        for i in range( len(self.profiles[nr]["l"]) ):
            text += str(self.profiles[nr]["l"][i])
            for j in range(len(rowNames)):
                text += "\t" + str(self.profiles[nr][rowNames[j]][i])
            text += "\n"
        self.clipboard.setText(text)

    def reScalePlot(self, param):                         # called when a spinbox value changed
        if type(param) != float:    
            # don't execute it twice, for both valueChanged(int) and valueChanged(str) signals
            return
        if self.dockwidget.sbMinVal.value() == self.dockwidget.sbMaxVal.value() == 0:
            # don't execute it on init
            return
        PlottingTool().reScalePlot(self.dockwidget, self.profiles, self.model, self.library, autoMode = False)
Example #20
0
     QTreeView
  
if __name__ == "__main__":
  
    app = QApplication(sys.argv)
  
    phonebook = {"Arthur": "Camelot",
                 "Monty": "The Circus",
                 "David": "Oslo"}
  
    model = QStandardItemModel(6, 4)
    row = 0
  
    for name, address in phonebook.items():
  
        model.setData(model.index(row, 0), QVariant(name))
        model.setData(model.index(row, 1), QVariant(address))
        font = QFont()
        font.setPointSize(16)
        font.setBold(True)
        model.setData(model.index(row, 0), QVariant(font), Qt.FontRole)
        model.setData(model.index(row, 1), QVariant(font), Qt.FontRole)
        row += 1
  
    model.setHeaderData(0, Qt.Horizontal, QVariant("Name"))
    model.setHeaderData(1, Qt.Horizontal, QVariant("Address"))
  
    tree = QTreeView()
    tree.setModel(model)
    tree.show()
  
class ListPairTableView(QTableView):
    """
    2-column table view that enables pairing of list data through combo boxes.
    """

    def __init__(self, parent=None):
        QTableView.__init__(self, parent)

        self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)

        self._pair_model = QStandardItemModel(1, 2, self)
        self._pair_model.dataChanged.connect(self._on_pair_data_changed)

        self.setModel(self._pair_model)
        self.horizontalHeader().setResizeMode(QHeaderView.Stretch)

        self._combo_delegate = PairComboBoxDelegate(self)
        self.setItemDelegate(self._combo_delegate)

    def set_header_labels(self, labels):
        """
        Set the table's header labels using labels.
        :param labels: Header labels.
        :type labels: list
        """
        if len(labels) < 2:
            return

        lbls = []
        for i in range(2):
            lbls.append(labels[i])

        self._pair_model.setHorizontalHeaderLabels(lbls)

    def clear_view(self):
        """
        Clears all row pairings in the view.
        """
        rows = self._pair_model.rowCount()
        self._pair_model.removeRows(0, rows)

        # Insert blank row
        self.append_row()

    def append_row(self):
        """
        Add a blank row after the last item in the view.
        """
        items = [QStandardItem(), QStandardItem()]

        self._pair_model.appendRow(items)

    def set_combo_selection(self, selection, empty_item=True):
        """
        Set combo selection for both columns. Any existing rows will be removed
        from the view.
        :param selection: A list containing two sub-lists for each column that
        correspond to the selection list for the combobox in each column.
        :type selection: list
        :param empty_item: True to insert an empty first item in each of the
        column comboboxes.
        :type empty_item: bool
        """
        self._combo_delegate.set_items_pair(selection, empty_item)

        self.clear_view()

    def _on_pair_data_changed(self, old_index, new_index):
        """
        This slot asserts whether selections in both columns in a row have
        been specified. If true, then automatically adds a new empty row
        for additional entries; If false, then the empty is removed from
        the view.
        :param old_index: Model index
        :type old_index: QModelIndex
        :param new_index: Model index
        :type new_index: QModelIndex
        """
        row_state = self.row_data_state(new_index.row())

        row_data = self.row_data(new_index.row())
        if row_state == 0:
            self._pair_model.removeRows(new_index.row(), 1)

            if self._pair_model.rowCount() == 0:
                self.append_row()

        elif row_state == 2:
            if not self.is_last_row_empty():
                self.append_row()

    def is_last_row_empty(self):
        """
        :return: True if the last row in the view does not contain any data,
        False if one or both columns contains data.
        :rtype: bool
        """
        last_row_idx = self._pair_model.rowCount() - 1

        last_row_state = self.row_data_state(last_row_idx)
        if last_row_state == 0:
            return True

        else:
            return False

    def row_data_state(self, row_index):
        """
        :param row_index: Row position
        :type row_index: int
        :return: 0 if data for each of the columns is empty. 1 if one column
        contains data and the other is empty. 2 if both columns contain data.
        :rtype: int
        """
        col_0_val, col_1_val = self.row_data(row_index)

        if col_0_val is None and col_1_val is None:
            return 0

        elif self._is_empty(col_0_val) and not self._is_empty(col_1_val):
            return 1

        elif not self._is_empty(col_0_val) and self._is_empty(col_1_val):
            return 1

        elif self._is_empty(col_0_val) and self._is_empty(col_1_val):
            return 0

        elif not self._is_empty(col_0_val) and not self._is_empty(col_1_val):
            return 2

    def _is_empty(self, val):
        if val is None:
            return True

        else:
            if (isinstance(val, str) or isinstance(val, unicode)) and not val:
                return True

        return False

    def row_data(self, row_index):
        """
        :param row_index: Row position
        :type row_index: int
        :return: Data in both first and second columns for the specified row.
        :rtype: tuple
        """
        if row_index >= 0:
            idx_col_0 = self._pair_model.index(row_index, 0)
            idx_col_1 = self._pair_model.index(row_index, 1)

            val_0 = self._pair_model.data(idx_col_0)
            val_1 = self._pair_model.data(idx_col_1)

            return val_0, val_1

        else:
            return None, None

    def column_pairings(self):
        """
        :return: Collection of column matchings specified as specified by the user.
        :rtype: dict
        """
        col_pairings = {}

        for row_idx in range(self._pair_model.rowCount()):
            if self.row_data_state(row_idx) != 0:
                col_val_0, col_val_1 = self.row_data(row_idx)
                col_pairings[col_val_0] = col_val_1

        return col_pairings
Example #22
0
class FormWidget(ui_formwidget.Ui_Form, WidgetBase):
    def __init__(self, parent=None):
        super(FormWidget, self).__init__(parent)
        self.setupUi(self)
        self.form = None

        self.iconlabel.mouseReleaseEvent = self.change_icon

        self._currentwidgetid = ''
        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()
        self.formlayersmodel = QgsLayerModel(watchregistry=True)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.layerCombo.setModel(self.formlayers)
        self.useablewidgets.setModel(self.possiblewidgetsmodel)
        self.fieldList.setModel(self.fieldsmodel)

        self.userwidgets.setModel(self.widgetmodel)
        self.userwidgets.selectionModel().currentChanged.connect(self.load_widget)
        self.widgetmodel.rowsRemoved.connect(self.set_widget_config_state)
        self.widgetmodel.rowsInserted.connect(self.set_widget_config_state)
        self.widgetmodel.modelReset.connect(self.set_widget_config_state)

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.addSectionButton.pressed.connect(self.add_section)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.expressionButton.clicked.connect(self.opendefaultexpression)
        self.expressionButton_2.clicked.connect(self.opendefaultexpression_advanced)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        for item, data in defaultevents:
            self.defaultEventsCombo.addItem(item, data)

        self.loadwidgettypes()

        self.formLabelText.textChanged.connect(self.form_name_changed)
        self.newStyleCheck.stateChanged.connect(self.form_style_changed)
        self.layerCombo.currentIndexChanged.connect(self.layer_updated)


        # Gross but ok for now.
        self.blockWidgets = [
            self.fieldList,
            self.nameText,
            self.sectionNameText,
            self.useablewidgets,
            self.hiddenCheck,
            self.requiredCheck,
            self.readonlyCombo,
            self.defaultEventsCombo,
            self.defaultvalueText,
            self.defaultLayerCombo,
            self.defaultFieldCombo,
            self.defaultValueExpression,
            self.savevalueCheck
        ]

        for widget in self.blockWidgets:
            self._connect_save_event(widget)

        self.blockWidgetSignels(True)

        self.useablewidgets.currentIndexChanged.connect(self.swapwidgetconfig)

        menu = QMenu("Field Actions")
        action = menu.addAction("Auto add all fields")
        action.triggered.connect(self.auto_add_fields)

        self.addWidgetButton.setMenu(menu)
        self.addWidgetButton.setPopupMode(QToolButton.MenuButtonPopup)

        self.defaultLayerCombo.layerChanged.connect(self.defaultFieldCombo.setLayer)
        self.addEvent.pressed.connect(self.addEventItem)
        self.btnDeleteForm.pressed.connect(ConfigEvents.deleteForm.emit)

    def unload_project(self):
        print "UNLOAD PROJECT!!!"
        self.blockWidgetSignels(True)

    def _connect_save_event(self, widget):
        if hasattr(widget, "textChanged"):
            widget.textChanged.connect(self._save_current_widget)
        if hasattr(widget, "currentIndexChanged"):
            widget.currentIndexChanged.connect(self._save_current_widget)
        if hasattr(widget, "stateChanged"):
            widget.stateChanged.connect(self._save_current_widget)

    def change_icon(self, *args):
        """
        Change the icon for the form
        """
        icon = QFileDialog.getOpenFileName(self, "Select form icon image", filter="Images (*.png *.svg)")
        if not icon:
            return
        ext = os.path.splitext(icon)[1]
        shutil.copy(icon, os.path.join(self.form.folder, "icon" + ext))
        self.set_icon(self.form.icon)
        self.treenode.emitDataChanged()

    def set_icon(self, path):
        """
        Set the forms icon preview
        :param path: The path to icon.
        """
        pixmap = QPixmap(path)
        w = self.iconlabel.width()
        h = self.iconlabel.height()
        self.iconlabel.setPixmap(pixmap.scaled(w, h, Qt.KeepAspectRatio))

    def layer_updated(self, index):
        """
        Called when the forms layer has changed.
        :param index: The index of the new layer.
        """
        if not self.selected_layer:
            return

        self.updatefields(self.selected_layer)

    def form_style_changed(self, newstyle):
        """
        Called when the form style has changed from label-above style to label-beside style.
        :param newstyle: True if to use the new label-above style forms.
        """
        self.form.settings['newstyle'] = newstyle
        self.treenode.emitDataChanged()

    def form_name_changed(self, text):
        """
        Called when the forms name has changed. Also updates the tree view to reflect the new name.
        :param text: The new text of the label.
        :return:
        """
        self.form.settings['label'] = text
        self.treenode.emitDataChanged()

    def updatewidgetname(self, index):
        """
        Update the widget name if the field has changed. Doesn't change the name if it has been user set already.
        :param index: index of the new field.
        """
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def opendefaultexpression_advanced(self):
        """
        Open the default expression builder for setting advanced default values based on QGIS Expressions.
        """
        layer = self.form.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultValueExpression.text()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultValueExpression.setText(dlg.expressionText())

    def opendefaultexpression(self):
        """
        Open the default expression builder for setting default values based on QGIS Expressions.
        """
        layer = self.form.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def formtabchanged(self, index):
        """
        Called when the tab widget changes tab.  Normally used to control when to render the form preview on demand.
        :param index: The index of the new tab.
        """
        # Don't generate the form preview if we are not on the preview tab.
        if index == 3:
            self.generate_form_preview()

    def generate_form_preview(self):
        """
        Create the form preview to show to the user.
        """
        form = self.form.copy()
        form.settings['widgets'] = list(self.widgetmodel.widgets())
        item = self.frame_2.layout().itemAt(0)
        if item and item.widget():
            item.widget().setParent(None)

        featureform = FeatureForm.from_form(form, form.settings, None, {})
        from roam import defaults
        defaultwidgets = form.widgetswithdefaults()
        layer = form.QGISLayer
        try:
            values = {}
            feature = layer.getFeatures().next()
            defaultvalues = defaults.default_values(defaultwidgets, feature, layer)
            values.update(defaultvalues)
            featureform.bindvalues(values)
        except StopIteration:
            pass

        self.frame_2.layout().addWidget(featureform)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        widgets = self.widgetmodel.widgets()
        for widget in widgets:
            if 'field' in widget:
                yield widget['field']

    def openformfolder(self, url):
        """
        Open the form folder using the OS file manager.
        :param url:
        :return:
        """
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.form.folder))

    def loadwidgettypes(self):
        """
        Load all supported widgets into the combobox for the form designer.
        :return:
        """
        self.useablewidgets.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.useablewidgets.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.useablewidgets.blockSignals(False)

    def set_widget_config_state(self, *args):
        """
        Enable or disable the widget config section based on widget count
        :param args: Unused.
        :return:
        """
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetConfigTabs.setEnabled(haswidgets)

    def add_section(self):
        """
        Add a new widget section into the form. Widget sections can be used to group
        widgets on the form together.
        """
        currentindex = self.userwidgets.currentIndex()
        widget = {"widget": "Section",
                  "name": "default"}
        index = self.widgetmodel.addwidget(widget, currentindex.parent())
        self.userwidgets.setCurrentIndex(index)

    def newwidget(self, field=None):
        """
        Create a new widget. Tries to match the field type to the right kind of widget as a best guess.
        """
        mapping = {QVariant.String: "Text",
                   QVariant.Int: "Number",
                   QVariant.Double: "Number(Double)",
                   QVariant.ByteArray: "Image",
                   QVariant.Date: "Date",
                   QVariant.DateTime: "Date"}
        widget = {}
        if not field:
            field = self.fieldsmodel.index(0, 0).data(Qt.UserRole)
            if not field:
                return
            widget['field'] = field.name()
        else:
            widget['field'] = field.name()

        try:
            widget['widget'] = mapping[field.type()]
        except KeyError:
            widget['widget'] = 'Text'
        # Grab the first field.

        widget['name'] = field.name().replace("_", " ").title()

        currentindex = self.userwidgets.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.userwidgets.setCurrentIndex(index)

    def auto_add_fields(self):
        """
        Auto add all fields to the form config. Any missing fields will be added.
        """
        used = list(self.usedfields())
        for field in self.selected_layer.pendingFields():
            if field.name() in used:
                continue

            self.newwidget(field)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def set_project(self, project, treenode):
        """
        Set the project for this widget also sets the form from the tree node.

        :note: This method is called from the parent node when the page and widget is loaded.
        :param project: The current project.j
        :param treenode: The current tree node.  Can be used to signel a update back to the tree for it to update it
        self.
        """
        roam.utils.debug("FormWidget: Set Project")

        self.blockWidgetSignels(True)

        super(FormWidget, self).set_project(project, treenode)
        self.formlayers.setSelectLayers(self.project.selectlayers)
        form = self.treenode.form
        self.form = form
        self.setform(self.form)

        self.blockWidgetSignels(False)

    def blockWidgetSignels(self, blocking):
        for widget in self.blockWidgets:
            widget.blockSignals(blocking)

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            """
            Get the first layer from the forms layer combo box
            """
            index = self.formlayers.index(0, 0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            """
            Find the layer with the same name in the layer combobox widget
            """
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])
        newstyleform = settings.setdefault('newstyle', True)
        self.set_icon(form.icon)

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.newStyleCheck.setChecked(newstyleform)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        if formtype == "auto":
            formtype = "Auto Generated"
        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.userwidgets.setCurrentIndex(index)
            # self.load_widget(index, None)

        for i in reversed(range(self.eventsLayout.count())):
            child = self.eventsLayout.itemAt(i)
            if child.widget() and isinstance(child.widget(), EventWidget):
                child = self.eventsLayout.takeAt(i)
                child.widget().deleteLater()

        events = settings.get('events', [])
        self.load_events(events)

        ## This has overhead so only do it when the tab is active.
        if self.formtab.currentIndex() == 3:
            self.generate_form_preview()

    def load_events(self, events):
        for event in events:
            self.addEventItem(data=event)

    def addEventItem(self, data=None):
        widget = EventWidget(self.form.QGISLayer, self.widgetmodel, self.eventsWidget)
        widget.removeItem.connect(self.removeEventItem)
        widget.set_data(data)
        self.eventsLayout.addWidget(widget)

    def removeEventItem(self, widget):
        child = self.eventsLayout.removeWidget(widget)
        widget.deleteLater()

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.current_config_widget
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def load_widget(self, index, last):
        """
        Update the UI with the config for the current selected widget.
        """
        self.blockWidgetSignels(True)

        if last:
            roam.utils.debug("Saving last widget")
            self._save_widget(last)

        widget = index.data(Qt.UserRole)
        if not widget:
            self.blockWidgetSignels(False)
            return

        try:
            roam.utils.debug("Loading widget: {0}".format(widget['_id']))
        except KeyError:
            pass
        widgettype = widget['widget']
        if widgettype == "Section":
            self.propertiesStack.setCurrentIndex(1)
            self.sectionNameText.blockSignals(True)
            name = widget['name']
            self.sectionNameText.setText(name)
            self.sectionNameText.blockSignals(False)
            return
        else:
            self.propertiesStack.setCurrentIndex(0)



        field = widget['field']
        self._currentwidgetid = widget.setdefault('_id', str(uuid.uuid4()))
        required = widget.setdefault('required', False)
        savevalue = widget.setdefault('rememberlastvalue', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)
        defaultevents = widget.setdefault('default_events', ['capture'])

        try:
            data = readonly[0]
        except IndexError:
            data = 'never'

        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)

        index = self.defaultEventsCombo.findData(defaultevents)
        self.defaultEventsCombo.setCurrentIndex(index)

        if not isinstance(default, dict):
            self.defaultTab.setCurrentIndex(0)
            self.defaultvalueText.setText(default)
        else:
            self.defaultTab.setCurrentIndex(1)
            layer = default['layer']
            # TODO Handle the case of many layer fall though with defaults
            # Not sure how to handle this in the UI just yet
            if isinstance(layer, list):
                layer = layer[0]

            if isinstance(layer, basestring):
                defaultfield = default['field']
                expression = default['expression']
                self.defaultValueExpression.setText(expression)
                layer = roam.api.utils.layer_by_name(layer)
                # self.defaultLayerCombo.setLayer(layer)
                self.defaultFieldCombo.setLayer(layer)
                self.defaultFieldCombo.setField(defaultfield)

        self.nameText.setText(name)
        self.requiredCheck.setChecked(required)
        self.savevalueCheck.setChecked(savevalue)
        self.hiddenCheck.setChecked(hidden)

        if field is not None:
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)

        index = self.useablewidgets.findText(widgettype)
        if index > -1:
            self.useablewidgets.setCurrentIndex(index)

        config = widget.get('config', {})
        self.updatewidgetconfig(config)

        self.blockWidgetSignels(False)

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.userwidgets.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def current_config_widget(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.useablewidgets.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetconfig(self, config):
        configwidget, _, _ = self.current_config_widget
        self.setconfigwidget(configwidget, config)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_current_widget)
        except TypeError:
            pass

        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_current_widget)

    def _save_current_widget(self, *args):
        _, index = self.currentuserwidget
        self._save_widget(index)

    def _save_widget(self, index):
        # roam.utils.debug("FormWidget: Save widget")
        print("SENDER: {}".format(self.sender().objectName()))
        if not self.project:
            return
        widgetdata = self._get_widget_config()
        try:
            roam.utils.debug("Saving widget {} in project {}".format(widgetdata['_id'], self.project.name))
        except KeyError:
            pass
        self.widgetmodel.setData(index, widgetdata, Qt.UserRole)

    def _get_default_config(self):
        if self.defaultTab.currentIndex() == 0:
            return self.defaultvalueText.text()
        else:
            default = {}
            default['layer'] = self.defaultLayerCombo.currentLayer().name()
            default['field'] = self.defaultFieldCombo.currentField()
            default['expression'] = self.defaultValueExpression.text()
            default['type'] = 'layer-value'
            return default

    def _get_widget_config(self):
        def current_field():
            row = self.fieldList.currentIndex()
            field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
            return field

        configwidget, _, widgettype = self.current_config_widget
        if self.propertiesStack.currentIndex() == 1:
            return {'name': self.sectionNameText.text(),
                    "widget": "Section"}

        widget = {}
        widget['field'] = current_field()
        widget['default'] = self._get_default_config()
        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['rememberlastvalue'] = self.savevalueCheck.isChecked()
        widget['_id'] = self._currentwidgetid
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['default_events'] = self.defaultEventsCombo.itemData(self.defaultEventsCombo.currentIndex())
        widget['hidden'] = self.hiddenCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        return widget

    @property
    def selected_layer(self):
        index = self.formlayers.index(self.layerCombo.currentIndex(), 0)
        layer = index.data(Qt.UserRole)
        return layer

    def write_config(self):
        roam.utils.debug("Write form config")
        if not self.selected_layer:
            return

        self._save_current_widget()
        self.form.settings['layer'] = self.selected_layer.name()
        formtype = self.formtypeCombo.currentText()
        self.form.settings['type'] = "auto" if formtype == "Auto Generated" else formtype
        self.form.settings['label'] = self.formLabelText.text()
        self.form.settings['newstyle'] = self.newStyleCheck.isChecked()
        self.form.settings['widgets'] = list(self.widgetmodel.widgets())
        events = []
        for i in range(self.eventsLayout.count()):
            child = self.eventsLayout.itemAt(i)
            if child.widget() and isinstance(child.widget(), EventWidget):
                widget = child.widget()
                eventdata = widget.get_data()
                events.append(eventdata)
        self.form.settings['events'] = events
Example #23
0
class Dialog_report(QDialog, Ui_Dialog_report):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)

        #construct Customer list class
        self.clist = Customer_list()

        #construct accounting class
        self.caccount = Dfile()

        #construct customer price class
        self.cprice = Customer_price()

        #construct standard table
        self.tablemodel = QStandardItemModel(31, len(PRODUCT_NAME))
        self.setTableheader()

        #save customer list int
        self.list_customer = self.clist.readCompany()
        self.setCombo(1, self.list_customer)

    def setMode(self, str_mode):

        if str_mode == 'init':
            self.clearAllshow()

    def setCombo(self, comboselect, m_str):
        """
        set combo box
        """
        if comboselect == 1:
            for i in m_str:
                self.comboBox_name.addItem(i)
        elif comboselect == 2:
            self.comboBox_date.clear()
            for i in m_str:
                self.comboBox_date.addItem(i)

    def clearAllshow(self):

        #clear all spin box
        self.setAllspin(0)

        #clear table
        self.clearTableview()

    def setAllspin(self, int_value):
        self.spinBox_1.setValue(int_value)
        self.spinBox_2.setValue(int_value)
        self.spinBox_3.setValue(int_value)
        self.spinBox_4.setValue(int_value)
        self.spinBox_5.setValue(int_value)
        self.spinBox_6.setValue(int_value)
        self.spinBox_7.setValue(int_value)
        self.spinBox_8.setValue(int_value)
        self.spinBox_9.setValue(int_value)
        self.spinBox_10.setValue(int_value)
        self.spinBox_11.setValue(int_value)
        self.spinBox_12.setValue(int_value)
        self.spinBox_13.setValue(int_value)
        self.spinBox_14.setValue(int_value)
        self.spinBox_15.setValue(int_value)
        self.spinBox_16.setValue(int_value)
        self.spinBox_17.setValue(int_value)
        self.spinBox_18.setValue(int_value)
        self.spinBox_19.setValue(int_value)
        self.spinBox_19.setValue(0)

    def setTableheader(self):

        #set header data
        self.tablemodel.setHeaderData(0, Qt.Horizontal, PRODUCT_NAME[0])
        self.tablemodel.setHeaderData(1, Qt.Horizontal, PRODUCT_NAME[1])
        self.tablemodel.setHeaderData(2, Qt.Horizontal, PRODUCT_NAME[2])
        self.tablemodel.setHeaderData(3, Qt.Horizontal, PRODUCT_NAME[3])
        self.tablemodel.setHeaderData(4, Qt.Horizontal, PRODUCT_NAME[4])
        self.tablemodel.setHeaderData(5, Qt.Horizontal, PRODUCT_NAME[5])

    def setTableview(self, dlist_data):
        """
        set data into tableview model
        """
        #show data
        row = 0
        for i in dlist_data:
            self.tablemodel.setData(self.tablemodel.index(row, 0),
                                    QVariant(i[0]))
            self.tablemodel.setData(self.tablemodel.index(row, 1),
                                    QVariant(i[1]))
            self.tablemodel.setData(self.tablemodel.index(row, 2),
                                    QVariant(i[2]))
            self.tablemodel.setData(self.tablemodel.index(row, 3),
                                    QVariant(i[3]))
            self.tablemodel.setData(self.tablemodel.index(row, 4),
                                    QVariant(i[4]))
            self.tablemodel.setData(self.tablemodel.index(row, 5),
                                    QVariant(i[5]))
            row += 1

        #set table into tableview
        self.tableView.setModel(self.tablemodel)

    def clearTableview(self):
        """
        clear table
        """
        #show data
        row = 0
        i = [0, 0, 0, 0, 0, 0]
        for row in range(31):
            self.tablemodel.setData(self.tablemodel.index(row, 0),
                                    QVariant(i[0]))
            self.tablemodel.setData(self.tablemodel.index(row, 1),
                                    QVariant(i[1]))
            self.tablemodel.setData(self.tablemodel.index(row, 2),
                                    QVariant(i[2]))
            self.tablemodel.setData(self.tablemodel.index(row, 3),
                                    QVariant(i[3]))
            self.tablemodel.setData(self.tablemodel.index(row, 4),
                                    QVariant(i[4]))
            self.tablemodel.setData(self.tablemodel.index(row, 5),
                                    QVariant(i[5]))

        #set table into tableview
        self.tableView.setModel(self.tablemodel)

    @pyqtSignature("QString")
    def on_comboBox_name_currentIndexChanged(self, p0):
        """
        when name index change, set combo date
        """
        #set to initial status
        self.setMode('init')

        #read combo text and dict value
        self.str_customercombo = str(self.comboBox_name.currentText().toUtf8())
        self.i_customercombo = self.clist.readCvalue(self.str_customercombo)

        #read all guest accounting data
        self.list_date = self.caccount.listDatafile(self.i_customercombo)

        self.setCombo(2, self.list_date)

    @pyqtSignature("QString")
    def on_comboBox_date_currentIndexChanged(self, p0):
        """
        search price data and load accounting into table
        """

        self.str_filename = str(self.comboBox_date.currentText())

        if self.str_filename != '':
            self.str_datecombo = self.str_filename[3:9]

            #get price version
            self.i_priceversion = self.cprice.selectPrice(
                self.i_customercombo, self.str_datecombo)

            #get price dict
            dict_price = self.cprice.readPrice(self.i_customercombo,
                                               self.i_priceversion)
            self.list_price = self.cprice.getClist(dict_price)

            #show price
            self.setPricespin(self.list_price)

            #show table
            self.caccount.open_dfile(self.str_filename)
            self.table_data = self.caccount.read_alldfile()
            self.setTableview(self.table_data)

            #calculate and show single amount
            self.eachamount = self.sumEachamount(self.table_data)
            self.setEachamount(self.eachamount)

            #calculate single price amount
            self.eachpriceamount = [
                self.eachamount[i] * self.list_price[i]
                for i in range(len(PRODUCT_NAME))
            ]
            self.setEachpriceamount(self.eachpriceamount)

            #show in total income
            self.spinBox_19.setValue(sum(self.eachpriceamount))

    def setPricespin(self, list):

        self.spinBox_1.setValue(list[0])
        self.spinBox_2.setValue(list[1])
        self.spinBox_3.setValue(list[2])
        self.spinBox_4.setValue(list[3])
        self.spinBox_5.setValue(list[4])
        self.spinBox_6.setValue(list[5])

    def setEachamount(self, list):

        self.spinBox_7.setValue(list[0])
        self.spinBox_8.setValue(list[1])
        self.spinBox_9.setValue(list[2])
        self.spinBox_10.setValue(list[3])
        self.spinBox_11.setValue(list[4])
        self.spinBox_12.setValue(list[5])

    def setEachpriceamount(self, list):

        self.spinBox_13.setValue(list[0])
        self.spinBox_14.setValue(list[1])
        self.spinBox_15.setValue(list[2])
        self.spinBox_16.setValue(list[3])
        self.spinBox_17.setValue(list[4])
        self.spinBox_18.setValue(list[5])

    #sum each item total amount
    def sumEachamount(self, duallist):

        eachamount = [0, 0, 0, 0, 0, 0]
        count = 0
        for i in duallist:
            for j in i:
                eachamount[count] += j
                count += 1
            count = 0

        return eachamount
Example #24
0
class QMapManager(QDialog):
	def __init__(self, config, parent=None):
		QDialog.__init__(self, parent)
		curdir = os.path.abspath(os.path.dirname(__file__))
		PyQt4.uic.loadUi(os.path.join(curdir,'manager.ui'), self)
		self.model = QStandardItemModel()
		self.projectsmodel = QStandardItemModel()
		self.projectlist.setModel(self.projectsmodel)
		self.clientlist.setModel(self.model)
		self.clientlist.selectionModel().selectionChanged.connect(self.update)
		self.installbutton.pressed.connect(self.installToClient)
		self.mapper = QDataWidgetMapper()
		self.mapper.setModel(self.model)
		self.mapper.addMapping(self.installpath, 1)
		self.config = config
		self.populateProjects()
		self.populateClients()
		
	def installToClient(self):
		index = self.clientlist.selectionModel().currentIndex()
		item = self.model.itemFromIndex(index)
		print "Deploying to " + item.text()
		build.deployTargetByName(item.text())

	def update(self, selected, deselected ):
		index = selected.indexes()[0]
		self.mapper.setCurrentModelIndex(index)
		item = self.model.itemFromIndex(index)
		settings = item.data()

		for row in xrange(0,self.projectsmodel.rowCount()):
			index = self.projectsmodel.index(row, 0)
			item = self.projectsmodel.itemFromIndex(index)
			item.setCheckState(Qt.Unchecked)

		projects = settings['projects']
		
		for project in projects:
			if project == "All":
				i = 0
				while self.projectsmodel.item(i):
					item = self.projectsmodel.item(i)
					item.setCheckState(Qt.Checked)
					i += 1
				break
			
			projectitem = self.projectsmodel.findItems(project)[0]
		 	projectitem.setCheckState(Qt.Checked)
				
	def populateClients(self):
		row = 0
		for client, settings in self.config['clients'].iteritems():
			name = QStandardItem(client)
			name.setData(settings)
			path = QStandardItem(settings['path'])
			self.model.insertRow(row, [name, path])
			row += 1

	def populateProjects(self):
		row = 0
		for project in getProjects():
			projectitem = QStandardItem(project.name)
			projectitem.setCheckable(True)
			self.projectsmodel.insertRow(row, projectitem)
			row += 1
Example #25
0
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Display confusion matrix constructed from results " \
                  "of evaluation of classifiers."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = ["Number of instances",
                  "Proportion of predicted",
                  "Proportion of actual"]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    UserAdviceMessages = [
        widget.Message(
                "Clicking on cells or in headers outputs the corresponding "
                "data instances",
                "click_cell")]

    def __init__(self):
        super().__init__()

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(
            box, self, "selected_learner", "learners",
            callback=self._learner_changed
        )
        box = gui.widgetBox(self.controlArea, "Show")

        gui.comboBox(box, self, "selected_quantity", items=self.quantities,
                     callback=self._update)

        box = gui.widgetBox(self.controlArea, "Select")

        gui.button(box, self, "Correct",
                   callback=self.select_correct, autoDefault=False)
        gui.button(box, self, "Misclassified",
                   callback=self.select_wrong, autoDefault=False)
        gui.button(box, self, "None",
                   callback=self.select_none, autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box, self, "append_predictions",
                     "Predictions", callback=self._invalidate)
        gui.checkBox(box, self, "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit",
                        "Send Data", "Auto send is on")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and not data.domain.has_discrete_class:
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = ["Learner #%i" % (i + 1)
                                 for i in range(nmodels)]

            item = self._item(0, 2)
            item.setData("Predicted", Qt.DisplayRole)
            item.setTextAlignment(Qt.AlignCenter)
            item.setFlags(Qt.NoItemFlags)

            self._set_item(0, 2, item)
            item = self._item(2, 0)
            item.setData("Actual", Qt.DisplayRole)
            item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
            item.setFlags(Qt.NoItemFlags)
            self.tableview.setItemDelegateForColumn(
                0, gui.VerticalItemDelegate())
            self._set_item(2, 0, item)
            self.tableview.setSpan(0, 2, 1, len(class_values))
            self.tableview.setSpan(2, 0, len(class_values), 1)

            for i in (0, 1):
                for j in (0, 1):
                    item = self._item(i, j)
                    item.setFlags(Qt.NoItemFlags)
                    self._set_item(i, j, item)

            for p, label in enumerate(self.headers):
                for i, j in ((1, p + 2), (p + 2, 1)):
                    item = self._item(i, j)
                    item.setData(label, Qt.DisplayRole)
                    item.setData(QBrush(QColor(208, 208, 208)),
                                 Qt.BackgroundColorRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled)
                    self._set_item(i, j, item)

            hor_header = self.tableview.horizontalHeader()
            if len(' '.join(self.headers)) < 120:
                hor_header.setResizeMode(QHeaderView.ResizeToContents)
            else:
                hor_header.setDefaultSectionSize(60)
            self.tablemodel.setRowCount(len(class_values) + 3)
            self.tablemodel.setColumnCount(len(class_values) + 3)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        # method
        self.learners = []

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )

    def select_none(self):
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect
            )

    def commit(self):
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [i for i, t in enumerate(zip(actual, predicted))
                        if t in indices]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values
                )
                metas = metas + (var,)

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [Orange.data.ContinuousVariable("p({})".format(value))
                         for value in class_var.values]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M,) + tuple(extra))
            domain = Orange.data.Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                metas
            )
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        self.commit()

    def _learner_changed(self):
        # The selected learner has changed
        indices = self.tableview.selectedIndexes()
        self._update()
        selection = QItemSelection()
        for sel in indices:
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )
        self.commit()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = self._item(i + 2, j + 2)
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            model = self.tablemodel
            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N + 2, i + 2, sum_item(int(colsum[i])))
                model.setItem(i + 2, N + 2, sum_item(int(rowsum[i])))

            model.setItem(N + 2, N + 2, sum_item(int(total)))
Example #26
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp
        self.reloadingProject = False
        self.loadedProject = None
        self.projectpath = None

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(
            self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)
        self.projectwidget.projectloaded.connect(self.projectLoaded)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(
            self.loadprojects)
        self.setuprootitems()

        ConfigEvents.deleteForm.connect(self.delete_form)

    def closeEvent(self, closeevent):
        self.save_page_config()
        closeevent.accept()

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.datanode = DataNode(folder=None)
        rootitem.appendRow(self.datanode)
        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)
        self.publishnode = PublishNode(folder=None)
        rootitem.appendRow(self.publishnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])

    def delete_form(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if not node.type() == Treenode.FormNode:
            return

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        print "Delete"
        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            print parentindex
            self.projectList.setCurrentIndex(parentindex)

    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    @property
    def active_project_folder(self):
        return self.projectpath

    @active_project_folder.setter
    def active_project_folder(self, value):
        self.projectpath = value
        pass

    def loadprojects(self, projectpath):
        self.active_project_folder = projectpath

        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def projectLoaded(self, project):
        roam.utils.info("Project loaded: {}".format(project.name))
        node = self.projectsnode.find_by_name(project.name)
        node.create_children()
        self.projectwidget.setpage(node.page, node, refreshingProject=True)
        self.reloadingProject = False
        self.loadedProject = project
        self.projectList.setExpanded(node.index(), True)

    def nodeselected(self, index, last, reloadProject=False):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project

        if project:
            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)
                return

        if node.nodetype == Treenode.ProjectNode and reloadProject:
            self.reloadingProject = True
            self.projectwidget.setproject(project)
            return

        if project and self.loadedProject != project:
            # Only load the project if it's different the current one.
            if self.loadedProject:
                lastnode = self.projectsnode.find_by_name(
                    self.loadedProject.name)
                self.projectList.setExpanded(lastnode.index(), False)
            ## We are closing the open project at this point. Watch for null ref after this.
            self.projectwidget.setproject(project)
            self.projectwidget.setpage(node.page, node)
            return
        else:
            self.projectwidget.setpage(node.page, node)

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

    def save_page_config(self):
        """
        Save the current page config
        """
        self.projectwidget.savePage(closing=True)

    def projectupdated(self, project):
        print "PROJECT UPDATED"
        node = self.projectsnode.find_by_name(project.name)
        self.projectList.selectionModel().select(
            node.index(), QItemSelectionModel.ClearAndSelect)
        self.nodeselected(node.index(), None, reloadProject=True)
Example #27
0
class FormWidget(ui_formwidget.Ui_Form, WidgetBase):
    def __init__(self, parent=None):
        super(FormWidget, self).__init__(parent)
        self.setupUi(self)
        self.form = None

        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()
        self.formlayersmodel = QgsLayerModel(watchregistry=True)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.layerCombo.setModel(self.formlayers)
        self.useablewidgets.setModel(self.possiblewidgetsmodel)
        self.fieldList.setModel(self.fieldsmodel)

        self.userwidgets.setModel(self.widgetmodel)
        self.userwidgets.selectionModel().currentChanged.connect(self.load_widget)
        self.widgetmodel.rowsRemoved.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.rowsInserted.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.modelReset.connect(self.setwidgetconfigvisiable)

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.expressionButton.clicked.connect(self.opendefaultexpression)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        self.loadwidgettypes()

        self.formLabelText.textChanged.connect(self.form_name_changed)
        self.layerCombo.currentIndexChanged.connect(self.layer_updated)

        self.fieldList.currentIndexChanged.connect(self._save_current_widget)
        self.nameText.textChanged.connect(self._save_current_widget)
        self.useablewidgets.currentIndexChanged.connect(self._save_current_widget)
        self.useablewidgets.currentIndexChanged.connect(self.swapwidgetconfig)

        menu = QMenu("Field Actions")
        action = menu.addAction("Auto add all fields")
        action.triggered.connect(self.auto_add_fields)

        self.addWidgetButton.setMenu(menu)
        self.addWidgetButton.setPopupMode(QToolButton.MenuButtonPopup)

        self.defaultLayerCombo.layerChanged.connect(self.default_layer_changed)

    def default_layer_changed(self, layer):
        self.defaultFieldCombo.setLayer(layer)

    def layer_updated(self, index):
        if not self.selected_layer:
            return

        self.updatefields(self.selected_layer)

    def form_name_changed(self, text):
        self.form.settings['label'] = self.formLabelText.text()
        self.treenode.emitDataChanged()

    def updatewidgetname(self, index):
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def opendefaultexpression(self):
        layer = self.form.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def formtabchanged(self, index):
        def setformpreview(form):
            item = self.frame_2.layout().itemAt(0)
            if item and item.widget():
                item.widget().setParent(None)

            featureform = FeatureForm.from_form(form, form.settings, None, {})
            from roam import defaults
            defaultwidgets = form.widgetswithdefaults()
            layer = form.QGISLayer
            try:
                values = {}
                feature = layer.getFeatures().next()
                defaultvalues = defaults.default_values(defaultwidgets, feature, layer)
                values.update(defaultvalues)
                featureform.bindvalues(values)
            except StopIteration:
                pass

            self.frame_2.layout().addWidget(featureform)

        if index == 1:
            self.form.settings['widgets'] = list(self.widgetmodel.widgets())
            setformpreview(self.form)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        widgets = self.widgetmodel.widgets()
        for widget in widgets:
            yield widget['field']

    def openformfolder(self, url):
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.form.folder))

    def loadwidgettypes(self):
        self.useablewidgets.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.useablewidgets.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.useablewidgets.blockSignals(False)

    def setwidgetconfigvisiable(self, *args):
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetConfigTabs.setVisible(haswidgets)

    def newwidget(self, field=None):
        """
        Create a new widget.  The default is a list.
        """
        mapping = {QVariant.String: "Text",
                   QVariant.Int: "Number",
                   QVariant.Double: "Number(Double)",
                   QVariant.ByteArray: "Image",
                   QVariant.Date: "Date",
                   QVariant.DateTime: "Date"}
        widget = {}
        if not field:
            field = self.fieldsmodel.index(0, 0).data(Qt.UserRole)
            if not field:
                return
            widget['field'] = field.name()
        else:
            widget['field'] = field.name()

        try:
            widget['widget'] = mapping[field.type()]
        except KeyError:
            widget['widget'] = 'Text'
        # Grab the first field.

        widget['name'] = field.name().replace("_", " ").title()

        currentindex = self.userwidgets.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.userwidgets.setCurrentIndex(index)

    def auto_add_fields(self):
        used = list(self.usedfields())
        for field in self.selected_layer.pendingFields():
            if field.name() in used:
                continue

            self.newwidget(field)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def set_project(self, project, treenode):
        super(FormWidget, self).set_project(project, treenode)
        self.formlayers.setSelectLayers(self.project.selectlayers)
        form = self.treenode.form
        self.form = form
        self.setform(self.form)

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            index = self.formlayers.index(0, 0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.userwidgets.setCurrentIndex(index)
            self.load_widget(index, None)

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.current_config_widget
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def load_widget(self, index, last):
        """
        Update the UI with the config for the current selected widget.
        """
        self.fieldList.blockSignals(True)
        self.nameText.blockSignals(True)
        self.useablewidgets.blockSignals(True)

        if last:
            self._save_widget(last)

        widget = index.data(Qt.UserRole)
        if not widget:
            self.fieldList.blockSignals(False)
            self.nameText.blockSignals(False)
            self.useablewidgets.blockSignals(False)
            return

        widgettype = widget['widget']
        field = widget['field']
        required = widget.setdefault('required', False)
        savevalue = widget.setdefault('rememberlastvalue', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)

        try:
            data = readonly[0]
        except IndexError:
            data = 'never'

        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)

        if not isinstance(default, dict):
            self.defaultTab.setCurrentIndex(0)
            self.defaultvalueText.setText(default)
        else:
            self.defaultTab.setCurrentIndex(1)
            layer = default['layer']
            # TODO Handle the case of many layer fall though with defaults
            # Not sure how to handle this in the UI just yet
            if isinstance(layer, list):
                layer = layer[0]

            if isinstance(layer, basestring):
                field = default['field']
                expression = default['expression']
                self.defaultValueExpression.setText(expression)
                layer = roam.api.utils.layer_by_name(layer)
                self.defaultLayerCombo.setLayer(layer)
                self.defaultFieldCombo.setLayer(layer)
                self.defaultFieldCombo.setField(field)

        self.nameText.setText(name)
        self.requiredCheck.setChecked(required)
        self.savevalueCheck.setChecked(savevalue)
        self.hiddenCheck.setChecked(hidden)

        if field is not None:
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)

        index = self.useablewidgets.findText(widgettype)
        if index > -1:
            self.useablewidgets.setCurrentIndex(index)

        config = widget.get('config', {})
        self.updatewidgetconfig(config)

        self.fieldList.blockSignals(False)
        self.nameText.blockSignals(False)
        self.useablewidgets.blockSignals(False)

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.userwidgets.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def current_config_widget(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.useablewidgets.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetconfig(self, config):
        configwidget, _, _ = self.current_config_widget
        self.setconfigwidget(configwidget, config)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_current_widget)
        except TypeError:
            pass

        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_current_widget)

    def _save_current_widget(self, *args):
        _, index = self.currentuserwidget
        self._save_widget(index)

    def _save_widget(self, index):
        widgetdata = self._get_widget_config()
        self.widgetmodel.setData(index, widgetdata, Qt.UserRole)

    def _get_default_config(self):
        if self.defaultTab.currentIndex() == 0:
            return self.defaultvalueText.text()
        else:
            default = {}
            default['layer'] = self.defaultLayerCombo.currentLayer().name()
            default['field'] = self.defaultFieldCombo.currentField()
            default['expression'] = self.defaultValueExpression.text()
            default['type'] = 'layer-value'
            return default

    def _get_widget_config(self):
        def current_field():
            row = self.fieldList.currentIndex()
            field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
            return field

        configwidget, _, widgettype = self.current_config_widget
        widget = {}
        widget['field'] = current_field()
        widget['default'] = self._get_default_config()
        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['rememberlastvalue'] = self.savevalueCheck.isChecked()
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['hidden'] = self.hiddenCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        return widget

    @property
    def selected_layer(self):
        index = self.formlayers.index(self.layerCombo.currentIndex(), 0)
        layer = index.data(Qt.UserRole)
        return layer

    def write_config(self):
        if not self.selected_layer:
            return

        self._save_current_widget()
        self.form.settings['layer'] = self.selected_layer.name()
        self.form.settings['type'] = self.formtypeCombo.currentText()
        self.form.settings['label'] = self.formLabelText.text()
        self.form.settings['widgets'] = list(self.widgetmodel.widgets())
Example #28
0
class ResultView(QWidget):
    """This class represent a search result view.
    """
    def __init__(self, filter='', attributes=[], resultlist=[], parent=None):
        """Initialize a result view for the `SearchPlugin`.

        :param filter: the filter applied on the search
        :type filter: string
        :param attributes: a list containing the attributes used in the
         search operation. Usually extracted from the `filter`.
        :type attributes: list
        :param resultlist: a list of `SmartDataObject` from the search
         operation.
        :type resultlist: list
        :param parent: the parent for this widget.
        :type parent: QWidget
        """
        super(ResultView, self).__init__(parent)
        self.setObjectName('ResultView')
        self.layout = QtGui.QVBoxLayout(self)

        # Only display the no-result message if resultlist is empty
        if len(resultlist) == 0:
            self.retranslate(all=False)
            self.onNoResult()
            return

        # The proxy model is used for sort and filter support
        self.proxymodel = QSortFilterProxyModel(self)
        self.proxymodel.setDynamicSortFilter(True)

        self.headerdata = ['dn']
        self.headerdata.extend(attributes)
        self.resultdata = resultlist

        # FIXME: should we create a custom item model ?
        self.model = QStandardItemModel(0, len(self.headerdata), parent=self)
        #self.model = ResultItemModel(self)
        #self.model = ResultItemModel(self.headerdata, self.resultdata, self)
        self.proxymodel.setSourceModel(self.model)

        self.resultview = QTreeView(self)
        self.resultview.setUniformRowHeights(True)
        self.resultview.setRootIsDecorated(False)
        self.resultview.setAlternatingRowColors(True)
        self.resultview.setSortingEnabled(True)

        self.resultview.setModel(self.proxymodel)

        # For right-click context menu
        self.resultview.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.resultview.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.layout.addWidget(self.resultview)
        # The filter box enables the user to filter the returned search
        # results. It becomes accessible with Ctrl-F (QKeySequence.Find)
        self.filterBox = ResultFilterWidget(self.headerdata, parent=self)
        self.filterBox.setVisible(False)

        self.layout.addWidget(self.filterBox)

        # We need to call the retranslate method before populating
        # the result data
        self.retranslate()
        #self.model.populateHeader(self.headerdata)
        #self.model.populateModel(self.resultdata)
        self.setHeaderData(self.headerdata)
        self.setResultData(self.resultdata)
        self.resultview.resizeColumnToContents(0)
        self.__createContextMenu()
        self.__connectSlots()

    def __connectSlots(self):
        """Connect signal and slots.
        """
        self.resultview.customContextMenuRequested.connect(
            self.onContextMenuRequested)
        self.filterBox.inputEdit.textChanged['QString'].connect(
            self.onFilterInputChanged)
        self.filterBox.columnBox.currentIndexChanged[int].connect(
            self.onFilterColumnChanged)

    def __getVSpacer(self):
        return QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding,
                                 QtGui.QSizePolicy.Minimum)

    def __createContextMenu(self):
        """Display the context menu.
        """
        self.contextMenu = QtGui.QMenu()
        self.contextMenuView = QtGui.QAction(self)
        self.contextMenuDelete = QtGui.QAction(self)
        self.contextMenuExport = QtGui.QAction(self)
        self.contextMenu.addAction(self.contextMenuView)
        self.contextMenu.addAction(self.contextMenuDelete)
        self.contextMenu.addAction(self.contextMenuExport)

        # Connect the context menu actions to the correct slots
        self.contextMenuView.triggered.connect(self.onViewItemsSelected)
        self.contextMenuDelete.triggered.connect(self.onDeleteItemsSelected)
        self.contextMenuExport.triggered.connect(self.onExportItemsSelected)

    def onNoResult(self):
        """Adds a styled *no result* message to the main layout.
        """
        font = QtGui.QFont()
        font.setBold(True)
        sadface = QtGui.QLabel(self)
        sadface.setPixmap(pixmapFromTheme('face-sad', ':/icons/48/face-sad'))
        noresult = QtGui.QLabel(self)
        noresult.setText(self.str_NO_RESULT)
        noresult.setFont(font)
        hlayout = QtGui.QHBoxLayout()
        hlayout.addItem(self.__getVSpacer())
        hlayout.addWidget(sadface)
        hlayout.addWidget(noresult)
        hlayout.addItem(self.__getVSpacer())

        self.layout.addLayout(hlayout)

    def setHeaderData(self, data=[]):
        """Populates the ``resultview`` model with header data.

        Parameters:

        - `data`: a list with header items. Usually this is the
          attributelist from the LDAP search.
        """
        i = 0
        for header in data:
            self.model.setHeaderData(i, QtCore.Qt.Horizontal, header)
            i += 1

    def setResultData(self, data=[]):
        """Populates the ``resultview`` model with result data.

        Parameters:

        - `data`: a list containing the SmartDataObjects representing
          items in the LDAP search result.
        """
        row = 0
        for obj in data:
            self.model.insertRow(row)
            col = 0
            for attr in self.headerdata:
                if self.isDistinguishedName(attr):
                    modelData = obj.getPrettyDN()
                elif self.isObjectClass(attr):
                    modelData = ','.join(obj.getObjectClasses())
                elif obj.hasAttribute(attr):
                    if obj.isAttributeBinary(attr):
                        modelData = self.str_BINARY_DATA
                    else:
                        modelData = ','.join(obj.getAttributeValueList(attr))

                self.model.setData(self.model.index(row, col), modelData)
                col += 1

            row += 1

    def isDistinguishedName(self, attr):
        """Returns ``True`` if `attr` is a distinguished name,
        ``False`` otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'dn'

    def isObjectClass(self, attr):
        """Returns ``True`` if `attr` is an object class, ``False``
        otherwise.

        Parameters:

        - `attr`: the LDAP string attribute value to check.
        """
        return attr.lower() == 'objectclass'

    def onContextMenuRequested(self, point):
        """Display the context menu
        """

        # FIXME: In order to be able to export, delete and view search
        # result entries. We should make use of the various dialogs in
        # the Browser plugin. Unitl we have refactored the design in a
        # way that allow us to use these without accessing the browser
        # modules, we simple don't provide these options yet.
        return

        self.selection = self.resultview.selectedIndexes()

        deleteSupport = True
        exportSupport = True

        rowsselected = len(self.selection) / len(self.headerdata)

        if not rowsselected > 0:
            self.contextMenu.setEnabled(False)
            self.contextMenu.exec_(self.resultview.mapToGlobal(point))
            return

        self.contextMenu.setEnabled(True)
        # Look over at Browser plugin for implementation of
        # multiselect and operation support validation
        print rowsselected

        self.contextMenuView.setEnabled(True)
        if rowsselected == 1:
            self.contextMenuView.setText(self.str_VIEW_ITEM)
        else:
            self.contextMenuView.setText(self.str_VIEW_ITEMS)

        if deleteSupport:
            self.contextMenuDelete.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuDelete.setText(self.str_DELETE_ITEM)
            else:
                self.contextMenuDelete.setText(self.str_DELETE_ITEMS)

        if exportSupport:
            self.contextMenuExport.setEnabled(True)
            if rowsselected == 1:
                self.contextMenuExport.setText(self.str_EXPORT_ITEM)
            else:
                self.contextMenuExport.setText(self.str_EXPORT_ITEMS)

        # Finally we execute the context menu
        self.contextMenu.exec_(self.resultview.mapToGlobal(point))

    def onViewItemsSelected(self):
        """Slot for the *view* context menu action.
        """
        raise NotImplementedError(
            'Need to implement a proper model for this to be supported')

    def onDeleteItemsSelected(self):
        """Slot for the *delete* context menu action.
        """
        msg = 'Delete from the Search Plugin is not implemented jet.'
        dialog = DeleteDialog(self, msg)
        dialog.setDeleteItems([])
        dialog.exec_()

    def onExportItemsSelected(self):
        """Slot for the 'export' context menu action.
        """
        msg = 'Export from the Search Plugin is not implemented jet.'
        dialog = ExportDialog(self, msg)
        # Only for proof of concept
        dialog.setExportData([])
        dialog.exec_()

    def onFilterBoxVisibilityChanged(self, visible):
        """Slot for the QKeySequence.Find.

        - `visible`: a boolean value indicating wether or not to toggle
          the filter box widget visibility on or off.
        """
        if visible:
            self.filterBox.setVisible(True)
            self.filterBox.inputEdit.setFocus()
        else:
            # I belive it's common practise to clear the filter when
            # the filter box is closed. This is at least the way the
            # filter boxes works for most webbrowsers.
            self.filterBox.inputEdit.clear()
            self.filterBox.setVisible(False)
            self.resultview.setFocus()

    def onFilterInputChanged(self, filter=''):
        """Slot for the filter input in the result filter widget.

        We get the selected syntax from the syntax combobox
        """
        # The PyQt4 QVariant is causing some problems here, when we try
        # to use the <combobox>.itemData directly, even though the data
        # holds valid QRexExp.PatternSyntax values.
        # We therefore need to explicitly make the QVariant and integer.
        i = self.filterBox.syntaxBox.currentIndex()
        syntaxIndex = self.filterBox.syntaxBox.itemData(i).toInt()[0]
        syntax = QtCore.QRegExp.PatternSyntax(syntaxIndex)
        # As of now we do filtering in a case insensitive way, until we
        # come up with a way to introduce case sensitivity selection in a
        # UI inexpensive way. We want to keep the filter widget as clean
        # and simple as possible.
        regex = QtCore.QRegExp(filter, QtCore.Qt.CaseInsensitive, syntax)
        self.proxymodel.setFilterRegExp(regex)

    def onFilterColumnChanged(self, index):
        """Slot for the column combobox in the filter box widget.
        """
        self.proxymodel.setFilterKeyColumn(index)

    def retranslate(self, all=True):
        """For dynamic translation support.
        """
        self.str_VIEW_ITEM = QtGui.QApplication.translate(
            'ResultView', 'View Item')
        self.str_VIEW_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'View Items')
        self.str_DELETE_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Delete Item')
        self.str_DELETE_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Delete Items')
        self.str_EXPORT_ITEM = QtGui.QApplication.translate(
            'ResultView', 'Export Item')
        self.str_EXPORT_ITEMS = QtGui.QApplication.translate(
            'ResultView', 'Export Items')
        self.str_NO_RESULT = QtGui.QApplication.translate(
            'ResultView', 'Sorry, no result to display!')
        self.str_BINARY_DATA = QtGui.QApplication.translate(
            'ResultView', 'Binary Data')
        if all:
            self.filterBox.retranslate()
Example #29
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp
        self.reloadingProject = False
        self.loadedProject = None

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)
        self.projectwidget.projectloaded.connect(self.projectLoaded)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(self.loadprojects)
        self.setuprootitems()

        ConfigEvents.deleteForm.connect(self.delete_form)

        # ## If all the layers are gone we need to reset back to the top state to get the widgets
        # ## back into sync.
        # QgsMapLayerRegistry.instance().removeAll.connect(self.reset_project)

    # def reset_project(self):
    #     print(QgsProject.instance().fileName())
    #     print("ALL REMOVED")
    #     node = self.projectsnode.find_by_filename(QgsProject.instance().fileName())

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])

    def delete_form(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if not node.type() == Treenode.FormNode:
            return

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        print "Delete"
        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            print parentindex
            self.projectList.setCurrentIndex(parentindex)

    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def projectLoaded(self, project):
        roam.utils.info("Project loaded: {}".format(project.name))
        node = self.projectsnode.find_by_name(project.name)
        node.create_children()
        self.projectwidget.setpage(node.page, node, refreshingProject=True)
        self.reloadingProject = False
        self.loadedProject = project
        self.projectList.setExpanded(node.index(), True )

    def nodeselected(self, index, last, reloadProject=False):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project

        if project:
            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)
                return

        if node.nodetype == Treenode.ProjectNode and reloadProject:
            self.reloadingProject = True
            self.projectwidget.setproject(project)
            return

        if project and self.loadedProject != project:
            # Only load the project if it's different the current one.
            roam.utils.info("Swapping to project: {}".format(project.name))
            if self.loadedProject:
                lastnode = self.projectsnode.find_by_name(self.loadedProject.name)
                self.projectList.setExpanded(lastnode.index(), False )
            ## We are closing the open project at this point. Watch for null ref after this.
            self.projectwidget.setproject(project)
            return
        else:
            roam.utils.debug("Setting page")
            self.projectwidget.setpage(node.page, node)

        if node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText("IntraMaps Roam Config Manager")

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

        self.projectwidget.projectbuttonframe.setVisible(not project is None)


    def projectupdated(self, project):
        # index = self.projectList.currentIndex()
        # node = find_node(index)
        # node.refresh()
        print "PROJECT UPDATED"
        node = self.projectsnode.find_by_name(project.name)
        self.projectList.selectionModel().select(node.index(), QItemSelectionModel.ClearAndSelect)
        self.nodeselected(node.index(), None, reloadProject=True)
Example #30
0
class FormWidget(ui_formwidget.Ui_Form, WidgetBase):
    def __init__(self, parent=None):
        super(FormWidget, self).__init__(parent)
        self.setupUi(self)
        self.form = None

        self.iconlabel.mouseReleaseEvent = self.change_icon

        self._currentwidgetid = ''
        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()
        self.formlayersmodel = QgsLayerModel(watchregistry=True)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.layerCombo.setModel(self.formlayers)
        self.useablewidgets.setModel(self.possiblewidgetsmodel)
        self.fieldList.setModel(self.fieldsmodel)

        self.userwidgets.setModel(self.widgetmodel)
        self.userwidgets.selectionModel().currentChanged.connect(self.load_widget)
        self.widgetmodel.rowsRemoved.connect(self.set_widget_config_state)
        self.widgetmodel.rowsInserted.connect(self.set_widget_config_state)
        self.widgetmodel.modelReset.connect(self.set_widget_config_state)

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.addSectionButton.pressed.connect(self.add_section)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.expressionButton.clicked.connect(self.opendefaultexpression)
        self.expressionButton_2.clicked.connect(self.opendefaultexpression_advanced)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        for item, data in defaultevents:
            self.defaultEventsCombo.addItem(item, data)

        self.loadwidgettypes()

        self.formLabelText.textChanged.connect(self.form_name_changed)
        self.newStyleCheck.stateChanged.connect(self.form_style_changed)
        self.layerCombo.currentIndexChanged.connect(self.layer_updated)


        # Gross but ok for now.
        self.blockWidgets = [
            self.fieldList,
            self.nameText,
            self.sectionNameText,
            self.useablewidgets,
            self.hiddenCheck,
            self.requiredCheck,
            self.readonlyCombo,
            self.defaultEventsCombo,
            self.defaultvalueText,
            self.defaultLayerCombo,
            self.defaultFieldCombo,
            self.defaultValueExpression,
            self.savevalueCheck
        ]

        for widget in self.blockWidgets:
            self._connect_save_event(widget)

        self.blockWidgetSignels(True)

        self.useablewidgets.currentIndexChanged.connect(self.swapwidgetconfig)

        menu = QMenu("Field Actions")
        action = menu.addAction("Auto add all fields")
        action.triggered.connect(self.auto_add_fields)

        self.addWidgetButton.setMenu(menu)
        self.addWidgetButton.setPopupMode(QToolButton.MenuButtonPopup)

        self.defaultLayerCombo.layerChanged.connect(self.defaultFieldCombo.setLayer)
        self.addEvent.pressed.connect(self.addEventItem)
        self.btnDeleteForm.pressed.connect(ConfigEvents.deleteForm.emit)

    def unload_project(self):
        print "UNLOAD PROJECT!!!"
        self.blockWidgetSignels(True)

    def _connect_save_event(self, widget):
        if hasattr(widget, "textChanged"):
            widget.textChanged.connect(self._save_current_widget)
        if hasattr(widget, "currentIndexChanged"):
            widget.currentIndexChanged.connect(self._save_current_widget)
        if hasattr(widget, "stateChanged"):
            widget.stateChanged.connect(self._save_current_widget)

    def change_icon(self, *args):
        """
        Change the icon for the form
        """
        icon = QFileDialog.getOpenFileName(self, "Select form icon image", filter="Images (*.png *.svg)")
        if not icon:
            return
        ext = os.path.splitext(icon)[1]
        shutil.copy(icon, os.path.join(self.form.folder, "icon" + ext))
        self.set_icon(self.form.icon)
        self.treenode.emitDataChanged()

    def set_icon(self, path):
        """
        Set the forms icon preview
        :param path: The path to icon.
        """
        pixmap = QPixmap(path)
        w = self.iconlabel.width()
        h = self.iconlabel.height()
        self.iconlabel.setPixmap(pixmap.scaled(w, h, Qt.KeepAspectRatio))

    def layer_updated(self, index):
        """
        Called when the forms layer has changed.
        :param index: The index of the new layer.
        """
        if not self.selected_layer:
            return

        self.updatefields(self.selected_layer)

    def form_style_changed(self, newstyle):
        """
        Called when the form style has changed from label-above style to label-beside style.
        :param newstyle: True if to use the new label-above style forms.
        """
        self.form.settings['newstyle'] = newstyle
        self.treenode.emitDataChanged()

    def form_name_changed(self, text):
        """
        Called when the forms name has changed. Also updates the tree view to reflect the new name.
        :param text: The new text of the label.
        :return:
        """
        self.form.settings['label'] = text
        self.treenode.emitDataChanged()

    def updatewidgetname(self, index):
        """
        Update the widget name if the field has changed. Doesn't change the name if it has been user set already.
        :param index: index of the new field.
        """
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def opendefaultexpression_advanced(self):
        """
        Open the default expression builder for setting advanced default values based on QGIS Expressions.
        """
        layer = self.form.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultValueExpression.text()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultValueExpression.setText(dlg.expressionText())

    def opendefaultexpression(self):
        """
        Open the default expression builder for setting default values based on QGIS Expressions.
        """
        layer = self.form.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def formtabchanged(self, index):
        """
        Called when the tab widget changes tab.  Normally used to control when to render the form preview on demand.
        :param index: The index of the new tab.
        """
        # Don't generate the form preview if we are not on the preview tab.
        if index == 3:
            self.generate_form_preview()

    def generate_form_preview(self):
        """
        Create the form preview to show to the user.
        """
        form = self.form.copy()
        form.settings['widgets'] = list(self.widgetmodel.widgets())
        item = self.frame_2.layout().itemAt(0)
        if item and item.widget():
            item.widget().setParent(None)

        featureform = FeatureForm.from_form(form, form.settings, None, {})
        from roam import defaults
        defaultwidgets = form.widgetswithdefaults()
        layer = form.QGISLayer
        try:
            values = {}
            feature = layer.getFeatures().next()
            defaultvalues = defaults.default_values(defaultwidgets, feature, layer)
            values.update(defaultvalues)
            featureform.bindvalues(values)
        except StopIteration:
            pass

        self.frame_2.layout().addWidget(featureform)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        widgets = self.widgetmodel.widgets()
        for widget in widgets:
            if 'field' in widget:
                yield widget['field']

    def openformfolder(self, url):
        """
        Open the form folder using the OS file manager.
        :param url:
        :return:
        """
        QDesktopServices.openUrl(QUrl.fromLocalFile(self.form.folder))

    def loadwidgettypes(self):
        """
        Load all supported widgets into the combobox for the form designer.
        :return:
        """
        self.useablewidgets.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.useablewidgets.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.useablewidgets.blockSignals(False)

    def set_widget_config_state(self, *args):
        """
        Enable or disable the widget config section based on widget count
        :param args: Unused.
        :return:
        """
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetConfigTabs.setEnabled(haswidgets)

    def add_section(self):
        """
        Add a new widget section into the form. Widget sections can be used to group
        widgets on the form together.
        """
        currentindex = self.userwidgets.currentIndex()
        widget = {"widget": "Section",
                  "name": "default"}
        index = self.widgetmodel.addwidget(widget, currentindex.parent())
        self.userwidgets.setCurrentIndex(index)

    def newwidget(self, field=None):
        """
        Create a new widget. Tries to match the field type to the right kind of widget as a best guess.
        """
        mapping = {QVariant.String: "Text",
                   QVariant.Int: "Number",
                   QVariant.Double: "Number(Double)",
                   QVariant.ByteArray: "Image",
                   QVariant.Date: "Date",
                   QVariant.DateTime: "Date"}
        widget = {}
        if not field:
            field = self.fieldsmodel.index(0, 0).data(Qt.UserRole)
            if not field:
                return
            widget['field'] = field.name()
        else:
            widget['field'] = field.name()

        try:
            widget['widget'] = mapping[field.type()]
        except KeyError:
            widget['widget'] = 'Text'
        # Grab the first field.

        widget['name'] = field.name().replace("_", " ").title()

        currentindex = self.userwidgets.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.userwidgets.setCurrentIndex(index)

    def auto_add_fields(self):
        """
        Auto add all fields to the form config. Any missing fields will be added.
        """
        used = list(self.usedfields())
        for field in self.selected_layer.pendingFields():
            if field.name() in used:
                continue

            self.newwidget(field)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def set_project(self, project, treenode):
        """
        Set the project for this widget also sets the form from the tree node.

        :note: This method is called from the parent node when the page and widget is loaded.
        :param project: The current project.j
        :param treenode: The current tree node.  Can be used to signel a update back to the tree for it to update it
        self.
        """
        roam.utils.debug("FormWidget: Set Project")

        self.blockWidgetSignels(True)

        super(FormWidget, self).set_project(project, treenode)
        self.formlayers.setSelectLayers(self.project.selectlayers)
        form = self.treenode.form
        self.form = form
        self.setform(self.form)

        self.blockWidgetSignels(False)

    def blockWidgetSignels(self, blocking):
        for widget in self.blockWidgets:
            widget.blockSignals(blocking)

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            """
            Get the first layer from the forms layer combo box
            """
            index = self.formlayers.index(0, 0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            """
            Find the layer with the same name in the layer combobox widget
            """
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])
        newstyleform = settings.setdefault('newstyle', True)
        self.set_icon(form.icon)

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.newStyleCheck.setChecked(newstyleform)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        if formtype == "auto":
            formtype = "Auto Generated"
        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.userwidgets.setCurrentIndex(index)
            # self.load_widget(index, None)

        for i in reversed(range(self.eventsLayout.count())):
            child = self.eventsLayout.itemAt(i)
            if child.widget() and isinstance(child.widget(), EventWidget):
                child = self.eventsLayout.takeAt(i)
                child.widget().deleteLater()

        events = settings.get('events', [])
        self.load_events(events)

        ## This has overhead so only do it when the tab is active.
        if self.formtab.currentIndex() == 3:
            self.generate_form_preview()

    def load_events(self, events):
        for event in events:
            self.addEventItem(data=event)

    def addEventItem(self, data=None):
        widget = EventWidget(self.form.QGISLayer, self.widgetmodel, self.eventsWidget)
        widget.removeItem.connect(self.removeEventItem)
        widget.set_data(data)
        self.eventsLayout.addWidget(widget)

    def removeEventItem(self, widget):
        child = self.eventsLayout.removeWidget(widget)
        widget.deleteLater()

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.current_config_widget
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def load_widget(self, index, last):
        """
        Update the UI with the config for the current selected widget.
        """
        self.blockWidgetSignels(True)

        if last:
            roam.utils.debug("Saving last widget")
            self._save_widget(last)

        widget = index.data(Qt.UserRole)
        if not widget:
            self.blockWidgetSignels(False)
            return

        try:
            roam.utils.debug("Loading widget: {0}".format(widget['_id']))
        except KeyError:
            pass
        widgettype = widget['widget']
        if widgettype == "Section":
            self.propertiesStack.setCurrentIndex(1)
            self.sectionNameText.blockSignals(True)
            name = widget['name']
            self.sectionNameText.setText(name)
            self.sectionNameText.blockSignals(False)
            return
        else:
            self.propertiesStack.setCurrentIndex(0)



        field = widget['field']
        self._currentwidgetid = widget.setdefault('_id', str(uuid.uuid4()))
        required = widget.setdefault('required', False)
        savevalue = widget.setdefault('rememberlastvalue', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)
        defaultevents = widget.setdefault('default_events', ['capture'])

        try:
            data = readonly[0]
        except IndexError:
            data = 'never'

        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)

        index = self.defaultEventsCombo.findData(defaultevents)
        self.defaultEventsCombo.setCurrentIndex(index)

        if not isinstance(default, dict):
            self.defaultTab.setCurrentIndex(0)
            self.defaultvalueText.setText(default)
        else:
            self.defaultTab.setCurrentIndex(1)
            layer = default['layer']
            # TODO Handle the case of many layer fall though with defaults
            # Not sure how to handle this in the UI just yet
            if isinstance(layer, list):
                layer = layer[0]

            if isinstance(layer, basestring):
                defaultfield = default['field']
                expression = default['expression']
                self.defaultValueExpression.setText(expression)
                layer = roam.api.utils.layer_by_name(layer)
                # self.defaultLayerCombo.setLayer(layer)
                self.defaultFieldCombo.setLayer(layer)
                self.defaultFieldCombo.setField(defaultfield)

        self.nameText.setText(name)
        self.requiredCheck.setChecked(required)
        self.savevalueCheck.setChecked(savevalue)
        self.hiddenCheck.setChecked(hidden)

        if field is not None:
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)

        index = self.useablewidgets.findText(widgettype)
        if index > -1:
            self.useablewidgets.setCurrentIndex(index)

        config = widget.get('config', {})
        self.updatewidgetconfig(config)

        self.blockWidgetSignels(False)

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.userwidgets.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def current_config_widget(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.useablewidgets.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetconfig(self, config):
        configwidget, _, _ = self.current_config_widget
        self.setconfigwidget(configwidget, config)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_current_widget)
        except TypeError:
            pass

        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_current_widget)

    def _save_current_widget(self, *args):
        _, index = self.currentuserwidget
        self._save_widget(index)

    def _save_widget(self, index):
        # roam.utils.debug("FormWidget: Save widget")
        print("SENDER: {}".format(self.sender().objectName()))
        if not self.project:
            return
        widgetdata = self._get_widget_config()
        try:
            roam.utils.debug("Saving widget {} in project {}".format(widgetdata['_id'], self.project.name))
        except KeyError:
            pass
        self.widgetmodel.setData(index, widgetdata, Qt.UserRole)

    def _get_default_config(self):
        if self.defaultTab.currentIndex() == 0:
            return self.defaultvalueText.text()
        else:
            default = {}
            default['layer'] = self.defaultLayerCombo.currentLayer().name()
            default['field'] = self.defaultFieldCombo.currentField()
            default['expression'] = self.defaultValueExpression.text()
            default['type'] = 'layer-value'
            return default

    def _get_widget_config(self):
        def current_field():
            row = self.fieldList.currentIndex()
            field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
            return field

        configwidget, _, widgettype = self.current_config_widget
        if self.propertiesStack.currentIndex() == 1:
            return {'name': self.sectionNameText.text(),
                    "widget": "Section"}

        widget = {}
        widget['field'] = current_field()
        widget['default'] = self._get_default_config()
        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['rememberlastvalue'] = self.savevalueCheck.isChecked()
        widget['_id'] = self._currentwidgetid
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['default_events'] = self.defaultEventsCombo.itemData(self.defaultEventsCombo.currentIndex())
        widget['hidden'] = self.hiddenCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        return widget

    @property
    def selected_layer(self):
        index = self.formlayers.index(self.layerCombo.currentIndex(), 0)
        layer = index.data(Qt.UserRole)
        return layer

    def write_config(self):
        roam.utils.debug("Write form config")
        if not self.selected_layer:
            return

        self._save_current_widget()
        self.form.settings['layer'] = self.selected_layer.name()
        formtype = self.formtypeCombo.currentText()
        self.form.settings['type'] = "auto" if formtype == "Auto Generated" else formtype
        self.form.settings['label'] = self.formLabelText.text()
        self.form.settings['newstyle'] = self.newStyleCheck.isChecked()
        self.form.settings['widgets'] = list(self.widgetmodel.widgets())
        events = []
        for i in range(self.eventsLayout.count()):
            child = self.eventsLayout.itemAt(i)
            if child.widget() and isinstance(child.widget(), EventWidget):
                widget = child.widget()
                eventdata = widget.get_data()
                events.append(eventdata)
        self.form.settings['events'] = events
Example #31
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal()
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.mapisloaded = False
        self.bar = None

        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.canvas.mapRenderer().setLabelingEngine(QgsPalLabeling())

        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()

        self.formlayersmodel = QgsLayerModel(watchregistry=False)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.selectlayermodel = CaptureLayersModel(watchregistry=False)
        self.selectlayerfilter = LayerTypeFilter()
        self.selectlayerfilter.setSourceModel(self.selectlayermodel)
        self.selectlayermodel.dataChanged.connect(self.selectlayerschanged)

        self.layerCombo.setModel(self.formlayers)
        self.widgetCombo.setModel(self.possiblewidgetsmodel)
        self.selectLayers.setModel(self.selectlayerfilter)
        self.selectLayers_2.setModel(self.selectlayerfilter)
        self.fieldList.setModel(self.fieldsmodel)

        self.widgetlist.setModel(self.widgetmodel)
        self.widgetlist.selectionModel().currentChanged.connect(self.updatecurrentwidget)
        self.widgetmodel.rowsRemoved.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.rowsInserted.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.modelReset.connect(self.setwidgetconfigvisiable)

        self.titleText.textChanged.connect(self.updatetitle)

        QgsProject.instance().readProject.connect(self._readproject)

        self.loadwidgettypes()

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        self.expressionButton.clicked.connect(self.opendefaultexpression)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        self.setpage(4)
        self.form = None

    def setaboutinfo(self):
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(str(QGis.QGIS_VERSION))

    def checkcapturelayers(self):
        haslayers = self.project.hascapturelayers()
        self.formslayerlabel.setVisible(not haslayers)
        return haslayers

    def opendefaultexpression(self):
        layer = self.currentform.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def openformfolder(self, url):
        openfolder(url)

    def selectlayerschanged(self, *args):
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.checkcapturelayers()
        self.selectlayersupdated.emit(self.project.selectlayers)

    def formtabchanged(self, index):
        # preview
        if index == 1:
            self.form.settings['widgets'] = list(self.widgetmodel.widgets())
            self.setformpreview(self.form)


    def setpage(self, page):
        self.stackedWidget.setCurrentIndex(page)

    def reloadproject(self, *args):
        self.setproject(self.project)

    def qgisprojectupdated(self, path):
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText("The QGIS project has been updated. <a href='reload'> "
                                         "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>")

    def openinqgis(self):
        projectfile = self.project.projectfile
        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
                                        .setdefault('qgislocation', qgislocation)

        try:
            openqgis(projectfile, qgislocation)
        except WindowsError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                               "Check qgislocation in roam.config", QgsMessageBar.WARNING)

    def openprojectfolder(self):
        folder = self.project.folder
        openfolder(folder)

    def setwidgetconfigvisiable(self, *args):
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetframe.setEnabled(haswidgets)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def newwidget(self):
        """
        Create a new widget.  The default is a list.
        """
        widget = {}
        widget['widget'] = 'Text'
        # Grab the first field.
        widget['field'] = self.fieldsmodel.index(0, 0).data(QgsFieldModel.FieldNameRole)
        currentindex = self.widgetlist.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.widgetlist.setCurrentIndex(index)

    def loadwidgettypes(self):
        self.widgetCombo.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.widgetCombo.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.widgetCombo.blockSignals(False)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        for widget in self.currentform.widgets:
            yield widget['field']

    @property
    def currentform(self):
        """
        Return the current selected form.
        """
        return self.form

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.widgetlist.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def currentwidgetconfig(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.widgetCombo.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetname(self, index):
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def _save_widgetfield(self, index):
        """
        Save the selected field for the current widget.

        Shows a error if the field is already used but will allow
        the user to still set it in the case of extra logic for that field
        in the forms Python logic.
        """
        widget, index = self.currentuserwidget
        row = self.fieldList.currentIndex()
        field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
        showwarning = field in self.usedfields()
        self.fieldwarninglabel.setVisible(showwarning)
        widget['field'] = field
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectedwidget(self, index):
        configwidget, index, widgettype = self.currentwidgetconfig
        widget, index = self.currentuserwidget
        if not widget:
            return

        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['hidden'] = self.hiddenCheck.isChecked()

        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_default(self):
        widget, index = self.currentuserwidget
        default = self.defaultvalueText.text()
        widget['default'] = default
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectionlayers(self, index, layer, value):
        config = self.project.settings

        self.selectlayermodel.dataChanged.emit(index, index)

    def _save_formtype(self, index):
        formtype = self.formtypeCombo.currentText()
        form = self.currentform
        form.settings['type'] = formtype

    def _save_formname(self, text):
        """
        Save the form label to the settings file.
        """
        try:
            form = self.currentform
            if form is None:
                return
            form.settings['label'] = text
            self.projectupdated.emit()
        except IndexError:
            return

    def _save_layer(self, index):
        """
        Save the selected layer to the settings file.
        """
        index = self.formlayers.index(index, 0)
        layer = index.data(Qt.UserRole)
        if not layer:
            return

        form = self.currentform
        if form is None:
            return

        form.settings['layer'] = layer.name()
        self.updatefields(layer)

    def setsplash(self, splash):
        pixmap = QPixmap(splash)
        w = self.splashlabel.width()
        h = self.splashlabel.height()
        self.splashlabel.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio))

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.disconnectsignals()
        self.mapisloaded = False
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.versionText.setText(project.version)
            self.selectlayermodel.config = project.settings
            self.formlayers.setSelectLayers(self.project.selectlayers)
            self.setsplash(project.splash)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def _closeqgisproject(self):
        if self.canvas.isDrawing():
            return

        self.canvas.freeze(True)
        self.formlayersmodel.removeall()
        self.selectlayermodel.removeall()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.freeze(False)

    def loadmap(self):
        if self.mapisloaded:
            return

        # This is a dirty hack to work around the timer that is in QgsMapCanvas in 2.2.
        # Refresh will stop the canvas timer
        # Repaint will redraw the widget.
        # loadmap is only called once per project load so it's safe to do this here.
        self.canvas.refresh()
        self.canvas.repaint()

        parser = roam.projectparser.ProjectParser.fromFile(self.project.projectfile)
        canvasnode = parser.canvasnode
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.canvas.refresh()

        self.mapisloaded = True

    def _readproject(self, doc):
        self.formlayersmodel.refresh()
        self.selectlayermodel.refresh()
        self._updateforproject(self.project)

    def _updateforproject(self, project):
        self.titleText.setText(project.name)
        self.descriptionText.setPlainText(project.description)

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.currentwidgetconfig
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def updatetitle(self, text):
        self.project.settings['title'] = text
        self.projectlabel.setText(text)
        self.projectupdated.emit()

    def updatewidgetconfig(self, config):
        widgetconfig, index, widgettype = self.currentwidgetconfig
        self.setconfigwidget(widgetconfig, config)

    def setformpreview(self, form):
        def removewidget():
            item = self.frame_2.layout().itemAt(0)
            if item and item.widget():
                item.widget().setParent(None)

        removewidget()

        featureform = FeatureForm.from_form(form, form.settings, None, {})

        self.frame_2.layout().addWidget(featureform)

    def connectsignals(self):
        self.formLabelText.textChanged.connect(self._save_formname)
        self.layerCombo.currentIndexChanged.connect(self._save_layer)
        self.formtypeCombo.currentIndexChanged.connect(self._save_formtype)

        #widget settings
        self.fieldList.currentIndexChanged.connect(self._save_widgetfield)
        self.requiredCheck.toggled.connect(self._save_selectedwidget)
        self.defaultvalueText.textChanged.connect(self._save_default)
        self.widgetCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.widgetCombo.currentIndexChanged.connect(self.swapwidgetconfig)
        self.nameText.textChanged.connect(self._save_selectedwidget)
        self.readonlyCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.hiddenCheck.toggled.connect(self._save_selectedwidget)

    def disconnectsignals(self):
        try:
            self.formLabelText.textChanged.disconnect(self._save_formname)
            self.layerCombo.currentIndexChanged.disconnect(self._save_layer)
            self.formtypeCombo.currentIndexChanged.disconnect(self._save_formtype)

            #widget settings
            self.fieldList.currentIndexChanged.disconnect(self._save_widgetfield)
            self.requiredCheck.toggled.disconnect(self._save_selectedwidget)
            self.defaultvalueText.textChanged.disconnect(self._save_default)
            self.widgetCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.widgetCombo.currentIndexChanged.disconnect(self.swapwidgetconfig)
            self.nameText.textChanged.disconnect(self._save_selectedwidget)
            self.readonlyCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.hiddenCheck.toggled.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            index = self.formlayers.index(0,0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer


        self.disconnectsignals()

        self.form = form

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.widgetlist.setCurrentIndex(index)
            self.updatecurrentwidget(index, None)

        self.connectsignals()

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

        #self.descriptionLabel.setText(configwidget.description)
        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_selectedwidget)

    def updatecurrentwidget(self, index, _):
        """
        Update the UI with the config for the current selected widget.
        """
        if not index.isValid():
            return

        widget = index.data(Qt.UserRole)
        widgettype = widget['widget']
        field = widget['field']
        required = widget.setdefault('required', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)

        try:
            data = readonly[0]
        except:
            data = 'never'

        self.readonlyCombo.blockSignals(True)
        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)
        self.readonlyCombo.blockSignals(False)

        self.defaultvalueText.blockSignals(True)
        if not isinstance(default, dict):
            self.defaultvalueText.setText(default)
            self.defaultvalueText.setEnabled(True)
            self.expressionButton.setEnabled(True)
        else:
            # TODO Handle the more advanced default values.
            self.defaultvalueText.setText("Advanced default set in config")
            self.defaultvalueText.setEnabled(False)
            self.expressionButton.setEnabled(False)
        self.defaultvalueText.blockSignals(False)

        self.nameText.blockSignals(True)
        self.nameText.setText(name)
        self.nameText.blockSignals(False)

        self.requiredCheck.blockSignals(True)
        self.requiredCheck.setChecked(required)
        self.requiredCheck.blockSignals(False)

        self.hiddenCheck.blockSignals(True)
        self.hiddenCheck.setChecked(hidden)
        self.hiddenCheck.blockSignals(False)

        if not field is None:
            self.fieldList.blockSignals(True)
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)
            self.fieldList.blockSignals(False)

        index = self.widgetCombo.findText(widgettype)
        self.widgetCombo.blockSignals(True)
        if index > -1:
            self.widgetCombo.setCurrentIndex(index)
        self.widgetCombo.blockSignals(False)

        self.updatewidgetconfig(config=widget.setdefault('config', {}))

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        title = self.titleText.text()
        description = self.descriptionText.toPlainText()
        version = str(self.versionText.text())

        settings = self.project.settings
        settings['title'] = title
        settings['description'] = description
        settings['version'] = version

        form = self.currentform
        if form:
            form.settings['widgets'] = list(self.widgetmodel.widgets())
            logger.debug(form.settings)

        self.project.save()
        self.projectsaved.emit()
Example #32
0
class ListPairTableView(QTableView):
    """
    2-column table view that enables pairing of list data through combo boxes.
    """
    def __init__(self, parent=None):
        QTableView.__init__(self, parent)

        self.setEditTriggers(QAbstractItemView.DoubleClicked
                             | QAbstractItemView.SelectedClicked)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)

        self._pair_model = QStandardItemModel(1, 2, self)
        self._pair_model.dataChanged.connect(self._on_pair_data_changed)

        self.setModel(self._pair_model)
        self.horizontalHeader().setResizeMode(QHeaderView.Stretch)

        self._combo_delegate = PairComboBoxDelegate(self)
        self.setItemDelegate(self._combo_delegate)

    def set_header_labels(self, labels):
        """
        Set the table's header labels using labels.
        :param labels: Header labels.
        :type labels: list
        """
        if len(labels) < 2:
            return

        lbls = []
        for i in range(2):
            lbls.append(labels[i])

        self._pair_model.setHorizontalHeaderLabels(lbls)

    def clear_view(self):
        """
        Clears all row pairings in the view.
        """
        rows = self._pair_model.rowCount()
        self._pair_model.removeRows(0, rows)

        #Insert blank row
        self.append_row()

    def append_row(self):
        """
        Add a blank row after the last item in the view.
        """
        items = [QStandardItem(), QStandardItem()]

        self._pair_model.appendRow(items)

    def set_combo_selection(self, selection, empty_item=True):
        """
        Set combo selection for both columns. Any existing rows will be removed
        from the view.
        :param selection: A list containing two sub-lists for each column that
        correspond to the selection list for the combobox in each column.
        :type selection: list
        :param empty_item: True to insert an empty first item in each of the
        column comboboxes.
        :type empty_item: bool
        """
        self._combo_delegate.set_items_pair(selection, empty_item)

        self.clear_view()

    def _on_pair_data_changed(self, old_index, new_index):
        """
        This slot asserts whether selections in both columns in a row have
        been specified. If true, then automatically adds a new empty row
        for additional entries; If false, then the empty is removed from
        the view.
        :param old_index: Model index
        :type old_index: QModelIndex
        :param new_index: Model index
        :type new_index: QModelIndex
        """
        row_state = self.row_data_state(new_index.row())

        row_data = self.row_data(new_index.row())
        if row_state == 0:
            self._pair_model.removeRows(new_index.row(), 1)

            if self._pair_model.rowCount() == 0:
                self.append_row()

        elif row_state == 2:
            if not self.is_last_row_empty():
                self.append_row()

    def is_last_row_empty(self):
        """
        :return: True if the last row in the view does not contain any data,
        False if one or both columns contains data.
        :rtype: bool
        """
        last_row_idx = self._pair_model.rowCount() - 1

        last_row_state = self.row_data_state(last_row_idx)
        if last_row_state == 0:
            return True

        else:
            return False

    def row_data_state(self, row_index):
        """
        :param row_index: Row position
        :type row_index: int
        :return: 0 if data for each of the columns is empty. 1 if one column
        contains data and the other is empty. 2 if both columns contain data.
        :rtype: int
        """
        col_0_val, col_1_val = self.row_data(row_index)

        if col_0_val is None and col_1_val is None:
            return 0

        elif self._is_empty(col_0_val) and not self._is_empty(col_1_val):
            return 1

        elif not self._is_empty(col_0_val) and self._is_empty(col_1_val):
            return 1

        elif self._is_empty(col_0_val) and self._is_empty(col_1_val):
            return 0

        elif not self._is_empty(col_0_val) and not self._is_empty(col_1_val):
            return 2

    def _is_empty(self, val):
        if val is None:
            return True

        else:
            if (isinstance(val, str) or isinstance(val, unicode)) and not val:
                return True

        return False

    def row_data(self, row_index):
        """
        :param row_index: Row position
        :type row_index: int
        :return: Data in both first and second columns for the specified row.
        :rtype: tuple
        """
        if row_index >= 0:
            idx_col_0 = self._pair_model.index(row_index, 0)
            idx_col_1 = self._pair_model.index(row_index, 1)

            val_0 = self._pair_model.data(idx_col_0)
            val_1 = self._pair_model.data(idx_col_1)

            return val_0, val_1

        else:
            return None, None

    def column_pairings(self):
        """
        :return: Collection of column matchings specified as specified by the user.
        :rtype: dict
        """
        col_pairings = {}

        for row_idx in range(self._pair_model.rowCount()):
            if self.row_data_state(row_idx) != 0:
                col_val_0, col_val_1 = self.row_data(row_idx)
                col_pairings[col_val_0] = col_val_1

        return col_pairings
Example #33
0
class TreeLegend(QObject):

    toggledLegend = pyqtSignal(list)
    descriptionLegend = pyqtSignal(str)

    def __init__(self, treeView):
        def init():
            self.setHeader()
            self.tree.setModel(self.model)
            self.headerView.setMovable(False)
            self.headerView.setClickable(True)
            self.tree.setSelectionMode(0)  # no selection

        super(TreeLegend, self).__init__()
        self.tree = treeView
        #
        self.hasConnect = self.layer = self.legendItems = None
        self.visibleItems = []
        self.model = QStandardItemModel(0, 1)
        self.headerView = self.tree.header()
        #
        init()
        self._connect()

    def __del__(self):
        if self.hasConnect:
            self._connect(False)
        self.model.clear()
        self.layer.legendChanged.disconnect(self.updateLegendItems)

    def _connect(self, isConnect=True):
        ss = [{
            'signal': self.tree.clicked,
            'slot': self.toggleItem
        }, {
            'signal': self.headerView.sectionClicked,
            'slot': self.toggleHeader
        }, {
            'signal': self.headerView.sectionDoubleClicked,
            'slot': self.emitDescription
        }]
        if isConnect:
            self.hasConnect = True
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            self.hasConnect = False
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def setHeader(self, data=None):
        if data is None:
            self.model.clear()
            nameHeader = 'Select Raster Layer(Palette)'
            font = QFont()
            font.setStrikeOut(False)
            headerModel = QStandardItem(nameHeader)
            headerModel.setData(font, Qt.FontRole)
            tip = "Raster with Palette(Single Band)"
            headerModel.setData(tip, Qt.ToolTipRole)
            self.model.setHorizontalHeaderItem(0, headerModel)
        else:
            headerModel = self.model.horizontalHeaderItem(0)
            label = "%s" % data['name']
            formatMgs = "Layer: %s\nSource: %s\nNumber Class: %d\nWidth: %d\nHeight: %d\nRes.X: %f\nRes.Y: %f\n\n* Double click copy to Clipboard"
            dataMsg = (data['name'], data['source'], data['num_class'],
                       data['width'], data['height'], data['resX'],
                       data['resY'])
            tip = formatMgs % dataMsg
            headerModel.setData(data, Qt.UserRole)
            headerModel.setData(label, Qt.DisplayRole)
            headerModel.setData(tip, Qt.ToolTipRole)

    def setLayer(self, layer):
        self.legendItems = layer.legendSymbologyItems()
        total = len(self.legendItems)
        self.visibleItems = [True for x in range(total)]
        data = {
            'name': layer.name(),
            'source': layer.source(),
            'num_class': total,
            'width': layer.width(),
            'height': layer.height(),
            'resX': layer.rasterUnitsPerPixelX(),
            'resY': layer.rasterUnitsPerPixelY()
        }
        self.setHeader(data)
        #
        if not self.layer is None:
            self.layer.legendChanged.disconnect(self.updateLegendItems)
        layer.legendChanged.connect(self.updateLegendItems)
        self.layer = layer

    def setLegend(self, values):
        def setHeader():
            headerModel = self.model.horizontalHeaderItem(0)
            data = headerModel.data(Qt.UserRole)
            data['num_class'] = len(values)
            self.setHeader(data)

        def createItem(item):
            (pixel, total) = item
            (legend, color) = self.legendItems[pixel]
            name = "[%d] %s" % (pixel, legend)
            tip = "Value pixel: %d\nTotal pixels: %d\nClass name: %s" % (
                pixel, total, legend)
            pix = QPixmap(16, 16)
            pix.fill(color)
            font.setStrikeOut(not self.visibleItems[pixel])
            #
            itemModel = QStandardItem(QIcon(pix), name)
            itemModel.setEditable(False)
            itemModel.setData(font, Qt.FontRole)
            itemModel.setData(tip, Qt.ToolTipRole)
            itemModel.setData(item, Qt.UserRole)
            #
            return itemModel

        setHeader()
        self.model.removeRows(0, self.model.rowCount())
        #
        font = QFont()
        for item in values:
            self.model.appendRow(createItem(item))

    def setEnabled(self, isEnable=True):
        self._connect(isEnable)
        self.tree.setEnabled(isEnable)

    def getLayerName(self):
        headerModel = self.model.horizontalHeaderItem(0)
        return headerModel.data(Qt.UserRole)['name']

    @pyqtSlot()
    def updateLegendItems(self):
        self.legendItems = self.layer.legendSymbologyItems()
        # Refresh legend
        rows = self.model.rowCount()
        row = 0
        while row < rows:
            index = self.model.index(row, 0)
            (pixel, total) = self.model.data(index, Qt.UserRole)
            (legend, color) = self.legendItems[pixel]
            pix = QPixmap(16, 16)
            pix.fill(color)
            self.model.setData(index, QIcon(pix), Qt.DecorationRole)
            row += 1

    @pyqtSlot('QModelIndex')
    def toggleItem(self, index):
        font = index.data(Qt.FontRole)
        strike = not font.strikeOut()
        font.setStrikeOut(strike)
        self.model.setData(index, font, Qt.FontRole)
        #
        (pixel, total) = index.data(Qt.UserRole)
        visible = not strike
        self.visibleItems[pixel] = visible
        #
        self.toggledLegend.emit(self.visibleItems)

    @pyqtSlot(int)
    def toggleHeader(self, logical):
        rowCount = self.model.rowCount()
        if rowCount == 0:
            return

        header = self.model.horizontalHeaderItem(0)
        font = header.data(Qt.FontRole)
        strike = not font.strikeOut()
        font.setStrikeOut(strike)
        header.setData(font, Qt.FontRole)
        #
        items = []
        row = 0
        while row < self.model.rowCount():
            index = self.model.index(row, 0)
            self.model.setData(index, font, Qt.FontRole)
            items.append(index.data(Qt.UserRole))
            row += 1

        visible = not strike
        for item in items:
            (pixel, total) = item
            self.visibleItems[pixel] = visible
        #
        self.toggledLegend.emit(self.visibleItems)

    @pyqtSlot(int)
    def emitDescription(self):
        def getDescription():
            data = self.model.horizontalHeaderItem(0).data(Qt.UserRole)
            formatMgs = "Layer: %s\nSource: %s\nNumber Class: %d\nWidth: %d\nHeight: %d\nRes.X: %f\nRes.Y: %f"
            dataMsg = (data['name'], data['source'], data['num_class'],
                       data['width'], data['height'], data['resX'],
                       data['resY'])
            descHeader = formatMgs % dataMsg
            #
            descItems = ["Value pixel;Total pixels;Class name"]
            rows = self.model.rowCount()
            row = 0
            while row < rows:
                index = self.model.index(row, 0)
                (pixel, total) = self.model.data(index, Qt.UserRole)
                (legend, color) = self.legendItems[pixel]
                descItems.append("%d;%d;%s" % (pixel, total, legend))
                row += 1

            return "%s\n\n%s" % (descHeader, '\n'.join(descItems))

        if self.model.rowCount() > 0:
            self.descriptionLegend.emit(getDescription())
class TemporalSpectralProfilePlugin:

    POINT_SELECTION = 0
    SELECTED_POLYGON = 1

    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.wdg = None
        self.pointTool = None

    def initGui(self):
        # create action
        self.action = QAction(
            QIcon(
                ":/plugins/temporalprofiletool/icons/temporalProfileIcon.png"),
            "Temporal/Spectral Profile", self.iface.mainWindow())
        self.action.setWhatsThis("Plots temporal/spectral profiles")
        QObject.connect(self.action, SIGNAL("triggered()"), self.run)
        self.aboutAction = QAction("About", self.iface.mainWindow())
        QObject.connect(self.aboutAction, SIGNAL("triggered()"), self.about)

        # add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu("&Profile Tool", self.action)
        self.iface.addPluginToMenu("&Profile Tool", self.aboutAction)

        #Init class variables
        self.pointTool = ProfiletoolMapTool(self.iface.mapCanvas(),
                                            self.action)  #the mouselistener
        self.dockOpened = False  #remember for not reopening dock if there's already one opened
        self.pointstoDraw = None  #Polyline in mapcanvas CRS analysed
        self.dblclktemp = None  #enable disctinction between leftclick and doubleclick
        self.mdl = None  #the model whitch in are saved layers analysed caracteristics
        self.selectionmethod = 0  #The selection method defined in option
        self.saveTool = self.canvas.mapTool(
        )  #Save the standard mapttool for restoring it at the end
        self.plotlibrary = None  #The plotting library to use
        self.pointSelectionInstructions = "Click on a raster for temporal/spectral profile (right click to cancel then quit)"
        self.selectedPolygonInstructions = 'Use "Select Features" tool to select polygon(s) designating AOI for which temporal/spectral profile should be calculated'

    def unload(self):
        if not self.wdg is None:
            self.wdg.close()
        self.iface.removeToolBarIcon(self.action)
        self.iface.removePluginMenu("&Profile Tool", self.action)
        self.iface.removePluginMenu("&Profile Tool", self.aboutAction)

    def run(self):
        # first, check posibility
        if self.checkIfOpening() == False:
            return

        #if dock not already opened, open the dock and all the necessary thing (model,doProfile...)
        if self.dockOpened == False:
            self.mdl = QStandardItemModel(0, 6)
            self.wdg = PTDockWidget(self.iface.mainWindow(), self.iface,
                                    self.mdl)
            self.wdg.showIt()
            self.doprofile = DoProfile(self.iface, self.wdg, self.pointTool,
                                       self)
            self.tableViewTool = TableViewTool()
            QObject.connect(self.wdg, SIGNAL("closed(PyQt_PyObject)"),
                            self.cleaning2)
            QObject.connect(self.wdg.tableView, SIGNAL("clicked(QModelIndex)"),
                            self._onClick)
            QObject.connect(self.wdg.pushButton_2, SIGNAL("clicked()"),
                            self.addLayer)
            QObject.connect(self.wdg.pushButton, SIGNAL("clicked()"),
                            self.removeLayer)
            QObject.connect(self.wdg.comboBox,
                            SIGNAL("currentIndexChanged(int)"),
                            self.selectionMethod)
            QObject.connect(self.wdg.cboLibrary,
                            SIGNAL("currentIndexChanged(int)"),
                            self.changePlotLibrary)
            QObject.connect(self.wdg.cboXAxis,
                            SIGNAL("currentIndexChanged(int)"),
                            self.changeXAxisLabeling)
            QObject.connect(self.wdg.leXAxisSteps, SIGNAL("editingFinished()"),
                            self.changeXAxisLabeling)
            QObject.connect(self.wdg.dateTimeEditCurrentTime,
                            SIGNAL("editingFinished()"),
                            self.changeXAxisLabeling)
            QObject.connect(self.wdg.spinBoxTimeExtent,
                            SIGNAL("editingFinished()"),
                            self.changeXAxisLabeling)
            QObject.connect(self.wdg.cboTimeExtent,
                            SIGNAL("currentIndexChanged(int)"),
                            self.changeXAxisLabeling)
            QObject.connect(self.wdg.cbTimeDimension,
                            SIGNAL("stateChanged(int)"),
                            self.changeXAxisLabeling)
            self.wdg.addOptionComboboxItems()
            self.addLayer()
            self.dockOpened = True
        #Listeners of mouse
        self.connectPointMapTool()
        #init the mouse listener comportement and save the classic to restore it on quit
        self.canvas.setMapTool(self.pointTool)

        #Help about what doing
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.iface.mainWindow().statusBar().showMessage(
                self.pointSelectionInstructions)
        elif self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            self.iface.mainWindow().statusBar().showMessage(
                self.selectedPolygonInstructions)

        QObject.connect(QgsMapLayerRegistry.instance(),
                        SIGNAL("layersWillBeRemoved (QStringList)"),
                        self.onLayersWillBeRemoved)

#************************************* Canvas listener actions **********************************************

# Used when layer is about to be removed from QGIS Map Layer Registry

    def onLayersWillBeRemoved(self, layersIds):
        if self.mdl is not None:
            for layerId in layersIds:
                for row in range(self.mdl.rowCount()):
                    if layerId == self.mdl.index(row, 3).data().id():
                        self.removeLayer(row)
                        self.onLayersWillBeRemoved(layersIds)
                        break

    # Use for selected polygon option
    def selectionChanged(self, layer):
        if not layer.geometryType() == QGis.Polygon:
            return
        fullGeometry = QgsGeometry()
        for feature in layer.selectedFeatures():
            if fullGeometry.isEmpty():
                fullGeometry = QgsGeometry(feature.constGeometry())
            else:
                fullGeometry = fullGeometry.combine(feature.constGeometry())
        if not fullGeometry.isEmpty():
            crs = osr.SpatialReference()
            crs.ImportFromProj4(str(layer.crs().toProj4()))
            self.doprofile.calculatePolygonProfile(fullGeometry, crs, self.mdl,
                                                   self.plotlibrary)

#************************************* Mouse listener actions ***********************************************
# Used for point selection option

    def moved(self, point):
        if self.wdg and not self.wdg.cbPlotWhenClick.isChecked():
            if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
                self.doubleClicked(point)
            if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
                pass

    def rightClicked(self, point):  #used to quit the current action
        self.cleaning()

    def leftClicked(self, point):
        self.doubleClicked(point)

    def doubleClicked(self, point):
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.iface.mainWindow().statusBar().showMessage(
                str(point.x()) + ", " + str(point.y()))
            self.doprofile.calculatePointProfile(point, self.mdl,
                                                 self.plotlibrary)
        if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            return

#***************************** open and quit options *******************************************

    def checkIfOpening(self):
        if self.iface.mapCanvas().layerCount() == 0:  #Check a layer is opened
            QMessageBox.warning(self.iface.mainWindow(), "Profile",
                                "First open a raster layer, please")
            return False

        layer = self.iface.activeLayer()

        if layer == None or not isProfilable(
                layer):  #Check if a raster layer is opened and selected
            if self.mdl == None or self.mdl.rowCount() == 0:
                QMessageBox.warning(self.iface.mainWindow(), "Profile Tool",
                                    "Please select a raster layer")
                return False

        return True

    def connectPointMapTool(self):
        QObject.connect(self.pointTool, SIGNAL("moved"), self.moved)
        QObject.connect(self.pointTool, SIGNAL("rightClicked"),
                        self.rightClicked)
        QObject.connect(self.pointTool, SIGNAL("leftClicked"),
                        self.leftClicked)
        QObject.connect(self.pointTool, SIGNAL("doubleClicked"),
                        self.doubleClicked)
        QObject.connect(self.pointTool, SIGNAL("deactivate"),
                        self.deactivatePointMapTool)

    def deactivatePointMapTool(self):  #enable clean exit of the plugin
        QObject.disconnect(self.pointTool, SIGNAL("moved"), self.moved)
        QObject.disconnect(self.pointTool, SIGNAL("leftClicked"),
                           self.leftClicked)
        QObject.disconnect(self.pointTool, SIGNAL("rightClicked"),
                           self.rightClicked)
        QObject.disconnect(self.pointTool, SIGNAL("doubleClicked"),
                           self.doubleClicked)
        self.iface.mainWindow().statusBar().showMessage("")

    def connectSelectedPolygonsTool(self):
        QObject.connect(self.iface.mapCanvas(),
                        SIGNAL("selectionChanged(QgsMapLayer *)"),
                        self.selectionChanged)

    def deactivateSelectedPolygonsTools(self):
        QObject.disconnect(self.iface.mapCanvas(),
                           SIGNAL("selectionChanged(QgsMapLayer *)"),
                           self.selectionChanged)

    def cleaning(self):  #used on right click
        self.canvas.unsetMapTool(self.pointTool)
        self.canvas.setMapTool(self.saveTool)
        self.iface.mainWindow().statusBar().showMessage("")

    def cleaning2(self):  #used when Dock dialog is closed
        QObject.disconnect(self.wdg.tableView, SIGNAL("clicked(QModelIndex)"),
                           self._onClick)
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.deactivatePointMapTool()
        else:
            self.deactivateSelectedPolygonsTools()
        self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION
        QObject.disconnect(self.wdg.comboBox,
                           SIGNAL("currentIndexChanged(int)"),
                           self.selectionMethod)
        QObject.disconnect(QgsMapLayerRegistry.instance(),
                           SIGNAL("layersWillBeRemoved (QStringList)"),
                           self.onLayersWillBeRemoved)
        self.mdl = None
        self.dockOpened = False
        self.cleaning()
        self.wdg = None

    #***************************** Options *******************************************

    def selectionMethod(self, item):
        if item == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION
            self.pointTool.setCursor(Qt.CrossCursor)
            self.deactivateSelectedPolygonsTools()
            self.connectPointMapTool()
            if not self.canvas.mapTool() == self.pointTool:
                self.canvas.setMapTool(self.pointTool)
            self.iface.mainWindow().statusBar().showMessage(
                self.pointSelectionInstructions)
            self.wdg.changeStatComboBoxItems(
                self.doprofile.getPointProfileStatNames())

        elif item == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            self.selectionmethod = TemporalSpectralProfilePlugin.SELECTED_POLYGON
            self.deactivatePointMapTool()
            self.connectSelectedPolygonsTool()
            self.iface.actionSelectRectangle().trigger()
            self.iface.mainWindow().statusBar().showMessage(
                self.selectedPolygonInstructions)
            self.wdg.changeStatComboBoxItems(
                self.doprofile.getPolygonProfileStatNames(), "mean")

    def changePlotLibrary(self, item):
        self.plotlibrary = self.wdg.cboLibrary.itemText(item)
        self.wdg.addPlotWidget(self.plotlibrary)
        self.changeXAxisLabeling()

    def changeXAxisLabeling(self):
        self.xAxisSteps = {}
        # default band number labeling
        if self.wdg.cboXAxis.currentIndex() == 0:
            self.doprofile.xAxisSteps = None
        # Labels from string
        elif self.wdg.cboXAxis.currentIndex() == 1:
            self.doprofile.xAxisSteps = self.wdg.leXAxisSteps.text().split(';')
            try:
                self.doprofile.xAxisSteps = [
                    float(x) for x in self.doprofile.xAxisSteps
                ]
            except ValueError:
                self.doprofile.xAxisSteps = None
                text = "Temporal/Spectral Profile Tool: The X-axis steps' string " + \
                              "is invalid. Using band numbers instead."
                self.iface.messageBar().pushWidget(
                    self.iface.messageBar().createMessage(text),
                    QgsMessageBar.WARNING, 5)
        # Labels based on time
        elif self.wdg.cboXAxis.currentIndex() == 2:
            self.doprofile.xAxisSteps = [
                "Timesteps",
                self.wdg.dateTimeEditCurrentTime.dateTime().toPyDateTime(),
                int(self.wdg.spinBoxTimeExtent.cleanText()),
                self.wdg.cboTimeExtent.currentText(),
                self.wdg.cbTimeDimension.isChecked()
            ]
            if self.plotlibrary == "Qwt5":
                text = "Temporal/Spectral Profile Tool: There is currently no support using " + \
                              "Time steps while using the Qwt plotlibrary"
                self.iface.messageBar().pushWidget(
                    self.iface.messageBar().createMessage(text),
                    QgsMessageBar.WARNING, 5)
                self.doprofile.xAxisSteps = None

    #************************* tableview function ******************************************

    def addLayer(self):
        layer = self.iface.activeLayer()
        if isProfilable(layer):
            self.tableViewTool.addLayer(self.iface, self.mdl, layer)

    def _onClick(self, index1):  #action when clicking the tableview
        self.tableViewTool.onClick(self.iface, self.wdg, self.mdl,
                                   self.plotlibrary, index1)

    def removeLayer(self, index=None):
        if index is None:
            index = self.tableViewTool.chooseLayerForRemoval(
                self.iface, self.mdl)

        if index is not None:
            self.tableViewTool.removeLayer(self.mdl, index)
            PlottingTool().clearData(self.wdg, self.mdl, self.plotlibrary)

    def about(self):
        from ui.dlgabout import DlgAbout
        DlgAbout(self.iface.mainWindow()).exec_()
class TemporalSpectralProfilePlugin:

    POINT_SELECTION = 0
    SELECTED_POLYGON = 1

    def __init__(self, iface):
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.wdg = None
        self.pointTool = None

    def initGui(self):
        # create action 
        self.action = QAction(QIcon(":/plugins/temporalprofiletool/icons/temporalProfileIcon.png"), "Temporal/Spectral Profile", self.iface.mainWindow())
        self.action.setWhatsThis("Plots temporal/spectral profiles")
        QObject.connect(self.action, SIGNAL("triggered()"), self.run)
        self.aboutAction = QAction("About", self.iface.mainWindow())
        QObject.connect(self.aboutAction, SIGNAL("triggered()"), self.about)

        # add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu("&Profile Tool", self.action)
        self.iface.addPluginToMenu("&Profile Tool", self.aboutAction)
        
        #Init class variables
        self.pointTool = ProfiletoolMapTool(self.iface.mapCanvas(),self.action)        #the mouselistener
        self.dockOpened = False        #remember for not reopening dock if there's already one opened
        self.pointstoDraw = None    #Polyline in mapcanvas CRS analysed
        self.dblclktemp = None        #enable disctinction between leftclick and doubleclick
        self.mdl = None                #the model whitch in are saved layers analysed caracteristics
        self.selectionmethod = 0                        #The selection method defined in option
        self.saveTool = self.canvas.mapTool()            #Save the standard mapttool for restoring it at the end
        self.plotlibrary = None                            #The plotting library to use
        self.pointSelectionInstructions = "Click on a raster for temporal/spectral profile (right click to cancel then quit)"
        self.selectedPolygonInstructions = 'Use "Select Features" tool to select polygon(s) designating AOI for which temporal/spectral profile should be calculated'

    def unload(self):
        if not self.wdg is None:
            self.wdg.close()
        self.iface.removeToolBarIcon(self.action)
        self.iface.removePluginMenu("&Profile Tool", self.action)
        self.iface.removePluginMenu("&Profile Tool", self.aboutAction)

    def run(self):
        # first, check posibility
        if self.checkIfOpening() == False:
            return

        #if dock not already opened, open the dock and all the necessary thing (model,doProfile...)
        if self.dockOpened == False:
            self.mdl = QStandardItemModel(0, 6)
            self.wdg = PTDockWidget(self.iface.mainWindow(), self.iface, self.mdl)
            self.wdg.showIt()
            self.doprofile = DoProfile(self.iface,self.wdg,self.pointTool, self)
            self.tableViewTool = TableViewTool()
            QObject.connect(self.wdg, SIGNAL( "closed(PyQt_PyObject)" ), self.cleaning2)
            QObject.connect(self.wdg.tableView,SIGNAL("clicked(QModelIndex)"), self._onClick) 
            QObject.connect(self.wdg.pushButton_2, SIGNAL("clicked()"), self.addLayer)
            QObject.connect(self.wdg.pushButton, SIGNAL("clicked()"), self.removeLayer)
            QObject.connect(self.wdg.comboBox, SIGNAL("currentIndexChanged(int)"), self.selectionMethod)
            QObject.connect(self.wdg.cboLibrary, SIGNAL("currentIndexChanged(int)"), self.changePlotLibrary)
            QObject.connect(self.wdg.cboXAxis, SIGNAL("currentIndexChanged(int)"), self.changeXAxisLabeling)
            QObject.connect(self.wdg.leXAxisSteps, SIGNAL("editingFinished()"), self.changeXAxisLabeling)
            QObject.connect(self.wdg.dateTimeEditCurrentTime, SIGNAL("editingFinished()"), self.changeXAxisLabeling) 
            QObject.connect(self.wdg.spinBoxTimeExtent, SIGNAL("editingFinished()"), self.changeXAxisLabeling) 
            QObject.connect(self.wdg.cboTimeExtent, SIGNAL("currentIndexChanged(int)"), self.changeXAxisLabeling)
            QObject.connect(self.wdg.cbTimeDimension, SIGNAL("stateChanged(int)"), self.changeXAxisLabeling)            
            self.wdg.addOptionComboboxItems()
            self.addLayer()    
            self.dockOpened = True
        #Listeners of mouse
        self.connectPointMapTool()
        #init the mouse listener comportement and save the classic to restore it on quit
        self.canvas.setMapTool(self.pointTool)
        
        #Help about what doing
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.iface.mainWindow().statusBar().showMessage(self.pointSelectionInstructions )
        elif self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            self.iface.mainWindow().statusBar().showMessage(self.selectedPolygonInstructions)
            
        QObject.connect(QgsMapLayerRegistry.instance(), SIGNAL("layersWillBeRemoved (QStringList)"), self.onLayersWillBeRemoved)

#************************************* Canvas listener actions **********************************************
    
    # Used when layer is about to be removed from QGIS Map Layer Registry
    def onLayersWillBeRemoved(self, layersIds):
        if self.mdl is not None:
            for layerId in layersIds:
                for row in range(self.mdl.rowCount()):
                    if layerId == self.mdl.index(row, 3).data().id():
                        self.removeLayer(row)
                        self.onLayersWillBeRemoved(layersIds)
                        break

    # Use for selected polygon option
    def selectionChanged(self, layer):
        if not layer.geometryType() == QGis.Polygon:
            return
        fullGeometry = QgsGeometry()
        for feature in layer.selectedFeatures():
            if fullGeometry.isEmpty():
                fullGeometry = QgsGeometry(feature.constGeometry())
            else:
                fullGeometry = fullGeometry.combine(feature.constGeometry())
        if not fullGeometry.isEmpty():
            crs = osr.SpatialReference()
            crs.ImportFromProj4(str(layer.crs().toProj4()))
            self.doprofile.calculatePolygonProfile(fullGeometry, crs, self.mdl, self.plotlibrary)

#************************************* Mouse listener actions ***********************************************
# Used for point selection option

    def moved(self, point):
        if self.wdg and not self.wdg.cbPlotWhenClick.isChecked():
            if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
                self.doubleClicked(point)
            if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
                pass

    def rightClicked(self, point):    #used to quit the current action
        self.cleaning()

    def leftClicked(self, point):
        self.doubleClicked(point)

    def doubleClicked(self, point):
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.iface.mainWindow().statusBar().showMessage(str(point.x())+", "+str(point.y()))
            self.doprofile.calculatePointProfile(point, self.mdl, self.plotlibrary)
        if self.selectionmethod == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            return

#***************************** open and quit options *******************************************
    
    def checkIfOpening(self):
        if self.iface.mapCanvas().layerCount() == 0:                    #Check a layer is opened
            QMessageBox.warning(self.iface.mainWindow(), "Profile", "First open a raster layer, please")
            return False

        layer = self.iface.activeLayer()
        
        if layer == None or not isProfilable(layer) :    #Check if a raster layer is opened and selected
            if self.mdl == None or self.mdl.rowCount() == 0:
                QMessageBox.warning(self.iface.mainWindow(), "Profile Tool", "Please select a raster layer")
                return False
                
        return True
    
    def connectPointMapTool(self):
        QObject.connect(self.pointTool, SIGNAL("moved"), self.moved)
        QObject.connect(self.pointTool, SIGNAL("rightClicked"), self.rightClicked)
        QObject.connect(self.pointTool, SIGNAL("leftClicked"), self.leftClicked)
        QObject.connect(self.pointTool, SIGNAL("doubleClicked"), self.doubleClicked)
        QObject.connect(self.pointTool, SIGNAL("deactivate"), self.deactivatePointMapTool)
        
    def deactivatePointMapTool(self):        #enable clean exit of the plugin
        QObject.disconnect(self.pointTool, SIGNAL("moved"), self.moved)
        QObject.disconnect(self.pointTool, SIGNAL("leftClicked"), self.leftClicked)
        QObject.disconnect(self.pointTool, SIGNAL("rightClicked"), self.rightClicked)
        QObject.disconnect(self.pointTool, SIGNAL("doubleClicked"), self.doubleClicked)
        self.iface.mainWindow().statusBar().showMessage("")

    def connectSelectedPolygonsTool(self):
        QObject.connect(self.iface.mapCanvas(), SIGNAL("selectionChanged(QgsMapLayer *)"), self.selectionChanged)
        
    def deactivateSelectedPolygonsTools(self):
        QObject.disconnect(self.iface.mapCanvas(), SIGNAL("selectionChanged(QgsMapLayer *)"), self.selectionChanged)

    def cleaning(self):            #used on right click
        self.canvas.unsetMapTool(self.pointTool)
        self.canvas.setMapTool(self.saveTool)
        self.iface.mainWindow().statusBar().showMessage("")

    def cleaning2(self):        #used when Dock dialog is closed
        QObject.disconnect(self.wdg.tableView,SIGNAL("clicked(QModelIndex)"), self._onClick)
        if self.selectionmethod == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.deactivatePointMapTool()
        else:
            self.deactivateSelectedPolygonsTools()
        self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION
        QObject.disconnect(self.wdg.comboBox, SIGNAL("currentIndexChanged(int)"), self.selectionMethod)
        QObject.disconnect(QgsMapLayerRegistry.instance(), SIGNAL("layersWillBeRemoved (QStringList)"), self.onLayersWillBeRemoved)
        self.mdl = None
        self.dockOpened = False
        self.cleaning()
        self.wdg = None

    #***************************** Options *******************************************

    def selectionMethod(self,item):
        if item == TemporalSpectralProfilePlugin.POINT_SELECTION:
            self.selectionmethod = TemporalSpectralProfilePlugin.POINT_SELECTION
            self.pointTool.setCursor(Qt.CrossCursor)
            self.deactivateSelectedPolygonsTools()
            self.connectPointMapTool()
            if not self.canvas.mapTool() == self.pointTool:
                self.canvas.setMapTool(self.pointTool)
            self.iface.mainWindow().statusBar().showMessage(self.pointSelectionInstructions)
            self.wdg.changeStatComboBoxItems(self.doprofile.getPointProfileStatNames())
            
        elif item == TemporalSpectralProfilePlugin.SELECTED_POLYGON:
            self.selectionmethod = TemporalSpectralProfilePlugin.SELECTED_POLYGON
            self.deactivatePointMapTool()
            self.connectSelectedPolygonsTool()
            self.iface.actionSelectRectangle().trigger()
            self.iface.mainWindow().statusBar().showMessage(self.selectedPolygonInstructions)
            self.wdg.changeStatComboBoxItems(self.doprofile.getPolygonProfileStatNames(), "mean")  
                
    def changePlotLibrary(self, item):
        self.plotlibrary = self.wdg.cboLibrary.itemText(item)
        self.wdg.addPlotWidget(self.plotlibrary)
        self.changeXAxisLabeling()
        
    def changeXAxisLabeling(self):
        self.xAxisSteps = {}
        # default band number labeling
        if self.wdg.cboXAxis.currentIndex() == 0:
            self.doprofile.xAxisSteps = None
        # Labels from string
        elif self.wdg.cboXAxis.currentIndex() == 1:
            self.doprofile.xAxisSteps = self.wdg.leXAxisSteps.text().split(';')
            try:
               self.doprofile.xAxisSteps = [ float(x) for x in self.doprofile.xAxisSteps ]
            except ValueError:
                self.doprofile.xAxisSteps = None
                text = "Temporal/Spectral Profile Tool: The X-axis steps' string " + \
                              "is invalid. Using band numbers instead."
                self.iface.messageBar().pushWidget(self.iface.messageBar().createMessage(text), 
                                                   QgsMessageBar.WARNING, 5)
        # Labels based on time
        elif self.wdg.cboXAxis.currentIndex() == 2: 
            self.doprofile.xAxisSteps = ["Timesteps", 
                                         self.wdg.dateTimeEditCurrentTime.dateTime().toPyDateTime(), 
                                         int(self.wdg.spinBoxTimeExtent.cleanText()),
                                         self.wdg.cboTimeExtent.currentText(),
                                         self.wdg.cbTimeDimension.isChecked()]
            if self.plotlibrary == "Qwt5":
                text = "Temporal/Spectral Profile Tool: There is currently no support using " + \
                              "Time steps while using the Qwt plotlibrary"
                self.iface.messageBar().pushWidget(self.iface.messageBar().createMessage(text), 
                                                   QgsMessageBar.WARNING, 5)
                self.doprofile.xAxisSteps = None

    #************************* tableview function ******************************************

    def addLayer(self):
        layer = self.iface.activeLayer()
        if isProfilable(layer):
            self.tableViewTool.addLayer(self.iface, self.mdl, layer)

    def _onClick(self,index1):                    #action when clicking the tableview
        self.tableViewTool.onClick(self.iface, self.wdg, self.mdl, self.plotlibrary, index1)

    def removeLayer(self, index = None):
        if index is None:
            index = self.tableViewTool.chooseLayerForRemoval(self.iface, self.mdl)
        
        if index is not None:
            self.tableViewTool.removeLayer(self.mdl, index)
            PlottingTool().clearData(self.wdg, self.mdl, self.plotlibrary)

    def about(self):
        from ui.dlgabout import DlgAbout
        DlgAbout(self.iface.mainWindow()).exec_()
Example #36
0
class ProjectWidget(Ui_Form, QWidget):
    SampleWidgetRole = Qt.UserRole + 1
    projectsaved = pyqtSignal()
    projectupdated = pyqtSignal()
    projectloaded = pyqtSignal(object)
    selectlayersupdated = pyqtSignal(list)
    projectlocationchanged = pyqtSignal(str)

    def __init__(self, parent=None):
        super(ProjectWidget, self).__init__(parent)
        self.setupUi(self)
        self.project = None
        self.mapisloaded = False
        self.bar = None

        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.canvas.mapRenderer().setLabelingEngine(QgsPalLabeling())

        self.fieldsmodel = QgsFieldModel()
        self.widgetmodel = WidgetsModel()
        self.possiblewidgetsmodel = QStandardItemModel()

        self.formlayersmodel = QgsLayerModel(watchregistry=False)
        self.formlayers = CaptureLayerFilter()
        self.formlayers.setSourceModel(self.formlayersmodel)

        self.selectlayermodel = CaptureLayersModel(watchregistry=False)
        self.selectlayerfilter = LayerTypeFilter()
        self.selectlayerfilter.setSourceModel(self.selectlayermodel)
        self.selectlayermodel.dataChanged.connect(self.selectlayerschanged)

        self.layerCombo.setModel(self.formlayers)
        self.widgetCombo.setModel(self.possiblewidgetsmodel)
        self.selectLayers.setModel(self.selectlayerfilter)
        self.selectLayers_2.setModel(self.selectlayerfilter)
        self.fieldList.setModel(self.fieldsmodel)

        self.widgetlist.setModel(self.widgetmodel)
        self.widgetlist.selectionModel().currentChanged.connect(self.updatecurrentwidget)
        self.widgetmodel.rowsRemoved.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.rowsInserted.connect(self.setwidgetconfigvisiable)
        self.widgetmodel.modelReset.connect(self.setwidgetconfigvisiable)

        self.titleText.textChanged.connect(self.updatetitle)

        QgsProject.instance().readProject.connect(self._readproject)

        self.loadwidgettypes()

        self.addWidgetButton.pressed.connect(self.newwidget)
        self.removeWidgetButton.pressed.connect(self.removewidget)

        self.roamVersionLabel.setText("You are running IntraMaps Roam version {}".format(roam.__version__))

        self.openProjectFolderButton.pressed.connect(self.openprojectfolder)
        self.openinQGISButton.pressed.connect(self.openinqgis)

        self.filewatcher = QFileSystemWatcher()
        self.filewatcher.fileChanged.connect(self.qgisprojectupdated)

        self.formfolderLabel.linkActivated.connect(self.openformfolder)
        self.projectupdatedlabel.linkActivated.connect(self.reloadproject)
        self.projectupdatedlabel.hide()
        self.formtab.currentChanged.connect(self.formtabchanged)

        self.expressionButton.clicked.connect(self.opendefaultexpression)

        self.fieldList.currentIndexChanged.connect(self.updatewidgetname)
        self.fieldwarninglabel.hide()

        for item, data in readonlyvalues:
            self.readonlyCombo.addItem(item, data)

        self.setpage(4)
        self.form = None

        self.projectlocations.currentIndexChanged[str].connect(self.projectlocationchanged.emit)

    def setaboutinfo(self):
        self.versionLabel.setText(roam.__version__)
        self.qgisapiLabel.setText(str(QGis.QGIS_VERSION))

    def checkcapturelayers(self):
        haslayers = self.project.hascapturelayers()
        self.formslayerlabel.setVisible(not haslayers)
        return haslayers

    def opendefaultexpression(self):
        layer = self.currentform.QGISLayer
        dlg = QgsExpressionBuilderDialog(layer, "Create default value expression", self)
        text = self.defaultvalueText.text().strip('[%').strip('%]').strip()
        dlg.setExpressionText(text)
        if dlg.exec_():
            self.defaultvalueText.setText('[% {} %]'.format(dlg.expressionText()))

    def openformfolder(self, url):
        openfolder(url)

    def selectlayerschanged(self, *args):
        self.formlayers.setSelectLayers(self.project.selectlayers)
        self.checkcapturelayers()
        self.selectlayersupdated.emit(self.project.selectlayers)

    def formtabchanged(self, index):
        # preview
        if index == 1:
            self.form.settings['widgets'] = list(self.widgetmodel.widgets())
            self.setformpreview(self.form)

    def setprojectfolders(self, folders):
        for folder in folders:
            self.projectlocations.addItem(folder)

    def setpage(self, page):
        self.stackedWidget.setCurrentIndex(page)

    def reloadproject(self, *args):
        self.setproject(self.project)

    def qgisprojectupdated(self, path):
        self.projectupdatedlabel.show()
        self.projectupdatedlabel.setText("The QGIS project has been updated. <a href='reload'> "
                                         "Click to reload</a>. <b style=\"color:red\">Unsaved data will be lost</b>")

    def openinqgis(self):
        projectfile = self.project.projectfile
        qgislocation = r'C:\OSGeo4W\bin\qgis.bat'
        qgislocation = roam.config.settings.setdefault('configmanager', {}) \
                                        .setdefault('qgislocation', qgislocation)

        try:
            openqgis(projectfile, qgislocation)
        except WindowsError:
            self.bar.pushMessage("Looks like I couldn't find QGIS",
                               "Check qgislocation in settings.config", QgsMessageBar.WARNING)

    def openprojectfolder(self):
        folder = self.project.folder
        openfolder(folder)

    def setwidgetconfigvisiable(self, *args):
        haswidgets = self.widgetmodel.rowCount() > 0
        self.widgetframe.setEnabled(haswidgets)

    def removewidget(self):
        """
        Remove the selected widget from the widgets list
        """
        widget, index = self.currentuserwidget
        if index.isValid():
            self.widgetmodel.removeRow(index.row(), index.parent())

    def newwidget(self):
        """
        Create a new widget.  The default is a list.
        """
        widget = {}
        widget['widget'] = 'List'
        # Grab the first field.
        widget['field'] = self.fieldsmodel.index(0, 0).data(QgsFieldModel.FieldNameRole)
        currentindex = self.widgetlist.currentIndex()
        currentitem = self.widgetmodel.itemFromIndex(currentindex)
        if currentitem and currentitem.iscontainor():
            parent = currentindex
        else:
            parent = currentindex.parent()
        index = self.widgetmodel.addwidget(widget, parent)
        self.widgetlist.setCurrentIndex(index)

    def loadwidgettypes(self):
        self.widgetCombo.blockSignals(True)
        for widgettype in roam.editorwidgets.core.supportedwidgets():
            try:
                configclass = configmanager.editorwidgets.widgetconfigs[widgettype]
            except KeyError:
                continue

            configwidget = configclass()
            item = QStandardItem(widgettype)
            item.setData(configwidget, Qt.UserRole)
            item.setData(widgettype, Qt.UserRole + 1)
            item.setIcon(QIcon(widgeticon(widgettype)))
            self.widgetCombo.model().appendRow(item)
            self.widgetstack.addWidget(configwidget)
        self.widgetCombo.blockSignals(False)

    def usedfields(self):
        """
        Return the list of fields that have been used by the the current form's widgets
        """
        for widget in self.currentform.widgets:
            yield widget['field']

    @property
    def currentform(self):
        """
        Return the current selected form.
        """
        return self.form

    @property
    def currentuserwidget(self):
        """
        Return the selected user widget.
        """
        index = self.widgetlist.currentIndex()
        return index.data(Qt.UserRole), index

    @property
    def currentwidgetconfig(self):
        """
        Return the selected widget in the widget combo.
        """
        index = self.widgetCombo.currentIndex()
        index = self.possiblewidgetsmodel.index(index, 0)
        return index.data(Qt.UserRole), index, index.data(Qt.UserRole + 1)

    def updatewidgetname(self, index):
        # Only change the edit text on name field if it's not already set to something other then the
        # field name.
        field = self.fieldsmodel.index(index, 0).data(QgsFieldModel.FieldNameRole)
        currenttext = self.nameText.text()
        foundfield = self.fieldsmodel.findfield(currenttext)
        if foundfield:
            self.nameText.setText(field)

    def _save_widgetfield(self, index):
        """
        Save the selected field for the current widget.

        Shows a error if the field is already used but will allow
        the user to still set it in the case of extra logic for that field
        in the forms Python logic.
        """
        widget, index = self.currentuserwidget
        row = self.fieldList.currentIndex()
        field = self.fieldsmodel.index(row, 0).data(QgsFieldModel.FieldNameRole)
        showwarning = field in self.usedfields()
        self.fieldwarninglabel.setVisible(showwarning)
        widget['field'] = field
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectedwidget(self, index):
        configwidget, index, widgettype = self.currentwidgetconfig
        widget, index = self.currentuserwidget
        if not widget:
            return

        widget['widget'] = widgettype
        widget['required'] = self.requiredCheck.isChecked()
        widget['config'] = configwidget.getconfig()
        widget['name'] = self.nameText.text()
        widget['read-only-rules'] = [self.readonlyCombo.itemData(self.readonlyCombo.currentIndex())]
        widget['hidden'] = self.hiddenCheck.isChecked()

        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_default(self):
        widget, index = self.currentuserwidget
        default = self.defaultvalueText.text()
        widget['default'] = default
        self.widgetmodel.setData(index, widget, Qt.UserRole)

    def _save_selectionlayers(self, index, layer, value):
        config = self.project.settings

        self.selectlayermodel.dataChanged.emit(index, index)

    def _save_formtype(self, index):
        formtype = self.formtypeCombo.currentText()
        form = self.currentform
        form.settings['type'] = formtype

    def _save_formname(self, text):
        """
        Save the form label to the settings file.
        """
        try:
            form = self.currentform
            if form is None:
                return
            form.settings['label'] = text
            self.projectupdated.emit()
        except IndexError:
            return

    def _save_layer(self, index):
        """
        Save the selected layer to the settings file.
        """
        index = self.formlayers.index(index, 0)
        layer = index.data(Qt.UserRole)
        if not layer:
            return

        form = self.currentform
        if form is None:
            return

        form.settings['layer'] = layer.name()
        self.updatefields(layer)

    def setsplash(self, splash):
        pixmap = QPixmap(splash)
        w = self.splashlabel.width()
        h = self.splashlabel.height()
        self.splashlabel.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio))

    def setproject(self, project, loadqgis=True):
        """
        Set the widgets active project.
        """
        self.disconnectsignals()
        self.mapisloaded = False
        self.filewatcher.removePaths(self.filewatcher.files())
        self.projectupdatedlabel.hide()
        self._closeqgisproject()

        if project.valid:
            self.startsettings = copy.deepcopy(project.settings)
            self.project = project
            self.projectlabel.setText(project.name)
            self.versionText.setText(project.version)
            self.selectlayermodel.config = project.settings
            self.formlayers.setSelectLayers(self.project.selectlayers)
            self.setsplash(project.splash)
            self.loadqgisproject(project, self.project.projectfile)
            self.filewatcher.addPath(self.project.projectfile)
            self.projectloaded.emit(self.project)

    def loadqgisproject(self, project, projectfile):
        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def _closeqgisproject(self):
        if self.canvas.isDrawing():
            return

        self.canvas.freeze(True)
        self.formlayersmodel.removeall()
        self.selectlayermodel.removeall()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.freeze(False)

    def loadmap(self):
        if self.mapisloaded:
            return

        # This is a dirty hack to work around the timer that is in QgsMapCanvas in 2.2.
        # Refresh will stop the canvas timer
        # Repaint will redraw the widget.
        # loadmap is only called once per project load so it's safe to do this here.
        self.canvas.refresh()
        self.canvas.repaint()

        parser = roam.projectparser.ProjectParser.fromFile(self.project.projectfile)
        canvasnode = parser.canvasnode
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.canvas.refresh()

        self.mapisloaded = True

    def _readproject(self, doc):
        self.formlayersmodel.refresh()
        self.selectlayermodel.refresh()
        self._updateforproject(self.project)

    def _updateforproject(self, project):
        self.titleText.setText(project.name)
        self.descriptionText.setPlainText(project.description)

    def swapwidgetconfig(self, index):
        widgetconfig, _, _ = self.currentwidgetconfig
        defaultvalue = widgetconfig.defaultvalue
        self.defaultvalueText.setText(defaultvalue)

        self.updatewidgetconfig({})

    def updatetitle(self, text):
        self.project.settings['title'] = text
        self.projectlabel.setText(text)
        self.projectupdated.emit()

    def updatewidgetconfig(self, config):
        widgetconfig, index, widgettype = self.currentwidgetconfig
        self.setconfigwidget(widgetconfig, config)

    def setformpreview(self, form):
        def removewidget():
            item = self.frame_2.layout().itemAt(0)
            if item and item.widget():
                item.widget().setParent(None)

        removewidget()

        featureform = FeatureForm.from_form(form, form.settings, None, {})

        self.frame_2.layout().addWidget(featureform)

    def connectsignals(self):
        self.formLabelText.textChanged.connect(self._save_formname)
        self.layerCombo.currentIndexChanged.connect(self._save_layer)
        self.formtypeCombo.currentIndexChanged.connect(self._save_formtype)

        #widget settings
        self.fieldList.currentIndexChanged.connect(self._save_widgetfield)
        self.requiredCheck.toggled.connect(self._save_selectedwidget)
        self.defaultvalueText.textChanged.connect(self._save_default)
        self.widgetCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.widgetCombo.currentIndexChanged.connect(self.swapwidgetconfig)
        self.nameText.textChanged.connect(self._save_selectedwidget)
        self.readonlyCombo.currentIndexChanged.connect(self._save_selectedwidget)
        self.hiddenCheck.toggled.connect(self._save_selectedwidget)

    def disconnectsignals(self):
        try:
            self.formLabelText.textChanged.disconnect(self._save_formname)
            self.layerCombo.currentIndexChanged.disconnect(self._save_layer)
            self.formtypeCombo.currentIndexChanged.disconnect(self._save_formtype)

            #widget settings
            self.fieldList.currentIndexChanged.disconnect(self._save_widgetfield)
            self.requiredCheck.toggled.disconnect(self._save_selectedwidget)
            self.defaultvalueText.textChanged.disconnect(self._save_default)
            self.widgetCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.widgetCombo.currentIndexChanged.disconnect(self.swapwidgetconfig)
            self.nameText.textChanged.disconnect(self._save_selectedwidget)
            self.readonlyCombo.currentIndexChanged.disconnect(self._save_selectedwidget)
            self.hiddenCheck.toggled.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

    def setform(self, form):
        """
        Update the UI with the currently selected form.
        """

        def getfirstlayer():
            index = self.formlayers.index(0,0)
            layer = index.data(Qt.UserRole)
            layer = layer.name()
            return layer

        def loadwidgets(widget):
            """
            Load the widgets into widgets model
            """
            self.widgetmodel.clear()
            self.widgetmodel.loadwidgets(form.widgets)

        def findlayer(layername):
            index = self.formlayersmodel.findlayer(layername)
            index = self.formlayers.mapFromSource(index)
            layer = index.data(Qt.UserRole)
            return index, layer


        self.disconnectsignals()

        self.form = form

        settings = form.settings
        label = form.label
        layername = settings.setdefault('layer', getfirstlayer())
        layerindex, layer = findlayer(layername)
        if not layer or not layerindex.isValid():
            return

        formtype = settings.setdefault('type', 'auto')
        widgets = settings.setdefault('widgets', [])

        self.formLabelText.setText(label)
        folderurl = "<a href='{path}'>{name}</a>".format(path=form.folder, name=os.path.basename(form.folder))
        self.formfolderLabel.setText(folderurl)
        self.layerCombo.setCurrentIndex(layerindex.row())
        self.updatefields(layer)

        index = self.formtypeCombo.findText(formtype)
        if index == -1:
            self.formtypeCombo.insertItem(0, formtype)
            self.formtypeCombo.setCurrentIndex(0)
        else:
            self.formtypeCombo.setCurrentIndex(index)

        loadwidgets(widgets)

        # Set the first widget
        index = self.widgetmodel.index(0, 0)
        if index.isValid():
            self.widgetlist.setCurrentIndex(index)
            self.updatecurrentwidget(index, None)

        self.connectsignals()

    def updatefields(self, layer):
        """
        Update the UI with the fields for the selected layer.
        """
        self.fieldsmodel.setLayer(layer)

    def setconfigwidget(self, configwidget, config):
        """
        Set the active config widget.
        """

        try:
            configwidget.widgetdirty.disconnect(self._save_selectedwidget)
        except TypeError:
            pass

        #self.descriptionLabel.setText(configwidget.description)
        self.widgetstack.setCurrentWidget(configwidget)
        configwidget.setconfig(config)

        configwidget.widgetdirty.connect(self._save_selectedwidget)

    def updatecurrentwidget(self, index, _):
        """
        Update the UI with the config for the current selected widget.
        """
        if not index.isValid():
            return

        widget = index.data(Qt.UserRole)
        widgettype = widget['widget']
        field = widget['field']
        required = widget.setdefault('required', False)
        name = widget.setdefault('name', field)
        default = widget.setdefault('default', '')
        readonly = widget.setdefault('read-only-rules', [])
        hidden = widget.setdefault('hidden', False)

        try:
            data = readonly[0]
        except:
            data = 'never'

        self.readonlyCombo.blockSignals(True)
        index = self.readonlyCombo.findData(data)
        self.readonlyCombo.setCurrentIndex(index)
        self.readonlyCombo.blockSignals(False)

        self.defaultvalueText.blockSignals(True)
        if not isinstance(default, dict):
            self.defaultvalueText.setText(default)
        else:
            # TODO Handle the more advanced default values.
            pass
        self.defaultvalueText.blockSignals(False)

        self.nameText.blockSignals(True)
        self.nameText.setText(name)
        self.nameText.blockSignals(False)

        self.requiredCheck.blockSignals(True)
        self.requiredCheck.setChecked(required)
        self.requiredCheck.blockSignals(False)

        self.hiddenCheck.blockSignals(True)
        self.hiddenCheck.setChecked(hidden)
        self.hiddenCheck.blockSignals(False)

        if not field is None:
            self.fieldList.blockSignals(True)
            index = self.fieldList.findData(field.lower(), QgsFieldModel.FieldNameRole)
            if index > -1:
                self.fieldList.setCurrentIndex(index)
            else:
                self.fieldList.setEditText(field)
            self.fieldList.blockSignals(False)

        index = self.widgetCombo.findText(widgettype)
        self.widgetCombo.blockSignals(True)
        if index > -1:
            self.widgetCombo.setCurrentIndex(index)
        self.widgetCombo.blockSignals(False)

        self.updatewidgetconfig(config=widget.setdefault('config', {}))

    def _saveproject(self):
        """
        Save the project config to disk.
        """
        title = self.titleText.text()
        description = self.descriptionText.toPlainText()
        version = str(self.versionText.text())

        settings = self.project.settings
        settings['title'] = title
        settings['description'] = description
        settings['version'] = version

        form = self.currentform
        if form:
            form.settings['widgets'] = list(self.widgetmodel.widgets())
            logger.debug(form.settings)

        self.project.save()
        self.projectsaved.emit()
Example #37
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(
            self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(
            self.loadprojects)
        self.setuprootitems()

        ConfigEvents.deleteForm.connect(self.delete_form)

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])

    def delete_form(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if not node.type() == Treenode.FormNode:
            return

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        print "Delete"
        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            print parentindex
            self.projectList.setCurrentIndex(parentindex)

    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage,
                                         QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def nodeselected(self, index, _):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project
        if project and not self.projectwidget.project == project:
            # Only load the project if it's different the current one.
            self.projectwidget.setproject(project)
            node.create_children()

            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)

        if node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText(
                "IntraMaps Roam Config Manager")

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

        self.projectwidget.projectbuttonframe.setVisible(not project is None)
        self.projectwidget.setpage(node.page, node)

    def projectupdated(self):
        index = self.projectList.currentIndex()
        node = find_node(index)
        node.refresh()
Example #38
0
class Dialog_report(QDialog, Ui_Dialog_report):
    """
    Class documentation goes here.
    """
    def __init__(self, parent = None):
        """
        Constructor
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)
        
                
        #construct Customer list class
        self.clist = Customer_list()        
                
        #construct accounting class
        self.caccount = Dfile()
        
        #construct customer price class
        self.cprice = Customer_price()
        
        #construct standard table
        self.tablemodel = QStandardItemModel(31,  len(PRODUCT_NAME) )
        self.setTableheader()

        #save customer list int
        self.list_customer = self.clist.readCompany()
        self.setCombo( 1,  self.list_customer )  
  
    def setMode(self, str_mode):
        
        if str_mode == 'init':
            self.clearAllshow()
            
    def setCombo(self, comboselect, m_str):
        """
        set combo box
        """
        if comboselect == 1:
            for i in m_str:
                self.comboBox_name.addItem(i)
        elif comboselect == 2:
            self.comboBox_date.clear()
            for i in m_str:                
                self.comboBox_date.addItem(i)
                
    
    def clearAllshow(self):
        
        #clear all spin box 
        self.setAllspin(0)
        
        #clear table
        self.clearTableview()
        
    def setAllspin(self,  int_value):
         self.spinBox_1.setValue(int_value)
         self.spinBox_2.setValue(int_value)
         self.spinBox_3.setValue(int_value)
         self.spinBox_4.setValue(int_value)
         self.spinBox_5.setValue(int_value)
         self.spinBox_6.setValue(int_value)
         self.spinBox_7.setValue(int_value)
         self.spinBox_8.setValue(int_value)
         self.spinBox_9.setValue(int_value)
         self.spinBox_10.setValue(int_value)
         self.spinBox_11.setValue(int_value)
         self.spinBox_12.setValue(int_value)
         self.spinBox_13.setValue(int_value)
         self.spinBox_14.setValue(int_value)
         self.spinBox_15.setValue(int_value)
         self.spinBox_16.setValue(int_value)
         self.spinBox_17.setValue(int_value)
         self.spinBox_18.setValue(int_value)
         self.spinBox_19.setValue(int_value)
         self.spinBox_19.setValue(0)
         
    def setTableheader(self):
                
        #set header data
        self.tablemodel.setHeaderData(0, Qt.Horizontal, PRODUCT_NAME[0] )
        self.tablemodel.setHeaderData(1, Qt.Horizontal, PRODUCT_NAME[1] )
        self.tablemodel.setHeaderData(2, Qt.Horizontal, PRODUCT_NAME[2] )
        self.tablemodel.setHeaderData(3, Qt.Horizontal, PRODUCT_NAME[3] )
        self.tablemodel.setHeaderData(4, Qt.Horizontal, PRODUCT_NAME[4] )
        self.tablemodel.setHeaderData(5, Qt.Horizontal, PRODUCT_NAME[5] )
        
    def setTableview(self,  dlist_data ):
        """
        set data into tableview model
        """
        #show data
        row = 0
        for i in dlist_data:
            self.tablemodel.setData(self.tablemodel.index(row, 0), QVariant(i[0]))
            self.tablemodel.setData(self.tablemodel.index(row, 1), QVariant(i[1]))
            self.tablemodel.setData(self.tablemodel.index(row, 2), QVariant(i[2]))
            self.tablemodel.setData(self.tablemodel.index(row, 3), QVariant(i[3]))
            self.tablemodel.setData(self.tablemodel.index(row, 4), QVariant(i[4]))
            self.tablemodel.setData(self.tablemodel.index(row, 5), QVariant(i[5]))
            row += 1
        
        #set table into tableview
        self.tableView.setModel(self.tablemodel)
        
    def clearTableview(self):
        """
        clear table
        """
        #show data
        row = 0
        i = [0, 0, 0, 0, 0, 0]
        for row in range(31):
            self.tablemodel.setData(self.tablemodel.index(row, 0), QVariant(i[0]))
            self.tablemodel.setData(self.tablemodel.index(row, 1), QVariant(i[1]))
            self.tablemodel.setData(self.tablemodel.index(row, 2), QVariant(i[2]))
            self.tablemodel.setData(self.tablemodel.index(row, 3), QVariant(i[3]))
            self.tablemodel.setData(self.tablemodel.index(row, 4), QVariant(i[4]))
            self.tablemodel.setData(self.tablemodel.index(row, 5), QVariant(i[5]))
        
        #set table into tableview
        self.tableView.setModel(self.tablemodel)
    
    @pyqtSignature("QString")
    def on_comboBox_name_currentIndexChanged(self, p0):
        """
        when name index change, set combo date
        """
        #set to initial status
        self.setMode('init')
        
        #read combo text and dict value
        self.str_customercombo = str( self.comboBox_name.currentText().toUtf8() )
        self.i_customercombo = self.clist.readCvalue( self.str_customercombo )
        
        #read all guest accounting data
        self.list_date = self.caccount.listDatafile( self.i_customercombo )
        
        self.setCombo(2, self.list_date)
    
    @pyqtSignature("QString")
    def on_comboBox_date_currentIndexChanged(self, p0):
        """
        search price data and load accounting into table
        """
        
        self.str_filename = str( self.comboBox_date.currentText() )
        
        if self.str_filename != '':
            self.str_datecombo = self.str_filename[3:9]
        
            #get price version
            self.i_priceversion = self.cprice.selectPrice( self.i_customercombo, self.str_datecombo )
            
            #get price dict
            dict_price = self.cprice.readPrice( self.i_customercombo, self.i_priceversion )
            self.list_price = self.cprice.getClist( dict_price )
            
            #show price 
            self.setPricespin( self.list_price )
            
            #show table
            self.caccount.open_dfile( self.str_filename )
            self.table_data = self.caccount.read_alldfile()
            self.setTableview( self.table_data )
            
            #calculate and show single amount
            self.eachamount = self.sumEachamount( self.table_data )
            self.setEachamount( self.eachamount )
            
            #calculate single price amount
            self.eachpriceamount = [ self.eachamount[i]*self.list_price[i] for i in range(len(PRODUCT_NAME))]
            self.setEachpriceamount( self.eachpriceamount )
            
            #show in total income
            self.spinBox_19.setValue( sum(self.eachpriceamount ) )
            
            
    def setPricespin( self,  list ):
        
         self.spinBox_1.setValue( list[0] )
         self.spinBox_2.setValue( list[1] )
         self.spinBox_3.setValue( list[2] )
         self.spinBox_4.setValue( list[3] )
         self.spinBox_5.setValue( list[4] )
         self.spinBox_6.setValue( list[5] )
    
    def setEachamount( self,  list ):
        
         self.spinBox_7.setValue( list[0] )
         self.spinBox_8.setValue( list[1] )
         self.spinBox_9.setValue( list[2] )
         self.spinBox_10.setValue( list[3] )
         self.spinBox_11.setValue( list[4] )
         self.spinBox_12.setValue( list[5] )
         
    def setEachpriceamount(self,  list ):
        
         self.spinBox_13.setValue( list[0] )
         self.spinBox_14.setValue( list[1] )
         self.spinBox_15.setValue( list[2] )
         self.spinBox_16.setValue( list[3] )
         self.spinBox_17.setValue( list[4] )
         self.spinBox_18.setValue( list[5] )
    
    #sum each item total amount
    def sumEachamount(self,  duallist ):
        
        eachamount = [0, 0, 0, 0, 0, 0]
        count = 0
        for i in duallist:
            for j in i:
                eachamount[count] += j
                count += 1
            count = 0
            
        return eachamount
Example #39
0
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Shows a confusion matrix."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [{"name": "Evaluation Results",
               "type": Orange.evaluation.testing.Results,
               "handler": "set_results"}]
    outputs = [{"name": "Selected Data",
                "type": Orange.data.Table}]

    quantities = ["Number of instances",
                  "Observed and expected instances",
                  "Proportion of predicted",
                  "Proportion of true"]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

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

        self.results = None
        self.learners = []
        self._invalidated = False

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(
            box, self, "selected_learner", "learners",
            callback=self._learner_changed
        )
        box = gui.widgetBox(self.controlArea, "Show")

        combo = gui.comboBox(box, self, "selected_quantity",
                             items=self.quantities,
                             callback=self._update)

        box = gui.widgetBox(self.controlArea, "Selection")

        gui.button(box, self, "Correct",
                   callback=self.select_correct, autoDefault=False)
        gui.button(box, self, "Misclassified",
                   callback=self.select_wrong, autoDefault=False)
        gui.button(box, self, "None",
                   callback=self.select_none, autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box, self, "append_predictions",
                     "Append class predictions", callback=self._invalidate)
        gui.checkBox(box, self, "append_probabilities",
                     "Append predicted class probabilities",
                     callback=self._invalidate)

        b = gui.button(box, self, "Commit", callback=self.commit, default=True)
        cb = gui.checkBox(box, self, "autocommit", "Commit automatically")
        gui.setStopper(self, b, cb, "_invalidated", callback=self.commit)

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        grid.addWidget(QLabel("Predicted"), 0, 1, Qt.AlignCenter)
        grid.addWidget(VerticalLabel("Correct Class"), 1, 0, Qt.AlignCenter)

        self.tablemodel = QStandardItemModel()
        self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers,
        )
        self.tableview.setModel(self.tablemodel)
        self.tableview.selectionModel().selectionChanged.connect(
            self._invalidate
        )
        grid.addWidget(self.tableview, 1, 1)
        self.mainArea.layout().addLayout(grid)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and \
                not isinstance(data.domain.class_var,
                               Orange.data.DiscreteVariable):
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            headers = class_values + [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'fitter_names' is set in 'Test Learners' widget.
            if hasattr(results, "fitter_names"):
                self.learners = results.fitter_names
            else:
                self.learners = ["L %i" % (i + 1) for i in range(nmodels)]

            self.tablemodel.setVerticalHeaderLabels(headers)
            self.tablemodel.setHorizontalHeaderLabels(headers)
            self.tablemodel.setRowCount(len(class_values) + 1)
            self.tablemodel.setColumnCount(len(class_values) + 1)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.learners = []
        self.results = None
        self.data = None
        self.tablemodel.clear()

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect
        )

    def select_none(self):
        self.tableview.selectionModel().clear()

    def commit(self):
        if self.results and self.data:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row(), ind.column()) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [i for i, t in enumerate(zip(actual, predicted))
                        if t in indices]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values
                )
                metas = metas + (var,)

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [Orange.data.ContinuousVariable("p({})".format(value))
                         for value in class_var.values]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]

            M = numpy.hstack((M,) + tuple(extra))
            domain = Orange.data.Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                metas
            )

            data = Orange.data.Table.from_numpy(domain, X, Y, M)

        else:
            data = None

        self.send("Selected Data", data)
        self._invalidated = False

    def _invalidate(self):
        if self.autocommit:
            self.commit()
        else:
            self._invalidated = True

    def _learner_changed(self):
        # The selected learner has changed
        self._update()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                priors = numpy.outer(rowsum, colsum) / total
                value = lambda i, j: \
                    "{} / {:5.3f}".format(cmatrix[i, j], priors[i, j])
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 3:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            model = self.tablemodel
            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = model.item(i, j)
                    if item is None:
                        item = QStandardItem()
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    model.setItem(i, j, item)

            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N, i, sum_item(int(colsum[i])))
                model.setItem(i, N, sum_item(int(rowsum[i])))

            model.setItem(N, N, sum_item(int(total)))
Example #40
0
class Emotion(QtGui.QWidget):
    EMOTION_DIR = "./resource/expression"
    WIDTH = 460
    HEIGHT = 300
    selectChanged = pyqtSignal(str)
    
    def __init__(self,parent=None):#
        super(Emotion,self).__init__(parent)
        super(Emotion,self).setWindowFlags(QtCore.Qt.Popup)
        self.resize(QSize(Emotion.WIDTH,Emotion.HEIGHT))
        self.setWindowTitle("表情選擇")
        #self.setModal(True)
        #self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
        self.emotion_initial()
        
    def emotion_initial(self):
        self.emotion_table = QTableView()
        self.emotion_table.horizontalHeader().setVisible(False)
        self.emotion_table.verticalHeader().setVisible(False)
        self.emotion_table.setMouseTracking(True)
        self.emotion_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.emotion_table.verticalHeader().setDefaultSectionSize(30)
        self.emotion_table.horizontalHeader().setDefaultSectionSize(30)
        self.emotion_table.setIconSize(QSize(30,30))
        self.emotion_table.entered.connect(self.showEmotionTips)
        self.emotion_model = QStandardItemModel()
        
        emotions = os.listdir(Emotion.EMOTION_DIR)
        i = 0
        emotions_size = len(emotions)
        emotions = sorted(emotions,cmp=emotioncmp)
        while i < emotions_size:
            self.add_emotion(emotions[i:i+14])
            i = i + 14
        self.emotion_table.setModel(self.emotion_model)
        self.emotion_table.clicked.connect(self.emotion_click) 
        #
        mainLayout=QVBoxLayout()
        mainLayout.addWidget(self.emotion_table)
        self.setLayout(mainLayout)
        #self.
    def showEmotionTips(self,index):
        if index.isValid():
            QToolTip.showText(QCursor.pos(), "Hello", None)
    
    def add_emotion(self,emotions):
        '''
        :param emotions a list of emotion will be adding to emotion table
        '''
        cells = []
        for emotion in emotions:
            item = QtGui.QStandardItem(QIcon(os.path.join(Emotion.EMOTION_DIR,emotion)),emotion)
            cells.append(item)
        self.emotion_model.appendRow(cells)

    #def eventFilter(self, event):
        #p = QCursor.pos() - self.pos()
        #item = self.emotion_table
        
    def emotion_click(self):
        row = self.emotion_table.currentIndex().row()
        column = self.emotion_table.currentIndex().column()
        #self.accept()
        self.close()
        self.selectChanged.emit(self.get_selected_emotion(row,column))
        
    def get_selected_emotion(self,row,column):
        emotion = self.emotion_model.data(self.emotion_model.index(row, column))
        if emotion:
            return str(emotion.toString())
        else:
            return "N/A"
Example #41
0
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Display confusion matrix constructed from results " \
                  "of evaluation of classifiers."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = [
        "Number of instances", "Proportion of predicted",
        "Proportion of actual"
    ]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    def __init__(self):
        super().__init__()

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(box,
                                        self,
                                        "selected_learner",
                                        "learners",
                                        callback=self._learner_changed)
        box = gui.widgetBox(self.controlArea, "Show")

        gui.comboBox(box,
                     self,
                     "selected_quantity",
                     items=self.quantities,
                     callback=self._update)

        box = gui.widgetBox(self.controlArea, "Select")

        gui.button(box,
                   self,
                   "Correct",
                   callback=self.select_correct,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Misclassified",
                   callback=self.select_wrong,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "None",
                   callback=self.select_none,
                   autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_predictions",
                     "Predictions",
                     callback=self._invalidate)
        gui.checkBox(box,
                     self,
                     "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit", "Send Data",
                        "Auto send is on")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and not data.domain.has_discrete_class:
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = [
                    "Learner #%i" % (i + 1) for i in range(nmodels)
                ]

            item = self._item(0, 2)
            item.setData("Predicted", Qt.DisplayRole)
            item.setTextAlignment(Qt.AlignCenter)
            item.setFlags(Qt.NoItemFlags)

            self._set_item(0, 2, item)
            item = self._item(2, 0)
            item.setData("Actual", Qt.DisplayRole)
            item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
            item.setFlags(Qt.NoItemFlags)
            self.tableview.setItemDelegateForColumn(0,
                                                    gui.VerticalItemDelegate())
            self._set_item(2, 0, item)
            self.tableview.setSpan(0, 2, 1, len(class_values))
            self.tableview.setSpan(2, 0, len(class_values), 1)

            for i in (0, 1):
                for j in (0, 1):
                    item = self._item(i, j)
                    item.setFlags(Qt.NoItemFlags)
                    self._set_item(i, j, item)

            for p, label in enumerate(self.headers):
                for i, j in ((1, p + 2), (p + 2, 1)):
                    item = self._item(i, j)
                    item.setData(label, Qt.DisplayRole)
                    item.setData(QBrush(QColor(208, 208, 208)),
                                 Qt.BackgroundColorRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled)
                    self._set_item(i, j, item)

            hor_header = self.tableview.horizontalHeader()
            if len(' '.join(self.headers)) < 120:
                hor_header.setResizeMode(QHeaderView.ResizeToContents)
            else:
                hor_header.setDefaultSectionSize(60)
            self.tablemodel.setRowCount(len(class_values) + 3)
            self.tablemodel.setColumnCount(len(class_values) + 3)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        # method
        self.learners = []

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def commit(self):
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [
                i for i, t in enumerate(zip(actual, predicted)) if t in indices
            ]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values)
                metas = metas + (var, )

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [
                    Orange.data.ContinuousVariable("p({})".format(value))
                    for value in class_var.values
                ]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M, ) + tuple(extra))
            domain = Orange.data.Domain(self.data.domain.attributes,
                                        self.data.domain.class_vars, metas)
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        self.commit()

    def _learner_changed(self):
        # The selected learner has changed
        indices = self.tableview.selectedIndexes()
        self._update()
        selection = QItemSelection()
        for sel in indices:
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)
        self.commit()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = self._item(i + 2, j + 2)
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            model = self.tablemodel
            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N + 2, i + 2, sum_item(int(colsum[i])))
                model.setItem(i + 2, N + 2, sum_item(int(rowsum[i])))

            model.setItem(N + 2, N + 2, sum_item(int(total)))
Example #42
0
class OWConfusionMatrix(widget.OWWidget):
    """Confusion matrix widget"""

    name = "Confusion Matrix"
    description = "Display a confusion matrix constructed from " \
                  "the results of classifier evaluations."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [("Evaluation Results", Orange.evaluation.Results, "set_results")]
    outputs = [("Selected Data", Orange.data.Table)]

    quantities = ["Number of instances",
                  "Proportion of predicted",
                  "Proportion of actual"]

    settingsHandler = settings.ClassValuesContextHandler()

    selected_learner = settings.Setting([0], schema_only=True)
    selection = settings.ContextSetting(set())
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

    UserAdviceMessages = [
        widget.Message(
            "Clicking on cells or in headers outputs the corresponding "
            "data instances",
            "click_cell")]

    def __init__(self):
        super().__init__()

        self.data = None
        self.results = None
        self.learners = []
        self.headers = []

        box = gui.vBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(
            box, self, "selected_learner", "learners",
            callback=self._learner_changed
        )
        box = gui.vBox(self.controlArea, "Show")

        gui.comboBox(box, self, "selected_quantity", items=self.quantities,
                     callback=self._update)

        box = gui.vBox(self.controlArea, "Select")

        gui.button(box, self, "Select Correct",
                   callback=self.select_correct, autoDefault=False)
        gui.button(box, self, "Select Misclassified",
                   callback=self.select_wrong, autoDefault=False)
        gui.button(box, self, "Clear Selection",
                   callback=self.select_none, autoDefault=False)

        self.outputbox = box = gui.vBox(self.controlArea, "Output")
        gui.checkBox(box, self, "append_predictions",
                     "Predictions", callback=self._invalidate)
        gui.checkBox(box, self, "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit",
                        "Send Selected", "Send Automatically")

        grid = QGridLayout()

        self.tablemodel = QStandardItemModel(self)
        view = self.tableview = QTableView(
            editTriggers=QTableView.NoEditTriggers)
        view.setModel(self.tablemodel)
        view.horizontalHeader().hide()
        view.verticalHeader().hide()
        view.horizontalHeader().setMinimumSectionSize(60)
        view.selectionModel().selectionChanged.connect(self._invalidate)
        view.setShowGrid(False)
        view.setItemDelegate(BorderedItemDelegate(Qt.white))
        view.clicked.connect(self.cell_clicked)
        grid.addWidget(view, 0, 0)
        self.mainArea.layout().addLayout(grid)

    def sizeHint(self):
        """Initial size"""
        return QSize(750, 490)

    def _item(self, i, j):
        return self.tablemodel.item(i, j) or QStandardItem()

    def _set_item(self, i, j, item):
        self.tablemodel.setItem(i, j, item)

    def _init_table(self, nclasses):
        item = self._item(0, 2)
        item.setData("Predicted", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignCenter)
        item.setFlags(Qt.NoItemFlags)

        self._set_item(0, 2, item)
        item = self._item(2, 0)
        item.setData("Actual", Qt.DisplayRole)
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignBottom)
        item.setFlags(Qt.NoItemFlags)
        self.tableview.setItemDelegateForColumn(0, gui.VerticalItemDelegate())
        self._set_item(2, 0, item)
        self.tableview.setSpan(0, 2, 1, nclasses)
        self.tableview.setSpan(2, 0, nclasses, 1)

        font = self.tablemodel.invisibleRootItem().font()
        bold_font = QFont(font)
        bold_font.setBold(True)

        for i in (0, 1):
            for j in (0, 1):
                item = self._item(i, j)
                item.setFlags(Qt.NoItemFlags)
                self._set_item(i, j, item)

        for p, label in enumerate(self.headers):
            for i, j in ((1, p + 2), (p + 2, 1)):
                item = self._item(i, j)
                item.setData(label, Qt.DisplayRole)
                item.setFont(bold_font)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                if p < len(self.headers) - 1:
                    item.setData("br"[j == 1], BorderRole)
                    item.setData(QColor(192, 192, 192), BorderColorRole)
                self._set_item(i, j, item)

        hor_header = self.tableview.horizontalHeader()
        if len(' '.join(self.headers)) < 120:
            hor_header.setResizeMode(QHeaderView.ResizeToContents)
        else:
            hor_header.setDefaultSectionSize(60)
        self.tablemodel.setRowCount(nclasses + 3)
        self.tablemodel.setColumnCount(nclasses + 3)

    def set_results(self, results):
        """Set the input results."""

        prev_sel_learner = self.selected_learner.copy()
        self.clear()
        self.warning()
        self.closeContext()

        data = None
        if results is not None and results.data is not None:
            data = results.data

        if data is not None and not data.domain.has_discrete_class:
            self.warning("Confusion Matrix cannot show regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is None:
            self.report_button.setDisabled(True)
        else:
            self.report_button.setDisabled(False)

            nmodels = results.predicted.shape[0]
            self.headers = class_values + \
                           [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = ["Learner #{}".format(i + 1)
                                 for i in range(nmodels)]

            self._init_table(len(class_values))
            self.openContext(data.domain.class_var)
            if not prev_sel_learner or prev_sel_learner[0] >= len(self.learners):
                self.selected_learner[:] = [0]
            else:
                self.selected_learner[:] = prev_sel_learner
            self._update()
            self._set_selection()
            self.unconditional_commit()

    def clear(self):
        """Reset the widget, clear controls"""
        self.results = None
        self.data = None
        self.tablemodel.clear()
        self.headers = []
        # Clear learners last. This action will invoke `_learner_changed`
        self.learners = []

    def select_correct(self):
        """Select the diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        """Select the off-diagonal elements of the matrix"""
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(2, n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        """Reset selection"""
        self.tableview.selectionModel().clear()

    def cell_clicked(self, model_index):
        """Handle cell click event"""
        i, j = model_index.row(), model_index.column()
        if not i or not j:
            return
        n = self.tablemodel.rowCount()
        index = self.tablemodel.index
        selection = None
        if i == j == 1 or i == j == n - 1:
            selection = QItemSelection(index(2, 2), index(n - 1, n - 1))
        elif i in (1, n - 1):
            selection = QItemSelection(index(2, j), index(n - 1, j))
        elif j in (1, n - 1):
            selection = QItemSelection(index(i, 2), index(i, n - 1))

        if selection is not None:
            self.tableview.selectionModel().select(
                selection, QItemSelectionModel.ClearAndSelect)

    def commit(self):
        """Output data instances corresponding to selected cells"""
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row() - 2, ind.column() - 2) for ind in indices}
            actual = self.results.actual
            learner_name = self.learners[self.selected_learner[0]]
            predicted = self.results.predicted[self.selected_learner[0]]
            selected = [i for i, t in enumerate(zip(actual, predicted))
                        if t in indices]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values
                )
                metas = metas + (var,)

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[self.selected_learner[0],
                                                   selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [Orange.data.ContinuousVariable("p({})".format(value))
                         for value in class_var.values]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M,) + tuple(extra))
            domain = Orange.data.Domain(
                self.data.domain.attributes,
                self.data.domain.class_vars,
                metas
            )
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        indices = self.tableview.selectedIndexes()
        self.selection = {(ind.row() - 2, ind.column() - 2) for ind in indices}
        self.commit()

    def _set_selection(self):
        selection = QItemSelection()
        index = self.tableview.model().index
        for row, col in self.selection:
            sel = index(row + 2, col + 2)
            selection.select(sel, sel)
        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def _learner_changed(self):
        self._update()
        self._set_selection()
        self.commit()

    def _update(self):
        def _isinvalid(x):
            return isnan(x) or isinf(x)

        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            cmatrix = confusion_matrix(self.results, self.selected_learner[0])
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            n = len(cmatrix)
            diag = numpy.diag_indices(n)

            colors = cmatrix.astype(numpy.double)
            colors[diag] = 0
            if self.selected_quantity == 0:
                normalized = cmatrix.astype(numpy.int)
                formatstr = "{}"
                div = numpy.array([colors.max()])
            else:
                if self.selected_quantity == 1:
                    normalized = 100 * cmatrix / colsum
                    div = colors.max(axis=0)
                else:
                    normalized = 100 * cmatrix / rowsum[:, numpy.newaxis]
                    div = colors.max(axis=1)[:, numpy.newaxis]
                formatstr = "{:2.1f} %"
            div[div == 0] = 1
            colors /= div
            colors[diag] = normalized[diag] / normalized[diag].max()

            for i in range(n):
                for j in range(n):
                    val = normalized[i, j]
                    col_val = colors[i, j]
                    item = self._item(i + 2, j + 2)
                    item.setData(
                        "NA" if _isinvalid(val) else formatstr.format(val),
                        Qt.DisplayRole)
                    bkcolor = QColor.fromHsl(
                        [0, 240][i == j], 160,
                        255 if _isinvalid(col_val) else int(255 - 30 * col_val))
                    item.setData(QBrush(bkcolor), Qt.BackgroundRole)
                    item.setData("trbl", BorderRole)
                    item.setToolTip("actual: {}\npredicted: {}".format(
                        self.headers[i], self.headers[j]))
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    self._set_item(i + 2, j + 2, item)

            bold_font = self.tablemodel.invisibleRootItem().font()
            bold_font.setBold(True)

            def _sum_item(value, border=""):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                item.setData(border, BorderRole)
                item.setData(QColor(192, 192, 192), BorderColorRole)
                return item

            for i in range(n):
                self._set_item(n + 2, i + 2, _sum_item(int(colsum[i]), "t"))
                self._set_item(i + 2, n + 2, _sum_item(int(rowsum[i]), "l"))
            self._set_item(n + 2, n + 2, _sum_item(int(rowsum.sum())))

    def send_report(self):
        """Send report"""
        if self.results is not None and self.selected_learner:
            self.report_table(
                "Confusion matrix for {} (showing {})".
                format(self.learners[self.selected_learner[0]],
                       self.quantities[self.selected_quantity].lower()),
                self.tableview)
Example #43
0
class Mineral_Params(QDialog, QTableView):
    def __init__(self,
                 Pressure=1e5,
                 Temperature=300,
                 Volume=None,
                 VolumeOff=True):
        super(Mineral_Params, self).__init__()
        self.Solidsolution = [an,ab,sp,hc,en,fs,mgts,odi,hpcen,hpcfs,di,he,cen,cats,jd,py,al,gr,mgmj,jdmj, \
                          capv,fo,fa,mgwa,fewa,mgri,feri,mgil,feil,co,mgpv,fepv,alpv,mppv,fppv,appv,mgcf,fecf, \
                          nacf,pe,wu,qtz,coes,st,an,ky,neph]
        self.row = len(self.Solidsolution)
        self.layout = QGridLayout(self)

        self.Pressure = Pressure
        self.Temperature = Temperature

        self.table = QTableView()
        self.model = QStandardItemModel(10, 10, self)
        if Volume is None:
            self.Volume = Volume
            self.model.setHorizontalHeaderLabels([
                'Name', 'formuala', 'Vp (km/s)', 'Vs (km/s)', 'Rho (kg/m³)',
                'F (KJ/mol)', 'V (cm³/mol)', 'K (GPa)', "k'", 'θ' + '(K)', 'γ',
                'q', 'G (GPa)', "G'", 'η'
            ])
        else:
            self.Volume = Volume[:-3]
            self.model.setHorizontalHeaderLabels([
                'Name', 'formuala', 'Vp (km/s)', 'Vs (km/s)', 'Rho (kg/m³)',
                'F (KJ/mol)', 'V (cm³/mol)', 'K (GPa)', "k'", 'θ' + '(K)', 'γ',
                'q', 'G (GPa)', "G'", 'η', 'Volume fraction'
            ])

        #self.model.setHorizontalHeaderLabels(['Name','formuala','Vp (km/s)','Vs (km/s)','Rho (kg/m³)','F (KJ/mol)','V (cm³/mol)','K (GPa)',"k'",'θ'+'(K)','γ','q','G (GPa)',"G'",'η','Volume fraction'])

        self.table.setModel(self.model)

        self.layout.addWidget(self.table, 0, 0, 1, 5)

        self.BTN()
        self.table_view()
        self.resize(1800, 800)
        #self.restore()

    def BTN(self):
        return 0
        self.Update = QPushButton(self)
        self.Update.setText("Update")
        self.Update.clicked.connect(self.update_data)
        self.Update.setAutoDefault(False)

        self.Restore = QPushButton(self)
        self.Restore.setText("Restore")
        self.Restore.clicked.connect(self.restore)
        self.Restore.setAutoDefault(False)

        self.Random = QPushButton(self)
        self.Random.setText("Random within error")
        self.Random.clicked.connect(self.random)
        self.Random.setAutoDefault(False)

        self.Save = QPushButton(self)
        self.Save.setText("Save")
        self.Save.clicked.connect(self.save)
        self.Save.setAutoDefault(False)

        self.Iutput = QPushButton(self)
        self.Iutput.setText("Load")
        self.Iutput.clicked.connect(self.iutput)
        self.Iutput.setAutoDefault(False)

        self.layout.addWidget(self.Update, 1, 0, 1, 1)
        self.layout.addWidget(self.Restore, 1, 1, 1, 1)
        self.layout.addWidget(self.Random, 1, 2, 1, 1)
        self.layout.addWidget(self.Save, 1, 3, 1, 1)
        self.layout.addWidget(self.Iutput, 1, 4, 1, 1)

    def save(self):
        options = QFileDialog.Options()
        fileName = QFileDialog.getSaveFileName(
            self,
            "QFileDialog.getSaveFileName()",
            "",
            "All Files (*);;Text Files (*.txt)",
            options=options)
        #except:
        #    fileName, _= QFileDialog.getSaveFileName(self,"QFileDialog.getSaveFileName()","","All Files (*);;Text Files (*.txt)", options=options)
        if PYQT == 5:
            fileName = fileName[0]
        else:
            pass
        if fileName:
            file1 = open(fileName, 'w')
            #print ('open',fileName)
            file1.write(
                'name, F_0,V_0,K_0,Kprime_0 Debye_0,grueneisen-0,q_0,G_0,Gprime_0, eta_s0 '
            )
            file1.write('\n')
            for i in self.Solidsolution:
                for j in i.parameters(self.Pressure, self.Temperature):
                    file1.write('{:8.8}'.format(j))
                    file1.write(' ')
                file1.write('\n')
            file1.close()
            #print ('save')

    def iutput(self):
        fileName = QFileDialog.getOpenFileName(self, 'Open file', '/home')
        if PYQT == 5:
            fileName = fileName[0]
        else:
            pass
        if fileName:
            file1 = open(fileName, 'r')
            next(file1)
            for i, line in enumerate(file1):
                line = line.split()
                newItem = QStandardItem(self.Solidsolution[i].name)
                self.model.setItem(i, 0, newItem)
                for col in range(len(line)):
                    try:
                        string = "{:5.2f}".format(line[col])
                    except:
                        string = line[col]
                    item = QStandardItem(string)
                    self.model.setItem(i, col, item)
            file1.close()
            #print (string)
        #print ('input')

        #self.table_view()

    def table_view(self):
        #self.row_list=[]
        for i in range(self.row):
            newItem = QStandardItem(self.Solidsolution[i].name)
            self.model.setItem(i, 0, newItem)
            params = self.Solidsolution[i].parameters(1e5, 300)
            for col in range(len(params)):
                try:
                    string = "{:5.2f}".format(params[col])
                except:
                    string = params[col]
                item = QStandardItem(string)
                self.model.setItem(i, col, item)
        if self.Volume is not None:
            for num, i in enumerate(self.Volume):
                newItem = QStandardItem(str(self.Volume[num]))
                self.model.setItem(num, 15, newItem)

    def random(self):
        for i in range(self.row):
            newItem = QStandardItem(self.Solidsolution[i].name)
            self.model.setItem(i, 0, newItem)
            params = self.Solidsolution[i].parameters_random(
                self.Pressure, self.Temperature)
            #print ('random')
            for col in range(len(params)):
                try:
                    string = "{:5.2f}".format(params[col])
                except:
                    string = params[col]
                item = QStandardItem(string)
                self.model.setItem(i, col, item)

        #self.table_view()
        #print ('random')

    def update_data(self):
        for i in range(len(self.Solidsolution)):
            params = []
            for j in range(15):
                index = self.model.index(i, j)
                params.append(self.model.itemData(index)[0])
            self.Solidsolution[i].change_parameters(params)
            a, b, c = self.Solidsolution[i].Vp_Vs(self.Pressure * 1e4,
                                                  self.Temperature)
            c *= 1000
            item = QStandardItem("{:5.2f}".format(a))
            self.model.setItem(i, 2, item)
            item = QStandardItem("{:5.2f}".format(b))
            self.model.setItem(i, 3, item)
            item = QStandardItem("{:5.2f}".format(c))
            self.model.setItem(i, 4, item)
        #print ('wf')

    def restore(self):
        try:
            address = os.path.join(os.path.dirname(__file__), 'EXPDATA',
                                   'All.txt')
            self.address = address
            file1 = open(address, 'r+')
        except:
            address = os.path.join(os.path.dirname(__file__),
                                   'Mineral_Physics', 'EXPDATA', 'All.txt')
            self.address = address
            file1 = open(address, 'r')
            next(file1)
            for i, line in enumerate(file1):
                line = line.split()
                newItem = QStandardItem(self.Solidsolution[i].name)
                self.model.setItem(i, 0, newItem)
                for col in range(len(line)):
                    try:
                        string = "{:5.2f}".format(line[col])
                    except:
                        string = line[col]
                    item = QStandardItem(string)
                    self.model.setItem(i, col, item)
            file1.close()
        self.update_data()
Example #44
0
class Main(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setup_signals()
        self.initialize_project()

    def initialize_project(self):
        print 'initializing'
        prj_files = glob.glob('../data/*.conf')

        self.projects = {}

        for config in prj_files:
            path_fname = list(os.path.split(os.path.abspath(config)))
            config_name = os.path.splitext(path_fname[1])[0]
            self.projects[config_name] = path_fname

        self.prj_model = QStandardItemModel(self.ui.ProjectsListView)
        for prj_name in self.projects:
            item = QStandardItem(prj_name)
            print "Adding " + prj_name
            self.prj_model.appendRow(item)

        self.ui.ProjectsListView.setModel(self.prj_model)
        self.ui.ProjectsListView.selectionModel().select(
            self.prj_model.index(0, 0, QModelIndex()),
            QtGui.QItemSelectionModel.Select)

    def show_about(self):
        print "its all about me!"
        dlg = QtGui.QDialog()
        ui = Ui_aboutDialog()
        ui.setupUi(dlg)
        dlg.show()
        dlg.exec_()

    def show_help(self):
        print "show Help!"
        pass

    def new_prj(self):
        print "create new tag name"
        dlg = QtGui.QDialog()
        TagNames(dlg)
        dlg.show()
        dlg.exec_()

    def delete_prj(self):
        print "Delete"

    def do_save(self):
        print "save it"

    def do_save_as(self):
        print "save as this"

    def prj_menu(self, QPos):
        self.listMenu = QtGui.QMenu(self.ui.ProjectsListView)

        menu_item = self.listMenu.addAction("New")
        self.connect(menu_item, QtCore.SIGNAL("triggered()"), self.new_prj)

        menu_item = self.listMenu.addAction("Save")
        self.connect(menu_item, QtCore.SIGNAL("triggered()"), self.do_save)

        menu_item = self.listMenu.addAction("Save as")
        self.connect(menu_item, QtCore.SIGNAL("triggered()"), self.do_save_as)

        menu_item = self.listMenu.addAction("Delete")
        self.connect(menu_item, QtCore.SIGNAL("triggered()"), self.delete_prj)

        parentPosition = self.ui.ProjectsListView.mapToGlobal(
            QtCore.QPoint(0, 0))
        self.listMenu.move(parentPosition + QPos)

        self.listMenu.show()

    def setup_signals(self):
        # will be setting up slots and signals here
        print "setting up slots and signals"
        QtCore.QObject.connect(self.ui.actionAbout,
                               QtCore.SIGNAL(_fromUtf8("activated()")),
                               self.show_about)

        QtCore.QObject.connect(self.ui.actionNew,
                               QtCore.SIGNAL(_fromUtf8("activated()")),
                               self.new_prj)

        QtCore.QObject.connect(self.ui.actionSave,
                               QtCore.SIGNAL(_fromUtf8("activated()")),
                               self.do_save)

        QtCore.QObject.connect(self.ui.actionSave_as,
                               QtCore.SIGNAL(_fromUtf8("activated()")),
                               self.do_save_as)

        QtCore.QObject.connect(self.ui.actionHelp,
                               QtCore.SIGNAL(_fromUtf8("activated()")),
                               self.show_help)

        self.ui.ProjectsListView.setContextMenuPolicy(
            QtCore.Qt.CustomContextMenu)
        self.ui.ProjectsListView.connect(
            self.ui.ProjectsListView,
            QtCore.SIGNAL("customContextMenuRequested(QPoint)"), self.prj_menu)
Example #45
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, roamapp, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.roamapp = roamapp

        # Nope!
        self.projectwidget.roamapp = roamapp
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.projectwidget.projectupdated.connect(self.projectupdated)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.projectlocationchanged.connect(self.loadprojects)
        self.setuprootitems()

    def raiseerror(self, *exinfo):
        self.bar.pushError(*exinfo)
        import roam.errors
        roam.errors.send_exception(exinfo)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

        self.pluginsnode = PluginsNode()
        pluginpath = os.path.join(self.roamapp.apppath, "plugins")
        rootitem.appendRow(self.pluginsnode)
        self.pluginsnode.add_plugin_paths([pluginpath])



    def delete_project(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def nodeselected(self, index, _):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        project = node.project
        if project and not self.projectwidget.project == project:
            # Only load the project if it's different the current one.
            self.projectwidget.setproject(project)
            node.create_children()

            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)

        if node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText("IntraMaps Roam Config Manager")

        if node.nodetype == Treenode.AddNew:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)
            return

        self.projectwidget.projectbuttonframe.setVisible(not project is None)
        self.projectwidget.setpage(node.page, node)

    def projectupdated(self):
        index = self.projectList.currentIndex()
        node = find_node(index)
        node.refresh()
Example #46
0
class ConfigManagerDialog(ui_configmanager.Ui_ProjectInstallerDialog, QDialog):
    def __init__(self, parent=None):
        super(ConfigManagerDialog, self).__init__(parent)
        self.setupUi(self)
        self.bar = roam.messagebaritems.MessageBar(self)
        self.projectwidget.bar = self.bar

        self.treemodel = QStandardItemModel()
        self.projectList.setModel(self.treemodel)
        self.projectList.setHeaderHidden(True)

        self.projectList.selectionModel().currentChanged.connect(self.nodeselected)

        self.projectwidget.adjustSize()
        self.setWindowFlags(Qt.Window)

        self.newProjectButton.pressed.connect(self.newproject)
        self.removeProjectButton.pressed.connect(self.deletebuttonpressed)
        self.projectwidget.projectupdated.connect(self.projectupdated)
        self.projectwidget.projectsaved.connect(self.projectupdated)
        self.projectwidget.projectloaded.connect(self.updateformsnode)
        self.projectwidget.selectlayersupdated.connect(self.updateformsnode)

        self.projectwidget.setaboutinfo()
        self.projectwidget.projects_page.newproject.connect(self.newproject)
        self.projectwidget.projects_page.projectlocationchanged.connect(self.loadprojects)
        self.setuprootitems()

    def updateformsnode(self, *args):
        haslayers = self.projectwidget.checkcapturelayers()
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.nodetype == Treenode.FormsNode:
            self.newProjectButton.setEnabled(haslayers)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        self.bar.pushError('Seems something has gone wrong. Press for more details',
                                  info)

    def setuprootitems(self):
        rootitem = self.treemodel.invisibleRootItem()
        self.roamnode = RoamNode()
        rootitem.appendRow(self.roamnode)

        self.projectsnode = ProjectsNode(folder=None)
        rootitem.appendRow(self.projectsnode)

    def newproject(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node and node.canadd:
            try:
                item = node.additem()
            except ValueError:
                return
            newindex = self.treemodel.indexFromItem(item)
            self.projectList.setCurrentIndex(newindex)

    def deletebuttonpressed(self):
        index = self.projectList.currentIndex()
        node = index.data(Qt.UserRole)
        if node.type() == Treenode.ProjectNode:
            self.projectwidget._closeqgisproject()

        title, removemessage = node.removemessage
        delete = node.canremove
        if node.canremove and removemessage:
            button = QMessageBox.warning(self, title, removemessage, QMessageBox.Yes | QMessageBox.No)
            delete = button == QMessageBox.Yes

        if delete:
            parentindex = index.parent()
            newindex = self.treemodel.index(index.row(), 0, parentindex)
            if parentindex.isValid():
                parent = parentindex.data(Qt.UserRole)
                parent.delete(index.row())

            self.projectList.setCurrentIndex(newindex)

    def addprojectfolders(self, folders):
        self.projectwidget.projects_page.setprojectfolders(folders)

    def loadprojects(self, projectpath):
        projects = roam.project.getProjects([projectpath])
        self.projectsnode.loadprojects(projects, projectsbase=projectpath)

        index = self.treemodel.indexFromItem(self.projectsnode)
        self.projectList.setCurrentIndex(index)
        self.projectList.expand(index)

    def nodeselected(self, index, _):
        node = index.data(Qt.UserRole)
        if node is None:
            return

        self.projectwidget.setpage(node.page)
        self.removeProjectButton.setEnabled(node.canremove)
        self.newProjectButton.setEnabled(node.canadd)
        #self.newProjectButton.setText(node.addtext)
        #self.removeProjectButton.setText(node.removetext)

        project = node.project
        if project and not self.projectwidget.project == project:
            # Only load the project if it's different the current one.
            self.projectwidget.setproject(project)

            validateresults = list(project.validate())
            if validateresults:
                text = "Here are some reasons we found: \n\n"
                for message in validateresults:
                    text += "- {} \n".format(message)

                self.projectwidget.reasons_label.setText(text)

        if node.nodetype == Treenode.FormNode:
            self.projectwidget.setform(node.form)
        elif node.nodetype == Treenode.RoamNode:
            self.projectwidget.projectlabel.setText("IntraMaps Roam Config Manager")
        elif node.nodetype == Treenode.MapNode:
            self.projectwidget.loadmap()
        elif node.nodetype == Treenode.FormsNode:
            haslayers = self.projectwidget.checkcapturelayers()
            self.newProjectButton.setEnabled(haslayers)


        self.projectwidget.projectbuttonframe.setVisible(not project is None)

    def projectupdated(self):
        index = self.projectList.currentIndex()
        self.treemodel.dataChanged.emit(index, index)
Example #47
0
class QMapManager(QDialog):
    def __init__(self, config, parent=None):
        QDialog.__init__(self, parent)
        curdir = os.path.abspath(os.path.dirname(__file__))
        PyQt4.uic.loadUi(os.path.join(curdir, 'manager.ui'), self)
        self.model = QStandardItemModel()
        self.projectsmodel = QStandardItemModel()
        self.projectlist.setModel(self.projectsmodel)
        self.clientlist.setModel(self.model)
        self.clientlist.selectionModel().selectionChanged.connect(self.update)
        self.installbutton.pressed.connect(self.installToClient)
        self.mapper = QDataWidgetMapper()
        self.mapper.setModel(self.model)
        self.mapper.addMapping(self.installpath, 1)
        self.config = config
        self.populateProjects()
        self.populateClients()

    def installToClient(self):
        index = self.clientlist.selectionModel().currentIndex()
        item = self.model.itemFromIndex(index)
        print "Deploying to " + item.text()
        build.deployTargetByName(item.text())

    def update(self, selected, deselected):
        index = selected.indexes()[0]
        self.mapper.setCurrentModelIndex(index)
        item = self.model.itemFromIndex(index)
        settings = item.data()

        for row in xrange(0, self.projectsmodel.rowCount()):
            index = self.projectsmodel.index(row, 0)
            item = self.projectsmodel.itemFromIndex(index)
            item.setCheckState(Qt.Unchecked)

        projects = settings['projects']

        for project in projects:
            if project == "All":
                i = 0
                while self.projectsmodel.item(i):
                    item = self.projectsmodel.item(i)
                    item.setCheckState(Qt.Checked)
                    i += 1
                break

            projectitem = self.projectsmodel.findItems(project)[0]
            projectitem.setCheckState(Qt.Checked)

    def populateClients(self):
        row = 0
        for client, settings in self.config['clients'].iteritems():
            name = QStandardItem(client)
            name.setData(settings)
            path = QStandardItem(settings['path'])
            self.model.insertRow(row, [name, path])
            row += 1

    def populateProjects(self):
        row = 0
        for project in getProjects():
            projectitem = QStandardItem(project.name)
            projectitem.setCheckable(True)
            self.projectsmodel.insertRow(row, projectitem)
            row += 1
Example #48
0
class Regression_PLOT_PyQt(QDialog):
    """
    This calss return a PyQt GUI with a regression plot
    """
    def __init__(self, Minerals=None, string=None, flag=None):
        super(Regression_PLOT_PyQt, self).__init__()
        if string is not None:
            self.stringK, self.stringG, self.stringRho, self.user_input = string
        else:
            self.stringK = None
            self.stringG = None
            self.stringRho = None
            self.user_input = False
        self.resize(1400, 600)
        self.Minerals = Minerals

        self.dirty = False
        #self.user_input = user_input

        self.Minerals.original_flag()
        self.Minerals.read_data()
        self.table = QTableView()
        self.model = QStandardItemModel(25, 7, self)
        self.model.setHorizontalHeaderLabels([
            'flag', 'Water Content', 'Iron Content', 'K (Gpa)', 'G (Gpa)',
            'Rho (g/cm³)', 'Reference'
        ])
        for i in range(len(self.Minerals.Flag)):
            a = self.Minerals.Return_original_data(i)
            for j in range(0, 7):
                item = QStandardItem(str(a[j]))
                self.model.setItem(i, j, item)
                if j != 0:
                    item.setFlags(Qt.ItemIsEnabled)
                    item.setBackground(QColor(211, 211, 211))

        if flag is not None:
            self.Minerals.change_flag(flag)
            for i in range(len(self.Minerals.Flag)):
                item = QStandardItem(str(self.Minerals.Flag[i]))
                self.model.setItem(i, 0, item)

        self.table.setModel(self.model)

        self.button = QPushButton('Update and use in thermoelastic model')
        self.button.clicked.connect(self.Update)
        self.button.setAutoDefault(False)
        self.button1 = QPushButton('Add data file ')
        self.button1.clicked.connect(self.Export)
        self.button1.setAutoDefault(False)
        self.layout = QGridLayout()

        self.label = QLabel()
        self.label.setText('''
        Please input equation, Water: water content (wt%) and Fe: iron content (mol%)
        for example -2.41*Water-30*Fe+81,K'=4.1,  
        ''')

        self.Kinput_formula = QLineEdit()
        self.Ginput_formula = QLineEdit()
        self.Rhoinput_formula = QLineEdit()
        if self.stringK is not None:
            self.Kinput_formula.setText(self.stringK)
            self.Ginput_formula.setText(self.stringG)
            self.Rhoinput_formula.setText(self.stringRho)
            self.Userinput()
        else:
            self.Kinput_formula.setText(
                self.Minerals.Show_fit_function(self.Minerals.function_K()[0],
                                                self.Minerals.function_K()[1],
                                                "K'",
                                                error=False))
            self.Ginput_formula.setText(
                self.Minerals.Show_fit_function(self.Minerals.function_G()[0],
                                                self.Minerals.function_G()[1],
                                                "G'",
                                                error=False))
            self.Rhoinput_formula.setText(
                self.Minerals.Show_fit_function(
                    self.Minerals.function_Rho()[0],
                    self.Minerals.function_Rho()[1],
                    '',
                    error=False))
        self.Kinput_formula.returnPressed.connect(self.Kformula)
        self.Ginput_formula.returnPressed.connect(self.Gformula)
        self.Rhoinput_formula.returnPressed.connect(self.Rhoformula)
        #self.connect(self.Kinput_formula,SIGNAL("returnPressed()"),self.Kformula)
        #self.connect(self.Ginput_formula,SIGNAL("returnPressed()"),self.Gformula)
        #self.connect(self.Rhoinput_formula,SIGNAL("returnPressed()"),self.Rhoformula)

        self.user_change_confrim = QPushButton('Change to user input')
        self.user_change_confrim.clicked.connect(self.Userinput)
        self.user_change_confrim.setAutoDefault(False)

        self.extension = QWidget()
        self.extensionLayout = QGridLayout()
        self.extensionLayout.setContentsMargins(0, 0, 0, 0)

        labelK = QLabel()
        labelK.setText('K<sub>0</sub>=')
        labelG = QLabel()
        labelG.setText('G<sub>0</sub>=')
        labelRho = QLabel()
        labelRho.setText('Rho<sub>0</sub>=')

        self.extensionLayout.addWidget(labelK, 0, 0, 1, 3)
        self.extensionLayout.addWidget(labelG, 1, 0, 1, 3)
        self.extensionLayout.addWidget(labelRho, 2, 0, 1, 3)
        self.extensionLayout.addWidget(self.Kinput_formula, 0, 1, 1, 3)
        self.extensionLayout.addWidget(self.Ginput_formula, 1, 1, 1, 3)
        self.extensionLayout.addWidget(self.Rhoinput_formula, 2, 1, 1, 3)
        self.extensionLayout.addWidget(self.user_change_confrim, 3, 0, 9, 4)

        self.extension.setLayout(self.extensionLayout)

        self.check_change = QCheckBox("user input")
        self.check_change.setChecked(False)
        self.check_change.toggled.connect(self.extension.setVisible)

        #self.PLOT(switch=True)
        self.PLOT()
        self.layout.addWidget(self.table, 0, 1, 1, 17)
        self.layout.addWidget(self.button, 2, 0, 1, 9)
        self.layout.addWidget(self.button1, 2, 9, 1, 9)
        self.layout.addWidget(self.check_change, 3, 0, 1, 1)
        self.layout.addWidget(self.label, 3, 3, 1, 5)
        self.layout.addWidget(self.extension, 4, 0, 1, 9)
        self.setLayout(self.layout)

        self.extension.hide()

    def Kformula(self):
        self.stringK = str(self.Kinput_formula.text())
        self.K0 = np.array(
            re.findall(r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?",
                       self.stringK))
        return self.K0, self.stringK

    def Gformula(self):
        self.stringG = str(self.Ginput_formula.text())
        self.G0 = np.array(
            re.findall(r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?",
                       self.stringG))
        return self.G0, self.stringG

    def Rhoformula(self):
        self.stringRho = str(self.Rhoinput_formula.text())
        self.Rho0 = np.array(
            re.findall(r"[+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?",
                       self.stringRho))
        return self.Rho0, self.stringRho

    def Userinput(self):
        self.Minerals.User_Change(self.Kformula(), self.Gformula(),
                                  self.Rhoformula())
        self.user_input = True
        self.PLOT()

    def PLOT(self, switch=True):
        if self.dirty == False:
            #self.Minerals.Clean_content()
            params = []
            for i in range(20):
                index = self.model.index(i, 0)
                try:
                    aa = (float(self.model.itemData(index)[0]))
                    #print (aa)
                    params.append(aa)
                except:
                    break
            #print (params)
            self.Minerals.Change_data_from_table(params)
            #print ('wf')
        else:
            self.model.setHorizontalHeaderLabels([
                'flag', 'Water Content', 'Iron Content', 'K (Gpa)', 'G (Gpa)',
                'Rho (g/cm3)', 'Reference'
            ])
            for i in range(len(self.Minerals.Flag)):
                #a=self.Minerals.Return_original_data(i)
                item = QStandardItem(str(self.Minerals.Flag[i]))
                self.model.setItem(i, 0, item)


#==============================================================================
#                 for j in range(1,7):
#                     item = QStandardItem(str(a[j]))
#                     self.model.setItem(i, j,item)
#                     if j != 0:
#                         item.setFlags(Qt.ItemIsEnabled)
#                         item.setBackground(QColor(211,211,211))
#==============================================================================

#print (self.Minerals.Change)

        if self.user_input == False:
            self.a, self.b, self.c = self.Minerals.PLOT(return_fig=False)
            #print (self.Minerals.number_of_data)
            self.Kinput_formula.setText(
                self.Minerals.Show_fit_function(self.Minerals.function_K()[0],
                                                self.Minerals.function_K()[1],
                                                "K'",
                                                error=False))
            self.Ginput_formula.setText(
                self.Minerals.Show_fit_function(self.Minerals.function_G()[0],
                                                self.Minerals.function_G()[1],
                                                "G'",
                                                error=False))
            self.Rhoinput_formula.setText(
                self.Minerals.Show_fit_function(
                    self.Minerals.function_Rho()[0],
                    self.Minerals.function_Rho()[1],
                    '',
                    error=False))

        else:
            self.a, self.b, self.c = self.Minerals.PLOT_input_formula(
                return_fig=False)

        self.canvas1 = FigureCanvas3D(self.a)
        self.canvas2 = FigureCanvas3D(self.b)
        self.canvas3 = FigureCanvas3D(self.c)
        self.canvas1.mpl_connect('pick_event', self.onpick)
        self.canvas2.mpl_connect('pick_event', self.onpick)
        self.canvas3.mpl_connect('pick_event', self.onpick)

        self.toolbar1 = NavigationToolbar(self.canvas1, self)
        self.toolbar2 = NavigationToolbar(self.canvas2, self)
        self.toolbar3 = NavigationToolbar(self.canvas3, self)

        self.layout1_widget = QWidget()
        self.layout1 = QGridLayout(self.layout1_widget)
        self.layout1_widget.setFixedSize(600, 600)
        self.layout1.addWidget(self.canvas1, 0, 1, 5, 5)
        self.layout1.addWidget(self.toolbar1, 5, 1, 1, 5)
        self.layout1.addWidget(self.canvas2, 6, 1, 5, 5)
        self.layout1.addWidget(self.toolbar2, 11, 1, 1, 5)
        self.layout1.addWidget(self.canvas3, 12, 1, 5, 5)
        self.layout1.addWidget(self.toolbar3, 17, 1, 1, 5)
        self.layout.addWidget(self.layout1_widget, 0, 0, 1, 1)

    def onpick(self, event):
        try:
            for i in range(6):
                self.model.item(self.ind,
                                i + 1).setBackground(QColor(211, 211, 211))
        except:
            pass

        count = -1
        for j in range(len((self.Minerals.Flag))):
            if self.Minerals.Flag[j] == 1:
                count += 1
                if count == event.ind[0]:
                    self.ind = j
                    break
        #print (self.ind)
        #self.ind = event.ind
        for i in range(6):
            self.model.item(self.ind,
                            i + 1).setBackground(QColor(111, 111, 111))

    def Update(self):
        self.user_input = False
        self.dirty = False
        self.check_change.setChecked(False)
        self.PLOT()

    def Export(self):
        self.dirty = True
        dialog = TextEditor(name=self.Minerals.name)
        if dialog.exec_():
            pass
        self.Minerals.read_data()
        self.PLOT()

    def ReturnString(self):
        aa = self.Minerals.Flag
        bb = str(int(aa[0]))
        for i in range(1, len(aa)):
            bb += str(int(aa[i]))
        return [self.stringK, self.stringG, self.stringRho,
                self.user_input], bb
Example #49
0
class OWConfusionMatrix(widget.OWWidget):
    name = "Confusion Matrix"
    description = "Shows a confusion matrix."
    icon = "icons/ConfusionMatrix.svg"
    priority = 1001

    inputs = [{
        "name": "Evaluation Results",
        "type": Orange.evaluation.Results,
        "handler": "set_results"
    }]
    outputs = [{"name": "Selected Data", "type": Orange.data.Table}]

    quantities = [
        "Number of instances", "Proportion of predicted",
        "Proportion of actual"
    ]

    selected_learner = settings.Setting([])
    selected_quantity = settings.Setting(0)
    append_predictions = settings.Setting(True)
    append_probabilities = settings.Setting(False)
    autocommit = settings.Setting(True)

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

        self.data = None
        self.results = None
        self.learners = []

        box = gui.widgetBox(self.controlArea, "Learners")

        self.learners_box = gui.listBox(box,
                                        self,
                                        "selected_learner",
                                        "learners",
                                        callback=self._learner_changed)
        box = gui.widgetBox(self.controlArea, "Show")

        gui.comboBox(box,
                     self,
                     "selected_quantity",
                     items=self.quantities,
                     callback=self._update)

        box = gui.widgetBox(self.controlArea, "Select")

        gui.button(box,
                   self,
                   "Correct",
                   callback=self.select_correct,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "Misclassified",
                   callback=self.select_wrong,
                   autoDefault=False)
        gui.button(box,
                   self,
                   "None",
                   callback=self.select_none,
                   autoDefault=False)

        self.outputbox = box = gui.widgetBox(self.controlArea, "Output")
        gui.checkBox(box,
                     self,
                     "append_predictions",
                     "Predictions",
                     callback=self._invalidate)
        gui.checkBox(box,
                     self,
                     "append_probabilities",
                     "Probabilities",
                     callback=self._invalidate)

        gui.auto_commit(self.controlArea, self, "autocommit", "Send Data",
                        "Auto send is on")

        grid = QGridLayout()
        grid.setContentsMargins(0, 0, 0, 0)
        grid.addWidget(QLabel("Predicted"), 0, 1, Qt.AlignCenter)
        grid.addWidget(VerticalLabel("Actual Class"), 1, 0, Qt.AlignCenter)

        self.tablemodel = QStandardItemModel()
        self.tableview = QTableView(editTriggers=QTableView.NoEditTriggers)
        self.tableview.setModel(self.tablemodel)
        self.tableview.selectionModel().selectionChanged.connect(
            self._invalidate)
        grid.addWidget(self.tableview, 1, 1)
        self.mainArea.layout().addLayout(grid)

    def set_results(self, results):
        """Set the input results."""

        self.clear()
        self.warning([0, 1])

        data = None
        if results is not None:
            if results.data is not None:
                data = results.data

        if data is not None and \
                not isinstance(data.domain.class_var,
                               Orange.data.DiscreteVariable):
            data = None
            results = None
            self.warning(
                0, "Confusion Matrix cannot be used for regression results.")

        self.results = results
        self.data = data

        if data is not None:
            class_values = data.domain.class_var.values
        elif results is not None:
            raise NotImplementedError

        if results is not None:
            nmodels, ntests = results.predicted.shape
            headers = class_values + [unicodedata.lookup("N-ARY SUMMATION")]

            # NOTE: The 'learner_names' is set in 'Test Learners' widget.
            if hasattr(results, "learner_names"):
                self.learners = results.learner_names
            else:
                self.learners = ["L %i" % (i + 1) for i in range(nmodels)]

            self.tablemodel.setVerticalHeaderLabels(headers)
            self.tablemodel.setHorizontalHeaderLabels(headers)
            self.tablemodel.setRowCount(len(class_values) + 1)
            self.tablemodel.setColumnCount(len(class_values) + 1)
            self.selected_learner = [0]
            self._update()

    def clear(self):
        self.results = None
        self.data = None
        self.tablemodel.clear()
        # Clear learners last. This action will invoke `_learner_changed`
        # method
        self.learners = []

    def select_correct(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()
        for i in range(n):
            index = self.tablemodel.index(i, i)
            selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_wrong(self):
        selection = QItemSelection()
        n = self.tablemodel.rowCount()

        for i in range(n):
            for j in range(i + 1, n):
                index = self.tablemodel.index(i, j)
                selection.select(index, index)
                index = self.tablemodel.index(j, i)
                selection.select(index, index)

        self.tableview.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

    def select_none(self):
        self.tableview.selectionModel().clear()

    def commit(self):
        if self.results is not None and self.data is not None \
                and self.selected_learner:
            indices = self.tableview.selectedIndexes()
            indices = {(ind.row(), ind.column()) for ind in indices}
            actual = self.results.actual
            selected_learner = self.selected_learner[0]
            learner_name = self.learners[selected_learner]
            predicted = self.results.predicted[selected_learner]
            selected = [
                i for i, t in enumerate(zip(actual, predicted)) if t in indices
            ]
            row_indices = self.results.row_indices[selected]

            extra = []
            class_var = self.data.domain.class_var
            metas = self.data.domain.metas

            if self.append_predictions:
                predicted = numpy.array(predicted[selected], dtype=object)
                extra.append(predicted.reshape(-1, 1))
                var = Orange.data.DiscreteVariable(
                    "{}({})".format(class_var.name, learner_name),
                    class_var.values)
                metas = metas + (var, )

            if self.append_probabilities and \
                    self.results.probabilities is not None:
                probs = self.results.probabilities[selected_learner, selected]
                extra.append(numpy.array(probs, dtype=object))
                pvars = [
                    Orange.data.ContinuousVariable("p({})".format(value))
                    for value in class_var.values
                ]
                metas = metas + tuple(pvars)

            X = self.data.X[row_indices]
            Y = self.data.Y[row_indices]
            M = self.data.metas[row_indices]
            row_ids = self.data.ids[row_indices]

            M = numpy.hstack((M, ) + tuple(extra))
            domain = Orange.data.Domain(self.data.domain.attributes,
                                        self.data.domain.class_vars, metas)
            data = Orange.data.Table.from_numpy(domain, X, Y, M)
            data.ids = row_ids
            data.name = learner_name

        else:
            data = None

        self.send("Selected Data", data)

    def _invalidate(self):
        self.commit()

    def _learner_changed(self):
        # The selected learner has changed
        self._update()
        self._invalidate()

    def _update(self):
        # Update the displayed confusion matrix
        if self.results is not None and self.selected_learner:
            index = self.selected_learner[0]
            cmatrix = confusion_matrix(self.results, index)
            colsum = cmatrix.sum(axis=0)
            rowsum = cmatrix.sum(axis=1)
            total = rowsum.sum()

            if self.selected_quantity == 0:
                value = lambda i, j: int(cmatrix[i, j])
            elif self.selected_quantity == 1:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / colsum[i])
                     if colsum[i] else "N/A")
            elif self.selected_quantity == 2:
                value = lambda i, j: \
                    ("{:2.1f} %".format(100 * cmatrix[i, j] / rowsum[i])
                     if colsum[i] else "N/A")
            else:
                assert False

            model = self.tablemodel
            for i, row in enumerate(cmatrix):
                for j, _ in enumerate(row):
                    item = model.item(i, j)
                    if item is None:
                        item = QStandardItem()
                    item.setData(value(i, j), Qt.DisplayRole)
                    item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                    item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
                    model.setItem(i, j, item)

            font = model.invisibleRootItem().font()
            bold_font = QFont(font)
            bold_font.setBold(True)

            def sum_item(value):
                item = QStandardItem()
                item.setData(value, Qt.DisplayRole)
                item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter)
                item.setFlags(Qt.ItemIsEnabled)
                item.setFont(bold_font)
                return item

            N = len(colsum)
            for i in range(N):
                model.setItem(N, i, sum_item(int(colsum[i])))
                model.setItem(i, N, sum_item(int(rowsum[i])))

            model.setItem(N, N, sum_item(int(total)))
Example #50
0
    item1 = QStandardItem()
    item1.setText("B")
    pixmap1 = QPixmap(50, 50)
    pixmap1.fill(Qt.blue)
    item1.setIcon(QIcon(pixmap1))
    item1.setToolTip("indexB")
    parentItem.appendRow(item1)

    # 创建新的标准项,这里使用了另一种方法来设置文本、图标和工具提示
    item2 = QStandardItem()
    pixmap2 = QPixmap(50, 50)
    pixmap2.fill(Qt.green)
    item2.setData("C", Qt.EditRole)  # 等同于  setText("C")
    item2.setData("indexC", Qt.ToolTipRole)  # 等同于  setToolTip("indexB")
    item2.setData(QIcon(pixmap2),
                  Qt.DecorationRole)  #等同于 setIcon(QIcon(pixmap1))
    parentItem.appendRow(item2)

    # 在树视图中显示模型
    view.setModel(model)
    view.show()

    # 获取item0的索引并输出item0的子项数目,然后输出了item1的显示文本和工具提示
    indexA = model.index(0, 0, QModelIndex())
    print "indexA row count: ", model.rowCount(indexA)
    indexB = model.index(0, 0, indexA)
    print "indexB text: ", model.data(indexB, Qt.EditRole).toString()
    print "indexB toolTip: ", model.data(indexB, Qt.ToolTipRole).toString()

    app.exec_()
Example #51
0
class NeoNavigationDock(QDockWidget, Ui_neoNavigationDock):
    """ Implements a navigation dock for Neo hierarchies. Tightly coupled
    with :class:`main_window_neo.MainWindowNeo`, the main reason for this
    class to exist is to keep the dock out of the general ui file.
    """

    object_removed = pyqtSignal()  # Signal to remove an object

    def __init__(self, parent):
        QDockWidget.__init__(self, parent)
        self.parent = parent

        self.setupUi(self)

        self.block_model = QStandardItemModel()
        self.segment_model = QStandardItemModel()
        self.channelgroup_model = QStandardItemModel()
        self.channel_model = QStandardItemModel()
        self.unit_model = QStandardItemModel()

        self.neoBlockList.setModel(self.block_model)
        self.neoSegmentList.setModel(self.segment_model)
        self.neoChannelGroupList.setModel(self.channelgroup_model)
        self.neoChannelList.setModel(self.channel_model)
        self.neoUnitList.setModel(self.unit_model)

        self.neoBlockList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.block_model))
        self.neoSegmentList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.segment_model))
        self.neoChannelGroupList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.channelgroup_model))
        self.neoChannelList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.channel_model))
        self.neoUnitList.doubleClicked.connect(
            lambda x: self._edit_item_annotations(x, self.unit_model))

        self.neoBlockList.selectionModel().selectionChanged.connect(
            self.selected_blocks_changed)
        self.neoChannelGroupList.selectionModel().selectionChanged.connect(
            self.selected_channel_groups_changed)
        self.neoChannelList.selectionModel().selectionChanged.connect(
            self.selected_channels_changed)
        self.neoUnitList.selectionModel().selectionChanged.connect(
            self.selected_units_changed)
        self.neoSegmentList.selectionModel().selectionChanged.connect(
            self.selected_segments_changed)

    def clear(self):
        """ Clear all lists
        """
        self.neoBlockList.clearSelection()
        self.block_model.clear()

    def get_letter_id(self, id_, small=False):
        """ Return a name consisting of letters given an integer
        """
        if id_ < 0:
            return ''

        name = ''
        id_ += 1
        if small:
            start = ord('a')
        else:
            start = ord('A')

        while id_ > 26:
            id_ -= 1
            name += chr(start + (id_ % 26))
            id_ /= 26

        name += chr(start + id_ - 1)

        return name[::-1]

    def ensure_not_filtered(self, objects, all_objects, filters):
        """ Deactivates all filters that prevent the the given sequence
        of objects to be displayed. The passed filter tuple list is
        modified to only include valid filters.

        :param sequence objects: The objects that need to be visible.
        :param sequence all_objects: The whole object list to be filtered,
            including objects that are allowed to be hidden.
        :param sequence filters: A sequence of (Filter, name) tuples.
        """
        objset = set(objects)
        if not objset.issubset(
                set(self.parent.filter_list(all_objects, filters))):
            i = 1
            while i <= len(filters):
                test_filters = filters[:i]
                if objset.issubset(set(self.parent.filter_list(
                        all_objects, test_filters))):
                    i += 1
                else:
                    test_filters[-1][0].active = False
                    filters.pop(i - 1)

        for o in objects:
            i = 0
            while i < len(filters):
                if self.parent.is_filtered(o, [filters[i]]):
                    filters[i][0].active = False
                    filters.pop(i)
                else:
                    i += 1

    def filter_ordered(self, objects, filters):
        """ Filter a sequence of objects with a sequence of filters. Apply
        the filters in the order given by the sequence. Return the filtered
        list.
        """
        for f in filters:
            if f[0].combined:
                objects = self.parent.filter_list(objects, [f])
            else:
                objects = [o for o in objects
                           if not self.parent.is_filtered(o, [f])]
        return objects

    def populate_neo_block_list(self):
        """ Fill the block list with appropriate entries.
        Qt.UserRole: The :class:`neo.Block` object
        """
        self.block_model.clear()

        filters = self.parent.get_active_filters('Block')

        blocks = self.filter_ordered(
            self.parent.block_names.keys(), filters)
        for b in blocks:
            item = QStandardItem(self.parent.block_names[b])
            item.setData(b, Qt.UserRole)
            self.block_model.appendRow(item)

        self.neoBlockList.setCurrentIndex(self.block_model.index(0, 0))
        self.set_blocks_label()
        if not blocks:
            self.selected_blocks_changed()

    def populate_neo_segment_list(self):
        """ Fill the segment list with appropriate entries.
        Qt.UserRole: The :class:`neo.Segment` object
        """
        self.segment_model.clear()

        segments = []
        for b in self.blocks():
            segments.extend(b.segments)

        filters = self.parent.get_active_filters('Segment')
        segments = self.filter_ordered(segments, filters)
        for i, s in enumerate(segments):
            if s.name:
                name = s.name + ' (%s-%i)' % \
                    (self.parent.block_ids[s.block], i)
            else:
                name = '%s-%i' % (self.parent.block_ids[s.block], i)

            new_item = QStandardItem(name)
            new_item.setData(s, Qt.UserRole)
            self.segment_model.appendRow(new_item)

        self.neoSegmentList.setCurrentIndex(self.segment_model.index(0, 0))
        if api.config.autoselect_segments:
            self.neoSegmentList.selectAll()
        self.selected_segments_changed()

    def populate_neo_channel_group_list(self):
        """ Fill the channel group list with appropriate entries.
        Qt.UserRole: The :class:`neo.RecordingChannelGroup` object
        """
        self.neoChannelGroupList.clearSelection()
        self.channelgroup_model.clear()
        self.parent.channel_group_names.clear()

        rcgs = []
        for b in self.blocks():
            rcgs.extend(b.recordingchannelgroups)

        filters = self.parent.get_active_filters(
            'Recording Channel Group')
        rcgs = self.filter_ordered(rcgs, filters)

        for i, rcg in enumerate(rcgs):
            self.parent.channel_group_names[rcg] = '%s-%s' % (
                self.parent.block_ids[rcg.block],
                self.get_letter_id(i, True))
            if rcg.name:
                name = rcg.name + ' (%s)' % \
                                  self.parent.channel_group_names[rcg]
            else:
                name = self.parent.channel_group_names[rcg]
            new_item = QStandardItem(name)
            new_item.setData(rcg, Qt.UserRole)
            self.channelgroup_model.appendRow(new_item)

        self.neoChannelGroupList.setCurrentIndex(
            self.channelgroup_model.index(0, 0))
        if api.config.autoselect_channel_groups:
            self.neoChannelGroupList.selectAll()
        elif not rcgs:
            self.selected_channel_groups_changed()

    def populate_neo_channel_list(self):
        """ Fill the channel list with appropriate entries. Data slots:
        Qt.UserRole: The :class:`neo.RecordingChannel`
        Qt.UserRole+1: The channel index
        """
        self.channel_model.clear()
        channels = set()

        rcs = []
        rc_group_name = {}
        for rcg in self.recording_channel_groups():
            for rc in rcg.recordingchannels:
                if not api.config.duplicate_channels and rc in channels:
                    continue
                channels.add(rc)
                rcs.append(rc)
                rc_group_name[rc] = self.parent.channel_group_names[rcg]

        filters = self.parent.get_active_filters(
            'Recording Channel')
        rcs = self.filter_ordered(rcs, filters)

        for rc in rcs:
            identifier = '%s.%d' % \
                         (rc_group_name[rc],
                          rc.index)
            if rc.name:
                name = rc.name + ' (%s)' % identifier
            else:
                name = identifier

            new_item = QStandardItem(name)
            new_item.setData(rc, Qt.UserRole)
            new_item.setData(rc.index, Qt.UserRole + 1)
            self.channel_model.appendRow(new_item)

        if api.config.autoselect_channels:
            self.neoChannelList.selectAll()
        self.selected_channels_changed()

    def populate_neo_unit_list(self):
        """ Fill the unit list with appropriate entries.
        Qt.UserRole: The :class:`neo.Unit` object
        """
        self.unit_model.clear()

        units = []
        for rcg in self.recording_channel_groups():
            units.extend(rcg.units)

        filters = self.parent.get_active_filters('Unit')
        units = self.filter_ordered(units, filters)

        for i, u in enumerate(units):
            if self.parent.is_filtered(u, filters):
                continue
            if u.name:
                name = u.name + ' (%s-%d)' % \
                       (self.parent.channel_group_names[rcg], i)
            else:
                name = '%s-%d' % (self.parent.channel_group_names[rcg], i)
            new_item = QStandardItem(name)
            new_item.setData(u, Qt.UserRole)
            self.unit_model.appendRow(new_item)

        if api.config.autoselect_units:
            self.neoUnitList.selectAll()
        self.selected_units_changed()

    def set_blocks_label(self):
        self.blocksLabel.setText(
            'Blocks (%d/%d):' % (len(self.neoBlockList.selectedIndexes()),
                                 self.block_model.rowCount()))

    def set_channel_groups_label(self):
        self.channelGroupsLabel.setText(
            'Channel Groups (%d/%d):' % (
                len(self.neoChannelGroupList.selectedIndexes()),
                self.channelgroup_model.rowCount()))

    def selected_blocks_changed(self):
        self.set_blocks_label()
        self.populate_neo_channel_group_list()
        self.populate_neo_segment_list()

    def selected_channel_groups_changed(self):
        self.set_channel_groups_label()
        self.populate_neo_channel_list()
        self.populate_neo_unit_list()

    def selected_channels_changed(self):
        self.channelsLabel.setText(
            'Channels (%d/%d):' % (
                len(self.neoChannelList.selectedIndexes()),
                self.channel_model.rowCount()))

    def selected_units_changed(self):
        self.unitsLabel.setText(
            'Units (%d/%d):' % (
                len(self.neoUnitList.selectedIndexes()),
                self.unit_model.rowCount()))

    def selected_segments_changed(self):
        self.segmentsLabel.setText(
            'Segments (%d/%d):' % (
                len(self.neoSegmentList.selectedIndexes()),
                self.segment_model.rowCount()))

    def _edit_item_annotations(self, index, model):
        api.annotation_editor(model.data(index, Qt.UserRole))

    def remove_selected(self, list_widget):
        """ Remove all selected objects from the given list widget.
        """
        items = list_widget.selectedIndexes()
        if len(items) < 1:
            return
        model = list_widget.model()

        question = ('Do you really want to remove %d %s' %
                    (len(items),
                    type(model.data(items[0], Qt.UserRole)).__name__))
        if len(items) > 1:
            question += 's'
        question += '?'

        if QMessageBox.question(
                self, 'Please confirm', question,
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.No:
            return

        for i in list_widget.selectedIndexes():
            data = model.data(i, Qt.UserRole)
            if isinstance(data, neo.Block):
                self.parent.block_names.pop(data)
            else:
                spykeutils.tools.remove_from_hierarchy(data)
            list_widget.selectionModel().select(i, QItemSelectionModel.Deselect)

        self.object_removed.emit()

    def _context_actions(self, list_widget):
        idx = list_widget.currentIndex()
        if not idx:
            return []

        data = list_widget.model().data(idx, Qt.UserRole)

        edit_action = QAction(get_icon('edit.png'),
                              'Edit annotations...', self)
        edit_action.triggered.connect(
            lambda x: self._edit_item_annotations(idx, list_widget.model()))

        delete_name = 'Delete %s' % type(data).__name__
        if len(list_widget.selectedIndexes()) > 1:
            delete_name += 's'
        delete_action = QAction(get_icon('editdelete.png'),
                                delete_name, self)
        delete_action.triggered.connect(
            lambda x:
            self.remove_selected(list_widget))

        return [edit_action, delete_action]

    def on_neoBlockList_customContextMenuRequested(self, pos):
        if not self.neoBlockList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoBlockList))
        context_menu.popup(self.neoBlockList.mapToGlobal(pos))

    def on_neoSegmentList_customContextMenuRequested(self, pos):
        if not self.neoSegmentList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoSegmentList))
        context_menu.popup(self.neoSegmentList.mapToGlobal(pos))

    def on_neoChannelGroupList_customContextMenuRequested(self, pos):
        if not self.neoChannelGroupList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelGroupList))
        context_menu.popup(self.neoChannelGroupList.mapToGlobal(pos))

    def on_neoChannelList_customContextMenuRequested(self, pos):
        if not self.neoChannelList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoChannelList))
        context_menu.popup(self.neoChannelList.mapToGlobal(pos))

    def on_neoUnitList_customContextMenuRequested(self, pos):
        if not self.neoUnitList.selectedIndexes():
            return
        context_menu = QMenu(self)
        context_menu.addActions(self._context_actions(self.neoUnitList))
        context_menu.popup(self.neoUnitList.mapToGlobal(pos))

    def blocks(self):
        """ Return selected :class:`neo.Block` objects.
        """
        return [self.block_model.data(i, Qt.UserRole) for i in
                self.neoBlockList.selectedIndexes()]

    def segments(self):
        """ Return selected :class:`neo.Segment` objects.
        """
        return [self.segment_model.data(i, Qt.UserRole) for i in
                self.neoSegmentList.selectedIndexes()]

    def recording_channel_groups(self):
        """ Return selected :class:`neo.RecordingChannelGroup` objects.
        """
        return [self.channelgroup_model.data(i, Qt.UserRole) for i in
                self.neoChannelGroupList.selectedIndexes()]

    def recording_channels(self):
        """ Return selected :class:`neo.RecordingChannel` objects.
        """
        return [self.channel_model.data(i, Qt.UserRole) for i in
                self.neoChannelList.selectedIndexes()]

    def units(self):
        """ Return selected :class:`neo.Unit` objects.
        """
        return [self.unit_model.data(i, Qt.UserRole) for i in
                self.neoUnitList.selectedIndexes()]

    def set_selection(self, data):
        """ Set the selected data.
        """
        block_list = []
        for b in data['blocks']:
            cl = None
            rp = None
            if len(b) > 2:
                cl = NeoDataProvider.find_io_class(b[2])
            if len(b) > 3:
                rp = b[3]
            loaded = NeoDataProvider.get_block(
                b[1], b[0], force_io=cl, read_params=rp)
            if loaded is None:
                raise IOError('One of the files contained in the '
                              'selection could not be loaded!')
            block_list.append(loaded)

        block_set = set([(b[0], b[1]) for b in data['blocks']])
        # Select blocks
        self.ensure_not_filtered(block_list, self.parent.block_names.keys(),
                                 self.parent.get_active_filters('Block'))
        self.populate_neo_block_list()
        selection = QItemSelection()
        for i in self.block_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            block = i.data(Qt.UserRole)
            t = (NeoDataProvider.block_indices[block],
                 self.parent.block_files[block])

            if t in block_set:
                selection.append(QItemSelectionRange(
                    self.block_model.indexFromItem(i)))
        self.neoBlockList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select segments
        seg_list = [block_list[idx[1]].segments[idx[0]]
                    for idx in data['segments']]
        all_segs = []
        for b in self.blocks():
            all_segs.extend(b.segments)
        self.ensure_not_filtered(seg_list, all_segs,
                                 self.parent.get_active_filters('Segment'))
        self.populate_neo_segment_list()

        selection = QItemSelection()
        for i in self.segment_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            segment = i.data(Qt.UserRole)
            if not segment.block in block_list:
                continue

            seg_idx = segment.block.segments.index(segment)
            block_idx = block_list.index(segment.block)
            if [seg_idx, block_idx] in data['segments']:
                selection.append(QItemSelectionRange(
                    self.segment_model.indexFromItem(i)))
        self.neoSegmentList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select recording channel groups
        rcg_list = [block_list[rcg[1]].recordingchannelgroups[rcg[0]]
                    for rcg in data['channel_groups']]
        all_rcgs = []
        for b in self.blocks():
            all_rcgs.extend(b.recordingchannelgroups)
        self.ensure_not_filtered(
            rcg_list, all_rcgs,
            self.parent.get_active_filters('Recording Channel Group'))
        self.populate_neo_channel_group_list()

        selection = QItemSelection()
        for i in self.channelgroup_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            rcg = i.data(Qt.UserRole)
            if not rcg.block in block_list:
                continue

            rcg_idx = rcg.block.recordingchannelgroups.index(rcg)
            block_idx = block_list.index(rcg.block)
            if [rcg_idx, block_idx] in data['channel_groups']:
                selection.append(QItemSelectionRange(
                    self.channelgroup_model.indexFromItem(i)))
        self.neoChannelGroupList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select channels
        rc_list = [rcg_list[rc[1]].recordingchannels[rc[0]]
                   for rc in data['channels']]
        all_rcs = []
        for rcg in self.recording_channel_groups():
            for rc in rcg.recordingchannels:
                if not api.config.duplicate_channels and rc in all_rcs:
                    continue
                all_rcs.append(rc)
        self.ensure_not_filtered(
            rc_list, all_rcs,
            self.parent.get_active_filters('Recording Channel'))
        self.populate_neo_channel_list()

        selection = QItemSelection()
        rcg_set = set(rcg_list)
        for i in self.channel_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            channel = i.data(Qt.UserRole)
            if not set(channel.recordingchannelgroups).intersection(rcg_set):
                continue

            for rcg in channel.recordingchannelgroups:
                if [rcg.recordingchannels.index(channel),
                        rcg_list.index(rcg)] in data['channels']:
                    selection.append(QItemSelectionRange(
                        self.channel_model.indexFromItem(i)))
                    break
        self.neoChannelList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        # Select units
        unit_list = [rcg_list[u[1]].units[u[0]]
                     for u in data['units']]
        all_units = []
        for rcg in self.recording_channel_groups():
            all_units.extend(rcg.units)
        self.ensure_not_filtered(
            unit_list, all_units,
            self.parent.get_active_filters('Unit'))
        self.populate_neo_unit_list()

        selection = QItemSelection()
        for i in self.unit_model.findItems(
                '*', Qt.MatchWrap | Qt.MatchWildcard):
            unit = i.data(Qt.UserRole)
            if unit.recordingchannelgroup not in rcg_list:
                continue

            rcg_idx = rcg_list.index(unit.recordingchannelgroup)
            unit_idx = unit.recordingchannelgroup.units.index(unit)
            if [unit_idx, rcg_idx] in data['units']:
                selection.append(QItemSelectionRange(
                    self.unit_model.indexFromItem(i)))
        self.neoUnitList.selectionModel().select(
            selection, QItemSelectionModel.ClearAndSelect)

        self.parent.refresh_filters()
Example #52
0
class MainWindow(mainwindow_widget, mainwindow_base):
    """
    Main application window
    """

    def __init__(self, settings):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.settings = settings
        roam.featureform.settings = settings.settings
        self.canvaslayers = []
        self.layerbuttons = []
        self.project = None
        self.selectionbands = defaultdict(partial(QgsRubberBand, self.canvas))
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.setWheelAction(QgsMapCanvas.WheelZoomToMouseCursor)
        self.bar = roam.messagebaritems.MessageBar(self)

        self.actionMap.setVisible(False)

        pal = QgsPalLabeling()
        self.canvas.mapRenderer().setLabelingEngine(pal)
        self.canvas.setFrameStyle(QFrame.NoFrame)
        self.menuGroup = QActionGroup(self)
        self.menuGroup.setExclusive(True)

        self.menuGroup.addAction(self.actionMap)
        self.menuGroup.addAction(self.actionDataEntry)
        self.menuGroup.addAction(self.actionProject)
        self.menuGroup.addAction(self.actionSync)
        self.menuGroup.addAction(self.actionSettings)
        self.menuGroup.triggered.connect(self.updatePage)

        self.editgroup = QActionGroup(self)
        self.editgroup.setExclusive(True)
        self.editgroup.addAction(self.actionPan)
        self.editgroup.addAction(self.actionZoom_In)
        self.editgroup.addAction(self.actionZoom_Out)
        self.editgroup.addAction(self.actionInfo)

        #TODO Extract GPS out into a service and remove UI stuff
        self.actionGPS = GPSAction(":/icons/gps", self.canvas, self.settings, self)
        self.projecttoolbar.addAction(self.actionGPS)

        self.projectwidget = ProjectsWidget(self)
        self.projectwidget.requestOpenProject.connect(self.loadProject)
        QgsProject.instance().readProject.connect(self._readProject)
        self.project_page.layout().addWidget(self.projectwidget)

        self.syncwidget = SyncWidget()
        self.syncpage.layout().addWidget(self.syncwidget)

        self.settingswidget = SettingsWidget(settings, self)
        self.settings_page.layout().addWidget(self.settingswidget)
        self.actionSettings.toggled.connect(self.settingswidget.populateControls)
        self.actionSettings.toggled.connect(self.settingswidget.readSettings)
        self.settingswidget.settingsupdated.connect(self.settingsupdated)

        self.dataentrywidget = DataEntryWidget(self.canvas, self.bar)
        self.widgetpage.layout().addWidget(self.dataentrywidget)

        self.dataentrywidget.rejected.connect(self.formrejected)
        self.dataentrywidget.featuresaved.connect(self.featureSaved)
        self.dataentrywidget.featuredeleted.connect(self.featuredeleted)
        self.dataentrywidget.failedsave.connect(self.failSave)
        self.dataentrywidget.helprequest.connect(self.showhelp)
        self.dataentrywidget.openimage.connect(self.openimage)

        def createSpacer(width=0, height=0):
            widget = QWidget()
            widget.setMinimumWidth(width)
            widget.setMinimumHeight(height)
            return widget

        gpsspacewidget = createSpacer(30)
        sidespacewidget = createSpacer(30)
        sidespacewidget2 = createSpacer(height=20)

        sidespacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sidespacewidget2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        gpsspacewidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

        self.topspaceraction = self.projecttoolbar.insertWidget(self.actionGPS, gpsspacewidget)

        def createlabel(text):
            style = """
                QLabel {
                        color: #706565;
                        font: 14px "Calibri" ;
                        }"""
            label = QLabel(text)
            label.setStyleSheet(style)

            return label

        self.projectlabel = createlabel("Project: {project}")
        self.userlabel = createlabel("User: {user}".format(user=getpass.getuser()))
        self.positionlabel = createlabel('')
        self.statusbar.addWidget(self.projectlabel)
        self.statusbar.addWidget(self.userlabel)
        spacer = createSpacer()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.statusbar.addWidget(spacer)
        self.statusbar.addWidget(self.positionlabel)

        self.menutoolbar.insertWidget(self.actionQuit, sidespacewidget2)
        self.menutoolbar.insertWidget(self.actionProject, sidespacewidget)
        self.stackedWidget.currentChanged.connect(self.updateUIState)

        self.panels = []

        self.connectButtons()

        self.band = QgsRubberBand(self.canvas)
        self.band.setIconSize(20)
        self.band.setWidth(10)
        self.band.setColor(QColor(186, 93, 212, 76))

        self.canvas_page.layout().insertWidget(0, self.projecttoolbar)
        self.dataentrymodel = QStandardItemModel(self)
        self.dataentrycombo = QComboBox(self.projecttoolbar)
        self.dataentrycombo.setIconSize(QSize(48,48))
        self.dataentrycombo.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.dataentrycombo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.dataentrycombo.setModel(self.dataentrymodel)
        self.dataentrycomboaction = self.projecttoolbar.insertWidget(self.topspaceraction, self.dataentrycombo)

        self.dataentrycombo.showPopup = self.selectdataentry

        self.biglist = BigList(self.canvas)
        self.biglist.setlabel("Select data entry form")
        self.biglist.setmodel(self.dataentrymodel)
        self.biglist.itemselected.connect(self.dataentrychanged)
        self.biglist.hide()

        self.centralwidget.layout().addWidget(self.statusbar)

        self.actionGPSFeature.setProperty('dataentry', True)

        self.infodock = InfoDock(self.canvas)
        self.infodock.requestopenform.connect(self.openForm)
        self.infodock.featureupdated.connect(self.highlightfeature)
        self.infodock.resultscleared.connect(self.clearselection)
        self.infodock.openurl.connect(self.viewurl)
        self.infodock.hide()
        self.hidedataentry()
        self.canvas.extentsChanged.connect(self.updatestatuslabel)
        self.projecttoolbar.toolButtonStyleChanged.connect(self.updatecombo)

    def selectdataentry(self, ):
        if self.dataentrycombo.count() == 0:
            return

        self.biglist.show()

    def viewurl(self, url):
        """
        Open a URL in Roam
        :param url:
        :return:
        """
        key = url.toString().lstrip('file://')
        try:
            # Hack. Eww fix me.
            data, imagetype = roam.htmlviewer.images[os.path.basename(key)]
        except KeyError:
            # It's not a image so lets just pass it of as a normal
            # URL
            QDesktopServices.openUrl(url)
            return

        pix = QPixmap()
        if imagetype == 'base64':
            pix.loadFromData(data)
        else:
            pix.load(data)

        self.openimage(pix)

    def openimage(self, pixmap):
        viewer = ImageViewer(self.stackedWidget)
        viewer.resize(self.stackedWidget.size())
        viewer.openimage(pixmap)

    def updatecombo(self, *args):
        self.dataentrycombo.setMinimumHeight(0)

    def settingsupdated(self, settings):
        settings.save()
        self.show()
        self.actionGPS.updateGPSPort()
        # eww!
        roam.featureform.settings = settings.settings

    def updatestatuslabel(self):
        extent = self.canvas.extent()
        self.positionlabel.setText("Map Center: {}".format(extent.center().toString()))

    def highlightselection(self, results):
        for layer, features in results.iteritems():
            band = self.selectionbands[layer]
            band.setColor(QColor(255, 0, 0, 150))
            band.setIconSize(20)
            band.setWidth(2)
            band.setBrushStyle(Qt.NoBrush)
            band.reset(layer.geometryType())
            for feature in features:
                band.addGeometry(feature.geometry(), layer)

    def clearselection(self):
        # Clear the main selection rubber band
        self.band.reset()
        # Clear the rest
        for band in self.selectionbands.itervalues():
            band.reset()

    def highlightfeature(self, layer, feature, features):
        self.clearselection()
        self.highlightselection({layer: features})
        self.band.setToGeometry(feature.geometry(), layer)

    def showmap(self):
        self.actionMap.setVisible(True)
        self.actionMap.trigger()

    def hidedataentry(self):
        self.actionDataEntry.setVisible(False)

    def showdataentry(self):
        self.actionDataEntry.setVisible(True)
        self.actionDataEntry.trigger()

    def dataentrychanged(self, index):
        wasactive = self.clearCapatureTools()

        if not index.isValid():
            return

        modelindex = index
        # modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        self.dataentrycombo.setCurrentIndex(index.row())
        self.createCaptureButtons(form, wasactive)

    def raiseerror(self, *exinfo):
        info = traceback.format_exception(*exinfo)
        item = self.bar.pushError('Seems something has gone wrong. Press for more details',
                                  info)

    def setMapTool(self, tool, *args):
        self.canvas.setMapTool(tool)

    def homeview(self):
        """
        Zoom the mapview canvas to the extents the project was opened at i.e. the
        default extent.
        """
        self.canvas.setExtent(self.defaultextent)
        self.canvas.refresh()

    def connectButtons(self):
        def connectAction(action, tool):
            action.toggled.connect(partial(self.setMapTool, tool))

        def cursor(name):
            pix = QPixmap(name)
            pix = pix.scaled(QSize(24,24))
            return QCursor(pix)

        self.zoomInTool = QgsMapToolZoom(self.canvas, False)
        self.zoomOutTool = QgsMapToolZoom(self.canvas, True)
        self.panTool = TouchMapTool(self.canvas)
        self.moveTool = MoveTool(self.canvas, [])
        self.infoTool = InfoTool(self.canvas)

        connectAction(self.actionZoom_In, self.zoomInTool)
        connectAction(self.actionZoom_Out, self.zoomOutTool)
        connectAction(self.actionPan, self.panTool)
        connectAction(self.actionMove, self.moveTool)
        connectAction(self.actionInfo, self.infoTool)

        self.zoomInTool.setCursor(cursor(':/icons/in'))
        self.zoomOutTool.setCursor(cursor(':/icons/out'))
        self.infoTool.setCursor(cursor(':/icons/info'))

        self.actionRaster.triggered.connect(self.toggleRasterLayers)

        self.infoTool.infoResults.connect(self.showInfoResults)

        # The edit toolbutton is currently not being used but leaving it for feature.
        self.moveTool.layersupdated.connect(self.actionMove.setEnabled)
        self.moveTool.layersupdated.connect(self.actionEdit_Tools.setEnabled)

        self.actionGPSFeature.triggered.connect(self.addFeatureAtGPS)
        self.actionGPSFeature.setEnabled(self.actionGPS.isConnected)
        self.actionGPS.gpsfixed.connect(self.actionGPSFeature.setEnabled)

        self.actionHome.triggered.connect(self.homeview)
        self.actionQuit.triggered.connect(self.exit)

    def showToolError(self, label, message):
        self.bar.pushMessage(label, message, QgsMessageBar.WARNING)

    def clearCapatureTools(self):
        captureselected = False
        for action in self.projecttoolbar.actions():
            if action.objectName() == "capture" and action.isChecked():
                captureselected = True

            if action.property('dataentry'):
                self.projecttoolbar.removeAction(action)
        return captureselected

    def createCaptureButtons(self, form, wasselected):
        tool = form.getMaptool()(self.canvas)
        for action in tool.actions:
            # Create the action here.
            if action.ismaptool:
                action.toggled.connect(partial(self.setMapTool, tool))

            # Set the action as a data entry button so we can remove it later.
            action.setProperty("dataentry", True)
            self.editgroup.addAction(action)
            self.layerbuttons.append(action)
            self.projecttoolbar.insertAction(self.topspaceraction, action)

            if action.isdefault:
                action.setChecked(wasselected)

        if hasattr(tool, 'geometryComplete'):
            add = partial(self.addNewFeature, form)
            tool.geometryComplete.connect(add)
        else:
            tool.finished.connect(self.openForm)
            tool.error.connect(partial(self.showToolError, form.label))

        self.projecttoolbar.insertAction(self.topspaceraction, self.actionGPSFeature)
        self.actionGPSFeature.setVisible(not tool.isEditTool())

    def createFormButtons(self, forms):
        """
            Create buttons for each form that is defined
        """
        self.dataentrymodel.clear()
        self.clearCapatureTools()


        def captureFeature(form):
            item = QStandardItem(QIcon(form.icon), form.icontext)
            item.setData(form, Qt.UserRole + 1)
            item.setSizeHint(QSize(item.sizeHint().width(), self.projecttoolbar.height()))
            self.dataentrymodel.appendRow(item)

        capabilitityhandlers = {"capture": captureFeature}

        failedforms = []
        for form in forms:
            valid, reasons = form.valid
            if not valid:
                roam.utils.log("Form is invalid for data entry because {}".format(reasons))
                failedforms.append((form, reasons))
                continue

            for capability in form.capabilities:
                try:
                    capabilitityhandlers[capability](form)
                except KeyError:
                    # Just ignore capabilities we don't support yet.
                    continue

        if failedforms:
            for form, reasons in failedforms:
                html = "<h3>{}</h3><br>{}".format(form.label,
                                             "<br>".join(reasons))
            self.bar.pushMessage("Form errors",
                                 "Looks like some forms couldn't be loaded",
                                 level=QgsMessageBar.WARNING, extrainfo=html)

        visible = self.dataentrymodel.rowCount() > 0
        self.dataentrycomboaction.setVisible(visible)
        self.dataentrycombo.setMinimumHeight(self.projecttoolbar.height())

        index = self.dataentrymodel.index(0, 0)
        self.dataentrychanged(index)

    def addFeatureAtGPS(self):
        """
        Add a record at the current GPS location.
        """
        index = self.dataentrycombo.currentIndex()
        modelindex = self.dataentrymodel.index(index, 0)
        form = modelindex.data(Qt.UserRole + 1)
        point = self.actionGPS.position
        point = QgsGeometry.fromPoint(point)
        self.addNewFeature(form=form, geometry=point)

    def clearToolRubberBand(self):
        """
        Clear the rubber band of the active tool if it has one
        """
        tool = self.canvas.mapTool()
        try:
            tool.clearBand()
        except AttributeError:
            # No clearBand method found, but that's cool.
            pass

    def showhelp(self, url):
        help = HelpPage(self.stackedWidget)
        help.setHelpPage(url)
        help.show()

    def dataentryfinished(self):
        self.hidedataentry()
        self.showmap()
        self.cleartempobjects()
        self.infodock.refreshcurrent()

    def featuredeleted(self):
        self.dataentryfinished()
        self.bar.pushMessage("Deleted", "Feature Deleted", QgsMessageBar.INFO, 1)
        self.canvas.refresh()

    def featureSaved(self):
        self.dataentryfinished()
        self.canvas.refresh()

    def failSave(self, messages):
        self.bar.pushError("Error when saving changes.", messages)

    def cleartempobjects(self):
        self.band.reset()
        self.clearToolRubberBand()

    def formrejected(self, message, level):
        self.dataentryfinished()
        if message:
            self.bar.pushMessage("Form Message", message, level, duration=2)

        self.cleartempobjects()

    def openForm(self, form, feature):
        """
        Open the form that is assigned to the layer
        """
        self.band.setToGeometry(feature.geometry(), form.QGISLayer)
        self.showdataentry()
        self.dataentrywidget.openform(feature=feature, form=form, project=self.project)

    def addNewFeature(self, form, geometry):
        """
        Add a new new feature to the given layer
        """
        layer = form.QGISLayer
        fields = layer.pendingFields()

        feature = QgsFeature(fields)
        feature.setGeometry(geometry)

        for index in xrange(fields.count()):
            pkindexes = layer.dataProvider().pkAttributeIndexes()
            if index in pkindexes and layer.dataProvider().name() == 'spatialite':
                continue

            value = layer.dataProvider().defaultValue(index)
            feature[index] = value

        self.openForm(form, feature)

    def exit(self):
        """
        Exit the application.
        """
        QApplication.exit(0)

    def showInfoResults(self, results):
        self.infodock.clearResults()
        forms = {}
        for layer in results.keys():
            layername = layer.name()
            if not layername in forms:
                forms[layername] = list(self.project.formsforlayer(layername))

        self.infodock.setResults(results, forms)
        self.infodock.show()

    def toggleRasterLayers(self):
        """
        Toggle all raster layers on or off.
        """
        if not self.canvaslayers:
            return

        #Freeze the canvas to save on UI refresh
        self.canvas.freeze()
        for layer in self.canvaslayers:
            if layer.layer().type() == QgsMapLayer.RasterLayer:
                layer.setVisible(not layer.isVisible())
            # Really!? We have to reload the whole layer set every time?
        # WAT?
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.freeze(False)
        self.canvas.refresh()

    def missingLayers(self, layers):
        """
        Called when layers have failed to load from the current project
        """
        roam.utils.warning("Missing layers")
        map(roam.utils.warning, layers)

        missinglayers = roam.messagebaritems.MissingLayerItem(layers,
                                                              parent=self.bar)
        self.bar.pushItem(missinglayers)

    def loadprojects(self, projects):
        """
        Load the given projects into the project
        list
        """
        projects = list(projects)
        self.projectwidget.loadProjectList(projects)
        self.syncwidget.loadprojects(projects)

    def updatePage(self, action):
        """
        Update the current stack page based on the current selected
        action
        """
        page = action.property("page")
        self.stackedWidget.setCurrentIndex(page)

    def show(self):
        """
        Override show method. Handles showing the app in fullscreen
        mode or just maximized
        """
        fullscreen = self.settings.settings.get("fullscreen", False)
        if fullscreen:
            self.showFullScreen()
        else:
            self.showMaximized()

    def viewprojects(self):
        self.stackedWidget.setCurrentIndex(1)

    def updateUIState(self, page):
        """
        Update the UI state to reflect the currently selected
        page in the stacked widget
        """
        pass

    @roam.utils.timeit
    def _readProject(self, doc):
        """
        readProject is called by QgsProject once the map layer has been
        populated with all the layers
        """
        parser = ProjectParser(doc)
        canvasnode = parser.canvasnode
        self.canvas.freeze()
        self.canvas.mapRenderer().readXML(canvasnode)
        self.canvaslayers = parser.canvaslayers()
        self.canvas.setLayerSet(self.canvaslayers)
        self.canvas.updateScale()
        self.projectOpened()
        self.canvas.freeze(False)
        self.canvas.refresh()
        self.showmap()

    @roam.utils.timeit
    def projectOpened(self):
        """
            Called when a new project is opened in QGIS.
        """
        projectpath = QgsProject.instance().fileName()
        self.project = Project.from_folder(os.path.dirname(projectpath))
        self.projectlabel.setText("Project: {}".format(self.project.name))
        self.createFormButtons(forms=self.project.forms)

        # Enable the raster layers button only if the project contains a raster layer.
        layers = QgsMapLayerRegistry.instance().mapLayers().values()
        hasrasters = any(layer.type() == QgsMapLayer.RasterLayer for layer in layers)
        self.actionRaster.setEnabled(hasrasters)
        self.defaultextent = self.canvas.extent()
        roam.utils.info("Extent: {}".format(self.defaultextent.toString()))

        # Show panels
        for panel in self.project.getPanels():
            self.mainwindow.addDockWidget(Qt.BottomDockWidgetArea, panel)
            self.panels.append(panel)

        # TODO Abstract this out
        if not self.project.selectlayers:
            selectionlayers = QgsMapLayerRegistry.instance().mapLayers().values()
        else:
            selectionlayers = []
            for layername in self.project.selectlayers:
                try:
                    layer = QgsMapLayerRegistry.instance().mapLayersByName(layername)[0]
                except IndexError:
                    roam.utils.warning("Can't find QGIS layer for select layer {}".format(layername))
                    continue
                selectionlayers.append(layer)

        self.infoTool.selectionlayers = selectionlayers
        self.actionPan.trigger()

    #noinspection PyArgumentList
    @roam.utils.timeit
    def loadProject(self, project):
        """
        Load a project into the application .
        """
        roam.utils.log(project)
        roam.utils.log(project.name)
        roam.utils.log(project.projectfile)
        roam.utils.log(project.valid)

        (passed, message) = project.onProjectLoad()

        if not passed:
            self.bar.pushMessage("Project load rejected", "Sorry this project couldn't"
                                                          "be loaded.  Click for me details.",
                                 QgsMessageBar.WARNING, extrainfo=message)
            return

        self.actionMap.trigger()

        self.closeProject()

        self.canvas.refresh()
        self.canvas.repaint()

        self.infodock.clearResults()

        # No idea why we have to set this each time.  Maybe QGIS deletes it for
        # some reason.
        self.badLayerHandler = BadLayerHandler(callback=self.missingLayers)
        QgsProject.instance().setBadLayerHandler(self.badLayerHandler)

        self.stackedWidget.setCurrentIndex(3)
        self.projectloading_label.setText("Project {} Loading".format(project.name))
        pixmap = QPixmap(project.splash)
        w = self.projectimage.width()
        h = self.projectimage.height()
        self.projectimage.setPixmap(pixmap.scaled(w,h, Qt.KeepAspectRatio))
        QApplication.processEvents()

        QDir.setCurrent(os.path.dirname(project.projectfile))
        fileinfo = QFileInfo(project.projectfile)
        QgsProject.instance().read(fileinfo)

    def closeProject(self):
        """
        Close the current open project
        """
        self.canvas.freeze()
        QgsMapLayerRegistry.instance().removeAllMapLayers()
        self.canvas.clear()
        self.canvas.freeze(False)
        for panel in self.panels:
            self.removeDockWidget(panel)
            del panel
            # Remove all the old buttons
        for action in self.layerbuttons:
            self.editgroup.removeAction(action)

        self.dataentrymodel.clear()
        self.panels = []
        self.project = None
        self.dataentrywidget.clear()
        self.hidedataentry()
        self.infodock.close()
Example #53
0
class Terminals(QWidget):
    ready = pyqtSignal(bool)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        self.ui = uic.loadUiType('terminal.ui')[0]()
        self.ui.setupUi(self)

        self.notifier = QSystemTrayIcon(QIcon('arrow-up-icon.png'), self)
        self.notifier.show()

        self.session = None
        self.model = None
        self.devices = {}
        self.mainloop = None
        self.delegate = None

    def test_display(self):
        if self.mainloop:
            self.mainloop.test_display()

    def update_device_config(self):
        if self.mainloop:
            self.mainloop.update_config()

    def terminal_open(self, addr):
        if self.mainloop:
            self.mainloop.terminal_open(addr)

    def terminal_close(self, addr):
        if self.mainloop:
            self.mainloop.terminal_close(addr)

    def begin_session(self, fio, access):
        if access in ['operator']:
            self.ui.cashier.setText(fio)
            self.session = (fio, access)
            return True
        return False

    def end_session(self, block=False):
        self.ui.cashier.setText(u'')
        self.session = None
        self.stop_mainloop(block)
        return True

    def on_mainloop_stopped(self):
        self.mainloop = None
        if self.session:
            self.start_mainloop()

    def start_mainloop(self):
        if self.mainloop is None:
            self.mainloop = Mainloop(devices=self.devices)
            self.mainloop.ready.connect(self.on_mainloop_ready)
            self.mainloop.stopped.connect(self.on_mainloop_stopped)
            self.mainloop.notify.connect(lambda title, msg: self.notifier.showMessage(title, msg))
            self.mainloop.start()

    def stop_mainloop(self, block=False):
        if self.mainloop:
            try:
                self.mainloop.state.disconnect()
                self.mainloop.ready.disconnect()
                self.mainloop.notify.disconnect()
            except TypeError:
                print 'NOTE: mainloop signals disconnect'

            if self.model:
                [self.ui.terminals.closePersistentEditor(self.model.index(row, 0))
                 for row in xrange(self.model.rowCount())]

            self.mainloop.stop(block)
        else:
            self.ready.emit(False)

    def reset_mainloop(self, devices):
        self.devices = devices
        if self.mainloop is None:
            self.start_mainloop()
        else:
            self.stop_mainloop()

    def on_mainloop_ready(self, ok, titles):
        if ok:
            self.model = QStandardItemModel(len(titles), 1)
            [self.model.setItem(i, QStandardItem(str(addr))) for i, addr in enumerate(titles.keys())]

            self.delegate = TerminalDelegate(self.mainloop, titles)
            self.mainloop.report.connect(self.delegate.report)
            self.mainloop.state.connect(self.delegate.state)

            self.ui.terminals.setModel(self.model)
            self.ui.terminals.setItemDelegateForColumn(0, self.delegate)
            [self.ui.terminals.openPersistentEditor(self.model.index(row, 0))
             for row in xrange(self.model.rowCount())]

            self.mainloop.db.free_places_update.connect(self.ui.free_places.setValue)
            self.mainloop.update_config()
        else:
            self.model = None
            self.mainloop = None

        self.ready.emit(ok)