tlightning: qt channel dialog, fix for shutdown when lightning disabled - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 094b939a24a0bc0de0c66a023249e9618764a9ab DIR parent 5e1412a83975b630a31ffbb625c9f36619c9d31b HTML Author: Janus <ysangkok@gmail.com> Date: Mon, 9 Apr 2018 17:36:50 +0200 lightning: qt channel dialog, fix for shutdown when lightning disabled Diffstat: M electrum/gui/qt/main_window.py | 8 ++++++++ A gui/qt/lightning_channels_list.py | 138 ++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 0 deletions(-) --- DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py t@@ -89,6 +89,7 @@ from .installwizard import WIF_HELP_TEXT from .history_list import HistoryList, HistoryModel from .update_checker import UpdateCheck, UpdateCheckThread from .lightning_invoice_list import LightningInvoiceList +from .lightning_channels_list import LightningChannelsList class StatusBarButton(QPushButton): t@@ -176,6 +177,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.lightning_invoices_tab = self.create_lightning_invoices_tab(wallet) tabs.addTab(self.lightning_invoices_tab, _("Lightning Invoices")) + self.lightning_channels_tab = self.create_lightning_channels_tab(wallet) + tabs.addTab(self.lightning_channels_tab, _("Lightning Channels")) + def add_optional_tab(tabs, tab, icon, description, name): tab.tab_icon = icon tab.tab_description = description t@@ -878,6 +882,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.lightning_invoice_list = LightningInvoiceList(self, wallet.network.lightningworker, wallet.network.lightningrpc) return self.lightning_invoice_list + def create_lightning_channels_tab(self, wallet): + self.lightning_channels_list = LightningChannelsList(self, wallet.network.lightningworker, wallet.network.lightningrpc) + return self.lightning_channels_list + def create_history_tab(self): self.history_model = HistoryModel(self) self.history_list = l = HistoryList(self, self.history_model) DIR diff --git a/gui/qt/lightning_channels_list.py b/gui/qt/lightning_channels_list.py t@@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +import binascii, base64 +from PyQt5 import QtCore, QtWidgets +from collections import OrderedDict +import logging +from electrum.lightning import lightningCall + +mapping = {0: "channel_point"} +revMapp = {"channel_point": 0} +datatable = OrderedDict([]) + +class MyTableRow(QtWidgets.QTreeWidgetItem): + def __init__(self, di): + strs = [str(di[mapping[key]]) for key in range(len(mapping))] + print(strs) + super(MyTableRow, self).__init__(strs) + assert isinstance(di, dict) + self.di = di + def __getitem__(self, idx): + return self.di[idx] + def __setitem__(self, idx, val): + self.di[idx] = val + try: + self.setData(revMapp[idx], QtCore.Qt.DisplayRole, '{0}'.format(val)) + except KeyError: + logging.warning("Lightning Channel field %s unknown", idx) + def __str__(self): + return str(self.di) + +def addChannelRow(new): + made = MyTableRow(new) + datatable[new["channel_point"]] = made + datatable.move_to_end(new["channel_point"], last=False) + return made + +def clickHandler(nodeIdInput, local_amt_inp, push_amt_inp, lightningRpc): + nodeId = nodeIdInput.text() + print("creating channel with connstr {}".format(nodeId)) + lightningCall(lightningRpc, "openchannel")(str(nodeId), local_amt_inp.text(), push_amt_inp.text()) + +class LightningChannelsList(QtWidgets.QWidget): + def create_menu(self, position): + menu = QtWidgets.QMenu() + cur = self._tv.currentItem() + channel_point = cur["channel_point"] + def close(): + params = [str(channel_point)] + (["--force"] if cur["active"] else []) + lightningCall(lightningRpc, "closechannel")(*params) + menu.addAction("Close channel", close) + menu.exec_(self._tv.viewport().mapToGlobal(position)) + def lightningWorkerHandler(self, sourceClassName, obj): + new = {} + for k, v in obj.items(): + try: + v = binascii.hexlify(base64.b64decode(v)).decode("ascii") + except: + pass + new[k] = v + try: + obj = datatable[new["channel_point"]] + except KeyError: + print("lightning channel_point {} unknown!".format(new["channel_point"])) + else: + for k, v in new.items(): + try: + if obj[k] != v: obj[k] = v + except KeyError: + obj[k] = v + def lightningRpcHandler(self, methodName, obj): + if methodName != "listchannels": + print("channel list ignoring reply {} to {}".format(obj, methodName)) + return + self._tv.clear() + for i in obj["channels"]: + self._tv.insertTopLevelItem(0, addChannelRow(i)) + + + def __init__(self, parent, lightningWorker, lightningRpc): + QtWidgets.QWidget.__init__(self, parent) + + def tick(): + lightningCall(lightningRpc, "listchannels")() + + timer = QtCore.QTimer(self) + timer.timeout.connect(tick) + timer.start(20000) + + lightningWorker.subscribe(self.lightningWorkerHandler) + lightningRpc.subscribe(self.lightningRpcHandler) + + self._tv=QtWidgets.QTreeWidget(self) + self._tv.setHeaderLabels([mapping[i] for i in range(len(mapping))]) + self._tv.setColumnCount(len(mapping)) + self._tv.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self._tv.customContextMenuRequested.connect(self.create_menu) + + nodeid_inp = QtWidgets.QLineEdit(self) + local_amt_inp = QtWidgets.QLineEdit(self) + push_amt_inp = QtWidgets.QLineEdit(self) + + button = QtWidgets.QPushButton('Open channel', self) + button.clicked.connect(lambda: clickHandler(nodeid_inp, local_amt_inp, push_amt_inp, lightningRpc)) + + l=QtWidgets.QVBoxLayout(self) + h=QtWidgets.QGridLayout(self) + nodeid_label = QtWidgets.QLabel(self) + nodeid_label.setText("Node ID") + local_amt_label = QtWidgets.QLabel(self) + local_amt_label.setText("Local amount (sat)") + push_amt_label = QtWidgets.QLabel(self) + push_amt_label.setText("Push amount (sat)") + h.addWidget(nodeid_label, 0, 0) + h.addWidget(local_amt_label, 0, 1) + h.addWidget(push_amt_label, 0, 2) + + h.addWidget(nodeid_inp, 1, 0) + h.addWidget(local_amt_inp, 1, 1) + h.addWidget(push_amt_inp, 1, 2) + h.addWidget(button, 1, 3) + h.setColumnStretch(0, 3) + h.setColumnStretch(1, 1) + h.setColumnStretch(2, 1) + h.setColumnStretch(3, 1) + l.addLayout(h) + l.addWidget(self._tv) + + self.resize(2500,1000) + +if __name__=="__main__": + from sys import argv, exit + + a=QtWidgets.QApplication(argv) + + w=LightningChannelsList() + w.show() + w.raise_() + + exit(a.exec_())