class AddAuthorized(QMainWindow): """ This class represents window, to manage access rights. User can add specified accounts to a list, which will be passed to a smart contract. """ def __init__(self, on_exit): super(AddAuthorized, self).__init__() self.on_exit = on_exit self.address_list = [] self.resize(gc.ADD_AUTHORIZED_WIDTH, gc.ADD_AUTHORIZED_HEIGHT) self.address = QLineEdit(self) self.address.resize(QSize(300, 30)) self.address.move(MARGIN, 0) self.add = QPushButton(self) self.add.resize(QSize(self.add.width(), 30)) self.add.setText("Add") self.add.clicked.connect(self.add_address) self.add.move(self.address.width() + 2 * MARGIN, 0) self.list = QListWidget(self) self.list.resize( QSize(self.address.width() + self.add.width() + MARGIN, 100)) self.list.move(MARGIN, self.add.height() + MARGIN) self.checkbox = QCheckBox(self) self.checkbox.move(MARGIN, self.list.pos().y() + self.list.height()) self.checkbox.setChecked(True) self.include_me = QLabel(self) self.include_me.setText("Include me") self.include_me.move(self.checkbox.pos().x() + 2 * MARGIN, self.checkbox.pos().y()) self.ok = QPushButton(self) self.ok.setText("OK") self.ok.clicked.connect(self.exit) self.ok.move(self.width() / 2 - self.ok.width() / 2, self.address.height() + self.list.height() + 3 * MARGIN) def add_address(self): address = self.address.text().strip() if BlockchainDB.is_address_valid(address): self.list.addItem(address) self.address_list.append(address) else: self.warning_box = QMessageBox() self.warning_box.setIcon(QMessageBox.Warning) self.warning_box.setStandardButtons(QMessageBox.Ok) self.warning_box.setText("Address is ill-formed") self.warning_box.setWindowTitle("Invalid address") self.warning_box.exec() def exit(self): self.close() self.on_exit(self.address_list, self.checkbox.isChecked())
class MultiSelectComboBox(QComboBox): search_bar_index = 0 updated = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.list_widget = QListWidget(self) self.line_edit = QLineEdit(self) self.search_bar = QLineEdit(self) current_item = QListWidgetItem(self.list_widget) self.search_bar.setPlaceholderText("Search...") self.search_bar.setClearButtonEnabled(True) # First item in the list is the SEARCH BAR self.list_widget.addItem(current_item) self.list_widget.setItemWidget(current_item, self.search_bar) self.line_edit.setReadOnly(True) self.line_edit.installEventFilter(self) self.setModel(self.list_widget.model()) self.setView(self.list_widget) self.setLineEdit(self.line_edit) self.search_bar.textChanged.connect(self.onSearch) self.activated.connect(self.itemClicked) def hidePopup(self): width = self.width() height = self.list_widget.height() x = QCursor.pos().x() - self.mapToGlobal( self.geometry().topLeft()).x() + self.geometry().x() y = QCursor.pos().y() - self.mapToGlobal( self.geometry().topLeft()).y() + self.geometry().y() if x >= 0 and x <= width and y >= self.height( ) and y <= height + self.height(): pass # Item was clicked do not hide popup else: super().hidePopup() def stateChanged(self, state=None): # state is unused selected_data = [] count = self.list_widget.count() for i in range(1, count): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) if check_box.isChecked(): selected_data.append(check_box.text()) if selected_data: self.line_edit.setText(', '.join(selected_data)) else: self.line_edit.clear() self.updated.emit() def addItem(self, text, user_data=None): # user_data is unused list_widget_item = QListWidgetItem(self.list_widget) check_box = QCheckBox(self) check_box.setText(text) self.list_widget.addItem(list_widget_item) self.list_widget.setItemWidget(list_widget_item, check_box) check_box.stateChanged.connect(self.stateChanged) def currentText(self): if self.line_edit.text(): return [_.strip() for _ in self.line_edit.text().split(",")] return [] def addItems(self, texts): for s in texts: self.addItem(s) def count(self): return max(0, self.list_widget.count() - 1) # Do not count the search bar def onSearch(self, s): for i in range(self.list_widget.count()): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) if s.lower() in check_box.text().lower(): self.list_widget.item(i).setHidden(False) else: self.list_widget.item(i).setHidden(True) def itemClicked(self, index): if index != self.search_bar_index: check_box = self.list_widget.itemWidget( self.list_widget.item(index)) check_box.setChecked(not check_box.isChecked()) def setSearchBarPlaceholderText(self, placeholder_text): self.search_bar.setPlaceholderText(placeholder_text) def setPlaceholderText(self, placeholder_text): self.line_edit.setPlaceholderText(placeholder_text) def clear(self): self.list_widget.clear() current_item = QListWidgetItem(self.list_widget) self.search_bar = QLineEdit(self) self.search_bar.setPlaceholderText("Search...") self.search_bar.setClearButtonEnabled(True) self.list_widget.addItem(current_item) self.list_widget.setItemWidget(current_item, self.search_bar) self.search_bar.textChanged.connect(self.onSearch) def wheelEvent(self, wheel_event): pass # Do not handle the wheel event def setCurrentText(self, text): pass def setCurrentTexts(self, texts): count = self.list_widget.count() for i in range(1, count): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) check_box_string = check_box.text() if check_box_string in texts: check_box.setChecked(True) def ResetSelection(self): count = self.list_widget.count() for i in range(1, count): check_box = self.list_widget.itemWidget(self.list_widget.item(i)) check_box.setChecked(False)
class TabBase(QWidget): def __init__(self, parent, category): super().__init__(parent) self.category = category self.app = parent.app self.status_bar = parent.status_bar self._measures = { name: { "measure": measure, "url": QUrl(measure.description()), } for name, measure in self.collect_measures() } self.descripion_browser = QWebEngineView() useragent = self.descripion_browser.page().profile().httpUserAgent() self.descripion_browser.page().profile().setHttpUserAgent( f"{useragent} FRDS") self.descripion_browser.setZoomFactor(0.75) layout = QVBoxLayout() # Control button self.all_measures_btn = QCheckBox("All Measures") self.all_measures_btn.setCheckState(Qt.Checked) self.start_btn = QPushButton("Start") (ctrl_layout := QHBoxLayout()).addWidget(self.start_btn) # Measure selection self.list_of_measures = QListWidget() self.measure_selection = self.create_measure_selection_layout() # Description and measure params self.measure_params = self.create_measure_params_widget() measure_selection_and_params = QGridLayout() measure_selection_and_params.addWidget(self.measure_selection, 1, 1, 1, 1) measure_selection_and_params.addWidget(self.measure_params, 1, 2, 1, 2) layout.addLayout(measure_selection_and_params) layout.addLayout(ctrl_layout) self.setLayout(layout) # Connect self.start_btn.clicked.connect(self.on_start_btn_clicked) self.all_measures_btn.clicked.connect(self.on_all_measures_btn_clicked) def collect_measures(self): for name, measure in inspect.getmembers(frds.measures, inspect.isclass): if (not inspect.isabstract(measure) and hasattr(measure, "category") and measure.category() is self.category): yield name, measure def on_all_measures_btn_clicked(self) -> None: """Select and deselect all measures""" checked = self.all_measures_btn.isChecked() for i in range(self.list_of_measures.count()): item = self.list_of_measures.item(i) item.setCheckState(Qt.Checked if checked else Qt.Unchecked) def create_measure_params_widget(self) -> QGroupBox: layout = QVBoxLayout() layout.addWidget(self.descripion_browser) measures_params = QGroupBox("Description") measures_params.setLayout(layout) return measures_params def create_measure_selection_layout(self) -> QGroupBox: layout = QVBoxLayout() layout.addWidget(self.all_measures_btn) for name, _ in self.collect_measures(): self.list_of_measures.addItem(name) h = self.list_of_measures.height() for i in range(self.list_of_measures.count()): item = self.list_of_measures.item(i) item.setSizeHint(QSize(0, int(h / 20))) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked) self.list_of_measures.itemPressed.connect(self.on_measure_selected) layout.addWidget(self.list_of_measures) measure_selection = QGroupBox("Measures") measure_selection.setLayout(layout) measure_selection.setMaximumWidth(MAIN_WINDOW_WIDTH // 3 - 20) measure_selection.setMinimumHeight(int(MAIN_WINDOW_HEIGHT * 0.7)) return measure_selection def on_measure_selected(self, item: QListWidgetItem) -> None: measure_name = item.text() url = self._measures.get(measure_name).get("url") self.descripion_browser.setUrl(url) def on_start_btn_clicked(self) -> None: """Start running estimation""" self.app.stopped = False self.measure_selection.setDisabled(True) self.start_btn.setDisabled(True) self.start_btn.setText("Running") measures_to_estimate = [] for i in range(self.list_of_measures.count()): item = self.list_of_measures.item(i) if item.checkState() == Qt.Checked: measures_to_estimate.append(item.text()) worker = Worker( frds.run.main, measures_to_estimate=measures_to_estimate, gui=True, ) worker.signals.finished.connect(self.on_completed) worker.signals.progress.connect(self.update_progress) worker.signals.error.connect(self.update_progress) self.app.threadpool.start(worker) def on_completed(self) -> None: """Callback when estimation is completed""" self.measure_selection.setDisabled(False) self.start_btn.setDisabled(False) self.start_btn.setText("Start") def update_progress(self, msg: str) -> None: """Update progress""" self.status_bar.showMessage(msg)