from electrum.i18n import _ from PyQt4.QtGui import * from PyQt4.QtCore import * import os.path import time import traceback import sys import threading import platform if platform.system() == 'Windows': MONOSPACE_FONT = 'Lucida Console' elif platform.system() == 'Darwin': MONOSPACE_FONT = 'Monaco' else: MONOSPACE_FONT = 'monospace' class WaitingDialog(QThread): def __init__(self, parent, message, run_task, on_success=None, on_complete=None): QThread.__init__(self) self.parent = parent self.d = QDialog(parent) self.d.setWindowTitle('Please wait') l = QLabel(message) vbox = QVBoxLayout(self.d) vbox.addWidget(l) self.run_task = run_task self.on_success = on_success self.on_complete = on_complete self.d.connect(self.d, SIGNAL('done'), self.close) self.d.show() def run(self): self.error = None try: self.result = self.run_task() except BaseException as e: traceback.print_exc(file=sys.stdout) self.error = str(e) self.d.emit(SIGNAL('done')) def close(self): self.d.accept() if self.error: QMessageBox.warning(self.parent, _('Error'), self.error, _('OK')) else: if self.on_success: if type(self.result) is not tuple: self.result = (self.result,) self.on_success(*self.result) if self.on_complete: self.on_complete() class Timer(QThread): def run(self): while True: self.emit(SIGNAL('timersignal')) time.sleep(0.5) class EnterButton(QPushButton): def __init__(self, text, func): QPushButton.__init__(self, text) self.func = func self.clicked.connect(func) def keyPressEvent(self, e): if e.key() == Qt.Key_Return: apply(self.func,()) class ThreadedButton(QPushButton): def __init__(self, text, func, on_success=None, before=None): QPushButton.__init__(self, text) self.before = before self.run_task = func self.on_success = on_success self.clicked.connect(self.do_exec) self.connect(self, SIGNAL('done'), self.done) self.connect(self, SIGNAL('error'), self.on_error) def done(self): if self.on_success: self.on_success() self.setEnabled(True) def on_error(self): QMessageBox.information(None, _("Error"), self.error) self.setEnabled(True) def do_func(self): self.setEnabled(False) try: self.result = self.run_task() except BaseException as e: traceback.print_exc(file=sys.stdout) self.error = str(e.message) self.emit(SIGNAL('error')) return self.emit(SIGNAL('done')) def do_exec(self): if self.before: self.before() t = threading.Thread(target=self.do_func) t.setDaemon(True) t.start() class HelpLabel(QLabel): def __init__(self, text, help_text): QLabel.__init__(self, text) self.help_text = help_text self.app = QCoreApplication.instance() self.font = QFont() def mouseReleaseEvent(self, x): QMessageBox.information(self, 'Help', self.help_text, 'OK') def enterEvent(self, event): self.font.setUnderline(True) self.setFont(self.font) self.app.setOverrideCursor(QCursor(Qt.PointingHandCursor)) return QLabel.enterEvent(self, event) def leaveEvent(self, event): self.font.setUnderline(False) self.setFont(self.font) self.app.setOverrideCursor(QCursor(Qt.ArrowCursor)) return QLabel.leaveEvent(self, event) class HelpButton(QPushButton): def __init__(self, text): QPushButton.__init__(self, '?') self.help_text = text self.setFocusPolicy(Qt.NoFocus) self.setFixedWidth(20) self.clicked.connect(self.onclick) def onclick(self): QMessageBox.information(self, 'Help', self.help_text, 'OK') class Buttons(QHBoxLayout): def __init__(self, *buttons): QHBoxLayout.__init__(self) self.addStretch(1) for b in buttons: self.addWidget(b) class CloseButton(QPushButton): def __init__(self, dialog): QPushButton.__init__(self, _("Close")) self.clicked.connect(dialog.close) self.setDefault(True) class CopyButton(QPushButton): def __init__(self, text_getter, app): QPushButton.__init__(self, _("Copy")) self.clicked.connect(lambda: app.clipboard().setText(text_getter())) class OkButton(QPushButton): def __init__(self, dialog, label=None): QPushButton.__init__(self, label or _("OK")) self.clicked.connect(dialog.accept) self.setDefault(True) class CancelButton(QPushButton): def __init__(self, dialog, label=None): QPushButton.__init__(self, label or _("Cancel")) self.clicked.connect(dialog.reject) def line_dialog(parent, title, label, ok_label, default=None): dialog = QDialog(parent) dialog.setMinimumWidth(500) dialog.setWindowTitle(title) dialog.setModal(1) l = QVBoxLayout() dialog.setLayout(l) l.addWidget(QLabel(label)) txt = QLineEdit() if default: txt.setText(default) l.addWidget(txt) l.addLayout(Buttons(CancelButton(dialog), OkButton(dialog, ok_label))) if dialog.exec_(): return unicode(txt.text()) def text_dialog(parent, title, label, ok_label, default=None): from qrtextedit import ScanQRTextEdit dialog = QDialog(parent) dialog.setMinimumWidth(500) dialog.setWindowTitle(title) dialog.setModal(1) l = QVBoxLayout() dialog.setLayout(l) l.addWidget(QLabel(label)) txt = ScanQRTextEdit() if default: txt.setText(default) l.addWidget(txt) l.addLayout(Buttons(CancelButton(dialog), OkButton(dialog, ok_label))) if dialog.exec_(): return unicode(txt.toPlainText()) def question(msg): return QMessageBox.question(None, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes def address_field(addresses): hbox = QHBoxLayout() address_e = QLineEdit() if addresses: address_e.setText(addresses[0]) def func(): i = addresses.index(str(address_e.text())) + 1 i = i % len(addresses) address_e.setText(addresses[i]) button = QPushButton(_('Address')) button.clicked.connect(func) hbox.addWidget(button) hbox.addWidget(address_e) return hbox, address_e def filename_field(parent, config, defaultname, select_msg): vbox = QVBoxLayout() vbox.addWidget(QLabel(_("Format"))) gb = QGroupBox("format", parent) b1 = QRadioButton(gb) b1.setText(_("CSV")) b1.setChecked(True) b2 = QRadioButton(gb) b2.setText(_("json")) vbox.addWidget(b1) vbox.addWidget(b2) hbox = QHBoxLayout() directory = config.get('io_dir', unicode(os.path.expanduser('~'))) path = os.path.join( directory, defaultname ) filename_e = QLineEdit() filename_e.setText(path) def func(): text = unicode(filename_e.text()) _filter = "*.csv" if text.endswith(".csv") else "*.json" if text.endswith(".json") else None p = unicode( QFileDialog.getSaveFileName(None, select_msg, text, _filter)) if p: filename_e.setText(p) button = QPushButton(_('File')) button.clicked.connect(func) hbox.addWidget(button) hbox.addWidget(filename_e) vbox.addLayout(hbox) def set_csv(v): text = unicode(filename_e.text()) text = text.replace(".json",".csv") if v else text.replace(".csv",".json") filename_e.setText(text) b1.clicked.connect(lambda: set_csv(True)) b2.clicked.connect(lambda: set_csv(False)) return vbox, filename_e, b1 class MyTreeWidget(QTreeWidget): def __init__(self, parent, create_menu, headers, stretch_column=None): QTreeWidget.__init__(self, parent) self.parent = parent self.setColumnCount(len(headers)) self.setHeaderLabels(headers) self.header().setStretchLastSection(False) self.setContextMenuPolicy(Qt.CustomContextMenu) self.itemActivated.connect(self.on_activated) self.customContextMenuRequested.connect(create_menu) # extend the syntax for consistency self.addChild = self.addTopLevelItem self.insertChild = self.insertTopLevelItem # editable column self.is_edit = False self.edit_column = stretch_column self.itemDoubleClicked.connect(self.edit_label) self.itemChanged.connect(self.label_changed) # stretch for i in range(len(headers)): self.header().setResizeMode(i, QHeaderView.Stretch if i == stretch_column else QHeaderView.ResizeToContents) self.setSortingEnabled(True) def on_activated(self, item): if not item: return for i in range(0,self.viewport().height()/5): if self.itemAt(QPoint(0,i*5)) == item: break else: return for j in range(0,30): if self.itemAt(QPoint(0,i*5 + j)) != item: break self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1)) def edit_label(self, item, column=None): if column is None: column = self.edit_column if column==self.edit_column and item.isSelected(): text = unicode(item.text(column)) key = str(item.data(0, Qt.UserRole).toString()) self.is_edit = True item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled) self.editItem(item, column) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled) self.is_edit = False def label_changed(self, item, column): if self.is_edit: return self.is_edit = True key = str(item.data(0, Qt.UserRole).toString()) text = unicode(item.text(self.edit_column)) changed = self.parent.wallet.set_label(key, text) if text: item.setForeground(self.edit_column, QBrush(QColor('black'))) else: text = self.parent.wallet.get_default_label(key) item.setText(self.edit_column, text) item.setForeground(self.edit_column, QBrush(QColor('gray'))) self.is_edit = False if changed: self.parent.update_history_tab() self.parent.update_completions() def get_leaves(self, root): child_count = root.childCount() if child_count == 0: yield root for i in range(child_count): item = root.child(i) for x in self.get_leaves(item): yield x def filter(self, p, columns): p = unicode(p).lower() for item in self.get_leaves(self.invisibleRootItem()): item.setHidden(all([unicode(item.text(column)).lower().find(p) == -1 for column in columns])) class ButtonsWidget(QWidget): def __init__(self): super(QWidget, self).__init__() self.buttons = [] def resizeButtons(self): frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) x = self.rect().right() - frameWidth y = self.rect().bottom() - frameWidth for button in self.buttons: sz = button.sizeHint() x -= sz.width() button.move(x, y - sz.height()) def addButton(self, icon_name, on_click, tooltip): button = QToolButton(self) button.setIcon(QIcon(icon_name)) button.setStyleSheet("QToolButton { border: none; hover {border: 1px} pressed {border: 1px} padding: 0px; }") button.setVisible(True) button.setToolTip(tooltip) button.clicked.connect(on_click) self.buttons.append(button) return button def addCopyButton(self, app): self.app = app f = lambda: self.app.clipboard().setText(str(self.text())) self.addButton(":icons/copy.png", f, _("Copy to Clipboard")) class ButtonsLineEdit(QLineEdit, ButtonsWidget): def __init__(self, text=None): QLineEdit.__init__(self, text) self.buttons = [] def resizeEvent(self, e): o = QLineEdit.resizeEvent(self, e) self.resizeButtons() return o class ButtonsTextEdit(QPlainTextEdit, ButtonsWidget): def __init__(self, text=None): QPlainTextEdit.__init__(self, text) self.setText = self.setPlainText self.text = self.toPlainText self.buttons = [] def resizeEvent(self, e): o = QPlainTextEdit.resizeEvent(self, e) self.resizeButtons() return o if __name__ == "__main__": app = QApplication([]) t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done", _('OK'))) t.start() app.exec_()