Exemplo n.º 1
0
 def excel_like_enter_filter(source: QtWidgets.QTableView,
                             event: QtCore.QEvent):
     if event.type() == event.KeyPress:
         if event.key() in [QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter]:
             if int(source.editTriggers()) > int(
                     QtWidgets.QAbstractItemView.NoEditTriggers):
                 next_row = source.currentIndex().row() + 1
                 if next_row + 1 > source.model().rowCount():
                     next_row -= 1
                 if source.state() == source.EditingState:
                     next_index = source.model().index(
                         next_row,
                         source.currentIndex().column())
                     source.setCurrentIndex(next_index)
                 else:
                     source.edit(source.currentIndex())
Exemplo n.º 2
0
    def edit(self, index, trigger, event):
        value = QTableView.edit(self, index, trigger, event)
        if int(trigger) in [2, 8, 16]:
            logMsg('user is now editing a cell')
            self.state_ = 'E'

        return value
Exemplo n.º 3
0
class Dip3TabWidget(QTabWidget):
    # Signals need to notify from not Qt thread
    alias_updated = pyqtSignal(str)
    diff_updated = pyqtSignal(dict)
    network_updated = pyqtSignal()

    def __init__(self, gui, wallet, *args, **kwargs):
        QTabWidget.__init__(self, *args, **kwargs)
        self.tab_bar = Dip3TabBar(self)
        self.setTabBar(self.tab_bar)
        self.setTabPosition(QTabWidget.East)
        self.gui = gui
        self.wallet = wallet
        self.manager = wallet.protx_manager
        self.mn_list = gui.network.mn_list if gui.network else None
        self.reg_cur_protx = ''
        self.w_cur_alias = ''
        self.w_cur_state = ''
        self.w_cur_idx = None

        self.wallet_mn_tab = self.create_wallet_mn_tab()
        self.registerd_mn_tab = self.create_registered_mn_tab()
        self.searchable_list = self.w_model
        self.currentChanged.connect(self.on_tabs_current_changed)

        if self.mn_list:
            self.tab_bar.update_heights(self.mn_list.protx_height,
                                        self.mn_list.llmq_human_height,
                                        gui.network.get_local_height())
            self.mn_list.register_callback(self.on_mn_list_diff_updated,
                                           ['mn-list-diff-updated'])
            self.gui.network.register_callback(self.on_cb_network_updated,
                                               ['network_updated'])
        self.manager.register_callback(self.on_manager_alias_updated,
                                       ['manager-alias-updated'])
        self.alias_updated.connect(self.on_alias_updated)
        self.diff_updated.connect(self.on_diff_updated)
        self.network_updated.connect(self.on_network_updated)

    @pyqtSlot()
    def on_tabs_current_changed(self):
        cur_widget = self.currentWidget()
        if cur_widget == self.wallet_mn_tab:
            self.searchable_list = self.w_model
        else:
            self.searchable_list = self.reg_model

    def on_mn_list_diff_updated(self, key, value):
        self.diff_updated.emit(value)

    def on_manager_alias_updated(self, key, value):
        self.alias_updated.emit(value)

    def on_cb_network_updated(self, key):
        self.network_updated.emit()

    @pyqtSlot(str)
    def on_alias_updated(self, alias):
        self.w_model.reload_data()

    @pyqtSlot()
    def on_network_updated(self):
        self.tab_bar.update_heights(None, None,
                                    self.gui.network.get_local_height())

    @pyqtSlot(dict)
    def on_diff_updated(self, value):
        state = value.get('state', self.mn_list.DIP3_DISABLED)
        if self.mn_list.load_mns and not self.mn_list.protx_loading:
            self.reg_model.reload_data()
            self.w_model.reload_data()

        if state == self.mn_list.DIP3_ENABLED:
            self.reg_search_btn.setEnabled(True)
        else:
            self.reg_search_btn.setEnabled(False)

        self.tab_bar.update_heights(self.mn_list.protx_height,
                                    self.mn_list.llmq_human_height,
                                    self.gui.network.get_local_height())
        self.update_registered_label()
        self.update_wallet_label()

    def registered_label(self):
        if not self.mn_list:
            return _('Offline')

        state = self.mn_list.protx_state
        if state == self.mn_list.DIP3_DISABLED:
            return _('DIP3 Masternodes is currently disabled.')

        count = len(self.mn_list.protx_mns)
        connected = self.gui.network.is_connected()
        loading = connected and self.mn_list.protx_loading
        ready = _('Loading') if loading else _('Found')
        return (_('%s %s registered DIP3 Masternodes.') % (ready, count))

    def update_registered_label(self):
        self.reg_label.setText(self.registered_label())

    def wallet_label(self):
        mns = self.manager.mns
        count = len(mns)
        mn_str = _('Masternode') if count == 1 else _('Masternodes')
        def_label_str = _('Wallet contains %s DIP3 %s.') % (count, mn_str)

        if not self.mn_list:
            return def_label_str

        state = self.mn_list.protx_state
        if state == self.mn_list.DIP3_DISABLED:
            return (_('DIP3 Masternodes is currently disabled.'))

        connected = self.gui.network.is_connected()
        loading = connected and self.mn_list.protx_loading
        if loading:
            height = self.mn_list.protx_height
            return (_('Loading DIP3 data at Height: %s.') % height)

        return def_label_str

    def update_wallet_label(self):
        self.w_label.setText(self.wallet_label())

    def create_registered_mn_tab(self):
        w = QWidget()
        hw = QWidget()

        self.reg_label = QLabel(self.registered_label())
        self.reg_search_btn = QPushButton(_('Search'))
        self.reg_search_btn.clicked.connect(self.on_reg_search)

        src_model = RegisteredMNsModel(self.mn_list)
        self.reg_model = Dip3FilterProxyModel()
        self.reg_model.setSourceModel(src_model)

        self.reg_view = QTableView()
        self.reg_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.reg_view.customContextMenuRequested.connect(self.create_reg_menu)
        self.reg_hheader = QHeaderView(Qt.Horizontal, self.reg_view)
        self.reg_hheader.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.reg_hheader.setStretchLastSection(True)

        self.reg_view.setHorizontalHeader(self.reg_hheader)
        self.reg_view.verticalHeader().hide()
        self.reg_view.setModel(self.reg_model)
        self.reg_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.reg_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.reg_view.doubleClicked.connect(self.reg_mn_dbl_clicked)

        sel_model = self.reg_view.selectionModel()
        sel_model.selectionChanged.connect(self.on_reg_selection_changed)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addWidget(self.reg_label)
        hbox.addStretch(1)
        hbox.addWidget(self.reg_search_btn)
        hw.setLayout(hbox)
        vbox.addWidget(hw)
        vbox.addWidget(self.reg_view)
        w.setLayout(vbox)
        self.addTab(w, read_QIcon('tab_search.png'), _('Registered MNs'))
        if not self.mn_list.protx_loading:
            self.reg_model.reload_data()
        return w

    def create_reg_menu(self, position):
        menu = QMenu()
        h = self.reg_cur_protx
        menu.addAction(_('Details'),
                       lambda: Dip3MNInfoDialog(self, protx_hash=h).show())
        menu.exec_(self.reg_view.viewport().mapToGlobal(position))

    def create_wallet_mn_tab(self):
        w = QWidget()
        hw = QWidget()

        self.w_label = QLabel(self.wallet_label())
        self.w_add_btn = QPushButton(_('Add / Import'))
        self.w_file_btn = QPushButton(_('File'))
        self.w_del_btn = QPushButton(_('Remove'))
        self.w_up_params_btn = QPushButton(_('Update Params'))
        self.w_up_coll_btn = QPushButton(_('Change Collateral'))
        self.w_protx_btn = QPushButton(_('Register'))
        self.w_up_srv_btn = QPushButton(_('Update Service'))
        self.w_up_reg_btn = QPushButton(_('Update Registrar'))
        self.w_add_btn.clicked.connect(self.on_add_masternode)
        self.w_file_btn.clicked.connect(self.on_file)
        self.w_del_btn.clicked.connect(self.on_del_masternode)
        self.w_up_params_btn.clicked.connect(self.on_update_params)
        self.w_up_coll_btn.clicked.connect(self.on_update_collateral)
        self.w_protx_btn.clicked.connect(self.on_make_pro_reg_tx)
        self.w_up_srv_btn.clicked.connect(self.on_make_pro_up_srv_tx)
        self.w_up_reg_btn.clicked.connect(self.on_make_pro_up_reg_tx)

        self.w_view = QTableView()
        self.w_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.w_view.customContextMenuRequested.connect(self.create_wallet_menu)
        self.w_hheader = QHeaderView(Qt.Horizontal, self.w_view)
        self.w_hheader.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.w_hheader.setStretchLastSection(True)

        self.w_view.setHorizontalHeader(self.w_hheader)
        self.w_view.verticalHeader().hide()
        self.w_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.w_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.w_view.doubleClicked.connect(self.w_mn_dbl_clicked)

        row_h = self.w_view.verticalHeader().defaultSectionSize()
        self.w_hheader.setMinimumSectionSize(row_h)
        src_model = WalletMNsModel(self.manager, self.mn_list, self.gui, row_h)
        src_model.dataChanged.connect(self.w_data_changed)
        self.w_model = Dip3FilterProxyModel()
        self.w_model.setSourceModel(src_model)
        self.w_view.setModel(self.w_model)

        sel_model = self.w_view.selectionModel()
        sel_model.selectionChanged.connect(self.on_wallet_selection_changed)
        self.w_model.modelReset.connect(self.on_wallet_model_reset)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addWidget(self.w_label)
        hbox.addStretch(1)
        hbox.addWidget(self.w_del_btn)
        hbox.addWidget(self.w_up_params_btn)
        hbox.addWidget(self.w_up_coll_btn)
        hbox.addWidget(self.w_protx_btn)
        hbox.addWidget(self.w_up_reg_btn)
        hbox.addWidget(self.w_up_srv_btn)
        hbox.addWidget(self.w_file_btn)
        hbox.addWidget(self.w_add_btn)
        hw.setLayout(hbox)
        vbox.addWidget(hw)
        vbox.addWidget(self.w_view)
        w.setLayout(vbox)
        self.addTab(w, read_QIcon('tab_dip3.png'), _('Wallet MNs'))
        if not self.mn_list.protx_loading:
            self.w_model.reload_data()
        return w

    @pyqtSlot()
    def on_reg_search(self):
        self.gui.toggle_search()

    def create_wallet_menu(self, position):
        a = self.w_cur_alias
        s = self.w_cur_state
        i = self.w_cur_idx
        if not i:
            return

        mn = self.manager.mns.get(a)
        owned = mn.is_owned
        operated = mn.is_operated
        protx_hash = mn.protx_hash
        removed = (s == WalletMNsModel.STATE_REMOVED)

        menu = QMenu()
        menu.addAction(_('Remove'), self.on_del_masternode)

        if not protx_hash:
            menu.addAction(_('Update Params'), self.on_update_params)

        if removed and owned:
            menu.addAction(_('Change Collateral'), self.on_update_collateral)

        if owned and not protx_hash:
            menu.addAction(_('Register Masternode'), self.on_make_pro_reg_tx)

        if operated and protx_hash and not removed:
            menu.addAction(_('Update Service'), self.on_make_pro_up_srv_tx)

        if owned and protx_hash and not removed:
            menu.addAction(_('Update Registrar'), self.on_make_pro_up_reg_tx)

        menu.addSeparator()
        menu.addAction(_('Rename Alias'), lambda: self.w_view.edit(i))
        menu.addSeparator()
        menu.addAction(_('Details'),
                       lambda: Dip3MNInfoDialog(self, alias=a).show())
        menu.exec_(self.w_view.viewport().mapToGlobal(position))

    @pyqtSlot()
    def on_update_collateral(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        removed = (self.w_cur_state == WalletMNsModel.STATE_REMOVED)
        if not mn or not mn.is_owned or not mn.protx_hash or not removed:
            return
        start_id = Dip3MasternodeWizard.COLLATERAL_PAGE
        wiz = Dip3MasternodeWizard(self, mn=mn, start_id=start_id)
        wiz.open()

    @pyqtSlot()
    def on_update_params(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or mn.protx_hash:
            return
        if mn.is_owned:
            start_id = Dip3MasternodeWizard.COLLATERAL_PAGE
        else:
            start_id = Dip3MasternodeWizard.SERVICE_PAGE
        wiz = Dip3MasternodeWizard(self, mn=mn, start_id=start_id)
        wiz.open()

    @pyqtSlot()
    def on_make_pro_reg_tx(self):
        try:
            pro_reg_tx = self.manager.prepare_pro_reg_tx(self.w_cur_alias)
        except ProRegTxExc as e:
            self.gui.show_error(e)
            return
        self.gui.payto_e.setText(self.wallet.get_unused_address())
        self.gui.extra_payload.set_extra_data(SPEC_PRO_REG_TX, pro_reg_tx)
        self.gui.show_extra_payload()
        self.gui.tabs.setCurrentIndex(self.gui.tabs.indexOf(self.gui.send_tab))

    @pyqtSlot()
    def on_file(self):
        wiz = Dip3FileWizard(self)
        wiz.open()

    @pyqtSlot()
    def on_add_masternode(self):
        wiz = Dip3MasternodeWizard(self)
        wiz.open()

    @pyqtSlot()
    def on_make_pro_up_srv_tx(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or not mn.protx_hash:
            return
        wiz = Dip3MasternodeWizard(self,
                                   mn=mn,
                                   start_id=Dip3MasternodeWizard.UPD_SRV_PAGE)
        wiz.open()

    @pyqtSlot()
    def on_make_pro_up_reg_tx(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or not mn.protx_hash:
            return
        wiz = Dip3MasternodeWizard(self,
                                   mn=mn,
                                   start_id=Dip3MasternodeWizard.UPD_REG_PAGE)
        wiz.open()

    @pyqtSlot()
    def on_del_masternode(self):
        alias = self.w_cur_alias
        mn = self.manager.mns.get(alias)
        if not mn:
            return
        if not self.gui.question(
                _('Do you want to remove the masternode '
                  'configuration for %s?') % alias):
            return
        if mn.protx_hash:
            if not self.gui.question(
                    _('Masternode %s has RroRegTxHash '
                      'already set. Are you sure?') % alias):
                return
        self.manager.remove_mn(self.w_cur_alias)
        self.w_model.reload_data()

    @pyqtSlot()
    def on_reg_selection_changed(self):
        sel = self.reg_view.selectionModel()
        if sel.hasSelection():
            idx = sel.selectedRows()[0]
            self.reg_cur_protx = idx.data()
        else:
            self.reg_cur_protx = ''

    @pyqtSlot()
    def on_wallet_model_reset(self):
        self.update_wallet_label()
        self.w_file_btn.show()
        self.w_add_btn.show()
        self.w_up_params_btn.hide()
        self.w_up_coll_btn.hide()
        self.w_protx_btn.hide()
        self.w_up_srv_btn.hide()
        self.w_up_reg_btn.hide()
        self.w_del_btn.hide()

    @pyqtSlot()
    def on_wallet_selection_changed(self):
        sel = self.w_view.selectionModel()
        if not sel.hasSelection():
            self.w_cur_alias = ''
            self.w_cur_state = ''
            self.w_cur_idx = None
            self.w_add_btn.show()
            self.w_file_btn.show()
            self.w_protx_btn.hide()
            self.w_del_btn.hide()
            self.w_up_params_btn.hide()
            self.w_up_coll_btn.hide()
            self.w_up_srv_btn.hide()
            self.w_up_reg_btn.hide()
            return
        self.w_add_btn.hide()
        self.w_file_btn.hide()

        idx = sel.selectedRows()[0]
        self.w_cur_alias = idx.data()
        self.w_cur_state = idx.sibling(idx.row(), 1).data(Qt.ToolTipRole)
        self.w_cur_idx = idx

        mn = self.manager.mns.get(self.w_cur_alias)
        owned = mn.is_owned
        operated = mn.is_operated
        protx_hash = mn.protx_hash
        removed = (self.w_cur_state == WalletMNsModel.STATE_REMOVED)

        self.w_del_btn.show()

        if not protx_hash:
            self.w_up_params_btn.show()
        else:
            self.w_up_params_btn.hide()

        if removed and owned:
            self.w_up_coll_btn.show()
        else:
            self.w_up_coll_btn.hide()

        if owned and not protx_hash:
            self.w_protx_btn.show()
        else:
            self.w_protx_btn.hide()

        if operated and protx_hash and not removed:
            self.w_up_srv_btn.show()
        else:
            self.w_up_srv_btn.hide()

        if owned and protx_hash and not removed:
            self.w_up_reg_btn.show()
        else:
            self.w_up_reg_btn.hide()

    @pyqtSlot(QModelIndex)
    def reg_mn_dbl_clicked(self, idx):
        row_idx = idx.sibling(idx.row(), 0)
        d = Dip3MNInfoDialog(self, protx_hash=row_idx.data())
        d.show()

    @pyqtSlot(QModelIndex)
    def w_mn_dbl_clicked(self, idx):
        col = idx.column()
        if col == WalletMNsModel.ALIAS:
            return
        row_idx = idx.sibling(idx.row(), 0)
        d = Dip3MNInfoDialog(self, alias=row_idx.data())
        d.show()

    @pyqtSlot(QModelIndex)
    def w_data_changed(self, idx):
        sel_model = self.w_view.selectionModel()
        sel_model.clear()
        sel_model.setCurrentIndex(
            idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        sel_model.select(
            idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
Exemplo n.º 4
0
class Dip3TabWidget(QTabWidget):
    # Signals need to notify from not Qt thread
    alias_updated = pyqtSignal(str)
    diff_updated = pyqtSignal(dict)
    info_updated = pyqtSignal(str)
    network_updated = pyqtSignal()

    def __init__(self, gui, wallet, *args, **kwargs):
        QTabWidget.__init__(self, *args, **kwargs)
        self.tab_bar = Dip3TabBar(self)
        self.setTabBar(self.tab_bar)
        self.setTabPosition(QTabWidget.East)
        self.gui = gui
        self.wallet = wallet
        self.manager = wallet.protx_manager
        self.mn_list = gui.network.mn_list if gui.network else None
        self.reg_cur_protx = ''
        self.reg_cur_idx = None
        self.w_cur_alias = ''
        self.w_cur_state = ''
        self.w_cur_idx = None

        self.wallet_mn_tab = self.create_wallet_mn_tab()
        if self.mn_list:
            self.registerd_mn_tab = self.create_registered_mn_tab()
        self.searchable_list = self.w_model
        self.currentChanged.connect(self.on_tabs_current_changed)

        if self.mn_list:
            self.tab_bar.update_stats(self.get_stats())
            util.register_callback(self.on_mn_list_diff_updated,
                                   ['mn-list-diff-updated'])
            util.register_callback(self.on_mn_list_info_updated,
                                   ['mn-list-info-updated'])
        if self.gui.network:
            util.register_callback(self.on_cb_network_updated,
                                   ['network_updated'])
        util.register_callback(self.on_manager_alias_updated,
                               ['manager-alias-updated'])
        self.alias_updated.connect(self.on_alias_updated)
        self.diff_updated.connect(self.on_diff_updated)
        self.info_updated.connect(self.on_info_updated)
        self.network_updated.connect(self.on_network_updated)

    def unregister_callbacks(self):
        if self.mn_list:
            util.unregister_callback(self.on_mn_list_diff_updated)
            util.unregister_callback(self.on_mn_list_info_updated)
        if self.gui.network:
            util.unregister_callback(self.on_cb_network_updated)
        util.unregister_callback(self.on_manager_alias_updated)

    def get_stats(self):
        mn_list = self.mn_list
        local_height = self.gui.network.get_local_height()
        protx_height = mn_list.protx_height
        llmq_height = mn_list.llmq_human_height
        protx_ready = 'Yes' if mn_list.protx_ready else 'No'
        llmq_ready = 'Yes' if mn_list.llmq_ready else 'No'
        completeness = mn_list.protx_info_completeness
        protx_info_completeness = '%s%%' % round(completeness * 100)
        return (local_height, protx_height, llmq_height, protx_ready,
                llmq_ready, protx_info_completeness)

    @pyqtSlot()
    def on_tabs_current_changed(self):
        cur_widget = self.currentWidget()
        if cur_widget == self.wallet_mn_tab:
            self.searchable_list = self.w_model
        else:
            self.searchable_list = self.reg_model

    def on_mn_list_diff_updated(self, key, value):
        self.diff_updated.emit(value)

    def on_mn_list_info_updated(self, key, value):
        self.info_updated.emit(value)

    def on_manager_alias_updated(self, key, value):
        self.alias_updated.emit(value)

    def on_cb_network_updated(self, key):
        self.network_updated.emit()

    @pyqtSlot(str)
    def on_alias_updated(self, alias):
        self.w_model.reload_data()
        self.restore_w_selection()

    @pyqtSlot()
    def on_network_updated(self):
        self.tab_bar.update_stats(self.get_stats())

    @pyqtSlot(dict)
    def on_diff_updated(self, value):
        state = value.get('state', self.mn_list.DIP3_DISABLED)
        if self.mn_list.load_mns and not self.mn_list.protx_loading:
            self.reg_model.reload_data()
            self.restore_reg_selection()
        self.w_model.reload_data()
        self.restore_w_selection()

        if state == self.mn_list.DIP3_ENABLED:
            self.reg_search_btn.setEnabled(True)
        else:
            self.reg_search_btn.setEnabled(False)

        self.tab_bar.update_stats(self.get_stats())
        self.update_registered_label()
        self.update_wallet_label()

    @pyqtSlot(str)
    def on_info_updated(self, value):
        self.tab_bar.update_stats(self.get_stats())

    def registered_label(self):
        if not self.mn_list:
            return _('Offline')

        state = self.mn_list.protx_state
        if state == self.mn_list.DIP3_DISABLED:
            return _('DIP3 Masternodes is currently disabled.')

        count = len(self.mn_list.protx_mns)
        connected = self.gui.network.is_connected()
        loading = connected and self.mn_list.protx_loading
        ready = _('Loading') if loading else _('Found')
        return (_('%s %s registered DIP3 Masternodes.') % (ready, count))

    def update_registered_label(self):
        self.reg_label.setText(self.registered_label())

    def wallet_label(self):
        mns = self.manager.mns
        count = len(mns)
        mn_str = _('Masternode') if count == 1 else _('Masternodes')
        def_label_str = _('Wallet contains %s DIP3 %s.') % (count, mn_str)

        if not self.mn_list:
            return def_label_str

        state = self.mn_list.protx_state
        if state == self.mn_list.DIP3_DISABLED:
            return (_('DIP3 Masternodes is currently disabled.'))

        connected = self.gui.network.is_connected()
        loading = connected and self.mn_list.protx_loading
        if loading:
            height = self.mn_list.protx_height
            return (_('Loading DIP3 data at Height: %s.') % height)

        return def_label_str

    def update_wallet_label(self):
        self.w_label.setText(self.wallet_label())

    def create_registered_mn_tab(self):
        w = QWidget()
        hw = QWidget()

        self.reg_label = QLabel(self.registered_label())
        self.reg_search_btn = QPushButton(_('Search'))
        self.reg_search_btn.clicked.connect(self.on_reg_search)

        src_model = RegisteredMNsModel(self.mn_list)
        self.reg_model = Dip3FilterProxyModel()
        self.reg_model.setSourceModel(src_model)

        self.reg_view = QTableView()
        self.reg_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.reg_view.customContextMenuRequested.connect(self.create_reg_menu)
        self.reg_hheader = QHeaderView(Qt.Horizontal, self.reg_view)
        self.reg_hheader.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.reg_hheader.setStretchLastSection(True)

        self.reg_view.setHorizontalHeader(self.reg_hheader)
        self.reg_view.verticalHeader().hide()
        self.reg_view.setModel(self.reg_model)
        self.reg_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.reg_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.reg_view.doubleClicked.connect(self.reg_mn_dbl_clicked)

        sel_model = self.reg_view.selectionModel()
        sel_model.selectionChanged.connect(self.on_reg_selection_changed)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addWidget(self.reg_label)
        hbox.addStretch(1)
        hbox.addWidget(self.reg_search_btn)
        hw.setLayout(hbox)
        vbox.addWidget(hw)
        vbox.addWidget(self.reg_view)
        w.setLayout(vbox)
        self.addTab(w, read_QIcon('tab_search.png'), _('Registered MNs'))
        if not self.mn_list.protx_loading:
            self.reg_model.reload_data()
            self.restore_reg_selection()
        return w

    def create_reg_menu(self, position):
        menu = QMenu()
        h = self.reg_cur_protx
        menu.addAction(_('Details'), lambda: self.show_protx_hash_info(h))
        menu.exec_(self.reg_view.viewport().mapToGlobal(position))

    def create_wallet_mn_tab(self):
        w = QWidget()
        hw = QWidget()

        self.w_label = QLabel(self.wallet_label())
        self.w_add_btn = QPushButton(_('Add / Import'))
        self.w_file_btn = QPushButton(_('File'))
        self.w_del_btn = QPushButton(_('Remove'))
        self.w_clear_protx_btn = QPushButton(_('Clear ProTxHash'))
        self.w_up_params_btn = QPushButton(_('Update Params'))
        self.w_protx_btn = QPushButton(_('Register'))
        self.w_up_reg_btn = QPushButton(_('Update Registrar'))
        self.w_up_rev_btn = QPushButton(_('Revoke'))
        self.w_new_bls_bnt = QPushButton(_('New BLS Keys'))
        self.w_up_srv_btn = QPushButton(_('Update Service'))
        self.w_add_btn.clicked.connect(self.on_add_masternode)
        self.w_file_btn.clicked.connect(self.on_file)
        self.w_del_btn.clicked.connect(self.on_del_masternode)
        self.w_clear_protx_btn.clicked.connect(self.on_clear_protx)
        self.w_up_params_btn.clicked.connect(self.on_update_params)
        self.w_protx_btn.clicked.connect(self.on_make_pro_reg_tx)
        self.w_up_reg_btn.clicked.connect(self.on_make_pro_up_reg_tx)
        self.w_up_rev_btn.clicked.connect(self.on_make_pro_up_rev_tx)
        self.w_new_bls_bnt.clicked.connect(self.on_new_bls)
        self.w_up_srv_btn.clicked.connect(self.on_make_pro_up_srv_tx)
        self.hide_mn_buttons()

        self.w_view = QTableView()
        self.w_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.w_view.customContextMenuRequested.connect(self.create_wallet_menu)
        self.w_hheader = QHeaderView(Qt.Horizontal, self.w_view)
        self.w_hheader.setSectionResizeMode(QHeaderView.ResizeToContents)
        self.w_hheader.setStretchLastSection(True)

        self.w_view.setHorizontalHeader(self.w_hheader)
        self.w_view.verticalHeader().hide()
        self.w_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.w_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.w_view.doubleClicked.connect(self.w_mn_dbl_clicked)

        row_h = self.w_view.verticalHeader().defaultSectionSize()
        self.w_hheader.setMinimumSectionSize(row_h)
        src_model = WalletMNsModel(self.manager, self.mn_list, self.gui, row_h)
        src_model.dataChanged.connect(self.w_data_changed)
        self.w_model = Dip3FilterProxyModel()
        self.w_model.setSourceModel(src_model)
        self.w_view.setModel(self.w_model)

        sel_model = self.w_view.selectionModel()
        sel_model.selectionChanged.connect(self.on_wallet_selection_changed)
        self.w_model.modelReset.connect(self.on_wallet_model_reset)

        hbox = QHBoxLayout()
        vbox = QVBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addWidget(self.w_label)
        hbox.addStretch(1)
        hbox.addWidget(self.w_add_btn)
        hw.setLayout(hbox)
        vbox.addWidget(hw)
        vbox.addWidget(self.w_view)
        self.addTab(w, read_QIcon('tab_dip3.png'), _('Wallet MNs'))
        if self.mn_list and not self.mn_list.protx_loading:
            self.w_model.reload_data()
            self.restore_w_selection()

        hw = QWidget()
        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(self.w_file_btn)
        hbox.addWidget(self.w_del_btn)
        hbox.addWidget(self.w_clear_protx_btn)
        hbox.addWidget(self.w_up_params_btn)
        hbox.addWidget(self.w_protx_btn)
        hbox.addWidget(self.w_up_reg_btn)
        hbox.addWidget(self.w_up_rev_btn)
        hbox.addWidget(self.w_new_bls_bnt)
        hbox.addWidget(self.w_up_srv_btn)
        hw.setLayout(hbox)
        vbox.addWidget(hw)
        w.setLayout(vbox)
        return w

    @pyqtSlot()
    def on_reg_search(self):
        self.gui.toggle_search()

    def create_wallet_menu(self, position):
        a = self.w_cur_alias
        s = self.w_cur_state
        i = self.w_cur_idx

        menu = QMenu()
        if not i or s == WalletMNsModel.STATE_LOADING:
            menu.addAction(_('Add / Import'), self.on_add_masternode)
            menu.addAction(_('File'), self.on_file)
            menu.exec_(self.w_view.viewport().mapToGlobal(position))
            return

        mn = self.manager.mns.get(a)
        owned = mn.is_owned
        operated = mn.is_operated
        protx_hash = mn.protx_hash
        removed = (s == WalletMNsModel.STATE_REMOVED)

        menu.addAction(_('Remove'), self.on_del_masternode)

        if removed:
            menu.addAction(_('Clear ProTxHash'), self.on_clear_protx)

        if not protx_hash:
            menu.addAction(_('Update Params'), self.on_update_params)

        if owned and not protx_hash:
            menu.addAction(_('Register'), self.on_make_pro_reg_tx)

        if (owned and protx_hash and not removed
                and self.wallet.is_mine(mn.owner_addr)):
            menu.addAction(_('Update Registrar'), self.on_make_pro_up_reg_tx)

        if operated and not owned and protx_hash and not removed:
            menu.addAction(_('Revoke'), self.on_make_pro_up_rev_tx)

        if operated and not owned and protx_hash:
            menu.addAction(_('New BLS Keys'), self.on_new_bls)

        if operated and protx_hash and not removed:
            menu.addAction(_('Update Service'), self.on_make_pro_up_srv_tx)

        menu.addSeparator()
        menu.addAction(_('Rename Alias'), lambda: self.w_view.edit(i))
        menu.addSeparator()
        menu.addAction(_('Details'), lambda: self.show_alias_info(a))

        menu.addSeparator()
        menu.addAction(_('Add / Import'), self.on_add_masternode)
        menu.addAction(_('File'), self.on_file)

        menu.exec_(self.w_view.viewport().mapToGlobal(position))

    @pyqtSlot()
    def on_new_bls(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or not mn.is_operated:
            return
        start_id = Dip3MasternodeWizard.BLS_KEYS_PAGE
        wiz = Dip3MasternodeWizard(self, mn=mn, start_id=start_id)
        wiz.open()

    @pyqtSlot()
    def on_update_params(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or mn.protx_hash:
            return
        if mn.is_owned:
            start_id = Dip3MasternodeWizard.COLLATERAL_PAGE
        else:
            start_id = Dip3MasternodeWizard.SERVICE_PAGE
        wiz = Dip3MasternodeWizard(self, mn=mn, start_id=start_id)
        wiz.open()

    @pyqtSlot()
    def on_make_pro_reg_tx(self):
        alias = self.w_cur_alias
        try:
            pro_reg_tx = self.manager.prepare_pro_reg_tx(alias)
        except ProRegTxExc as e:
            self.gui.show_error(e)
            return
        mn = self.manager.mns.get(alias)
        gui = self.gui
        gui.do_clear()
        if mn.collateral.is_null:
            gui.amount_e.setText('1000')
        mn_addrs = [mn.owner_addr, mn.voting_addr, mn.payout_address]
        for addr in self.wallet.get_unused_addresses():
            if addr not in mn_addrs:
                gui.payto_e.setText(addr)
                break
        gui.extra_payload.set_extra_data(SPEC_PRO_REG_TX, pro_reg_tx, alias)
        gui.show_extra_payload()
        gui.tabs.setCurrentIndex(self.gui.tabs.indexOf(self.gui.send_tab))

    @pyqtSlot()
    def on_file(self):
        wiz = Dip3FileWizard(self)
        wiz.open()

    @pyqtSlot()
    def on_add_masternode(self):
        wiz = Dip3MasternodeWizard(self)
        wiz.open()

    @pyqtSlot()
    def on_make_pro_up_srv_tx(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or not mn.protx_hash:
            return
        wiz = Dip3MasternodeWizard(self,
                                   mn=mn,
                                   start_id=Dip3MasternodeWizard.UPD_SRV_PAGE)
        wiz.open()

    @pyqtSlot()
    def on_make_pro_up_rev_tx(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or not mn.protx_hash:
            return
        d = RevokeOperatiorDialog(self, mn)
        if d.exec_() and d.reason is not None:
            try:
                rev_tx = self.manager.prepare_pro_up_rev_tx(mn.alias, d.reason)
            except ProRegTxExc as e:
                self.gui.show_error(e)
                return
            gui = self.gui
            gui.do_clear()
            mn_addrs = [mn.owner_addr, mn.voting_addr, mn.payout_address]
            for addr in self.manager.wallet.get_unused_addresses():
                if addr not in mn_addrs:
                    gui.payto_e.setText(addr)
                    break
            tx_type = SPEC_PRO_UP_REV_TX
            gui.extra_payload.set_extra_data(tx_type, rev_tx, mn.alias)
            gui.show_extra_payload()
            gui.tabs.setCurrentIndex(gui.tabs.indexOf(gui.send_tab))

    @pyqtSlot()
    def on_make_pro_up_reg_tx(self):
        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or not mn.protx_hash:
            return
        wiz = Dip3MasternodeWizard(self,
                                   mn=mn,
                                   start_id=Dip3MasternodeWizard.UPD_REG_PAGE)
        wiz.open()

    @pyqtSlot()
    def on_del_masternode(self):
        alias = self.w_cur_alias
        mn = self.manager.mns.get(alias)
        if not mn:
            return
        if not self.gui.question(
                _('Do you want to remove the masternode '
                  'configuration for %s?') % alias):
            return
        if mn.protx_hash:
            if not self.gui.question(
                    _('Masternode %s has ProRegTxHash '
                      'already set. Are you sure?') % alias):
                return
        self.manager.remove_mn(self.w_cur_alias)
        self.w_model.reload_data()

    @pyqtSlot()
    def on_clear_protx(self):
        alias = self.w_cur_alias
        mn = self.manager.mns.get(alias)
        if not mn:
            return
        q = '\n'.join([
            _('Clearing of ProTxHash allow reuse of masternode.'),
            _('Do you want to clear ProTxHash for %s?') % alias
        ])
        if not self.gui.question(q):
            return
        if not mn.service.port:  # service cleared on Revoke operator
            mn.service = ProTxService('', ProTxMN.default_port())
        mn.protx_hash = ''
        self.manager.update_mn(alias, mn)
        self.w_model.reload_alias(alias)

    @pyqtSlot()
    def on_reg_selection_changed(self):
        sel = self.reg_view.selectionModel()
        if sel.hasSelection():
            idx = sel.selectedRows()[0]
            self.reg_cur_idx = idx
            self.reg_cur_protx = idx.data()
        else:
            self.reg_cur_idx = None
            self.reg_cur_protx = ''

    def restore_reg_selection(self):
        cur_idx = self.reg_cur_idx
        if not cur_idx:
            return
        row_count = self.reg_model.sourceModel().row_count
        if row_count == 0 or row_count <= cur_idx.row():
            self.reg_cur_idx = None
            self.reg_cur_protx = ''
        else:
            self.reg_view.selectRow(cur_idx.row())

    def hide_mn_buttons(self):
        self.w_del_btn.hide()
        self.w_clear_protx_btn.hide()
        self.w_up_params_btn.hide()
        self.w_protx_btn.hide()
        self.w_up_reg_btn.hide()
        self.w_up_rev_btn.hide()
        self.w_new_bls_bnt.hide()
        self.w_up_srv_btn.hide()

    @pyqtSlot()
    def on_wallet_model_reset(self):
        self.update_wallet_label()
        self.hide_mn_buttons()

    @pyqtSlot()
    def on_wallet_selection_changed(self):
        sel = self.w_view.selectionModel()
        if not sel.hasSelection():
            self.w_cur_alias = ''
            self.w_cur_state = ''
            self.w_cur_idx = None
            self.hide_mn_buttons()
            return

        idx = sel.selectedRows()[0]
        self.w_cur_alias = idx.data()
        self.w_cur_state = idx.sibling(idx.row(), 1).data(Qt.ToolTipRole)
        self.w_cur_idx = idx

        mn = self.manager.mns.get(self.w_cur_alias)
        if not mn or self.w_cur_state == WalletMNsModel.STATE_LOADING:
            return

        owned = mn.is_owned
        operated = mn.is_operated
        protx_hash = mn.protx_hash
        removed = (self.w_cur_state == WalletMNsModel.STATE_REMOVED)

        self.w_del_btn.show()

        if removed:
            self.w_clear_protx_btn.show()

        if not protx_hash:
            self.w_up_params_btn.show()
        else:
            self.w_up_params_btn.hide()

        if operated and not owned and protx_hash:
            self.w_new_bls_bnt.show()
        else:
            self.w_new_bls_bnt.hide()

        if owned and not protx_hash:
            self.w_protx_btn.show()
        else:
            self.w_protx_btn.hide()

        if operated and not owned and protx_hash and not removed:
            self.w_up_rev_btn.show()
        else:
            self.w_up_rev_btn.hide()

        if operated and protx_hash and not removed:
            self.w_up_srv_btn.show()
        else:
            self.w_up_srv_btn.hide()

        if (owned and protx_hash and not removed
                and self.wallet.is_mine(mn.owner_addr)):
            self.w_up_reg_btn.show()
        else:
            self.w_up_reg_btn.hide()

    def restore_w_selection(self):
        cur_idx = self.w_cur_idx
        if not cur_idx:
            return
        row_count = self.w_model.sourceModel().row_count
        if row_count == 0 or row_count <= cur_idx.row():
            self.w_cur_alias = ''
            self.w_cur_state = ''
            self.w_cur_idx = None
        else:
            self.w_view.selectRow(cur_idx.row())

    @pyqtSlot(QModelIndex)
    def reg_mn_dbl_clicked(self, idx):
        row_idx = idx.sibling(idx.row(), 0)
        self.show_protx_hash_info(row_idx.data())

    def show_protx_hash_info(self, protx_hash):
        d = Dip3MNInfoDialog(self, protx_hash=protx_hash)
        mn_info_dialogs.append(d)
        d.show()

    @pyqtSlot(QModelIndex)
    def w_mn_dbl_clicked(self, idx):
        col = idx.column()
        if col == WalletMNsModel.ALIAS:
            return
        row_idx = idx.sibling(idx.row(), 0)
        self.show_alias_info(row_idx.data())

    def show_alias_info(self, alias):
        d = Dip3MNInfoDialog(self, alias=alias)
        mn_info_dialogs.append(d)
        d.show()

    @pyqtSlot(QModelIndex)
    def w_data_changed(self, idx):
        sel_model = self.w_view.selectionModel()
        sel_model.clear()
        sel_model.setCurrentIndex(
            idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        sel_model.select(
            idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
class ReferenceDataDlg(QDialog):
    def __init__(self, parent=None):
        super(ReferenceDataDlg, self).__init__(parent)

        self.model = QSqlTableModel(self)
        self.model.setTable("reference")
        self.model.setSort(ID, Qt.AscendingOrder)
        self.model.setHeaderData(ID, Qt.Horizontal, "ID")
        self.model.setHeaderData(CATEGORY, Qt.Horizontal, "小车编号")
        self.model.setHeaderData(SHORTDESC, Qt.Horizontal, "检查记录")
        self.model.setHeaderData(LONGDESC, Qt.Horizontal, "巡检日期")
        self.model.select()

        self.view = QTableView()
        self.view.setModel(self.model)
        self.view.setSelectionMode(QTableView.SingleSelection)
        self.view.setSelectionBehavior(QTableView.SelectRows)
        self.view.setColumnHidden(ID, True)
        self.view.resizeColumnsToContents()

        buttonBox = QDialogButtonBox()
        addButton = buttonBox.addButton("&添加", QDialogButtonBox.ActionRole)
        deleteButton = buttonBox.addButton("&删除", QDialogButtonBox.ActionRole)
        sortButton = buttonBox.addButton("&排序", QDialogButtonBox.ActionRole)
        if not MAC:
            addButton.setFocusPolicy(Qt.NoFocus)
            deleteButton.setFocusPolicy(Qt.NoFocus)
            sortButton.setFocusPolicy(Qt.NoFocus)

        menu = QMenu(self)
        sortByCategoryAction = menu.addAction("按小车编号排序")
        sortByDescriptionAction = menu.addAction("按检查记录排序")
        sortByIDAction = menu.addAction("按编号顺序排序")
        sortButton.setMenu(menu)
        closeButton = buttonBox.addButton(QDialogButtonBox.Close)

        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addWidget(buttonBox)
        self.setLayout(layout)

        addButton.clicked.connect(self.addRecord)
        deleteButton.clicked.connect(self.deleteRecord)
        sortByCategoryAction.triggered.connect(lambda: self.sort(CATEGORY))
        sortByDescriptionAction.triggered.connect(lambda: self.sort(SHORTDESC))
        sortByIDAction.triggered.connect(lambda: self.sort(ID))
        closeButton.clicked.connect(self.accept)
        self.setWindowTitle("巡检历史数据")
        self.setWindowIcon(
            QtGui.QIcon('icon/update_128px_1156069_easyicon.net.ico'))

    def addRecord(self):
        row = self.model.rowCount()
        self.model.insertRow(row)
        index = self.model.index(row, CATEGORY)
        self.view.setCurrentIndex(index)
        self.view.edit(index)

    def deleteRecord(self):
        index = self.view.currentIndex()
        if not index.isValid():
            return
        record = self.model.record(index.row())
        category = record.value(CATEGORY)
        desc = record.value(SHORTDESC)
        if (QMessageBox.question(self, "Reference Data",
                                 ("确定删除 {1} 的数据?".format(desc, category)),
                                 QMessageBox.Yes
                                 | QMessageBox.No) == QMessageBox.No):
            return
        self.model.removeRow(index.row())
        self.model.submitAll()
        self.model.select()

    def sort(self, column):
        self.model.setSort(column, Qt.AscendingOrder)
        self.model.select()
Exemplo n.º 6
0
class EditPandasTable(QWidget):
    """A Widget to display and edit a pandas DataFrame
    """
    def __init__(self, data=pandas.DataFrame([]), ui_buttons=True, parent=None):
        super().__init__(parent)
        self.ui_buttons = ui_buttons

        self.model = EditPandasModel(data)
        self.view = QTableView()
        self.view.setModel(self.model)

        self.layout = QHBoxLayout()

        self.rows_chkbx = QSpinBox()
        self.rows_chkbx.setMinimum(1)
        self.cols_chkbx = QSpinBox()
        self.cols_chkbx.setMinimum(1)

        self.init_ui()

    def init_ui(self):
        self.layout.addWidget(self.view)

        if self.ui_buttons:
            bt_layout = QVBoxLayout()

            addr_layout = QHBoxLayout()
            addr_bt = QPushButton('Add Row')
            addr_bt.clicked.connect(self.add_row)
            addr_layout.addWidget(addr_bt)
            addr_layout.addWidget(self.rows_chkbx)
            bt_layout.addLayout(addr_layout)

            addc_layout = QHBoxLayout()
            addc_bt = QPushButton('Add Column')
            addc_bt.clicked.connect(self.add_column)
            addc_layout.addWidget(addc_bt)
            addc_layout.addWidget(self.cols_chkbx)
            bt_layout.addLayout(addc_layout)

            rmr_bt = QPushButton('Remove Row')
            rmr_bt.clicked.connect(self.remove_row)
            bt_layout.addWidget(rmr_bt)

            rmc_bt = QPushButton('Remove Column')
            rmc_bt.clicked.connect(self.remove_column)
            bt_layout.addWidget(rmc_bt)

            edit_bt = QPushButton('Edit')
            edit_bt.clicked.connect(self.edit_item)
            bt_layout.addWidget(edit_bt)

            editrh_bt = QPushButton('Edit Row-Header')
            editrh_bt.clicked.connect(self.edit_row_header)
            bt_layout.addWidget(editrh_bt)

            editch_bt = QPushButton('Edit Column-Header')
            editch_bt.clicked.connect(self.edit_col_header)
            bt_layout.addWidget(editch_bt)

            self.layout.addLayout(bt_layout)

        self.setLayout(self.layout)

    def content_changed(self):
        """Informs ModelView about external change made in data
        """
        self.model.layoutChanged.emit()

    def update_data(self):
        """Has to be called, when model._data is rereferenced by for example add_row
        to keep external data updated

        Returns
        -------
        data : pandas.DataFrame
            The DataFrame of this widget

        Notes
        -----
        You can overwrite this function in a subclass e.g. to update an objects attribute
        (e.g. obj.data = self.model._data)
        """

        return self.model._data

    def replace_data(self, new_data):
        """Replaces model._data with new_data
        """
        self.model._data = new_data
        self.content_changed()

    def add_row(self):
        row = self.view.selectionModel().currentIndex().row()
        # Add row at the bottom if nothing is selected
        if row == -1 or len(self.view.selectionModel().selectedIndexes()) == 0:
            row = len(self.model._data.index)
        self.model.insertRows(row, self.rows_chkbx.value())
        self.update_data()

    def add_column(self):
        column = self.view.selectionModel().currentIndex().column()
        # Add column to the right if nothing is selected
        if column == -1 or len(self.view.selectionModel().selectedIndexes()) == 0:
            column = len(self.model._data.columns)
        self.model.insertColumns(column, self.cols_chkbx.value())
        self.update_data()

    def remove_row(self):
        rows = sorted(set([ix.row() for ix in self.view.selectionModel().selectedIndexes()]), reverse=True)
        for row in rows:
            self.model.removeRow(row)
        self.update_data()

    def remove_column(self):
        columns = sorted(set([ix.column() for ix in self.view.selectionModel().selectedIndexes()]), reverse=True)
        for column in columns:
            self.model.removeColumn(column)
        self.update_data()

    def edit_item(self):
        self.view.edit(self.view.selectionModel().currentIndex())

    def edit_row_header(self):
        row = self.view.selectionModel().currentIndex().row()
        old_value = self.model._data.index[row]
        text, ok = QInputDialog.getText(self, 'Change Row-Header', f'Change {old_value} in row {row} to:')
        if text and ok:
            self.model.setHeaderData(row, Qt.Vertical, text)

    def edit_col_header(self):
        column = self.view.selectionModel().currentIndex().column()
        old_value = self.model._data.columns[column]
        text, ok = QInputDialog.getText(self, 'Change Column-Header', f'Change {old_value} in column {column} to:')
        if text and ok:
            self.model.setHeaderData(column, Qt.Horizontal, text)
Exemplo n.º 7
0
class MainForm(QDialog):
    def __init__(self):
        super(MainForm, self).__init__()

        self.assetModel = QSqlRelationalTableModel(self)
        self.assetModel.setTable("assets")
        self.assetModel.setRelation(CATEGORYID,
                                    QSqlRelation("categories", "id", "name"))
        self.assetModel.setSort(ROOM, Qt.AscendingOrder)
        self.assetModel.setHeaderData(ID, Qt.Horizontal, "ID")
        self.assetModel.setHeaderData(NAME, Qt.Horizontal, "Name")
        self.assetModel.setHeaderData(CATEGORYID, Qt.Horizontal, "Category")
        self.assetModel.setHeaderData(ROOM, Qt.Horizontal, "Room")
        self.assetModel.select()

        self.assetView = QTableView()
        self.assetView.setModel(self.assetModel)
        self.assetView.setItemDelegate(AssetDelegate(self))
        self.assetView.setSelectionMode(QTableView.SingleSelection)
        self.assetView.setSelectionBehavior(QTableView.SelectRows)
        self.assetView.setColumnHidden(ID, True)
        self.assetView.resizeColumnsToContents()
        assetLabel = QLabel("A&ssets")
        assetLabel.setBuddy(self.assetView)

        self.logModel = QSqlRelationalTableModel(self)
        self.logModel.setTable("logs")
        self.logModel.setRelation(ACTIONID,
                                  QSqlRelation("actions", "id", "name"))
        self.logModel.setSort(DATE, Qt.AscendingOrder)
        self.logModel.setHeaderData(DATE, Qt.Horizontal, "Date")
        self.logModel.setHeaderData(ACTIONID, Qt.Horizontal, "Action")
        self.logModel.select()

        self.logView = QTableView()
        self.logView.setModel(self.logModel)
        self.logView.setItemDelegate(LogDelegate(self))
        self.logView.setSelectionMode(QTableView.SingleSelection)
        self.logView.setSelectionBehavior(QTableView.SelectRows)
        self.logView.setColumnHidden(ID, True)
        self.logView.setColumnHidden(ASSETID, True)
        self.logView.resizeColumnsToContents()
        self.logView.horizontalHeader().setStretchLastSection(True)
        logLabel = QLabel("&Logs")
        logLabel.setBuddy(self.logView)

        addAssetButton = QPushButton("&Add Asset")
        deleteAssetButton = QPushButton("&Delete Asset")
        addActionButton = QPushButton("Add A&ction")
        deleteActionButton = QPushButton("Delete Ac&tion")
        editActionsButton = QPushButton("&Edit Actions...")
        editCategoriesButton = QPushButton("Ed&it Categories...")
        quitButton = QPushButton("&Quit")
        for button in (addAssetButton, deleteAssetButton, addActionButton,
                       deleteActionButton, editActionsButton,
                       editCategoriesButton, quitButton):
            if MAC:
                button.setDefault(False)
                button.setAutoDefault(False)
            else:
                button.setFocusPolicy(Qt.NoFocus)

        dataLayout = QVBoxLayout()
        dataLayout.addWidget(assetLabel)
        dataLayout.addWidget(self.assetView, 1)
        dataLayout.addWidget(logLabel)
        dataLayout.addWidget(self.logView)
        buttonLayout = QVBoxLayout()
        buttonLayout.addWidget(addAssetButton)
        buttonLayout.addWidget(deleteAssetButton)
        buttonLayout.addWidget(addActionButton)
        buttonLayout.addWidget(deleteActionButton)
        buttonLayout.addWidget(editActionsButton)
        buttonLayout.addWidget(editCategoriesButton)
        buttonLayout.addStretch()
        buttonLayout.addWidget(quitButton)
        layout = QHBoxLayout()
        layout.addLayout(dataLayout, 1)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)

        #self.connect(self.assetView.selectionModel(),
        #SIGNAL(("currentRowChanged(QModelIndex,QModelIndex)")),
        #self.assetChanged)
        self.assetView.selectionModel().currentRowChanged.connect(
            self.assetChanged)
        addAssetButton.clicked.connect(self.addAsset)
        deleteAssetButton.clicked.connect(self.deleteAsset)
        addActionButton.clicked.connect(self.addAction)
        deleteActionButton.clicked.connect(self.deleteAction)
        editActionsButton.clicked.connect(self.editActions)
        editCategoriesButton.clicked.connect(self.editCategories)
        quitButton.clicked.connect(self.done)

        self.assetChanged(self.assetView.currentIndex())
        self.setMinimumWidth(650)
        self.setWindowTitle("Asset Manager")

    def done(self, result=1):
        query = QSqlQuery()
        query.exec_("DELETE FROM logs WHERE logs.assetid NOT IN"
                    "(SELECT id FROM assets)")
        QDialog.done(self, 1)

    def assetChanged(self, index):
        if index.isValid():
            record = self.assetModel.record(index.row())
            #print(index.row())
            id = record.value("id")
            self.logModel.setFilter("assetid = {0}".format(id))
        else:
            self.logModel.setFilter("assetid = -1")
        #self.logModel.reset() # workaround for Qt <= 4.3.3/SQLite bug
        #self.logModel.beginResetModel()
        self.logModel.select()
        self.logView.horizontalHeader().setVisible(
            self.logModel.rowCount() > 0)
        if PYQT_VERSION_STR < "4.1.0":
            self.logView.setColumnHidden(ID, True)
            self.logView.setColumnHidden(ASSETID, True)
        #self.logModel.endResetModel()

    def addAsset(self):
        row = (self.assetView.currentIndex().row()
               if self.assetView.currentIndex().isValid() else 0)

        QSqlDatabase.database().transaction()
        self.assetModel.insertRow(row)
        index = self.assetModel.index(row, NAME)
        self.assetView.setCurrentIndex(index)

        assetid = 1
        query = QSqlQuery()
        query.exec_("SELECT MAX(id) FROM assets")
        if query.next():
            assetid = query.value(0)
        query.prepare("INSERT INTO logs (assetid, date, actionid) "
                      "VALUES (:assetid, :date, :actionid)")
        query.bindValue(":assetid", assetid + 1)
        query.bindValue(":date", QDate.currentDate())
        query.bindValue(":actionid", ACQUIRED)
        query.exec_()
        QSqlDatabase.database().commit()
        #self.logModel.select()
        self.assetView.edit(index)

    def deleteAsset(self):
        index = self.assetView.currentIndex()
        if not index.isValid():
            return
        QSqlDatabase.database().transaction()
        record = self.assetModel.record(index.row())
        assetid = record.value(ID)
        logrecords = 1
        query = QSqlQuery(
            "SELECT COUNT(*) FROM logs WHERE assetid = {0}".format(assetid))
        if query.next():
            logrecords = query.value(0)
        msg = ("<font color=red>Delete</font><br><b>{0}</b>"
               "<br>from room {1}").format(record.value(NAME),
                                           record.value(ROOM))
        if logrecords > 1:
            msg += (", along with {0} log records".format(logrecords))
        msg += "?"
        if (QMessageBox.question(self, "Delete Asset", msg, QMessageBox.Yes
                                 | QMessageBox.No) == QMessageBox.No):
            QSqlDatabase.database().rollback()
            return
        #query.exec_("DELETE FROM logs WHERE assetid = {0}"
        #             .format(assetid))

        #use model API
        self.logModel.setFilter("assetid={0}".format(assetid))
        self.logModel.select()
        if self.logModel.rowCount() > 0:
            self.logModel.removeRows(0, self.logModel.rowCount())
            self.logModel.submitAll()

        self.assetModel.removeRow(index.row())
        self.assetModel.submitAll()
        QSqlDatabase.database().commit()
        self.assetModel.select()
        self.assetChanged(self.assetView.currentIndex())

    def addAction(self):
        index = self.assetView.currentIndex()
        if not index.isValid():
            return
        QSqlDatabase.database().transaction()
        record = self.assetModel.record(index.row())
        assetid = record.value(ID)

        row = self.logModel.rowCount()
        self.logModel.insertRow(row)
        self.logModel.setData(self.logModel.index(row, ASSETID), assetid)
        self.logModel.setData(self.logModel.index(row, DATE),
                              QDate.currentDate())
        QSqlDatabase.database().commit()
        index = self.logModel.index(row, ACTIONID)
        self.logView.setCurrentIndex(index)
        self.logView.edit(index)

    def deleteAction(self):
        index = self.logView.currentIndex()
        if not index.isValid():
            return
        record = self.logModel.record(index.row())
        action = record.value(ACTIONID)
        if action == "Acquired":
            QMessageBox.information(
                self, "Delete Log",
                "The 'Acquired' log record cannot be deleted.<br>"
                "You could delete the entire asset instead.")
            return
        when = str(record.value(DATE))
        if (QMessageBox.question(self, "Delete Log",
                                 "Delete log<br>{0} {1}?".format(when, action),
                                 QMessageBox.Yes
                                 | QMessageBox.No) == QMessageBox.No):
            return
        self.logModel.removeRow(index.row())
        self.logModel.submitAll()
        self.logModel.select()

    def editActions(self):
        form = ReferenceDataDlg("actions", "Action", self)
        form.exec_()

    def editCategories(self):
        form = ReferenceDataDlg("categories", "Category", self)
        form.exec_()
Exemplo n.º 8
0
class ReferenceDataDlg(QDialog):
    def __init__(self, table, title, parent=None):
        super(ReferenceDataDlg, self).__init__(parent)

        self.model = QSqlTableModel(self)
        self.model.setTable(table)
        self.model.setSort(NAME, Qt.AscendingOrder)
        self.model.setHeaderData(ID, Qt.Horizontal, "ID")
        self.model.setHeaderData(NAME, Qt.Horizontal, "Name")
        self.model.setHeaderData(DESCRIPTION, Qt.Horizontal, "Description")
        self.model.select()

        self.view = QTableView()
        self.view.setModel(self.model)
        self.view.setSelectionMode(QTableView.SingleSelection)
        self.view.setSelectionBehavior(QTableView.SelectRows)
        self.view.setColumnHidden(ID, True)
        self.view.resizeColumnsToContents()
        addButton = QPushButton("&Add")
        deleteButton = QPushButton("&Delete")
        okButton = QPushButton("&OK")
        if not MAC:
            addButton.setFocusPolicy(Qt.NoFocus)
            deleteButton.setFocusPolicy(Qt.NoFocus)

        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(addButton)
        buttonLayout.addWidget(deleteButton)
        buttonLayout.addStretch()
        buttonLayout.addWidget(okButton)
        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)

        addButton.clicked.connect(self.addRecord)
        deleteButton.clicked.connect(self.deleteRecord)
        okButton.clicked.connect(self.accept)

        self.setWindowTitle(
            "Asset Manager - Edit {0} Reference Data".format(title))

    def addRecord(self):
        row = self.model.rowCount()
        self.model.insertRow(row)
        index = self.model.index(row, NAME)
        self.view.setCurrentIndex(index)
        self.view.edit(index)

    def deleteRecord(self):
        index = self.view.currentIndex()
        if not index.isValid():
            return
        #QSqlDatabase.database().transaction()
        record = self.model.record(index.row())
        id = record.value(ID)
        table = self.model.tableName()
        query = QSqlQuery()
        if table == "actions":
            query.exec_("SELECT COUNT(*) FROM logs "
                        "WHERE actionid = {0}".format(id))
        elif table == "categories":
            query.exec_("SELECT COUNT(*) FROM assets "
                        "WHERE categoryid = {0}".format(id))
        count = 0
        if query.next():
            count = query.value(0)
        if count:
            QMessageBox.information(
                self, "Delete {0}".format(table),
                ("Cannot delete {0}<br>"
                 "from the {1} table because it is used by "
                 "{2} records").format(record.value(NAME), table, count))
            #QSqlDatabase.database().rollback()
            return
        self.model.removeRow(index.row())
        self.model.submitAll()
        self.model.select()
Exemplo n.º 9
0
class SubtitleEditor(SubTab):
    def __init__(self, filePath, subtitleData, parent = None):
        name = os.path.split(filePath)[1]
        super(SubtitleEditor, self).__init__(name, parent)
        self.__initWidgets()
        self.__initContextMenu()

        self._settings = SubSettings()

        self._filePath = filePath
        self._movieFilePath = None
        self._subtitleData = subtitleData

        self.refreshSubtitles()

        # Some signals
        self._subtitleData.fileChanged.connect(self.fileChanged)
        self._subtitleData.subtitlesAdded.connect(self._subtitlesAdded)
        self._subtitleData.subtitlesRemoved.connect(self._subtitlesRemoved)
        self._subtitleData.subtitlesChanged.connect(self._subtitlesChanged)
        self._model.itemChanged.connect(self._subtitleEdited)
        self.customContextMenuRequested.connect(self.showContextMenu)

    def __initWidgets(self):
        minimalSizePolicy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # List of subtitles
        subListDelegate = SubListItemDelegate()
        self._model = QStandardItemModel(0, 3, self)
        self._model.setHorizontalHeaderLabels([_("Begin"), _("End"), _("Subtitle")])
        self._subList = QTableView(self)
        self._subList.setModel(self._model)
        self._subList.setItemDelegateForColumn(0, subListDelegate)
        self._subList.setItemDelegateForColumn(1, subListDelegate)
        self._subList.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)

        self._searchBar = SearchBar(self)
        self._searchBar.hide()

        # Top toolbar
        toolbar = QHBoxLayout()
        toolbar.setAlignment(Qt.AlignLeft)
        #toolbar.addWidget(someWidget....)
        toolbar.addStretch(1)

        # Main layout
        grid = QGridLayout()
        grid.setSpacing(10)
        grid.setContentsMargins(0, 3, 0, 0)
        grid.addLayout(toolbar, 0, 0, 1, 1) # stretch to the right
        grid.addWidget(self._subList, 1, 0)
        grid.addWidget(self._searchBar, 2, 0)
        self.setLayout(grid)

    def __initContextMenu(self):
        self._contextMenu = QMenu(self)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        af = ActionFactory(self)

        insertSub = af.create(title = _("&Insert subtitle"), icon = "list-add",
            connection = self.insertNewSubtitle)
        self._contextMenu.addAction(insertSub)

        insertSub = af.create(title = _("&Add subtitle"), icon = "list-add",
            connection = self.addNewSubtitle)
        self._contextMenu.addAction(insertSub)

        removeSub = af.create(title = _("&Remove subtitles"), icon = "list-remove",
            connection = self.removeSelectedSubtitles)
        self._contextMenu.addAction(removeSub)

    def _changeRowBackground(self, rowNo, bg):
        with DisableSignalling(self._model.itemChanged, self._subtitleEdited):
            for columnNo in range(self._model.columnCount()):
                item = self._model.item(rowNo, columnNo)
                item.setBackground(bg)

    def _changeItemData(self, item, val, role):
        with DisableSignalling(self._model.itemChanged, self._subtitleEdited):
            item.setData(val, role)

    def _handleIncorrectItem(self, item):
        with DisableSignalling(self._model.itemChanged, self._subtitleEdited):
            item.setData(False, CustomDataRoles.ErrorFlagRole)

        bg = item.background()
        rowNo = item.row()
        self._changeRowBackground(rowNo, Qt.red)
        QTimer.singleShot(600, lambda rowNo=rowNo, bg=bg: self._changeRowBackground(rowNo, bg))

    def _subtitleEdited(self, item):
        modelIndex = item.index()
        column = modelIndex.column()
        subNo = modelIndex.row()

        errorFlag = item.data(CustomDataRoles.ErrorFlagRole)
        if errorFlag is True:
            self._handleIncorrectItem(item)
        else:
            # TODO: timeStart and timeEnd might be displayed in a frame format in a bright future.
            # Check it and create FrameTime properly in that case.
            # TODO: Maybe add column numbers to some kind of enum to avoid magic numbers?
            oldSubtitle = self.subtitles[subNo]
            newSubtitle = oldSubtitle.clone()
            if 0 == column:
                timeStart = item.data(CustomDataRoles.FrameTimeRole)
                newSubtitle.change(start = timeStart)
            elif 1 == column:
                timeEnd = item.data(CustomDataRoles.FrameTimeRole)
                newSubtitle.change(end = timeEnd)
            elif 2 == column:
                newSubtitle.change(text = item.text())
            command = ChangeSubtitle(self.filePath, oldSubtitle, newSubtitle, subNo)
            self._subtitleData.execute(command)

    def _subtitlesAdded(self, path, subNos):
        if path != self.filePath:
            return

        subtitles = self.subtitles
        for subNo in subNos:
            row = createRow(subtitles[subNo])
            self._model.insertRow(subNo, row)

    def _subtitlesRemoved(self, path, subNos):
        if path != self.filePath:
            return

        for subNo in subNos:
            self._model.removeRow(subNo)

    def _subtitlesChanged(self, path, subNos):
        if path != self.filePath:
            return

        for subNo in subNos:
            self.refreshSubtitle(subNo)

    def _createNewSubtitle(self, data, subNo):
        fps = data.fps # data is passed to avoid unnecessary copies
        minFrameTime = FrameTime(fps, frames = 1)

        # calculate correct minimum subtitle start time
        if subNo > 0:
            timeStart = data.subtitles[subNo - 1].end + minFrameTime
        else:
            timeStart = FrameTime(fps, frames = 0)

        # calculate correct maximum subtitle end time
        if subNo < data.subtitles.size():
            try:
                timeEnd = data.subtitles[subNo].start - minFrameTime
            except SubException:
                timeEnd = FrameTime(fps, frames = 0)
        else:
            timeEnd = timeStart + FrameTime(fps, frames = 50)

        # add subtitle to DataModel
        sub = Subtitle(timeStart, timeEnd, "")
        command = AddSubtitle(self.filePath, subNo, sub)
        with DisableSignalling(self._subtitleData.subtitlesAdded, self._subtitlesAdded):
            self._subtitleData.execute(command)

        # create subtitle graphical representation in editor sub list
        row = createRow(sub)
        self._model.insertRow(subNo, row)
        index = self._model.index(subNo, 2)
        self._subList.clearSelection()
        self._subList.setCurrentIndex(index)
        self._subList.edit(index)

    def addNewSubtitle(self):
        data = self.data
        subNo = data.subtitles.size()
        indices = self._subList.selectedIndexes()
        if len(indices) > 0:
            rows = [index.row() for index in indices]
            subNo = max(rows) + 1
        self._createNewSubtitle(data, subNo)

    def insertNewSubtitle(self):
        data = self.data
        subNo = 0
        indices = self._subList.selectedIndexes()
        if len(indices) > 0:
            rows = [index.row() for index in indices]
            subNo = max(rows)
        self._createNewSubtitle(data, subNo)

    def removeSelectedSubtitles(self):
        indices = self._subList.selectedIndexes()
        if len(indices) > 0:
            rows = list(set([index.row() for index in indices]))
            command = RemoveSubtitles(self.filePath, rows)
            self._subtitleData.execute(command)
            if self._model.rowCount() > rows[-1]:
                self._subList.selectRow(rows[-1])
            else:
                self._subList.selectRow(self._model.rowCount() - 1)

    def highlight(self):
        self._searchBar.show()
        self._searchBar.highlight()

    def showContextMenu(self):
        self._contextMenu.exec(QCursor.pos())

    def changeInputEncoding(self, encoding):
        data = self._subtitleData.data(self.filePath)
        if encoding != data.inputEncoding:
            try:
                data.encode(encoding)
            except UnicodeDecodeError:
                message = QMessageBox(
                    QMessageBox.Warning,
                    _("Decoding error"),
                    _("Cannot decode subtitles to '%s' encoding.\nPlease try different encoding.") % encoding,
                    QMessageBox.Ok, self
                )
                message.exec()
            except LookupError:
                message = QMessageBox(QMessageBox.Warning,
                    _("Unknown encoding"), _("Unknown encoding: '%s'") % encoding,
                    QMessageBox.Ok, self
                )
                message.exec()
            else:
                # TODO: outputEncoding
                command = ChangeData(self.filePath, data, _("Input encoding: %s") % encoding)
                self._subtitleData.execute(command)

    def changeOutputEncoding(self, encoding):
        data = self._subtitleData.data(self.filePath)
        if encoding != data.outputEncoding:
            data.outputEncoding = encoding
            command = ChangeData(self.filePath, data, _("Output encoding: %s") % encoding)
            self._subtitleData.execute(command)

    def changeSubFormat(self, fmt):
        data = self._subtitleData.data(self.filePath)
        if data.outputFormat != fmt:
            data.outputFormat = fmt
            command = ChangeData(self.filePath, data)
            self._subtitleData.execute(command)

    def changeFps(self, fps):
        data = self.data
        if data.fps != fps:
            data.subtitles.changeFps(fps)
            data.fps = fps
            command = ChangeData(self.filePath, data, _("FPS: %s") % fps)
            self._subtitleData.execute(command)

    def changeVideoPath(self, path):
        data = self.data
        if data.videoPath != path:
            data.videoPath = path
            command = ChangeData(self.filePath, data, _("Video path: %s") % path)
            self._subtitleData.execute(command)

    def offset(self, seconds):
        data = self.data
        fps = data.subtitles.fps
        if fps is None:
            log.error(_("No FPS for '%s' (empty subtitles)." % self.filePath))
            return
        ft = FrameTime(data.subtitles.fps, seconds=seconds)
        data.subtitles.offset(ft)
        command = ChangeData(self.filePath, data,
                             _("Offset by: %s") % ft.toStr())
        self._subtitleData.execute(command)

    def detectFps(self):
        data = self.data
        if data.videoPath is not None:
            fpsInfo = File.detectFpsFromMovie(data.videoPath)
            if data.videoPath != fpsInfo.videoPath or data.fps != fpsInfo.fps:
                data.videoPath = fpsInfo.videoPath
                data.subtitles.changeFps(fpsInfo.fps)
                data.fps = fpsInfo.fps
                command = ChangeData(self.filePath, data, _("Detected FPS: %s") % data.fps)
                self._subtitleData.execute(command)

    def fileChanged(self, filePath):
        if filePath == self._filePath:
            self.refreshSubtitles()

    def refreshSubtitle(self, subNo):
        sub = self.subtitles[subNo]
        self._model.removeRow(subNo)
        self._model.insertRow(subNo, createRow(sub))

    def refreshSubtitles(self):
        self._model.removeRows(0, self._model.rowCount())
        for sub in self.subtitles:
            self._model.appendRow(createRow(sub))

    def updateTab(self):
        self.refreshSubtitles()

    def selectedSubtitles(self):
        rows = self.selectedRows()
        subtitleList = [self.subtitles[row] for row in rows]
        return subtitleList

    def selectedRows(self):
        indices = self._subList.selectedIndexes()
        # unique list
        rows = list(set([index.row() for index in indices]))
        rows.sort()
        return rows

    def selectRow(self, row):
        self._subList.selectRow(row)

    @property
    def filePath(self):
        return self._filePath

    @property
    def movieFilePath(self):
        return self._movieFilePath

    @property
    def data(self):
        return self._subtitleData.data(self.filePath)

    @property
    def subtitles(self):
        return self.data.subtitles

    @property
    def history(self):
        return self._subtitleData.history(self.filePath)

    @property
    def inputEncoding(self):
        return self.data.inputEncoding

    @property
    def outputEncoding(self):
        return self.data.outputEncoding

    @property
    def outputFormat(self):
        return self.data.outputFormat
Exemplo n.º 10
0
class ReferenceDataDlg(QDialog):
    def __init__(self, parent=None):
        super(ReferenceDataDlg, self).__init__(parent)

        self.model = QSqlTableModel(self)
        self.model.setTable("reference")
        self.model.setSort(ID, Qt.AscendingOrder)
        self.model.setHeaderData(ID, Qt.Horizontal, "ID")
        self.model.setHeaderData(NAME, Qt.Horizontal, "NAME")
        self.model.setHeaderData(Description, Qt.Horizontal, "Description")
        self.model.setHeaderData(AGE, Qt.Horizontal, "AGE")
        self.model.select()

        self.view = QTableView()
        self.view.setModel(self.model)
        self.view.setSelectionMode(QTableView.SingleSelection)
        self.view.setSelectionBehavior(QTableView.SelectRows)
        self.view.setColumnHidden(ID, True)
        self.view.resizeColumnsToContents()

        buttonBox = QDialogButtonBox()
        addButton = buttonBox.addButton("&Add", QDialogButtonBox.ActionRole)
        deleteButton = buttonBox.addButton("&Delete",
                                           QDialogButtonBox.ActionRole)
        sortButton = buttonBox.addButton("&Sort", QDialogButtonBox.ActionRole)
        if not MAC:
            addButton.setFocusPolicy(Qt.NoFocus)
            deleteButton.setFocusPolicy(Qt.NoFocus)
            sortButton.setFocusPolicy(Qt.NoFocus)

        menu = QMenu(self)
        sortByNAMEAction = menu.addAction("Sort by &NAME")
        sortByDescriptionAction = menu.addAction("Sort by &Description")
        sortByIDAction = menu.addAction("Sort by &ID")
        sortButton.setMenu(menu)
        closeButton = buttonBox.addButton(QDialogButtonBox.Close)

        layout = QVBoxLayout()
        layout.addWidget(self.view)
        layout.addWidget(buttonBox)
        self.setLayout(layout)

        addButton.clicked.connect(self.addRecord)
        deleteButton.clicked.connect(self.deleteRecord)
        sortByNAMEAction.triggered.connect(lambda: self.sort(NAME))
        sortByDescriptionAction.triggered.connect(
            lambda: self.sort(Description))
        sortByIDAction.triggered.connect(lambda: self.sort(ID))
        closeButton.clicked.connect(self.accept)
        self.setWindowTitle("数据库小程序")

    def addRecord(self):
        row = self.model.rowCount()
        self.model.insertRow(row)
        index = self.model.index(row, NAME)
        self.view.setCurrentIndex(index)
        self.view.edit(index)

    def deleteRecord(self):
        index = self.view.currentIndex()
        if not index.isValid():
            return
        record = self.model.record(index.row())
        NAME = record.value(NAME)
        desc = record.value(Description)
        if (QMessageBox.question(
                self, "数据库小程序",
            ("Delete {0} from NAME {1}?".format(desc, NAME)),
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.No):
            return
        self.model.removeRow(index.row())
        self.model.submitAll()
        self.model.select()

    def sort(self, column):
        self.model.setSort(column, Qt.AscendingOrder)
        self.model.select()
Exemplo n.º 11
0
class ApiDatabaseDialog(QDialog):
    def __init__(self):
        super().__init__()

        self.setMinimumSize(QSize(800, 400))

        config_folder = os.path.join(os.path.expanduser("~"), '.config',
                                     'CSV_Viewer')
        os.makedirs(config_folder, exist_ok=True)
        db_file = "apilinks.sqlite"
        db_path = os.path.join(config_folder, db_file)

        self.db = QSqlDatabase("QSQLITE")
        self.db.setDatabaseName(db_path)
        if self.db.open():
            if os.path.getsize(db_path) == 0:
                query = QSqlQuery(db=self.db)
                query.exec_("CREATE TABLE links(address TEXT)")
                demo = "http://climatedataapi.worldbank.org/climateweb/rest/v1/country/cru/tas/year/POL.csv"
                query.exec_(f"INSERT INTO links VALUES ('{demo}')")
                self.db.commit()
        else:
            QMessageBox.warning(self, "Error", "API links database not open.")

        self.table = QTableView()
        self.model = QSqlTableModel(db=self.db)
        self.model.setTable('links')
        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.model.select()

        # set headers
        column_titles = {"address": "API Address"}
        for n, t in column_titles.items():
            idx = self.model.fieldIndex(n)
            self.model.setHeaderData(idx, Qt.Horizontal, t)

        self.table.setModel(self.model)
        self.table.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
        self.table.setSelectionMode(QtWidgets.QTableView.SingleSelection)
        self.table.setColumnWidth(0, 750)
        self.table.selectRow(0)
        self.table.setFocus()

        self.layout = QVBoxLayout()
        QBtn = QDialogButtonBox.Ok
        self.buttonBox = QDialogButtonBox(QBtn)

        style_add = self.buttonBox.style()
        icon = style_add.standardIcon(QStyle.SP_DialogYesButton)
        self.button_add = QPushButton(icon, "&Add")
        self.button_add.setStatusTip("Add new api link")
        self.button_add.clicked.connect(self.add_link)

        style_del = self.buttonBox.style()
        icon = style_del.standardIcon(QStyle.SP_DialogCloseButton)
        self.button_del = QPushButton(icon, "&Delete")
        self.button_del.setStatusTip("Delete api link")
        self.button_del.clicked.connect(self.del_link)

        self.buttonBox.accepted.connect(self.accept)

        self.layout.addWidget(self.table)
        layout_btn = QHBoxLayout()
        layout_btn.addWidget(self.button_add)
        layout_btn.addWidget(self.button_del)
        layout_btn.addSpacerItem(QSpacerItem(150, 10, QSizePolicy.Expanding))
        layout_btn.addWidget(self.buttonBox)

        self.layout.addLayout(layout_btn)
        self.setLayout(self.layout)

    def closeEvent(self, event) -> None:
        """ Quit dialog """
        self.db.close()

    def add_link(self):
        self.model.insertRows(self.model.rowCount(), 1)
        self.table.setFocus()
        self.table.selectRow(self.model.rowCount() - 1)
        index = self.table.currentIndex()
        self.table.edit(index)

    def del_link(self):
        if self.model.rowCount() > 0:
            index = self.table.currentIndex()
            self.model.removeRow(index.row())
            self.model.submitAll()
            self.table.setRowHidden(index.row(), True)
            if index.row() == 0:
                current = 0
            else:
                current = index.row() - 1
            self.table.selectRow(current)