Beispiel #1
0
class MatrixDialog(WindowModalDialog):

    def __init__(self, parent):
        super(MatrixDialog, self).__init__(parent)
        self.setWindowTitle(_("Trezor Matrix Recovery"))
        self.num = 9
        self.loop = QEventLoop()

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(MATRIX_RECOVERY))

        grid = QGridLayout()
        grid.setSpacing(0)
        self.char_buttons = []
        for y in range(3):
            for x in range(3):
                button = QPushButton('?')
                button.clicked.connect(partial(self.process_key, ord('1') + y * 3 + x))
                grid.addWidget(button, 3 - y, x)
                self.char_buttons.append(button)
        vbox.addLayout(grid)

        self.backspace_button = QPushButton("<=")
        self.backspace_button.clicked.connect(partial(self.process_key, Qt.Key_Backspace))
        self.cancel_button = QPushButton(_("Cancel"))
        self.cancel_button.clicked.connect(partial(self.process_key, Qt.Key_Escape))
        buttons = Buttons(self.backspace_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()

    def refresh(self):
        for y in range(3):
            self.char_buttons[3 * y + 1].setEnabled(self.num == 9)

    def is_valid(self, key):
        return key >= ord('1') and key <= ord('9')

    def process_key(self, key):
        self.data = None
        if key == Qt.Key_Backspace:
            self.data = '\010'
        elif key == Qt.Key_Escape:
            self.data = 'x'
        elif self.is_valid(key):
            self.char_buttons[key - ord('1')].setFocus()
            self.data = '%c' % key
        if self.data:
            self.loop.exit(0)

    def keyPressEvent(self, event):
        self.process_key(event.key())
        if not self.data:
            QDialog.keyPressEvent(self, event)

    def get_matrix(self, num):
        self.num = num
        self.refresh()
        self.loop.exec_()
Beispiel #2
0
def _processPendingEvents():
    """Process pending application events."""

    # Create an event loop to run in. Otherwise, we need to use the
    # QApplication main loop, which may already be running and therefore
    # unusable.
    qe = QEventLoop()

    # Create a single-shot timer. Could use QTimer.singleShot(),
    # but can't cancel this / disconnect it.
    timer = QTimer()
    timer.setSingleShot(True)
    timer.timeout.connect(qe.quit)
    timer.start(1)

    # Wait for an emitted signal.
    qe.exec_()

    # Clean up: don't allow the timer to call qe.quit after this
    # function exits, which would produce "interesting" behavior.
    timer.stop()
    # Stopping the timer may not cancel timeout signals in the
    # event queue. Disconnect the signal to be sure that loop
    # will never receive a timeout after the function exits.
    timer.timeout.disconnect(qe.quit)
Beispiel #3
0
def waitForSignal(signal, message="", timeout=0):
    """Waits (max timeout msecs if given) for a signal to be emitted.

    It the waiting lasts more than 2 seconds, a progress dialog is displayed
    with the message.

    Returns True if the signal was emitted.
    Return False if the wait timed out or the dialog was canceled by the user.

    """
    loop = QEventLoop()
    dlg = QProgressDialog(minimum=0, maximum=0, labelText=message)
    dlg.setWindowTitle(appinfo.appname)
    dlg.setWindowModality(Qt.ApplicationModal)
    QTimer.singleShot(2000, dlg.show)
    dlg.canceled.connect(loop.quit)
    if timeout:
        QTimer.singleShot(timeout, dlg.cancel)
    stop = lambda: loop.quit()
    signal.connect(stop)
    loop.exec_()
    signal.disconnect(stop)
    dlg.hide()
    dlg.deleteLater()
    return not dlg.wasCanceled()
Beispiel #4
0
class AppDecorator(QObject):
    documentWasCreatedSignal = pyqtSignal(object)  # doc
    documentWindowWasCreatedSignal = pyqtSignal(object, object)  # doc, window
    def __init__(self,argv):
        self.qApp = QApplication(argv)
        super(AppDecorator, self).__init__()
        from cadnano.gui.views.preferences import Preferences
        self.prefs = Preferences()
        #icon = QIcon(ICON_PATH)
        #self.qApp.setWindowIcon(icon)


        self.document_controllers = set()  # Open documents
        self.active_document = None
        self.vh = {}  # Newly created VirtualHelix register here by idnum.
        self.vhi = {}
        self.partItem = None

        global decode
        global Document
        global DocumentController
        from cadnano.gui.views.pathview import pathstyles as styles
        styles.setFontMetrics()

    # def prefsClicked(self):
    #     self.prefs.showDialog()

    def exec_(self):
        if hasattr(self, 'qApp'):
            mainWindow = MainWindow()
            mainWindow.show()
            self.mainEventLoop = QEventLoop()
            self.mainEventLoop.exec_()
Beispiel #5
0
 def f2():
     future = ac.start(em2.g, lambda x: x, QThread.currentThread())
     # The doneSignal won't be processed without an event loop. A
     # thread pool doesn't create one, so make our own to run ``g``.
     qe = QEventLoop()
     future._signalInvoker.doneSignal.connect(qe.exit)
     qe.exec_()
Beispiel #6
0
    def send_request(self, post=None, data={}):
        loop = QEventLoop()
        self.r.setUrl(self.url)
        if post:
            encoded_data = self._urlencode_post_data(data)
            pprint(encoded_data)
            self.reply_post = self.conn.post(self.r, encoded_data)
            self.reply_post.downloadProgress.connect(self.prepare_responce)

        else:
            self.reply = self.conn.get(self.r)
            self.reply.finished.connect(self.prepare_responce)
        # return \
        loop.exec()
Beispiel #7
0
 def run(self, installSignalHandlers=True):
     if self._ownApp:
         self._blockApp = self.qApp
     else:
         self._blockApp = QEventLoop()
     self.runReturn()
     self._blockApp.exec_()
Beispiel #8
0
    def __init__(self, parent):
        super(MatrixDialog, self).__init__(parent)
        self.setWindowTitle(_("Trezor Matrix Recovery"))
        self.num = 9
        self.loop = QEventLoop()

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(MATRIX_RECOVERY))

        grid = QGridLayout()
        grid.setSpacing(0)
        self.char_buttons = []
        for y in range(3):
            for x in range(3):
                button = QPushButton('?')
                button.clicked.connect(partial(self.process_key, ord('1') + y * 3 + x))
                grid.addWidget(button, 3 - y, x)
                self.char_buttons.append(button)
        vbox.addLayout(grid)

        self.backspace_button = QPushButton("<=")
        self.backspace_button.clicked.connect(partial(self.process_key, Qt.Key_Backspace))
        self.cancel_button = QPushButton(_("Cancel"))
        self.cancel_button.clicked.connect(partial(self.process_key, Qt.Key_Escape))
        buttons = Buttons(self.backspace_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Beispiel #9
0
    def __init__(self, parent):
        super(CharacterDialog, self).__init__(parent)
        self.setWindowTitle(_("KeepKey Seed Recovery"))
        self.character_pos = 0
        self.word_pos = 0
        self.loop = QEventLoop()
        self.word_help = QLabel()
        self.char_buttons = []

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(CHARACTER_RECOVERY))
        hbox = QHBoxLayout()
        hbox.addWidget(self.word_help)
        for i in range(4):
            char_button = CharacterButton('*')
            char_button.setMaximumWidth(36)
            self.char_buttons.append(char_button)
            hbox.addWidget(char_button)
        self.accept_button = CharacterButton(_("Accept Word"))
        self.accept_button.clicked.connect(partial(self.process_key, 32))
        self.rejected.connect(partial(self.loop.exit, 1))
        hbox.addWidget(self.accept_button)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        self.finished_button = QPushButton(_("Seed Entered"))
        self.cancel_button = QPushButton(_("Cancel"))
        self.finished_button.clicked.connect(partial(self.process_key,
                                                     Qt.Key_Return))
        self.cancel_button.clicked.connect(self.rejected)
        buttons = Buttons(self.finished_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()
Beispiel #10
0
 def waitForCallback(self):
     assert self._state == CallbackFutureState.WAITING, 'Only callbacks being waited for may be skipped.'
     # Run events until the callback is invoked.
     self._qEventLoop = QEventLoop()
     self._qEventLoop.exec_()
     self._qEventLoop = None
     assert self._state == CallbackFutureState.COMPLETE
Beispiel #11
0
    def commRqData(self, requestName, trCode, inquiry, screenNo):
        """
        키움서버에 TR 요청을 한다.

        조회요청메서드이며 빈번하게 조회요청시, 시세과부하 에러값 -200이 리턴된다.

        :param requestName: string - TR 요청명(사용자 정의)
        :param trCode: string
        :param inquiry: int - 조회(0: 조회, 2: 남은 데이터 이어서 요청)
        :param screenNo: string - 화면번호(4자리)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(requestName, str)
                and isinstance(trCode, str)
                and isinstance(inquiry, int)
                and isinstance(screenNo, str)):

            raise ParameterTypeError()

        returnCode = self.dynamicCall("CommRqData(QString, QString, int, QString)", requestName, trCode, inquiry, screenNo)

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("commRqData(): " + ReturnCode.CAUSE[returnCode])

        # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다.
        self.requestLoop = QEventLoop()
        self.requestLoop.exec_()
Beispiel #12
0
 def __init__(self, file_info, parent=None):
     super(Ace, self).__init__(parent)
     self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
     self.parent = parent
     self.file_info = file_info
     self.language = EditorHelper.lang_from_file_info(file_info)
     self.waitForReady = False
     self.loop = QEventLoop()
     
     settings = self.settings()
     settings.setAttribute(QWebSettings.JavascriptCanAccessClipboard, True)
     settings.setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
     self.inspector = QWebInspector(self)
     showInspectorAction = QAction('showInspector', self)
     showInspectorAction.triggered.connect(self.showInspector)
     self.addAction(showInspectorAction)
     
     self.modificationChanged.connect(self.modification_changed)
     self.main_frame().javaScriptWindowObjectCleared.connect(self.__self_js)
     pckg, file_name = 'ace_editor', 'ace_editor.html'
     resource = pkg_resources.resource_string(pckg, file_name)
     html_template = str(resource, 'utf-8')
     #insert file content
     with open(self.file_info.absoluteFilePath(), 'r') as f:
         text = f.read()
         text = html.escape(text)
         html_template = html_template.replace('{{ content }}', text)
     base_url = QUrl.fromLocalFile(os.path.dirname(__file__))
     
     self.setHtml(html_template, base_url)
     self.modified = False
     
     if not self.waitForReady:
         self.loop.exec()
Beispiel #13
0
 def exec_(self):
     if hasattr(self, 'qApp'):
         from initialization.ui_loader import UiLoader
         loader = UiLoader()
         loader.mainWindow.show()
         self.mainEventLoop = QEventLoop()
         self.mainEventLoop.exec_()
Beispiel #14
0
    def comm_connect(self):
        """
        로그인을 시도합니다.

        수동 로그인일 경우, 로그인창을 출력해서 로그인을 시도.
        자동 로그인일 경우, 로그인창 출력없이 로그인 시도.
        """
        self.dynamicCall("CommConnect()")
        self.login_loop = QEventLoop()
        self.login_loop.exec_()
Beispiel #15
0
class CallbackFuture:
    def __init__(self,
      # A CallbackManager instance to register with.
      callbackManager,
      # _`callbackToWrap`: The callback function to (optionally) invoke, which this future will wrap.
      callbackToWrap):

        self.callbackManager = callbackManager
        self._callbackToWrap = callbackToWrap
        self._state = CallbackFutureState.READY
        self._shouldInvokeCallback = True
        self._qEventLoop = None

    # Return the callback wrapper for this future and mark it as waiting.
    def callback(self):
        assert self._state == CallbackFutureState.READY, 'A callback may only be obtained once.'
        self._state = CallbackFutureState.WAITING
        self.callbackManager.add(self)
        return self._callbackWrapper

    # The callback wrapper. Update state, then invoke the wrapped callback.
    def _callbackWrapper(self, *args, **kwargs):
        assert self._state == CallbackFutureState.WAITING
        self._state = CallbackFutureState.COMPLETE
        self.callbackManager.remove(self)
        # If waiting for the callback, stop!
        if self._qEventLoop:
            self._qEventLoop.quit()
        if self._shouldInvokeCallback:
            self._callbackToWrap(*args, **kwargs)

    def skipCallback(self):
        assert self._state == CallbackFutureState.WAITING, 'Only callbacks being waited for may be skipped.'
        self._shouldInvokeCallback = False

    # Wait until the wrapped callback is completed (invoked or skipped).
    def waitForCallback(self):
        assert self._state == CallbackFutureState.WAITING, 'Only callbacks being waited for may be skipped.'
        # Run events until the callback is invoked.
        self._qEventLoop = QEventLoop()
        self._qEventLoop.exec_()
        self._qEventLoop = None
        assert self._state == CallbackFutureState.COMPLETE
Beispiel #16
0
    def terminate(self):
        # Only run this once.
        if self._isAlive:
            self._isAlive = False
            self._terminate()

            # Waiting for the thread or thread pool to shut down may have
            # placed completed jobs in this thread's queue. Process them now to
            # avoid any surprises (terminated `_AsyncAbstractController`_
            # instances still invoke callbacks, making it seem that the
            # terminate didn't fully terminate. It did, but still leaves g_
            # callbacks to be run in the event queue.)
            el = QEventLoop(self.parent())
            QTimer.singleShot(0, el.exit)
            el.exec_()

            # Delete the `QObject <http://doc.qt.io/qt-5/qobject.html>`_
            # underlying this class, which disconnects all signals.
            sip.delete(self)
Beispiel #17
0
    def __enter__(self):
        # Create an event loop to run in. Otherwise, we need to use the papp
        # (QApplication) main loop, which may already be running and therefore
        # unusable.
        self.qe = QEventLoop()

        # Connect both signals to a slot which quits the event loop.
        self.signal.connect(self.signalSlot)

        return self
Beispiel #18
0
 def __init__(self, config, app, plugins):
     QDialog.__init__(self, None)
     BaseWizard.__init__(self, config, plugins)
     self.setWindowTitle('Vialectrum  -  ' + _('Install Wizard'))
     self.app = app
     self.config = config
     # Set for base base class
     self.language_for_seed = config.get('language')
     self.setMinimumSize(600, 400)
     self.accept_signal.connect(self.accept)
     self.title = QLabel()
     self.main_widget = QWidget()
     self.back_button = QPushButton(_("Back"), self)
     self.back_button.setText(_('Back') if self.can_go_back() else _('Cancel'))
     self.next_button = QPushButton(_("Next"), self)
     self.next_button.setDefault(True)
     self.logo = QLabel()
     self.please_wait = QLabel(_("Please wait..."))
     self.please_wait.setAlignment(Qt.AlignCenter)
     self.icon_filename = None
     self.loop = QEventLoop()
     self.rejected.connect(lambda: self.loop.exit(0))
     self.back_button.clicked.connect(lambda: self.loop.exit(1))
     self.next_button.clicked.connect(lambda: self.loop.exit(2))
     outer_vbox = QVBoxLayout(self)
     inner_vbox = QVBoxLayout()
     inner_vbox.addWidget(self.title)
     inner_vbox.addWidget(self.main_widget)
     inner_vbox.addStretch(1)
     inner_vbox.addWidget(self.please_wait)
     inner_vbox.addStretch(1)
     scroll_widget = QWidget()
     scroll_widget.setLayout(inner_vbox)
     scroll = QScrollArea()
     scroll.setWidget(scroll_widget)
     scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
     scroll.setWidgetResizable(True)
     icon_vbox = QVBoxLayout()
     icon_vbox.addWidget(self.logo)
     icon_vbox.addStretch(1)
     hbox = QHBoxLayout()
     hbox.addLayout(icon_vbox)
     hbox.addSpacing(5)
     hbox.addWidget(scroll)
     hbox.setStretchFactor(scroll, 1)
     outer_vbox.addLayout(hbox)
     outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
     self.set_icon('vialectrum.png')
     self.show()
     self.raise_()
     self.refresh_gui()  # Need for QT on MacOSX.  Lame.
Beispiel #19
0
 def __execJavaScript(self, script):
     """
     Private function to execute a JavaScript function Synchroneously.
     
     @param script JavaScript script source to be executed
     @type str
     @return result of the script
     @rtype depending upon script result
     """
     from PyQt5.QtCore import QEventLoop
     loop = QEventLoop()
     resultDict = {"res": None}
     
     def resultCallback(res, resDict=resultDict):
         if loop and loop.isRunning():
             resDict["res"] = res
             loop.quit()
     
     self.previewView.page().runJavaScript(
         script, resultCallback)
     
     loop.exec_()
     return resultDict["res"]
Beispiel #20
0
    def commKwRqData(self, codes, inquiry, codeCount, requestName, screenNo, typeFlag=0):
        """
        복수종목조회 메서드(관심종목조회 메서드라고도 함).

        이 메서드는 setInputValue() 메서드를 이용하여, 사전에 필요한 값을 지정하지 않는다.
        단지, 메서드의 매개변수에서 직접 종목코드를 지정하여 호출하며,
        데이터 수신은 receiveTrData() 이벤트에서 아래 명시한 항목들을 1회 수신하며,
        이후 receiveRealData() 이벤트를 통해 실시간 데이터를 얻을 수 있다.

        복수종목조회 TR 코드는 OPTKWFID 이며, 요청 성공시 아래 항목들의 정보를 얻을 수 있다.

        종목코드, 종목명, 현재가, 기준가, 전일대비, 전일대비기호, 등락율, 거래량, 거래대금,
        체결량, 체결강도, 전일거래량대비, 매도호가, 매수호가, 매도1~5차호가, 매수1~5차호가,
        상한가, 하한가, 시가, 고가, 저가, 종가, 체결시간, 예상체결가, 예상체결량, 자본금,
        액면가, 시가총액, 주식수, 호가시간, 일자, 우선매도잔량, 우선매수잔량,우선매도건수,
        우선매수건수, 총매도잔량, 총매수잔량, 총매도건수, 총매수건수, 패리티, 기어링, 손익분기,
        잔본지지, ELW행사가, 전환비율, ELW만기일, 미결제약정, 미결제전일대비, 이론가,
        내재변동성, 델타, 감마, 쎄타, 베가, 로

        :param codes: string - 한번에 100종목까지 조회가능하며 종목코드사이에 세미콜론(;)으로 구분.
        :param inquiry: int - api 문서는 bool 타입이지만, int로 처리(0: 조회, 1: 남은 데이터 이어서 조회)
        :param codeCount: int - codes에 지정한 종목의 갯수.
        :param requestName: string
        :param screenNo: string
        :param typeFlag: int - 주식과 선물옵션 구분(0: 주식, 3: 선물옵션), 주의: 매개변수의 위치를 맨 뒤로 이동함.
        :return: list - 중첩 리스트 [[종목코드, 종목명 ... 종목 정보], [종목코드, 종목명 ... 종목 정보]]
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(codes, str)
                and isinstance(inquiry, int)
                and isinstance(codeCount, int)
                and isinstance(requestName, str)
                and isinstance(screenNo, str)
                and isinstance(typeFlag, int)):

            raise ParameterTypeError()

        returnCode = self.dynamicCall("CommKwRqData(QString, QBoolean, int, int, QString, QString)",
                                      codes, inquiry, codeCount, typeFlag, requestName, screenNo)

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("commKwRqData(): " + ReturnCode.CAUSE[returnCode])

        # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다.
        self.requestLoop = QEventLoop()
        self.requestLoop.exec_()
Beispiel #21
0
    def getConditionLoad(self):
        """ 조건식 목록 요청 메서드 """

        if not self.getConnectState():
            raise KiwoomConnectError()

        isLoad = self.dynamicCall("GetConditionLoad()")

        # 요청 실패시
        if not isLoad:
            raise KiwoomProcessingError("getConditionLoad(): 조건식 요청 실패")

        # receiveConditionVer() 이벤트 메서드에서 루프 종료
        self.conditionLoop = QEventLoop()
        self.conditionLoop.exec_()
Beispiel #22
0
    def sendOrder(self, requestName, screenNo, accountNo, orderType, code, qty, price, hogaType, originOrderNo):

        """
        주식 주문 메서드

        sendOrder() 메소드 실행시,
        OnReceiveMsg, OnReceiveTrData, OnReceiveChejanData 이벤트가 발생한다.
        이 중, 주문에 대한 결과 데이터를 얻기 위해서는 OnReceiveChejanData 이벤트를 통해서 처리한다.
        OnReceiveTrData 이벤트를 통해서는 주문번호를 얻을 수 있는데, 주문후 이 이벤트에서 주문번호가 ''공백으로 전달되면,
        주문접수 실패를 의미한다.

        :param requestName: string - 주문 요청명(사용자 정의)
        :param screenNo: string - 화면번호(4자리)
        :param accountNo: string - 계좌번호(10자리)
        :param orderType: int - 주문유형(1: 신규매수, 2: 신규매도, 3: 매수취소, 4: 매도취소, 5: 매수정정, 6: 매도정정)
        :param code: string - 종목코드
        :param qty: int - 주문수량
        :param price: int - 주문단가
        :param hogaType: string - 거래구분(00: 지정가, 03: 시장가, 05: 조건부지정가, 06: 최유리지정가, 그외에는 api 문서참조)
        :param originOrderNo: string - 원주문번호(신규주문에는 공백, 정정및 취소주문시 원주문번호르 입력합니다.)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(requestName, str)
                and isinstance(screenNo, str)
                and isinstance(accountNo, str)
                and isinstance(orderType, int)
                and isinstance(code, str)
                and isinstance(qty, int)
                and isinstance(price, int)
                and isinstance(hogaType, str)
                and isinstance(originOrderNo, str)):

            raise ParameterTypeError()

        returnCode = self.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
                                      [requestName, screenNo, accountNo, orderType, code, qty, price, hogaType, originOrderNo])

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("sendOrder(): " + ReturnCode.CAUSE[returnCode])

        # receiveTrData() 에서 루프종료
        self.orderLoop = QEventLoop()
        self.orderLoop.exec_()
Beispiel #23
0
 def __init__(self, parent):
     super().__init__(parent)
     self.set_url('http://google.ru')
     conn = QNetworkAccessManager()
     self.conn = conn
     self.r = QNetworkRequest()
     self.r.attribute(QNetworkRequest.CookieSaveControlAttribute, QVariant(True))
     # self.r.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded")
     # self.r.setRawHeader("Referer", "http://www.facebook.com/")
     # self.r.setRawHeader("Host", "www.facebook.com")
     self.cj = QNetworkCookieJar()
     conn.setCookieJar(self.cj)
     conn.createRequest = self._create_request
     self.wv = WebView()
     self.wv.show()
     self.wv.page().setNetworkAccessManager(conn)
     # self.wv.auth()
     self.loop = QEventLoop()
     pass
Beispiel #24
0
    def sendCondition(self, screenNo, conditionName, conditionIndex, isRealTime):
        """
        종목 조건검색 요청 메서드

        이 메서드로 얻고자 하는 것은 해당 조건에 맞는 종목코드이다.
        해당 종목에 대한 상세정보는 setRealReg() 메서드로 요청할 수 있다.
        요청이 실패하는 경우는, 해당 조건식이 없거나, 조건명과 인덱스가 맞지 않거나, 조회 횟수를 초과하는 경우 발생한다.

        조건검색에 대한 결과는
        1회성 조회의 경우, receiveTrCondition() 이벤트로 결과값이 전달되며
        실시간 조회의 경우, receiveTrCondition()과 receiveRealCondition() 이벤트로 결과값이 전달된다.

        :param screenNo: string
        :param conditionName: string - 조건식 이름
        :param conditionIndex: int - 조건식 인덱스
        :param isRealTime: int - 조건검색 조회구분(0: 1회성 조회, 1: 실시간 조회)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screenNo, str)
                and isinstance(conditionName, str)
                and isinstance(conditionIndex, int)
                and isinstance(isRealTime, int)):
            raise ParameterTypeError()

        isRequest = self.dynamicCall("SendCondition(QString, QString, int, int",
                                     screenNo, conditionName, conditionIndex, isRealTime)

        if not isRequest:
            raise KiwoomProcessingError("sendCondition(): 조건검색 요청 실패")

        # receiveTrCondition() 이벤트 메서드에서 루프 종료
        self.conditionLoop = QEventLoop()
        self.conditionLoop.exec_()
class Kiwoom(QAxWidget):
    def __init__(self):
        super().__init__()

        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

        # Loop 변수
        # 비동기 방식으로 동작되는 이벤트를 동기화(순서대로 동작) 시킬 때
        self.login_loop = None
        self.request_loop = None
        self.order_loop = None
        self.condition_loop = None

        # 서버구분
        self.server_gubun = None

        # 조건식
        self.condition = None

        # 에러
        self.error = None

        # 주문번호
        self.order_no = ""

        # 조회
        self.inquiry = 0

        # 서버에서 받은 메시지
        self.msg = ""

        # 예수금 d+2
        self.data_opw00001 = 0

        # 보유종목 정보
        self.data_opw00018 = {'account_evaluation': [], 'stocks': []}

        # 주가상세정보
        self.data_opt10081 = [] * 15
        self.data_opt10086 = [] * 23

        # signal & slot
        self.OnEventConnect.connect(self.event_connect)
        self.OnReceiveTrData.connect(self.on_receive_tr_data)
        self.OnReceiveChejanData.connect(self.on_receive_chejan_data)
        self.OnReceiveRealData.connect(self.receive_real_data)
        self.OnReceiveMsg.connect(self.receive_msg)
        self.OnReceiveConditionVer.connect(self.receive_condition_ver)
        self.OnReceiveTrCondition.connect(self.receive_tr_condition)
        self.OnReceiveRealCondition.connect(self.receive_real_condition)

        # 로깅용 설정파일
        logging.config.fileConfig('logging.conf')
        self.log = logging.getLogger('Kiwoom')

    ###############################################################
    # 로깅용 메서드 정의                                               #
    ###############################################################

    def logger(origin):
        def wrapper(*args, **kwargs):
            args[0].log.debug('{} args - {}, kwargs - {}'.format(
                origin.__name__, args, kwargs))
            return origin(*args, **kwargs)

        return wrapper

    ###############################################################
    # 이벤트 정의                                                    #
    ###############################################################

    def event_connect(self, return_code):
        """
        통신 연결 상태 변경시 이벤트

        return_code 0이면 로그인 성공
        그 외에는 ReturnCode 클래스 참조.

        :param return_code: int
        """
        try:
            if return_code == ReturnCode.OP_ERR_NONE:
                if self.get_login_info("GetServerGubun", True):
                    self.msg += "실서버 연결 성공" + "\r\n\r\n"
                else:
                    self.msg += "모의투자서버 연결 성공" + "\r\n\r\n"
            else:
                self.msg += "연결 끊김: 원인 - " + ReturnCode.CAUSE[
                    return_code] + "\r\n\r\n"
        except Exception as error:
            self.log.error('eventConnect {}'.format(error))
        finally:
            # commConnect() 메서드에 의해 생성된 루프를 종료시킨다.
            # 로그인 후, 통신이 끊길 경우를 대비해서 예외처리함.
            try:
                self.login_loop.exit()
            except AttributeError:
                pass

    def receive_msg(self, screen_no, request_name, tr_code, msg):
        """
        수신 메시지 이벤트

        서버로 어떤 요청을 했을 때(로그인, 주문, 조회 등), 그 요청에 대한 처리내용을 전달해준다.

        :param screen_no: string - 화면번호(4자리, 사용자 정의, 서버에 조회나 주문을 요청할 때 이 요청을 구별하기 위한 키값)
        :param request_name: string - TR 요청명(사용자 정의)
        :param tr_code: string
        :param msg: string - 서버로 부터의 메시지
        """

        if request_name == "서버구분":

            if msg.find('모의투자') < 0:
                self.server_gubun = 1

            else:
                self.server_gubun = 0

            try:
                self.order_loop.exit()
            except AttributeError:
                pass
            finally:
                return

        self.msg += request_name + ": " + msg + "\r\n\r\n"

    def on_receive_tr_data(self, screen_no, request_name, tr_code, record_name,
                           inquiry, unused0, unused1, unused2, unused3):
        """
        TR 수신 이벤트

        조회요청 응답을 받거나 조회데이터를 수신했을 때 호출됩니다.
        request_name tr_code comm_rq_data()메소드의 매개변수와 매핑되는 값 입니다.
        조회데이터는 이 이벤트 메서드 내부에서 comm_get_data() 메서드를 이용해서 얻을 수 있습니다.

        :param screen_no: string - 화면번호(4자리)
        :param request_name: string - TR 요청명(comm_rq_data() 메소드 호출시 사용된 requestName)
        :param tr_code: string
        :param record_name: string
        :param inquiry: string - 조회('0': 남은 데이터 없음, '2': 남은 데이터 있음)
        """

        print(
            "on_receive_tr_data 실행: screen_no: %s, request_name: %s, tr_code: %s, record_name: %s, inquiry: %s"
            % (screen_no, request_name, tr_code, record_name, inquiry))

        # 주문번호와 주문루프
        self.order_no = self.comm_get_data(tr_code, "", request_name, 0,
                                           "주문번호")

        try:
            self.order_loop.exit()
        except AttributeError:
            pass

        self.inquiry = inquiry

        if request_name == "관심종목정보요청":
            data = self.get_comm_data_ex(tr_code, "관심종목정보")
            """ commGetData
            cnt = self.getRepeatCnt(trCode, requestName)

            for i in range(cnt):
                data = self.commGetData(trCode, "", requestName, i, "종목명")
                print(data)
            """

        if request_name == "주식일봉차트조회요청":
            data = self.get_comm_data_ex(tr_code, "주식일봉차트조회")
            if data is not None:
                data = list(
                    map(
                        lambda x: list(
                            map(
                                lambda y: y.replace('+', '').replace(
                                    '--', '-'), x)),
                        np.array(data)[:, 1:8].tolist()))
                data = list(
                    map(
                        lambda x: list(
                            map(lambda y: int(y) if y != '' else 0, x)), data))
                self.data_opt10081.extend(data)
                date = str(data[0][3])
                dt = datetime.strptime(date, "%Y%m%d")
                if dt <= self.start_date:
                    self.inquiry = 0
            if inquiry == "0" or self.inquiry == 0:
                col_name = ['현재가', '거래량', '거래대금', '일자', '시가', '고가', '저가']
                self.data_opt10081 = DataFrame(self.data_opt10081,
                                               columns=col_name)

        if request_name == "일별주가요청":
            data = self.get_comm_data_ex(tr_code, "일별주가요청")
            if data is not None:
                data = list(
                    map(
                        lambda x: list(
                            map(
                                lambda y: y.replace('+', '').replace(
                                    '--', '-'), x)), data))
                data = list(
                    map(
                        lambda x: list(
                            map(lambda y: float(y)
                                if y != '' else 0, x)), data))
                self.data_opt10086.extend(data)
                date = str(int(data[0][0]))
                dt = datetime.strptime(date, "%Y%m%d")
                if dt <= self.start_date:
                    self.inquiry = 0
            if inquiry == "0" or self.inquiry == 0:
                col_name = [
                    '일자', '시가', '고가', '저가', '종가', '전일비', '등락률', '거래량',
                    '금액(백만)', '신용비', '개인', '기관', '외인수량', '외국계', '프로그램', '외인비',
                    '체결강도', '외인보유', '외인비중', '외인순매수', '기관순매수', '개인순매수', '신용잔고율'
                ]
                self.data_opt10086 = DataFrame(self.data_opt10086,
                                               columns=col_name)

        if request_name == "예수금상세현황요청":
            estimate_day2_deposit = self.comm_get_data(tr_code, "",
                                                       request_name, 0,
                                                       "d+2추정예수금")
            estimate_day2_deposit = self.change_format(estimate_day2_deposit)
            self.data_opw00001 = estimate_day2_deposit

        if request_name == '계좌평가잔고내역요청':
            # 계좌 평가 정보
            account_evaluation = []
            key_list = ["총매입금액", "총평가금액", "총평가손익금액", "총수익률(%)", "추정예탁자산"]

            for key in key_list:
                value = self.comm_get_data(tr_code, "", request_name, 0, key)

                if key.startswith("총수익률"):
                    value = self.change_format(value, 1)
                else:
                    value = self.change_format(value)
                account_evaluation.append(value)
            self.data_opw00018['account_evaluation'] = account_evaluation

            # 보유 종목 정보
            cnt = self.get_repeat_cnt(tr_code, request_name)
            key_list = ["종목명", "보유수량", "매입가", "현재가", "평가손익", "수익률(%)", "종목번호"]
            for i in range(cnt):
                stock = []
                for key in key_list:
                    value = self.comm_get_data(tr_code, "", request_name, i,
                                               key)
                    if key.startswith("수익률"):
                        value = self.change_format(value, 2)
                    elif key != "종목명" and key != "종목번호":
                        value = self.change_format(value)
                    stock.append(value)
                self.data_opw00018['stocks'].append(stock)
        try:
            self.request_loop.exit()
        except AttributeError:
            pass

    def receive_real_data(self, code, real_type, real_data):
        """
        실시간 데이터 수신 이벤트

        실시간 데이터를 수신할 때 마다 호출되며,
        set_real_reg() 메서드로 등록한 실시간 데이터도 이 이벤트 메서드에 전달됩니다.
        get_comm_real_data() 메서드를 이용해서 실시간 데이터를 얻을 수 있습니다.

        :param code: string - 종목코드
        :param real_type: string - 실시간 타입(KOA의 실시간 목록 참조)
        :param real_data: string - 실시간 데이터 전문
        """

        try:
            self.log.debug("[receiveRealData]")
            self.log.debug("({})".format(real_type))

            if real_type not in RealType.REALTYPE:
                return

            data = []

            if code != "":
                data.append(code)
                codeOrNot = code
            else:
                codeOrNot = real_type

            for fid in sorted(RealType.REALTYPE[real_type].keys()):
                value = self.get_comm_real_data(codeOrNot, fid)
                data.append(value)

            # TODO: DB에 저장
            self.log.debug(data)

        except Exception as e:
            self.log.error('{}'.format(e))

    def on_receive_chejan_data(self, gubun, item_cnt, fid_list):
        print("gubun: ", gubun)
        print(self.GetChejanData(9203))
        print(self.GetChejanData(302))
        print(self.GetChejanData(900))
        print(self.GetChejanData(901))

    def get_codelist_by_market(self, market):
        func = 'GetCodeListByMarket("%s")' % market
        codes = self.dynamicCall(func)
        return codes.split(';')

    ###############################################################
    # 메서드 정의: 로그인 관련 메서드                                    #
    ###############################################################

    def comm_connect(self):
        """
        로그인을 시도합니다.

        수동 로그인일 경우, 로그인창을 출력해서 로그인을 시도.
        자동 로그인일 경우, 로그인창 출력없이 로그인 시도.
        """
        self.dynamicCall("CommConnect()")
        self.login_loop = QEventLoop()
        self.login_loop.exec_()

    def get_connect_state(self):
        """
        현재 접속상태를 반환합니다.

        반환되는 접속상태는 아래와 같습니다.
        0: 미연결, 1: 연결

        :return: int
        """
        ret = self.dynamicCall("GetConnectState()")
        return ret

    def get_login_info(self, tag, is_connect_state=False):
        """
        사용자의 tag에 해당하는 정보를 반환한다.

        tag에 올 수 있는 값은 아래와 같다.
        ACCOUNT_CNT: 전체 계좌의 개수를 반환한다.
        ACCNO: 전체 계좌 목록을 반환한다. 계좌별 구분은 ;(세미콜론) 이다.
        USER_ID: 사용자 ID를 반환한다.
        USER_NAME: 사용자명을 반환한다.
        GetServerGubun: 접속서버 구분을 반환합니다.(0: 모의투자, 그외: 실서버)

        :param tag: string
        :param is_connect_state: bool - 접속상태을 확인할 필요가 없는 경우 True로 설정.
        :return: string
        """
        if not is_connect_state:
            if not self.get_connect_state():
                raise KiwoomConnectError()

        if not isinstance(tag, str):
            raise ParameterTypeError()

        if tag not in [
                'ACCOUNT_CNT', 'ACCNO', 'USER_ID', 'USER_NAME',
                'GetServerGubun'
        ]:
            raise ParameterValueError()

        cmd = 'GetLoginInfo("%s")' % tag
        info = self.dynamicCall(cmd)

        if tag == 'GetServerGubun' and info == "":
            if self.server_gubun == None:
                account_list = self.get_login_info("ACCNO").split(';')
                self.send_order("서버구분", "0102", account_list[0], 1, "066570",
                                0, 0, "05", "")
            info = self.server_gubun
        return info

    #################################################################
    # 메서드 정의: 조회 관련 메서드                                        #
    # 시세조회, 관심종목 조회, 조건검색 등 이들의 합산 조회 횟수가 1초에 5회까지 허용 #
    #################################################################

    def set_input_value(self, id, value):
        self.dynamicCall("SetInputValue(QString, QString)", id, value)

    def comm_rq_data(self, request_name, tr_code, inquiry, screen_no):
        """
        키움서버에 TR 요청을 한다.

        조회요청메서드이며 빈번하게 조회요청시, 시세과부하 에러값 -200이 리턴된다.

        :param request_name: string - TR 요청명(사용자 정의)
        :param tr_code: string
        :param inquiry: int - 조회(0: 조회, 2: 남은 데이터 이어서 요청)
        :param screen_no: string - 화면번호(4자리)
        """

        if not self.get_connect_state():
            raise KiwoomConnectError()

        if not (isinstance(request_name, str) and isinstance(tr_code, str)
                and isinstance(inquiry, int) and isinstance(screen_no, str)):
            raise ParameterTypeError()

        return_code = self.dynamicCall(
            "CommRqData(QString, QString, int, QString)", request_name,
            tr_code, inquiry, screen_no)

        if return_code != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("comm_rq_data(): " +
                                        ReturnCode.CAUSE[return_code])

        # 루프 생성: receive_tr_data() 메서드에서 루프를 종료시킨다.
        self.request_loop = QEventLoop()
        self.request_loop.exec_()

    def comm_get_data(self, code, real_type, field_name, index, item_name):
        """
        데이터 획득 메서드

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 조회데이터를 얻어오는 메서드입니다.

        :param code: string
        :param real_type: string - TR 요청시 ""(빈문자)로 처리
        :param field_name: string - TR 요청명(comm_rq_data() 메소드 호출시 사용된 field_name)
        :param index: int
        :param item_name: string - 수신 데이터에서 얻고자 하는 값의 키(출력항목이름)
        :return: string
        """
        ret = self.dynamicCall(
            "CommGetData(QString, QString, QString, int, QString)", code,
            real_type, field_name, index, item_name)
        return ret.strip()

    def get_repeat_cnt(self, tr_code, request_name):
        """
        서버로 부터 전달받은 데이터의 갯수를 리턴합니다.(멀티데이터의 갯수)

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        키움 OpenApi+에서는 데이터를 싱글데이터와 멀티데이터로 구분합니다.
        싱글데이터란, 서버로 부터 전달받은 데이터 내에서, 중복되는 키(항목이름)가 하나도 없을 경우.
        예를들면, 데이터가 '종목코드', '종목명', '상장일', '상장주식수' 처럼 키(항목이름)가 중복되지 않는 경우를 말합니다.
        반면 멀티데이터란, 서버로 부터 전달받은 데이터 내에서, 일정 간격으로 키(항목이름)가 반복될 경우를 말합니다.
        예를들면, 10일간의 일봉데이터를 요청할 경우 '종목코드', '일자', '시가', '고가', '저가' 이러한 항목이 10번 반복되는 경우입니다.
        이러한 멀티데이터의 경우 반복 횟수(=데이터의 갯수)만큼, 루프를 돌면서 처리하기 위해 이 메서드를 이용하여 멀티데이터의 갯수를 얻을 수 있습니다.

        :param tr_code: string
        :param request_name: string - TR 요청명(comm_rq_data() 메소드 호출시 사용된 request_name)
        :return: int
        """
        ret = self.dynamicCall("GetRepeatCnt(QString, QString)", tr_code,
                               request_name)
        return ret

    def get_comm_data_ex(self, tr_code, multi_data_name):
        """
        멀티데이터 획득 메서드

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        :param tr_code: string
        :param multi_data_name: string - KOA에 명시된 멀티데이터명
        :return: list - 중첩리스트
        """

        if not (isinstance(tr_code, str) and isinstance(multi_data_name, str)):
            raise ParameterTypeError()

        data = self.dynamicCall("GetCommDataEx(QString, QString)", tr_code,
                                multi_data_name)
        return data

    def commKwRqData(self,
                     codes,
                     inquiry,
                     codeCount,
                     requestName,
                     screenNo,
                     typeFlag=0):
        """
        복수종목조회 메서드(관심종목조회 메서드라고도 함).

        이 메서드는 setInputValue() 메서드를 이용하여, 사전에 필요한 값을 지정하지 않는다.
        단지, 메서드의 매개변수에서 직접 종목코드를 지정하여 호출하며,
        데이터 수신은 receiveTrData() 이벤트에서 아래 명시한 항목들을 1회 수신하며,
        이후 receiveRealData() 이벤트를 통해 실시간 데이터를 얻을 수 있다.

        복수종목조회 TR 코드는 OPTKWFID 이며, 요청 성공시 아래 항목들의 정보를 얻을 수 있다.

        종목코드, 종목명, 현재가, 기준가, 전일대비, 전일대비기호, 등락율, 거래량, 거래대금,
        체결량, 체결강도, 전일거래량대비, 매도호가, 매수호가, 매도1~5차호가, 매수1~5차호가,
        상한가, 하한가, 시가, 고가, 저가, 종가, 체결시간, 예상체결가, 예상체결량, 자본금,
        액면가, 시가총액, 주식수, 호가시간, 일자, 우선매도잔량, 우선매수잔량,우선매도건수,
        우선매수건수, 총매도잔량, 총매수잔량, 총매도건수, 총매수건수, 패리티, 기어링, 손익분기,
        잔본지지, ELW행사가, 전환비율, ELW만기일, 미결제약정, 미결제전일대비, 이론가,
        내재변동성, 델타, 감마, 쎄타, 베가, 로

        :param codes: string - 한번에 100종목까지 조회가능하며 종목코드사이에 세미콜론(;)으로 구분.
        :param inquiry: int - api 문서는 bool 타입이지만, int로 처리(0: 조회, 1: 남은 데이터 이어서 조회)
        :param codeCount: int - codes에 지정한 종목의 갯수.
        :param requestName: string
        :param screenNo: string
        :param typeFlag: int - 주식과 선물옵션 구분(0: 주식, 3: 선물옵션), 주의: 매개변수의 위치를 맨 뒤로 이동함.
        :return: list - 중첩 리스트 [[종목코드, 종목명 ... 종목 정보], [종목코드, 종목명 ... 종목 정보]]
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(codes, str) and isinstance(inquiry, int) and
                isinstance(codeCount, int) and isinstance(requestName, str)
                and isinstance(screenNo, str) and isinstance(typeFlag, int)):
            raise ParameterTypeError()

        returnCode = self.dynamicCall(
            "CommKwRqData(QString, QBoolean, int, int, QString, QString)",
            codes, inquiry, codeCount, typeFlag, requestName, screenNo)

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("commKwRqData(): " +
                                        ReturnCode.CAUSE[returnCode])

        # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다.
        self.requestLoop = QEventLoop()
        self.requestLoop.exec_()

    ###############################################################
    # 메서드 정의: 실시간 데이터 처리 관련 메서드                           #
    ###############################################################

    def disconnect_real_data(self, screen_no):
        """
        해당 화면번호로 설정한 모든 실시간 데이터 요청을 제거합니다.

        화면을 종료할 때 반드시 이 메서드를 호출해야 합니다.

        :param screen_no: string
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not isinstance(screen_no, str):
            raise ParameterTypeError()

        self.dynamicCall("DisconnectRealData(QString)", screen_no)

    def get_comm_real_data(self, code, fid):
        """
        실시간 데이터 획득 메서드

        이 메서드는 반드시 receiveRealData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        :param code: string - 종목코드
        :param fid: - 실시간 타입에 포함된 fid
        :return: string - fid에 해당하는 데이터
        """

        if not (isinstance(code, str) and isinstance(fid, int)):
            raise ParameterTypeError()

        value = self.dynamicCall("GetCommRealData(QString, int)", code, fid)

        return value

    def set_real_reg(self, screen_no, codes, fids, real_reg_type):
        """
        실시간 데이터 요청 메서드

        종목코드와 fid 리스트를 이용해서 실시간 데이터를 요청하는 메서드입니다.
        한번에 등록 가능한 종목과 fid 갯수는 100종목, 100개의 fid 입니다.
        실시간등록타입을 0으로 설정하면, 첫 실시간 데이터 요청을 의미하며
        실시간등록타입을 1로 설정하면, 추가등록을 의미합니다.

        실시간 데이터는 실시간 타입 단위로 receiveRealData() 이벤트로 전달되기 때문에,
        이 메서드에서 지정하지 않은 fid 일지라도, 실시간 타입에 포함되어 있다면, 데이터 수신이 가능하다.

        :param screen_no: string
        :param codes: string - 종목코드 리스트(종목코드;종목코드;...)
        :param fids: string - fid 리스트(fid;fid;...)
        :param real_reg_type: string - 실시간등록타입(0: 첫 등록, 1: 추가 등록)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screen_no, str) and isinstance(codes, str)
                and isinstance(fids, str) and isinstance(real_reg_type, str)):
            raise ParameterTypeError()

        self.dynamicCall("SetRealReg(QString, QString, QString, QString)",
                         screen_no, codes, fids, real_reg_type)

    def set_real_remove(self, screen_no, code):
        """
        실시간 데이터 중지 메서드

        set_real_reg() 메서드로 등록한 종목만, 이 메서드를 통해 실시간 데이터 받기를 중지 시킬 수 있습니다.

        :param screen_no: string - 화면번호 또는 ALL 키워드 사용가능
        :param code: string - 종목코드 또는 ALL 키워드 사용가능
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screen_no, str) and isinstance(code, str)):
            raise ParameterTypeError()

        self.dynamicCall("SetRealRemove(QString, QString)", screen_no, code)

    ###############################################################
    # 메서드 정의: 조건검색 관련 메서드와 이벤트                            #
    ###############################################################

    def receive_condition_ver(self, receive, msg):
        """
        getConditionLoad() 메서드의 조건식 목록 요청에 대한 응답 이벤트

        :param receive: int - 응답결과(1: 성공, 나머지 실패)
        :param msg: string - 메세지
        """

        try:
            if not receive:
                return

            self.condition = self.get_condition_name_list()
            print("조건식 개수: ", len(self.condition))

            for key in self.condition.keys():
                print("조건식: ", key, ": ", self.condition[key])
                print("key type: ", type(key))

        except Exception as e:
            print(e)

        finally:
            self.condition_loop.exit()

    def receive_tr_condition(self, screen_no, codes, condition_name,
                             condition_index, inquiry):
        """
        (1회성, 실시간) 종목 조건검색 요청시 발생되는 이벤트

        :param screen_no: string
        :param codes: string - 종목코드 목록(각 종목은 세미콜론으로 구분됨)
        :param condition_name: string - 조건식 이름
        :param condition_index: int - 조건식 인덱스
        :param inquiry: int - 조회구분(0: 남은데이터 없음, 2: 남은데이터 있음)
        """

        print("[receive_tr_condition]")

        try:
            if codes == "":
                return

            code_list = codes.split(';')
            del code_list[-1]

            print(code_list)
            print("종목개수: ", len(code_list))

        finally:
            self.condition_loop.exit()

    def receive_real_condition(self, code, event, condition_name,
                               condition_index):
        """
        실시간 종목 조건검색 요청시 발생되는 이벤트

        :param code: string - 종목코드
        :param event: string - 이벤트종류("I": 종목편입, "D": 종목이탈)
        :param condition_name: string - 조건식 이름
        :param condition_index: string - 조건식 인덱스(여기서만 인덱스가 string 타입으로 전달됨)
        """

        print("[receive_real_condition]")

        print("종목코드: ", code)
        print("이벤트: ", "종목편입" if event == "I" else "종목이탈")

    def get_condition_load(self):
        """ 조건식 목록 요청 메서드 """

        if not self.get_connect_state():
            raise KiwoomConnectError()

        is_load = self.dynamicCall("GetConditionLoad()")

        # 요청 실패시
        if not is_load:
            raise KiwoomProcessingError("getConditionLoad(): 조건식 요청 실패")

        # receiveConditionVer() 이벤트 메서드에서 루프 종료
        self.condition_loop = QEventLoop()
        self.condition_loop.exec_()

    def get_condition_name_list(self):
        """
        조건식 획득 메서드

        조건식을 딕셔너리 형태로 반환합니다.
        이 메서드는 반드시 receiveConditionVer() 이벤트 메서드안에서 사용해야 합니다.

        :return: dict - {인덱스:조건명, 인덱스:조건명, ...}
        """

        data = self.dynamicCall("get_condition_name_list()")

        if data == "":
            raise KiwoomProcessingError(
                "get_condition_name_list(): 사용자 조건식이 없습니다.")

        conditionList = data.split(';')
        del conditionList[-1]

        condition_dictionary = {}

        for condition in conditionList:
            key, value = condition.split('^')
            condition_dictionary[int(key)] = value

        return condition_dictionary

    def send_condition(self, screen_no, condition_name, condition_index,
                       is_real_time):
        """
        종목 조건검색 요청 메서드

        이 메서드로 얻고자 하는 것은 해당 조건에 맞는 종목코드이다.
        해당 종목에 대한 상세정보는 set_real_reg() 메서드로 요청할 수 있다.
        요청이 실패하는 경우는, 해당 조건식이 없거나, 조건명과 인덱스가 맞지 않거나, 조회 횟수를 초과하는 경우 발생한다.

        조건검색에 대한 결과는
        1회성 조회의 경우, receiveTrCondition() 이벤트로 결과값이 전달되며
        실시간 조회의 경우, receiveTrCondition()과 receiveRealCondition() 이벤트로 결과값이 전달된다.

        :param screen_no: string
        :param condition_name: string - 조건식 이름
        :param condition_index: int - 조건식 인덱스
        :param is_real_time: int - 조건검색 조회구분(0: 1회성 조회, 1: 실시간 조회)
        """

        if not self.get_connect_state():
            raise KiwoomConnectError()

        if not (isinstance(screen_no, str) and isinstance(condition_name, str)
                and isinstance(condition_index, int)
                and isinstance(is_real_time, int)):
            raise ParameterTypeError()

        is_request = self.dynamicCall(
            "SendCondition(QString, QString, int, int", screen_no,
            condition_name, condition_index, is_real_time)

        if not is_request:
            raise KiwoomProcessingError("sendCondition(): 조건검색 요청 실패")

        # receiveTrCondition() 이벤트 메서드에서 루프 종료
        self.condition_loop = QEventLoop()
        self.condition_loop.exec_()

    def send_condition_stop(self, screen_no, condition_name, condition_index):
        """ 종목 조건검색 중지 메서드 """

        if not self.get_connect_state():
            raise KiwoomConnectError()

        if not (isinstance(screen_no, str) and isinstance(condition_name, str)
                and isinstance(condition_index, int)):
            raise ParameterTypeError()

        self.dynamicCall("SendConditionStop(QString, QString, int)", screen_no,
                         condition_name, condition_index)

    ###############################################################
    # 메서드 정의: 주문과 잔고처리 관련 메서드                              #
    # 1초에 5회까지 주문 허용                                          #
    ###############################################################

    def send_order(self, request_name, screen_no, account_no, order_type, code,
                   qty, price, hoga_type, origin_order_no):
        """
        주식 주문 메서드

        send_order() 메소드 실행시,
        OnReceiveMsg, OnReceiveTrData, OnReceiveChejanData 이벤트가 발생한다.
        이 중, 주문에 대한 결과 데이터를 얻기 위해서는 OnReceiveChejanData 이벤트를 통해서 처리한다.
        OnReceiveTrData 이벤트를 통해서는 주문번호를 얻을 수 있는데, 주문후 이 이벤트에서 주문번호가 ''공백으로 전달되면,
        주문접수 실패를 의미한다.

        :param request_name: string - 주문 요청명(사용자 정의)
        :param screen_no: string - 화면번호(4자리)
        :param account_no: string - 계좌번호(10자리)
        :param order_type: int - 주문유형(1: 신규매수, 2: 신규매도, 3: 매수취소, 4: 매도취소, 5: 매수정정, 6: 매도정정)
        :param code: string - 종목코드
        :param qty: int - 주문수량
        :param price: int - 주문단가
        :param hoga_type: string - 거래구분(00: 지정가, 03: 시장가, 05: 조건부지정가, 06: 최유리지정가, 그외에는 api 문서참조)
        :param origin_order_no: string - 원주문번호(신규주문에는 공백, 정정및 취소주문시 원주문번호르 입력합니다.)
        """
        if not self.get_connect_state():
            raise KiwoomConnectError()

        if not (isinstance(request_name, str) and isinstance(screen_no, str)
                and isinstance(account_no, str)
                and isinstance(order_type, int) and isinstance(code, str)
                and isinstance(qty, int) and isinstance(price, int)
                and isinstance(hoga_type, str)
                and isinstance(origin_order_no, str)):
            raise ParameterTypeError()

        return_code = self.dynamicCall(
            "SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
            [
                request_name, screen_no, account_no, order_type, code, qty,
                price, hoga_type, origin_order_no
            ])

        if return_code != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("send_order(): " +
                                        ReturnCode.CAUSE[return_code])

        # receiveTrData() 에서 루프종료
        self.order_loop = QEventLoop()
        self.order_loop.exec_()

    def GetChejanData(self, nFid):
        cmd = 'GetChejanData("%s")' % nFid
        ret = self.dynamicCall(cmd)
        return ret

    ###############################################################
    # 기타 메서드 정의                                                #
    ###############################################################

    def get_code_list(self, *market):
        """
        여러 시장의 종목코드를 List 형태로 반환하는 헬퍼 메서드.

        :param market: Tuple - 여러 개의 문자열을 매개변수로 받아 Tuple로 처리한다.
        :return: List
        """
        code_list = []
        for m in market:
            tmp_list = self.get_codelist_by_market(m)
            code_list += tmp_list
        return code_list

    def get_master_code_name(self, code):
        """
        종목코드의 한글명을 반환한다.

        :param code: string - 종목코드
        :return: string - 종목코드의 한글명
        """

        if not self.get_connect_state():
            raise KiwoomConnectError()

        if not isinstance(code, str):
            raise ParameterTypeError()

        cmd = 'GetMasterCodeName("%s")' % code
        name = self.dynamicCall(cmd)
        return name

    def change_format(self, data, percent=0):
        if percent == 0:
            d = int(data)
            format_data = '{:-,d}'.format(d)
        elif percent == 1:
            f = float(data)
            f -= 100
            format_data = '{:-,.2f}'.format(f)
        elif percent == 2:
            f = float(data)
            format_data = '{:-,.2f}'.format(f)
        return format_data

    def opw_data_reset(self):
        """ 잔고 및 보유종목 데이터 초기화 """
        self.data_opw00001 = 0
        self.data_opw00018 = {'account_evaluation': [], 'stocks': []}
Beispiel #26
0
    def render_action(self, action):
        from PyQt5.QtCore import QEventLoop, QPropertyAnimation, QEasingCurve, QPoint
        from PyQt5.QtGui import QPixmap
        from PyQt5.QtWidgets import QApplication, QWidget, QLabel

        if self._app is None:
            # Create the window
            self._app = QApplication([])
            self._loop = QEventLoop(self._app)
            self._win = QLabel()
            self._win.resize(800, 600)
            self._win.setPixmap(QPixmap("qrcodes/terminals_bg.png"))
            self._win.show()

            # Two terminal indicators
            self._win_terminals = []

            for i in range(2):
                t = QLabel(self._win)
                t.resize(128, 128)
                t.setPixmap(QPixmap("qrcodes/packet.png"))
                t.move(56, [132, 339][i])
                t.show()

                self._win_terminals.append(t)

            # The agent
            self._agent = QLabel(self._win)
            self._agent.resize(128, 128)
            self._agent.move(*LOC_ROOT)
            self._agent.show()
            self._agent_on = QPixmap("qrcodes/agent_packet.png")
            self._agent_off = QPixmap("qrcodes/agent.png")

            # Reward indicator
            self._reward_label = QLabel(self._win)

        # Update the terminal indicators
        for i in range(2):
            self._win_terminals[i].setVisible(self.terminals[i] > 0)

        # Move the agent
        ann = QPropertyAnimation(self._agent, b"pos")
        ann.setStartValue(self._agent.pos())
        ann.setEndValue(QPoint(*([LOC_ROOT, LOC_T1, LOC_T2][action])))
        ann.setDuration(1000)  # One second
        ann.setEasingCurve(QEasingCurve.InOutQuad)

        if action == 0 and self._last_reward == 2.0:
            self._agent.setPixmap(
                self._agent_on)  # Go to the root with an object
        else:
            self._agent.setPixmap(self._agent_off)

        # Move and display the reward label if a reward was obtained
        if self._last_reward != 0.0:
            rann = QPropertyAnimation(self._reward_label, b"pos")
            self._reward_label.setText('<h1>%+i</h1>' % self._last_reward)
            self._reward_label.show()

            start = [LOC_ROOT, LOC_T1, LOC_T2][self._last_action]
            start = (start[0] - 20, start[1] + 50)
            end = (start[0] - 20, start[1] - 50)

            rann.setStartValue(QPoint(*start))
            rann.setEndValue(QPoint(*end))
            rann.setDuration(1000)
            rann.start()

        ann.finished.connect(self._loop.quit)
        ann.start()
        self._loop.exec_()

        self._reward_label.hide()
        self._app.processEvents()
Beispiel #27
0
    def _runHtmlBuilder(self):
        # Build the commond line for Sphinx.
        if core.config()['Sphinx']['AdvancedMode']:
            htmlBuilderCommandLine = core.config()['Sphinx']['Cmdline']
            if sys.platform.startswith('linux'):
                # If Linux is used, then subprocess cannot take the whole
                # commandline as the name of an executable file. Module shlex
                # has to be used to parse commandline.
                htmlBuilderCommandLine = shlex.split(htmlBuilderCommandLine)
        else:
            # For available builder options, refer to: http://sphinx-doc.org/builders.html
            htmlBuilderCommandLine = [core.config()['Sphinx']['Executable'],
              # Place doctrees in the ``_build`` directory; by default, Sphinx
              # places this in _build/html/.doctrees.
              '-d', os.path.join('_build', 'doctrees'),
              # Source directory -- the current directory, since we'll chdir to
              # the project directory before executing this.
              '.',
              # Build directory
              core.config()['Sphinx']['OutputPath']]

        # Invoke it.
        try:
            # Clear the log at the beginning of a Sphinx build.
            self.logWindowClear.emit()

            cwd = core.config()['Sphinx']['ProjectPath']
            # If the command line is already a string (advanced mode), just print it.
            # Otherwise, it's a list that should be transformed to a string.
            if isinstance(htmlBuilderCommandLine, str):
                htmlBuilderCommandLineStr = htmlBuilderCommandLine
            else:
                htmlBuilderCommandLineStr = ' '.join(htmlBuilderCommandLine)
            self.logWindowText.emit('{} : {}\n\n'.format(cwd,
                                                         htmlBuilderCommandLineStr))

            # Run Sphinx, reading stdout in a separate thread.
            self._qe = QEventLoop()
            # Sphinx will output just a carriage return (0x0D) to simulate a
            # single line being updated by build status and the build
            # progresses. Without universal newline support here, we'll wait
            # until the build is complete (with a \n\r) to report any build
            # progress! So, enable universal newlines, so that each \r will be
            # treated as a separate line, providing immediate feedback on build
            # progress.
            popen = open_console_output(htmlBuilderCommandLine, cwd=cwd,
                                        universal_newlines=True)
            # Perform reads in an event loop. The loop is exit when all reads
            # have completed. We can't simply start the _stderr_read thread
            # here, because calls to self._qe_exit() will be ignored until
            # we're inside the event loop.
            QTimer.singleShot(0, lambda: self._popen_read(popen))
            self._qe.exec_()
        except OSError as ex:
            return (
                'Failed to execute HTML builder:\n'
                '{}\n'.format(str(ex)) +
                'Go to Settings -> Settings -> CodeChat to set HTML'
                ' builder configurations.')

        return self._stderr
Beispiel #28
0
 def wait_ms(self, timeout):
     ''' Block loop until timeout (ms) elapses.
     '''
     loop = QEventLoop()
     QTimer.singleShot(timeout, loop.exit)
     loop.exec_()
Beispiel #29
0
            self.file_writer_group.root_dir_entry.setText(
                p['Writer']['Data dir'])
            self.file_writer_group.ctset_fmt_entry.setText(
                p['Writer']['CT scan name'])
            self.file_writer_group.dsetname_entry.setText(
                p['Writer']['Filename'])
            self.file_writer_group.bigtiff_checkbox.setChecked(
                p['Writer']['Big tiffs'])
            self.file_writer_group.separate_scans_checkbox.setChecked(
                p['Writer']['Separate scans'])
        except:
            warning_message('Cannot enter file-writer settings correctly')


if __name__ == '__main__':
    parsed_args, unparsed_args = process_cl_args()
    if parsed_args.debug:
        log.log_to_console(level=logging.DEBUG)
    # QApplication expects the first argument to be the program name.
    qt_args = sys.argv[:1] + unparsed_args
    app = QApplication(qt_args)
    loop = QEventLoop(app)
    root_dir = os.path.dirname(os.path.abspath(__file__))
    style_file = QFile(os.path.join(root_dir, "styles/breeze/dark.qss"))
    style_file.open(QFile.ReadOnly | QFile.Text)
    stream = QTextStream(style_file)
    # Set application style to dark; Comment following line to unset
    # app.setStyleSheet(stream.readAll())
    ex = GUI()
    sys.exit(app.exec_())
    def responseComplete(self):
        request = self.serverInterface().requestHandler()
        params = request.parameterMap()

        # SERVICE=RENDERGEOJSON -- we are taking over
        if params.get('SERVICE', '').upper() == 'RENDERGEOJSON':
            request.clear()
            try:
                # Parse parameters
                geojson = params.get('GEOJSON')
                if not geojson:
                    raise ParameterError('Parameter GEOJSON must be set.')

                style = params.get('STYLE')
                if not style:
                    raise ParameterError('Parameter STYLE must be set.')

                try:
                    width = int(params.get('WIDTH'))
                except TypeError:
                    raise ParameterError('Parameter WIDTH must be integer.')
                try:
                    height = int(params.get('HEIGHT'))
                except TypeError:
                    raise ParameterError('Parameter HEIGHT must be integer.')

                try:
                    dpi = int(params.get('DPI', 96))
                except TypeError:
                    raise ParameterError('Parameter DPI must be integer.')

                try:
                    minx, miny, maxx, maxy = params.get('BBOX').split(',')
                    bbox = QgsRectangle(float(minx), float(miny), float(maxx),
                                        float(maxy))
                except (ValueError, AttributeError):
                    raise ParameterError(
                        'Parameter BBOX must be specified in the form `min_x,min_y,max_x,max_y`.'
                    )

                url = geojson
                geojson_file_name = self._resolve_url(geojson)

                if '$type' in style:
                    polygon_style = self._resolve_url(
                        style.replace('$type', 'polygons'))
                    line_style = self._resolve_url(
                        style.replace('$type', 'lines'))
                    point_style = self._resolve_url(
                        style.replace('$type', 'points'))
                else:
                    polygon_style = self._resolve_url(style)
                    line_style = polygon_style
                    point_style = polygon_style

                polygon_layer = QgsVectorLayer(
                    geojson_file_name + '|geometrytype=Polygon', 'polygons',
                    'ogr')
                self._load_style(polygon_layer, polygon_style)
                line_layer = QgsVectorLayer(
                    geojson_file_name + '|geometrytype=Line', 'lines', 'ogr')
                self._load_style(line_layer, line_style)
                point_layer = QgsVectorLayer(
                    geojson_file_name + '|geometrytype=Point', 'points', 'ogr')
                self._load_style(point_layer, point_style)

                settings = QgsMapSettings()
                settings.setOutputSize(QSize(width, height))
                settings.setOutputDpi(dpi)
                settings.setExtent(bbox)
                settings.setLayers([polygon_layer, line_layer, point_layer])
                settings.setBackgroundColor(QColor(Qt.transparent))
                renderer = QgsMapRendererParallelJob(settings)

                event_loop = QEventLoop()
                renderer.finished.connect(event_loop.quit)
                renderer.start()

                event_loop.exec_()

                img = renderer.renderedImage()
                img.setDotsPerMeterX(dpi * 39.37)
                img.setDotsPerMeterY(dpi * 39.37)
                image_data = QByteArray()
                buf = QBuffer(image_data)
                buf.open(QIODevice.WriteOnly)
                img.save(buf, 'PNG')

                request.setResponseHeader('Content-type', 'image/png')
                request.appendBody(image_data)
            except ParameterError as e:
                QgsMessageLog.logMessage(
                    "RenderGeojson.responseComplete :: ParameterError")
                request.setResponseHeader('Content-type', 'text/plain')
                request.appendBody(str(e).encode('utf-8'))
            except:
                QgsMessageLog.logMessage(
                    "RenderGeojson.responseComplete :: Exception")
                QgsMessageLog.logMessage(
                    "RenderGeojson.responseComplete ::   {}".format(
                        traceback.format_exc()))
                request.setResponseHeader('Content-type', 'text/plain')
                request.appendBody(b'Unhandled error')
                request.appendBody(traceback.format_exc().encode('utf-8'))
def q_sleep(t_s):
    loop = QEventLoop()
    QTimer.singleShot(int(t_s * 1000), loop.quit)
    loop.exec_()
Beispiel #32
0
class CadnanoQt(QObject):
    dontAskAndJustDiscardUnsavedChanges = False
    documentWasCreatedSignal = pyqtSignal(object)  # doc
    documentWindowWasCreatedSignal = pyqtSignal(object, object)  # doc, window

    def __init__(self, argv):
        """Create the application object
        """
        self.argns, unused = util.parse_args(argv, use_gui=True)
        # util.init_logging(self.argns.__dict__)
        # logger.info("CadnanoQt initializing...")
        if argv is None:
            argv = sys.argv
        self.argv = argv
        # print("initializing new CadnanoQt", type(QCoreApplication.instance()))
        if QCoreApplication.instance() is None:
            self.qApp = QApplication(argv)
            assert(QCoreApplication.instance() is not None)
            self.qApp.setOrganizationDomain("cadnano.org")
        else:
            self.qApp = qApp
        super(CadnanoQt, self).__init__()
        # print("initialized new CadnanoQt")
        from cadnano.views.preferences import Preferences
        self.prefs = Preferences()
        self.icon = icon = QIcon(ICON_PATH1)
        icon.addFile(ICON_PATH2, QSize(256, 256))
        icon.addFile(ICON_PATH3, QSize(48, 48))
        self.qApp.setWindowIcon(icon)
        self.main_event_loop = None
        self.document_controllers = set()  # Open documents
        self.active_document = None
        self._document = None
        self.documentWasCreatedSignal.connect(self.wirePrefsSlot)
    # end def

    def document(self) -> DocT:
        return self._document
    # end def

    def finishInit(self):
        global decodeFile
        global Document
        global DocumentController
        from cadnano.document import Document
        from cadnano.fileio.decode import decodeFile
        from cadnano.controllers.documentcontroller import DocumentController
        from cadnano.views.pathview import pathstyles as styles

        styles.setFontMetrics()

        doc = Document()
        self._document = self.createDocument(base_doc=doc)

        if os.environ.get('CADNANO_DISCARD_UNSAVED', False) and not self.ignoreEnv():
            self.dontAskAndJustDiscardUnsavedChanges = True
        self.dontAskAndJustDiscardUnsavedChanges = True
    # end def

    def exec_(self):
        if hasattr(self, 'qApp'):
            self.main_event_loop = QEventLoop()
            self.main_event_loop.exec_()

    def destroyApp(self):
        """Destroy the QApplication.

        Do not set `self.qApp = None` in this method.
        Do it external to the CadnanoQt class
        """
        global decodeFile
        global Document
        global DocumentController
        # print("documentWasCreatedSignal", self.documentWasCreatedSignal)
        if self.document_controllers:
            self.documentWasCreatedSignal.disconnect(self.wirePrefsSlot)
        decodeFile = None
        Document = None
        DocumentController = None
        self.document_controllers.clear()
        self.qApp.quit()
    # end def

    def ignoreEnv(self):
        return os.environ.get('CADNANO_IGNORE_ENV_VARS_EXCEPT_FOR_ME', False)

    def createDocument(self, base_doc: DocT = None):
        global DocumentController
        # print("CadnanoQt createDocument begin")
        default_file = self.argns.file or os.environ.get('CADNANO_DEFAULT_DOCUMENT', None)
        if default_file is not None and base_doc is not None:
            default_file = os.path.expanduser(default_file)
            default_file = os.path.expandvars(default_file)
            dc = DocumentController(base_doc)
            # logger.info("Loading cadnano file %s to base document %s", default_file, base_doc)
            decodeFile(default_file, document=base_doc)
            dc.setFileName(default_file)
            print("Loaded default document: %s" % (default_file))
        else:
            doc_ctrlr_count = len(self.document_controllers)
            # logger.info("Creating new empty document...")
            if doc_ctrlr_count == 0:  # first dc
                # dc adds itself to app.document_controllers
                dc = DocumentController(base_doc)
            elif doc_ctrlr_count == 1:  # dc already exists
                dc = list(self.document_controllers)[0]
                dc.newDocument()  # tell it to make a new doucment
        # print("CadnanoQt createDocument done")
        return dc.document()

    def prefsClicked(self):
        self.prefs.showDialog()

    def wirePrefsSlot(self, document: DocT):
        """MUST CALL THIS TO SET PREFERENCES :class:`Document`
        """
        self.prefs.document = document
class Prompt(QLabel):
    """Blocking prompt widget asking the user a question.

    The prompt is initialized with a question and displays the question, its title and
    the valid keybindings. Calling ``run`` blocks the UI until a valid keybinding to
    answer/abort the question was given.

    Class Attributes:
        BINDINGS: Valid keybindings to answer/abort the question.

    Attributes:
        question: Question object defining title, question and answer.
        loop: Event loop used to block the UI.
    """

    STYLESHEET = """
    QLabel {
        font: {prompt.font};
        color: {prompt.fg};
        background-color: {prompt.bg};
        padding: {prompt.padding};
        border-top-right-radius: {prompt.border_radius};
        border-top: {prompt.border} {prompt.border.color};
        border-right: {prompt.border} {prompt.border.color};
    }
    """

    BINDINGS = (
        ("y", "Yes"),
        ("n", "No"),
        ("<return>", "No"),
        ("<escape>", "Abort"),
    )

    def __init__(self, question: api.prompt.Question, *, parent):
        super().__init__(parent=parent)
        self.question = question
        self.loop = QEventLoop()

        styles.apply(self)
        header = f"<h3>{question.title}</h3>{question.body}"
        self.setText(header + self.bindings_table())
        _logger.debug("Initialized %s", self)

        self.setFocus()
        self.adjustSize()
        self.raise_()
        self.show()

    def __str__(self):
        return f"prompt for '{self.question.title}'"

    @classmethod
    def bindings_table(cls):
        """Return a formatted html table with the valid keybindings."""
        return utils.format_html_table(
            (f"<b>{utils.escape_html(binding)}</b>", command)
            for binding, command in cls.BINDINGS
        )

    def run(self):
        """Run the blocking event loop."""
        _logger.debug("Running blocking %s", self)
        self.loop.exec_()

    def update_geometry(self, _width: int, bottom: int):
        y = bottom - self.height()
        self.setGeometry(0, y, self.width(), self.height())

    def leave(self, *, answer=None):
        """Leave the prompt by answering the question and quitting the loop."""
        _logger.debug("Leaving %s with '%s'", self, answer)
        self.question.answer = answer
        self.loop.quit()
        self.loop.deleteLater()
        self.deleteLater()
        api.modes.current().widget.setFocus()

    def keyPressEvent(self, event):
        """Leave the prompt on a valid key binding."""
        if event.key() == Qt.Key_Y:
            self.leave(answer=True)
        elif event.key() in (Qt.Key_N, Qt.Key_Return):
            self.leave(answer=False)
        elif event.key() == Qt.Key_Escape:
            self.leave()

    def focusOutEvent(self, event):
        """Leave the prompt without answering when unfocused."""
        if self.loop.isRunning():
            self.leave()
        super().focusOutEvent(event)
import sys

from PyQt5.QAxContainer import QAxWidget
from PyQt5.QtCore import QEventLoop
from PyQt5.QtWidgets import QApplication

app = QApplication(sys.argv)
control = QAxWidget("{A1574A0D-6BFA-4BD7-9020-DED88711818D}")

loop = QEventLoop()


def comm_connect():
    err = control.dynamicCall("CommConnect()")
    if err < 0:
        raise ValueError(err)
    loop.exec_()


def on_event_connect(errcode):
    if errcode < 0:
        raise ValueError(errcode)
    if errcode == 0:
        print("Connected!")
    control.OnEventConnect.disconnect(on_event_connect)
    loop.exit()


control.OnEventConnect.connect(on_event_connect)

comm_connect()
Beispiel #35
0
def run():
    """
    Creates all the top-level assets for the application, sets things up and
    then runs the application. Specific tasks include:

    - set up logging
    - create an application object
    - create an editor window and status bar
    - display a splash screen while starting
    - close the splash screen after startup timer ends
    """
    setup_logging()
    logging.info("\n\n-----------------\n\nStarting Mu {}".format(__version__))
    logging.info(platform.uname())
    logging.info("Python path: {}".format(sys.path))
    logging.info("Language code: {}".format(i18n.language_code))

    #
    # Load settings from known locations and register them for
    # autosave
    #
    settings.init()

    # Images (such as toolbar icons) aren't scaled nicely on retina/4k displays
    # unless this flag is set
    os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
    if hasattr(Qt, "AA_EnableHighDpiScaling"):
        QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

    # An issue in PyQt5 v5.13.2 to v5.15.1 makes PyQt5 application
    # hang on Mac OS 11 (Big Sur)
    # Setting this environment variable fixes the problem.
    # See issue #1147 for more information
    os.environ["QT_MAC_WANTS_LAYER"] = "1"

    # The app object is the application running on your computer.
    app = QApplication(sys.argv)
    # By default PyQt uses the script name (run.py)
    app.setApplicationName("mu")
    # Set hint as to the .desktop files name
    app.setDesktopFileName("mu.codewith.editor")
    app.setApplicationVersion(__version__)
    app.setAttribute(Qt.AA_DontShowIconsInMenus)

    # Display a friendly "splash" icon.
    splash = AnimatedSplash(load_movie("splash_screen"))
    splash.show()
    app.processEvents()

    # Create a blocking thread upon which to run the StartupWorker and which
    # will process the events for animating the splash screen.
    initLoop = QEventLoop()
    thread = QThread()
    worker = StartupWorker()
    worker.moveToThread(thread)
    thread.started.connect(worker.run)
    worker.finished.connect(thread.quit)
    worker.finished.connect(worker.deleteLater)
    # Stop the blocking event loop when the thread is finished.
    thread.finished.connect(initLoop.quit)
    thread.finished.connect(thread.deleteLater)
    thread.start()
    initLoop.exec()  # start processing the pending StartupWorker.

    # Create the "window" we'll be looking at.
    editor_window = Window()

    @editor_window.load_theme.connect
    def load_theme(theme):
        if theme == "contrast":
            app.setStyleSheet(CONTRAST_STYLE)
        elif theme == "night":
            app.setStyleSheet(NIGHT_STYLE)
        else:
            app.setStyleSheet(DAY_STYLE)

    splash.finish(editor_window)

    # Make sure all windows have the Mu icon as a fallback
    app.setWindowIcon(load_icon(editor_window.icon))
    # Create the "editor" that'll control the "window".
    editor = Editor(view=editor_window)
    editor.setup(setup_modes(editor, editor_window))
    # Setup the window.
    editor_window.closeEvent = editor.quit
    editor_window.setup(editor.debug_toggle_breakpoint, editor.theme)
    # Connect the various UI elements in the window to the editor.
    editor_window.connect_tab_rename(editor.rename_tab, "Ctrl+Shift+S")
    editor_window.connect_find_replace(editor.find_replace, "Ctrl+F")
    # Connect find again both forward and backward ('Shift+F3')
    find_again_handlers = (editor.find_again, editor.find_again_backward)
    editor_window.connect_find_again(find_again_handlers, "F3")
    editor_window.connect_toggle_comments(editor.toggle_comments, "Ctrl+K")
    editor.connect_to_status_bar(editor_window.status_bar)

    # Restore the previous session along with files passed by the os
    editor.restore_session(sys.argv[1:])

    # Stop the program after the application finishes executing.
    sys.exit(app.exec_())
Beispiel #36
0
class QtReactor(posixbase.PosixReactorBase):
    implements(IReactorFDSet)

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.iterate)

        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp = QCoreApplication([])
            self._ownApp = True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp = False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)

    def _add(self, xer, primary, type):
        """
        Private method for adding a descriptor from the event loop.

        It takes care of adding it if  new or modifying it if already added
        for another state (read -> read/write for example).
        """

        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)

    def addReader(self, reader):
        """
        Add a FileDescriptor for notification of data available to read.
        """

        self._add(reader, self._reads, QSocketNotifier.Read)

    def addWriter(self, writer):
        """
        Add a FileDescriptor for notification of data available to write.
        """

        self._add(writer, self._writes, QSocketNotifier.Write)

    def _remove(self, xer, primary):
        """
        Private method for removing a descriptor from the event loop.

        It does the inverse job of _add, and also add a check in case of the fd
        has gone away.
        """

        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

    def removeReader(self, reader):
        """
        Remove a Selectable for notification of data available to read.
        """

        self._remove(reader, self._reads)

    def removeWriter(self, writer):
        """
        Remove a Selectable for notification of data available to write.
        """

        self._remove(writer, self._writes)

    def removeAll(self):
        """
        Remove all selectables, and return a list of them.
        """

        rv = self._removeAll(self._reads, self._writes)
        return rv

    def getReaders(self):
        return self._reads.keys()

    def getWriters(self):
        return self._writes.keys()

    def callLater(self, howlong, *args, **kargs):
        rval = super(QtReactor, self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval

    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()

    def _iterate(self, delay=None, fromqt=False):
        """
        See twisted.internet.interfaces.IReactorCore.iterate.
        """

        self.runUntilCurrent()
        self.doIteration(delay, fromqt)

    iterate = _iterate

    def doIteration(self, delay=None, fromqt=False):
        """
        This method is called by a Qt timer or by network activity on a file descriptor
        """

        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        delay = max(delay, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        if self.timeout() is None:
            timeout = 0.1
        elif self.timeout() == 0:
            timeout = 0
        else:
            timeout = self.timeout()
        self._timer.setInterval(timeout * 1000)
        self._timer.start()

    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()

    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
Beispiel #37
0
class CharacterDialog(WindowModalDialog):

    def __init__(self, parent):
        super(CharacterDialog, self).__init__(parent)
        self.setWindowTitle(_("KeepKey Seed Recovery"))
        self.character_pos = 0
        self.word_pos = 0
        self.loop = QEventLoop()
        self.word_help = QLabel()
        self.char_buttons = []

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(CHARACTER_RECOVERY))
        hbox = QHBoxLayout()
        hbox.addWidget(self.word_help)
        for i in range(4):
            char_button = CharacterButton('*')
            char_button.setMaximumWidth(36)
            self.char_buttons.append(char_button)
            hbox.addWidget(char_button)
        self.accept_button = CharacterButton(_("Accept Word"))
        self.accept_button.clicked.connect(partial(self.process_key, 32))
        self.rejected.connect(partial(self.loop.exit, 1))
        hbox.addWidget(self.accept_button)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        self.finished_button = QPushButton(_("Seed Entered"))
        self.cancel_button = QPushButton(_("Cancel"))
        self.finished_button.clicked.connect(partial(self.process_key,
                                                     Qt.Key_Return))
        self.cancel_button.clicked.connect(self.rejected)
        buttons = Buttons(self.finished_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()

    def refresh(self):
        self.word_help.setText("Enter seed word %2d:" % (self.word_pos + 1))
        self.accept_button.setEnabled(self.character_pos >= 3)
        self.finished_button.setEnabled((self.word_pos in (11, 17, 23)
                                         and self.character_pos >= 3))
        for n, button in enumerate(self.char_buttons):
            button.setEnabled(n == self.character_pos)
            if n == self.character_pos:
                button.setFocus()

    def is_valid_alpha_space(self, key):
        # Auto-completion requires at least 3 characters
        if key == ord(' ') and self.character_pos >= 3:
            return True
        # Firmware aborts protocol if the 5th character is non-space
        if self.character_pos >= 4:
            return False
        return (key >= ord('a') and key <= ord('z')
                or (key >= ord('A') and key <= ord('Z')))

    def process_key(self, key):
        self.data = None
        if key == Qt.Key_Return and self.finished_button.isEnabled():
            self.data = {'done': True}
        elif key == Qt.Key_Backspace and (self.word_pos or self.character_pos):
            self.data = {'delete': True}
        elif self.is_valid_alpha_space(key):
            self.data = {'character': chr(key).lower()}
        if self.data:
            self.loop.exit(0)

    def keyPressEvent(self, event):
        self.process_key(event.key())
        if not self.data:
            QDialog.keyPressEvent(self, event)

    def get_char(self, word_pos, character_pos):
        self.word_pos = word_pos
        self.character_pos = character_pos
        self.refresh()
        if self.loop.exec_():
            self.data = None  # User cancelled
Beispiel #38
0
class TerminalsEnv(gym.Env):
    """ Two terminals, A and B, contain objects that a robot has to carry to the
        root R. Terminals contain a finite number of objects, and the robot gets
        a negative reward when it goes to an empty one. This means that once a
        terminal is empty, the robot must remember to now go to the other one.
    """
    metadata = {'render.modes': ['human']}

    def __init__(self):
        self.action_space = spaces.Discrete(3)  # Go to root, A or B
        self.observation_space = spaces.Discrete(
            2)  # "Empty" (0) or "Full" (1), the root always returns 0
        self._render_enable = False
        self._app = None
        self._last_reward = 0.0
        self._last_action = 0

        self._seed()

    def _render(self, mode, close):
        self._render_enable = True

    def _seed(self, seed=None):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def _step(self, action):
        assert self.action_space.contains(action)

        # Animate the current action if needed
        if self._render_enable:
            self.render_action(action)

        self._last_action = action

        if action == 0:
            # Going to the root does nothing
            self._last_reward = 0.0
            return 0, self._last_reward, False, {}
        else:
            terminal = action - 1

            if self.terminals[terminal] > 0:
                # There are objects left in the terminal, remove one
                self.terminals[terminal] -= 1
                self._last_reward = 2.0

                return 1, self._last_reward, False, {}
            else:
                # The terminal is empty, refill the other ones
                self.rounds -= 1
                self._last_reward = -2.0
                done = (self.rounds == 0)

                for i in range(TERMINALS):
                    if i != terminal:
                        self.terminals[i] = random.randint(TOP, TOP * 2)

                return 0, self._last_reward, done, {}

    def _reset(self):
        self.terminals = [random.randint(TOP, TOP * 2)] * TERMINALS
        self.rounds = random.randint(ROUNDS, ROUNDS + 1)
        self._last_reward = 0.0

        if self._render_enable and self._app is not None:
            # Ensure that the agent starts at the root
            self._agent.move(*LOC_ROOT)

        return 0  # Implicitly start at the root with no information

    def render_action(self, action):
        from PyQt5.QtCore import QEventLoop, QPropertyAnimation, QEasingCurve, QPoint
        from PyQt5.QtGui import QPixmap
        from PyQt5.QtWidgets import QApplication, QWidget, QLabel

        if self._app is None:
            # Create the window
            self._app = QApplication([])
            self._loop = QEventLoop(self._app)
            self._win = QLabel()
            self._win.resize(800, 600)
            self._win.setPixmap(QPixmap("qrcodes/terminals_bg.png"))
            self._win.show()

            # Two terminal indicators
            self._win_terminals = []

            for i in range(2):
                t = QLabel(self._win)
                t.resize(128, 128)
                t.setPixmap(QPixmap("qrcodes/packet.png"))
                t.move(56, [132, 339][i])
                t.show()

                self._win_terminals.append(t)

            # The agent
            self._agent = QLabel(self._win)
            self._agent.resize(128, 128)
            self._agent.move(*LOC_ROOT)
            self._agent.show()
            self._agent_on = QPixmap("qrcodes/agent_packet.png")
            self._agent_off = QPixmap("qrcodes/agent.png")

            # Reward indicator
            self._reward_label = QLabel(self._win)

        # Update the terminal indicators
        for i in range(2):
            self._win_terminals[i].setVisible(self.terminals[i] > 0)

        # Move the agent
        ann = QPropertyAnimation(self._agent, b"pos")
        ann.setStartValue(self._agent.pos())
        ann.setEndValue(QPoint(*([LOC_ROOT, LOC_T1, LOC_T2][action])))
        ann.setDuration(1000)  # One second
        ann.setEasingCurve(QEasingCurve.InOutQuad)

        if action == 0 and self._last_reward == 2.0:
            self._agent.setPixmap(
                self._agent_on)  # Go to the root with an object
        else:
            self._agent.setPixmap(self._agent_off)

        # Move and display the reward label if a reward was obtained
        if self._last_reward != 0.0:
            rann = QPropertyAnimation(self._reward_label, b"pos")
            self._reward_label.setText('<h1>%+i</h1>' % self._last_reward)
            self._reward_label.show()

            start = [LOC_ROOT, LOC_T1, LOC_T2][self._last_action]
            start = (start[0] - 20, start[1] + 50)
            end = (start[0] - 20, start[1] - 50)

            rann.setStartValue(QPoint(*start))
            rann.setEndValue(QPoint(*end))
            rann.setDuration(1000)
            rann.start()

        ann.finished.connect(self._loop.quit)
        ann.start()
        self._loop.exec_()

        self._reward_label.hide()
        self._app.processEvents()
Beispiel #39
0
class Downloader(QObject):
    finished = pyqtSignal(bool)

    def __init__(self,
                 des_path,
                 source_path,
                 reqTimeout=10,
                 readTimeout=30,
                 try_time=3):
        super(Downloader, self).__init__()
        self.retStatus = False
        self.source_path = source_path
        self.des_path = des_path
        self.bak_file = des_path + ".bak"
        self.try_time = try_time
        self.readTimeout = readTimeout * 1000
        self.reqTimeout = reqTimeout * 1000
        self.data = None

        # request timer, default 5 sec
        self.reqTimer = QTimer()
        self.reqTimer.timeout.connect(self.onReqTimeOut)
        # read data timer, default no time unlimit
        self.readTimer = QTimer()
        self.readTimer.timeout.connect(self.onReadTimeOut)

        self.networkManager = QNetworkAccessManager()
        self.request()

        if os.path.exists(self.bak_file):
            os.remove(self.bak_file)
        if os.path.exists(self.des_path):
            os.rename(self.des_path, self.bak_file)
        self.fp = open(self.des_path, "wb")
        self.reqTimer.start(self.reqTimeout)
        self.eventLoop = QEventLoop()
        self.finished.connect(self.eventLoop.quit)
        self.eventLoop.exec_()
        self.fp.close()
        if self.retStatus is False:
            if os.path.exists(self.des_path):
                os.remove(self.des_path)
            if os.path.exists(self.bak_file):
                os.rename(self.bak_file, self.des_path)
        else:
            if os.path.exists(self.bak_file):
                os.remove(self.bak_file)

    def destoryRequestReply(self, reply):
        reply.blockSignals(True)
        reply.close()
        reply = None

    def requestAgain(self):
        self.destoryRequestReply(self.reply)
        self.request()

    def request(self):
        try:
            if self.try_time:
                self.reply = self.networkManager.get(
                    QNetworkRequest(QUrl(self.source_path)))
                self.reply.readyRead.connect(self.startRead)
                self.reply.downloadProgress.connect(self.onProgress)
                self.try_time = self.try_time - 1
            else:
                self.finished.emit(self.retStatus)
                print("no try times, emit finished")
                self.reqTimer.stop()
        except Exception as e:
            print(e)

    def onReadTimeOut(self):
        # stop read timer
        print("read time out")
        self.readTimer.stop()
        # reset file data
        if self.fp.seekable():
            self.fp.truncate(0)
            self.fp.seek(0, 0)
        else:
            self.fp.close()
            self.fp = open(self.des_path, "wb")
        # request again, start request timer
        self.requestAgain()
        self.reqTimer.start(self.reqTimeout)

    def onReqTimeOut(self):
        self.requestAgain()

    def startRead(self):
        self.reqTimer.stop()
        self.reply.readyRead.disconnect(self.startRead)

    def onProgress(self, cur, total):
        self.readTimer.start(self.readTimeout)
        if self.writeFile() is False:
            self.endRead(False)
        if cur == total and total > 0:
            self.endRead(True)
        self.readTimer.stop()

    def endRead(self, successful):
        if successful:
            print("finish download %s" % self.des_path)
            self.retStatus = True
            self.finished.emit(self.retStatus)
            self.destoryRequestReply(self.reply)
        else:
            self.requestAgain()
            self.reqTimer.start(self.reqTimeout)

    def writeFile(self):
        try:
            data = self.reply.readAll()
            if data:
                readLen = len(data)
                if readLen <= 0:
                    e = Exception("Invaild Data, Request again")
                    raise e
                self.fp.write(data)
            return True
        except Exception as e:
            print("Exception happen in Function[writeFile]: " + str(e))
            return False
Beispiel #40
0
 def exec_(self):
     if hasattr(self, 'qApp'):
         self.main_event_loop = QEventLoop()
         self.main_event_loop.exec_()
Beispiel #41
0
class Kiwoom(QAxWidget):
    def __init__(self):
        super().__init__()

        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

        self.conditionbuy = True

        # Loop 변수
        # 비동기 방식으로 동작되는 이벤트를 동기화(순서대로 동작) 시킬 때
        self.loginLoop = None
        self.requestLoop = None
        self.orderLoop = None
        self.conditionLoop = None

        # 서버구분
        self.server = None

        # 조건식
        self.condition = None
        self.conditionCodeList = []

        # 에러
        self.error = None

        # 주문번호
        self.orderNo = ""

        # 조회
        self.inquiry = 0

        # 서버에서 받은 메시지
        self.msg = ""

        # 예수금 d+2
        self.opw00001Data = 0

        # 보유종목 정보
        self.opw00018Data = {'accountEvaluation': [], 'stocks': []}

        self.opt10001Data = {'code': [], 'high': [], 'low': []}

        # signal & slot
        self.OnEventConnect.connect(self.eventConnect)
        self.OnReceiveTrData.connect(self.receiveTrData)
        self.OnReceiveChejanData.connect(self.receiveChejanData)
        self.OnReceiveRealData.connect(self.receiveRealData)
        self.OnReceiveMsg.connect(self.receiveMsg)
        self.OnReceiveConditionVer.connect(self.receiveConditionVer)
        self.OnReceiveTrCondition.connect(self.receiveTrCondition)
        self.OnReceiveRealCondition.connect(self.receiveRealCondition)

        # 로깅용 설정파일
        logging.config.fileConfig('logging.conf')
        self.log = logging.getLogger('Kiwoom')

        # 내가 추가
        self.accountList = None
        self.order_time = time.time()

        self.request_thread_worker = RequestThreadWorker(self)
        self.request_thread = QThread()
        self.request_thread_worker.moveToThread(self.request_thread)
        self.request_thread.started.connect(self.request_thread_worker.run)
        self.request_thread.start()

    ###############################################################
    # 로깅용 메서드 정의                                               #
    ###############################################################

    def logger(origin):
        def wrapper(*args, **kwargs):
            args[0].log.debug('{} args - {}, kwargs - {}'.format(
                origin.__name__, args, kwargs))
            return origin(*args, **kwargs)

        return wrapper

    ###############################################################
    # 이벤트 정의                                                    #
    ###############################################################
    @SyncRequestDecorator.kiwoom_sync_callback
    def eventConnect(self, returnCode):
        """
        통신 연결 상태 변경시 이벤트

        returnCode가 0이면 로그인 성공
        그 외에는 ReturnCode 클래스 참조.

        :param returnCode: int
        """

        try:
            if returnCode == ReturnCode.OP_ERR_NONE:

                self.server = self.getLoginInfo("GetServerGubun", True)

                if len(self.server) == 0 or self.server != "1":
                    self.msg += "실서버 연결 성공" + "\r\n\r\n"

                else:
                    self.msg += "모의투자서버 연결 성공" + "\r\n\r\n"

            else:
                self.msg += "연결 끊김: 원인 - " + ReturnCode.CAUSE[
                    returnCode] + "\r\n\r\n"

        except Exception as error:
            self.log.error('eventConnect {}'.format(error))

        finally:
            # commConnect() 메서드에 의해 생성된 루프를 종료시킨다.
            # 로그인 후, 통신이 끊길 경우를 대비해서 예외처리함.
            try:
                self.loginLoop.exit()
            except AttributeError:
                pass

    def receiveMsg(self, screenNo, requestName, trCode, msg):
        """
        수신 메시지 이벤트

        서버로 어떤 요청을 했을 때(로그인, 주문, 조회 등), 그 요청에 대한 처리내용을 전달해준다.

        :param screenNo: string - 화면번호(4자리, 사용자 정의, 서버에 조회나 주문을 요청할 때 이 요청을 구별하기 위한 키값)
        :param requestName: string - TR 요청명(사용자 정의)
        :param trCode: string
        :param msg: string - 서버로 부터의 메시지
        """

        self.msg += requestName + ": " + msg + "\r\n\r\n"

    @SyncRequestDecorator.kiwoom_sync_callback
    def receiveTrData(self, screenNo, requestName, trCode, recordName, inquiry,
                      deprecated1, deprecated2, deprecated3, deprecated4):
        """
        TR 수신 이벤트

        조회요청 응답을 받거나 조회데이터를 수신했을 때 호출됩니다.
        requestName과 trCode는 commRqData()메소드의 매개변수와 매핑되는 값 입니다.
        조회데이터는 이 이벤트 메서드 내부에서 getCommData() 메서드를 이용해서 얻을 수 있습니다.

        :param screenNo: string - 화면번호(4자리)
        :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName)
        :param trCode: string
        :param recordName: string
        :param inquiry: string - 조회('0': 남은 데이터 없음, '2': 남은 데이터 있음)
        """

        print("receiveTrData 실행: ", screenNo, requestName, trCode, recordName,
              inquiry)

        # 주문번호와 주문루프
        self.orderNo = self.commGetData(trCode, "", requestName, 0, "주문번호")

        try:
            self.orderLoop.exit()
        except AttributeError:
            pass

        self.inquiry = inquiry

        if requestName == "관심종목정보요청":
            data = self.getCommDataEx(trCode, "관심종목정보")
            print(type(data))
            print(data)
            """ commGetData
            cnt = self.getRepeatCnt(trCode, requestName)

            for i in range(cnt):
                data = self.commGetData(trCode, "", requestName, i, "종목명")
                print(data)
            """

            # elif requestName == "주식일봉차트조회요청":
            #     data = self.getCommDataEx(trCode, "주식일봉차트조회")

            #     colName = ['종목코드', '현재가', '거래량', '거래대금', '일자', '시가', '고가', '저가',
            #                '수정주가구분', '수정비율', '대업종구분', '소업종구분', '종목정보', '수정주가이벤트', '전일종가']

            #     data = DataFrame(data, columns=colName)

            #     print(type(data))
            #     print(data.head(5))
            """ commGetData
            cnt = self.getRepeatCnt(trCode, requestName)

            for i in range(cnt):
                date = self.commGetData(trCode, "", requestName, i, "일자")
                open = self.commGetData(trCode, "", requestName, i, "시가")
                high = self.commGetData(trCode, "", requestName, i, "고가")
                low = self.commGetData(trCode, "", requestName, i, "저가")
                close = self.commGetData(trCode, "", requestName, i, "현재가")
                print(date, ": ", open, ' ', high, ' ', low, ' ', close)
            """

        elif requestName == "예수금상세현황요청":
            deposit = self.commGetData(trCode, "", requestName, 0, "d+2추정예수금")
            deposit = self.changeFormat(deposit)
            self.opw00001Data = deposit

        elif requestName == "주식기본정보요청":
            code = self.commGetData(trCode, "", requestName, 0, "종목코드")
            high = self.commGetData(trCode, "", requestName, 0, "고가")
            low = self.commGetData(trCode, "", requestName, 0, "저가")

            self.opt10001Data["code"].append(code)
            self.opt10001Data["high"].append(high)
            self.opt10001Data["low"].append(low)

            print("{} {} {}".format(code, high, low))

        #opt10081
        elif requestName == "주식일봉차트조회요청":
            data_cnt = self.getRepeatCnt(trCode, requestName)

            for i in range(data_cnt):
                date = self.commGetData(trCode, "", requestName, i, "일자")
                open = self.commGetData(trCode, "", requestName, i, "시가")
                high = self.commGetData(trCode, "", requestName, i, "고가")
                low = self.commGetData(trCode, "", requestName, i, "저가")
                close = self.commGetData(trCode, "", requestName, i, "현재가")
                volume = self.commGetData(trCode, "", requestName, i, "거래량")

                self.ohlcv['date'].append(date)
                self.ohlcv['open'].append(int(open))
                self.ohlcv['high'].append(int(high))
                self.ohlcv['low'].append(int(low))
                self.ohlcv['close'].append(int(close))
                self.ohlcv['volume'].append(int(volume))

        elif requestName == "계좌평가잔고내역요청":
            # 계좌 평가 정보
            accountEvaluation = []
            keyList = ["총매입금액", "총평가금액", "총평가손익금액", "총수익률(%)", "추정예탁자산"]

            for key in keyList:
                value = self.commGetData(trCode, "", requestName, 0, key)

                if key.startswith("총수익률"):
                    value = self.changeFormat(value, 1)
                else:
                    value = self.changeFormat(value)

                accountEvaluation.append(value)

            self.opw00018Data['accountEvaluation'] = accountEvaluation

            # 보유 종목 정보
            cnt = self.getRepeatCnt(trCode, requestName)
            keyList = ["종목명", "보유수량", "매입가", "현재가", "평가손익", "수익률(%)", "종목번호"]

            for i in range(cnt):
                stock = []

                for key in keyList:
                    value = self.commGetData(trCode, "", requestName, i, key)

                    if key.startswith("수익률"):
                        value = self.changeFormat(value, 2)
                    elif key == "종목번호":
                        value = value[1:]

                    elif key != "종목명":
                        value = self.changeFormat(value)

                    stock.append(value)

                self.opw00018Data['stocks'].append(stock)

        try:
            self.requestLoop.exit()
        except AttributeError:
            pass

    def receiveRealData(self, code, realType, realData):
        print("receiveRealData executed")
        """
        실시간 데이터 수신 이벤트

        실시간 데이터를 수신할 때 마다 호출되며,
        setRealReg() 메서드로 등록한 실시간 데이터도 이 이벤트 메서드에 전달됩니다.
        getCommRealData() 메서드를 이용해서 실시간 데이터를 얻을 수 있습니다.

        :param code: string - 종목코드
        :param realType: string - 실시간 타입(KOA의 실시간 목록 참조)
        :param realData: string - 실시간 데이터 전문
        """

        try:
            self.log.debug("[receiveRealData]")
            self.log.debug("({})".format(realType))

            if realType not in RealType.REALTYPE:
                return

            data = []

            if code != "":
                data.append(code)
                codeOrNot = code
            else:
                codeOrNot = realType

            for fid in sorted(RealType.REALTYPE[realType].keys()):
                value = self.getCommRealData(codeOrNot, fid)
                data.append(value)
            print(data)  # 권민
            # TODO: DB에 저장
            self.log.debug(data)

        except Exception as e:
            self.log.error('{}'.format(e))

    def receiveChejanData(self, gubun, itemCnt, fidList):
        """
        주문 접수/확인 수신시 이벤트

        주문요청후 주문접수, 체결통보, 잔고통보를 수신할 때 마다 호출됩니다.

        :param gubun: string - 체결구분('0': 주문접수/주문체결, '1': 잔고통보, '3': 특이신호)
        :param itemCnt: int - fid의 갯수
        :param fidList: string - fidList 구분은 ;(세미콜론) 이다.
        """

        fids = fidList.split(';')
        print("[receiveChejanData]")
        print("gubun: ", gubun, "itemCnt: ", itemCnt, "fidList: ", fidList)
        print("========================================")
        print("[ 구분: ",
              self.getChejanData(913) if '913' in fids else '잔고통보', "]")
        for fid in fids:
            print(
                FidList.CHEJAN[int(fid)]
                if int(fid) in FidList.CHEJAN else fid, ": ",
                self.getChejanData(int(fid)))
        print("========================================")

    ###############################################################
    # 메서드 정의: 로그인 관련 메서드                                    #
    ###############################################################

    def commConnect(self):
        """
        로그인을 시도합니다.

        수동 로그인일 경우, 로그인창을 출력해서 로그인을 시도.
        자동 로그인일 경우, 로그인창 출력없이 로그인 시도.
        """

        self.dynamicCall("CommConnect()")
        self.loginLoop = QEventLoop()
        self.loginLoop.exec_()

    def getConnectState(self):
        """
        현재 접속상태를 반환합니다.

        반환되는 접속상태는 아래와 같습니다.
        0: 미연결, 1: 연결

        :return: int
        """

        state = self.dynamicCall("GetConnectState()")
        return state

    def getLoginInfo(self, tag, isConnectState=False):
        """
        사용자의 tag에 해당하는 정보를 반환한다.

        tag에 올 수 있는 값은 아래와 같다.
        ACCOUNT_CNT: 전체 계좌의 개수를 반환한다.
        ACCNO: 전체 계좌 목록을 반환한다. 계좌별 구분은 ;(세미콜론) 이다.
        USER_ID: 사용자 ID를 반환한다.
        USER_NAME: 사용자명을 반환한다.
        GetServerGubun: 접속서버 구분을 반환합니다.("1": 모의투자, 그외(빈 문자열포함): 실서버)

        :param tag: string
        :param isConnectState: bool - 접속상태을 확인할 필요가 없는 경우 True로 설정.
        :return: string
        """

        if not isConnectState:
            if not self.getConnectState():
                raise KiwoomConnectError()

        if not isinstance(tag, str):
            raise ParameterTypeError()

        if tag not in [
                'ACCOUNT_CNT', 'ACCNO', 'USER_ID', 'USER_NAME',
                'GetServerGubun'
        ]:
            raise ParameterValueError()

        if tag == "GetServerGubun":
            info = self.getServerGubun()
        else:
            cmd = 'GetLoginInfo("%s")' % tag
            info = self.dynamicCall(cmd)

        return info

    def getServerGubun(self):
        """
        서버구분 정보를 반환한다.
        리턴값이 "1"이면 모의투자 서버이고, 그 외에는 실서버(빈 문자열포함).

        :return: string
        """

        ret = self.dynamicCall("KOA_Functions(QString, QString)",
                               "GetServerGubun", "")
        return ret

    #################################################################
    # 메서드 정의: 조회 관련 메서드                                        #
    # 시세조회, 관심종목 조회, 조건검색 등 이들의 합산 조회 횟수가 1초에 5회까지 허용 #
    #################################################################

    def setInputValue(self, key, value):
        """
        TR 전송에 필요한 값을 설정한다.

        :param key: string - TR에 명시된 input 이름
        :param value: string - key에 해당하는 값
        """

        if not (isinstance(key, str) and isinstance(value, str)):
            raise ParameterTypeError()

        self.dynamicCall("SetInputValue(QString, QString)", key, value)

    # @SyncRequestDecorator.kiwoom_sync_request
    def commRqData(self, requestName, trCode, inquiry, screenNo):
        """
        키움서버에 TR 요청을 한다.

        조회요청메서드이며 빈번하게 조회요청시, 시세과부하 에러값 -200이 리턴된다.

        :param requestName: string - TR 요청명(사용자 정의)
        :param trCode: string
        :param inquiry: int - 조회(0: 조회, 2: 남은 데이터 이어서 요청)
        :param screenNo: string - 화면번호(4자리)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(requestName, str) and isinstance(trCode, str)
                and isinstance(inquiry, int) and isinstance(screenNo, str)):

            raise ParameterTypeError()

        returnCode = self.dynamicCall(
            "CommRqData(QString, QString, int, QString)", requestName, trCode,
            inquiry, screenNo)

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("commRqData(): " +
                                        ReturnCode.CAUSE[returnCode])

        # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다.
        self.requestLoop = QEventLoop()
        self.requestLoop.exec_()

    # @SyncRequestDecorator.kiwoom_sync_callback
    def commGetData(self, trCode, realType, requestName, index, key):
        """
        데이터 획득 메서드

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 조회데이터를 얻어오는 메서드입니다.
        getCommData() 메서드로 위임.

        :param trCode: string
        :param realType: string - TR 요청시 ""(빈문자)로 처리
        :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName)
        :param index: int
        :param key: string
        :return: string
        """

        return self.getCommData(trCode, requestName, index, key)

    def getCommData(self, trCode, requestName, index, key):
        """
        데이터 획득 메서드

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 조회데이터를 얻어오는 메서드입니다.

        :param trCode: string
        :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName)
        :param index: int
        :param key: string - 수신 데이터에서 얻고자 하는 값의 키(출력항목이름)
        :return: string
        """

        if not (isinstance(trCode, str) and isinstance(requestName, str)
                and isinstance(index, int) and isinstance(key, str)):
            raise ParameterTypeError()

        data = self.dynamicCall("GetCommData(QString, QString, int, QString)",
                                trCode, requestName, index, key)
        return data.strip()

    def getRepeatCnt(self, trCode, requestName):
        """
        서버로 부터 전달받은 데이터의 갯수를 리턴합니다.(멀티데이터의 갯수)

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        키움 OpenApi+에서는 데이터를 싱글데이터와 멀티데이터로 구분합니다.
        싱글데이터란, 서버로 부터 전달받은 데이터 내에서, 중복되는 키(항목이름)가 하나도 없을 경우.
        예를들면, 데이터가 '종목코드', '종목명', '상장일', '상장주식수' 처럼 키(항목이름)가 중복되지 않는 경우를 말합니다.
        반면 멀티데이터란, 서버로 부터 전달받은 데이터 내에서, 일정 간격으로 키(항목이름)가 반복될 경우를 말합니다.
        예를들면, 10일간의 일봉데이터를 요청할 경우 '종목코드', '일자', '시가', '고가', '저가' 이러한 항목이 10번 반복되는 경우입니다.
        이러한 멀티데이터의 경우 반복 횟수(=데이터의 갯수)만큼, 루프를 돌면서 처리하기 위해 이 메서드를 이용하여 멀티데이터의 갯수를 얻을 수 있습니다.

        :param trCode: string
        :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName)
        :return: int
        """

        if not (isinstance(trCode, str) and isinstance(requestName, str)):
            raise ParameterTypeError()

        count = self.dynamicCall("GetRepeatCnt(QString, QString)", trCode,
                                 requestName)
        return count

    def getCommDataEx(self, trCode, multiDataName):
        """
        멀티데이터 획득 메서드

        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        :param trCode: string
        :param multiDataName: string - KOA에 명시된 멀티데이터명
        :return: list - 중첩리스트
        """

        if not (isinstance(trCode, str) and isinstance(multiDataName, str)):
            raise ParameterTypeError()

        data = self.dynamicCall("GetCommDataEx(QString, QString)", trCode,
                                multiDataName)
        return data

    def commKwRqData(self,
                     codes,
                     inquiry,
                     codeCount,
                     requestName,
                     screenNo,
                     typeFlag=0):
        """
        복수종목조회 메서드(관심종목조회 메서드라고도 함).

        이 메서드는 setInputValue() 메서드를 이용하여, 사전에 필요한 값을 지정하지 않는다.
        단지, 메서드의 매개변수에서 직접 종목코드를 지정하여 호출하며,
        데이터 수신은 receiveTrData() 이벤트에서 아래 명시한 항목들을 1회 수신하며,
        이후 receiveRealData() 이벤트를 통해 실시간 데이터를 얻을 수 있다.

        복수종목조회 TR 코드는 OPTKWFID 이며, 요청 성공시 아래 항목들의 정보를 얻을 수 있다.

        종목코드, 종목명, 현재가, 기준가, 전일대비, 전일대비기호, 등락율, 거래량, 거래대금,
        체결량, 체결강도, 전일거래량대비, 매도호가, 매수호가, 매도1~5차호가, 매수1~5차호가,
        상한가, 하한가, 시가, 고가, 저가, 종가, 체결시간, 예상체결가, 예상체결량, 자본금,
        액면가, 시가총액, 주식수, 호가시간, 일자, 우선매도잔량, 우선매수잔량,우선매도건수,
        우선매수건수, 총매도잔량, 총매수잔량, 총매도건수, 총매수건수, 패리티, 기어링, 손익분기,
        잔본지지, ELW행사가, 전환비율, ELW만기일, 미결제약정, 미결제전일대비, 이론가,
        내재변동성, 델타, 감마, 쎄타, 베가, 로

        :param codes: string - 한번에 100종목까지 조회가능하며 종목코드사이에 세미콜론(;)으로 구분.
        :param inquiry: int - api 문서는 bool 타입이지만, int로 처리(0: 조회, 1: 남은 데이터 이어서 조회)
        :param codeCount: int - codes에 지정한 종목의 갯수.
        :param requestName: string
        :param screenNo: string
        :param typeFlag: int - 주식과 선물옵션 구분(0: 주식, 3: 선물옵션), 주의: 매개변수의 위치를 맨 뒤로 이동함.
        :return: list - 중첩 리스트 [[종목코드, 종목명 ... 종목 정보], [종목코드, 종목명 ... 종목 정보]]
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(codes, str) and isinstance(inquiry, int) and
                isinstance(codeCount, int) and isinstance(requestName, str)
                and isinstance(screenNo, str) and isinstance(typeFlag, int)):

            raise ParameterTypeError()

        returnCode = self.dynamicCall(
            "CommKwRqData(QString, QBoolean, int, int, QString, QString)",
            codes, inquiry, codeCount, typeFlag, requestName, screenNo)

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("commKwRqData(): " +
                                        ReturnCode.CAUSE[returnCode])

        # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다.
        self.requestLoop = QEventLoop()
        self.requestLoop.exec_()

    ###############################################################
    # 메서드 정의: 실시간 데이터 처리 관련 메서드                           #
    ###############################################################

    def disconnectRealData(self, screenNo):
        """
        해당 화면번호로 설정한 모든 실시간 데이터 요청을 제거합니다.

        화면을 종료할 때 반드시 이 메서드를 호출해야 합니다.

        :param screenNo: string
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not isinstance(screenNo, str):
            raise ParameterTypeError()

        self.dynamicCall("DisconnectRealData(QString)", screenNo)

    def getCommRealData(self, code, fid):
        print("getCommRealData executed")
        """
        실시간 데이터 획득 메서드

        이 메서드는 반드시 receiveRealData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        :param code: string - 종목코드
        :param fid: - 실시간 타입에 포함된 fid
        :return: string - fid에 해당하는 데이터
        """

        if not (isinstance(code, str) and isinstance(fid, int)):
            raise ParameterTypeError()

        value = self.dynamicCall("GetCommRealData(QString, int)", code, fid)

        return value

    def setRealReg(self, screenNo, codes, fids, realRegType):
        """
        실시간 데이터 요청 메서드

        종목코드와 fid 리스트를 이용해서 실시간 데이터를 요청하는 메서드입니다.
        한번에 등록 가능한 종목과 fid 갯수는 100종목, 100개의 fid 입니다.
        실시간등록타입을 0으로 설정하면, 첫 실시간 데이터 요청을 의미하며
        실시간등록타입을 1로 설정하면, 추가등록을 의미합니다.

        실시간 데이터는 실시간 타입 단위로 receiveRealData() 이벤트로 전달되기 때문에,
        이 메서드에서 지정하지 않은 fid 일지라도, 실시간 타입에 포함되어 있다면, 데이터 수신이 가능하다.

        :param screenNo: string
        :param codes: string - 종목코드 리스트(종목코드;종목코드;...)
        :param fids: string - fid 리스트(fid;fid;...)
        :param realRegType: string - 실시간등록타입(0: 첫 등록, 1: 추가 등록)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screenNo, str) and isinstance(codes, str)
                and isinstance(fids, str) and isinstance(realRegType, str)):
            raise ParameterTypeError()

        self.dynamicCall("SetRealReg(QString, QString, QString, QString)",
                         screenNo, codes, fids, realRegType)

    def setRealRemove(self, screenNo, code):
        """
        실시간 데이터 중지 메서드

        setRealReg() 메서드로 등록한 종목만, 이 메서드를 통해 실시간 데이터 받기를 중지 시킬 수 있습니다.

        :param screenNo: string - 화면번호 또는 ALL 키워드 사용가능
        :param code: string - 종목코드 또는 ALL 키워드 사용가능
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screenNo, str) and isinstance(code, str)):
            raise ParameterTypeError()

        self.dynamicCall("SetRealRemove(QString, QString)", screenNo, code)

    ###############################################################
    # 메서드 정의: 조건검색 관련 메서드와 이벤트                            #
    ###############################################################

    @SyncRequestDecorator.kiwoom_sync_callback
    def receiveConditionVer(self, receive, msg):
        """
        getConditionLoad() 메서드의 조건식 목록 요청에 대한 응답 이벤트

        :param receive: int - 응답결과(1: 성공, 나머지 실패)
        :param msg: string - 메세지
        """
        print("receiveConditionVer excecuted")
        try:
            if not receive:
                return

            self.condition = self.getConditionNameList()
            print("조건식 개수: ", len(self.condition))

            for key in self.condition.keys():
                print("조건식: ", key, ": ", self.condition[key])
                print("key type: ", type(key))

        except Exception as e:
            print(e)

        finally:
            self.conditionLoop.exit()

    @SyncRequestDecorator.kiwoom_sync_callback
    def receiveTrCondition(self, screenNo, codes, conditionName,
                           conditionIndex, inquiry):
        print("receiveTrCondition executed")
        """
        (1회성, 실시간) 종목 조건검색 요청시 발생되는 이벤트

        :param screenNo: string
        :param codes: string - 종목코드 목록(각 종목은 세미콜론으로 구분됨)
        :param conditionName: string - 조건식 이름
        :param conditionIndex: int - 조건식 인덱스
        :param inquiry: int - 조회구분(0: 남은데이터 없음, 2: 남은데이터 있음)
        """

        print("[receiveTrCondition]")
        try:
            if codes == "":
                return

            codeList = codes.split(';')
            del codeList[-1]

            print(codeList)
            print("종목개수: ", len(codeList))
            self.conditionCodeList = codeList

        finally:
            self.conditionLoop.exit()

    def receiveRealCondition(self, code, event, conditionName, conditionIndex):
        print("receiveRealCondition executed")
        """
        실시간 종목 조건검색 요청시 발생되는 이벤트

        :param code: string - 종목코드
        :param event: string - 이벤트종류("I": 종목편입, "D": 종목이탈)
        :param conditionName: string - 조건식 이름
        :param conditionIndex: string - 조건식 인덱스(여기서만 인덱스가 string 타입으로 전달됨)
        """

        print("[receiveRealCondition]")
        print("종목코드: ", code)
        print("이벤트: ", "종목편입" if event == "I" else "종목이탈")
        # if code not in self.conditionCodeList:
        #     self.conditionCodeList.append(code)
        # # print(self.conditionCodeList)
        # # if self.conditionbuy == True and event == "I" and time.time() - self.order_time > 0.3 :
        # self.sendOrder("자동매수주문", "0102", account_no, 1, code, 1, 0, "03", "")
        #     self.order_time = time.time()
        #     print("condition send buy done")

    def getConditionLoad(self):
        print("getConditionLoad executed")
        """ 조건식 목록 요청 메서드 """

        if not self.getConnectState():
            raise KiwoomConnectError()

        isLoad = self.dynamicCall("GetConditionLoad()")
        # 요청 실패시
        if not isLoad:
            raise KiwoomProcessingError("getConditionLoad(): 조건식 요청 실패")

        # receiveConditionVer() 이벤트 메서드에서 루프 종료
        self.conditionLoop = QEventLoop()
        self.conditionLoop.exec_()

    def getConditionNameList(self):
        print("getConditionNameList executed")
        """
        조건식 획득 메서드

        조건식을 딕셔너리 형태로 반환합니다.
        이 메서드는 반드시 receiveConditionVer() 이벤트 메서드안에서 사용해야 합니다.

        :return: dict - {인덱스:조건명, 인덱스:조건명, ...}
        """

        data = self.dynamicCall("GetConditionNameList()")

        if data == "":
            raise KiwoomProcessingError(
                "getConditionNameList(): 사용자 조건식이 없습니다.")

        conditionList = data.split(';')
        del conditionList[-1]

        conditionDictionary = {}

        for condition in conditionList:
            key, value = condition.split('^')
            conditionDictionary[int(key)] = value

        return conditionDictionary

    def sendCondition(self, screenNo, conditionName, conditionIndex,
                      isRealTime):
        print("sendCondition executed")
        """
        종목 조건검색 요청 메서드

        이 메서드로 얻고자 하는 것은 해당 조건에 맞는 종목코드이다.
        해당 종목에 대한 상세정보는 setRealReg() 메서드로 요청할 수 있다.
        요청이 실패하는 경우는, 해당 조건식이 없거나, 조건명과 인덱스가 맞지 않거나, 조회 횟수를 초과하는 경우 발생한다.

        조건검색에 대한 결과는
        1회성 조회의 경우, receiveTrCondition() 이벤트로 결과값이 전달되며
        실시간 조회의 경우, receiveTrCondition()과 receiveRealCondition() 이벤트로 결과값이 전달된다.

        :param screenNo: string
        :param conditionName: string - 조건식 이름
        :param conditionIndex: int - 조건식 인덱스
        :param isRealTime: int - 조건검색 조회구분(0: 1회성 조회, 1: 실시간 조회)
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screenNo, str) and isinstance(conditionName, str)
                and isinstance(conditionIndex, int)
                and isinstance(isRealTime, int)):
            raise ParameterTypeError()

        isRequest = self.dynamicCall(
            "SendCondition(QString, QString, int, int", screenNo,
            conditionName, conditionIndex, isRealTime)

        # if not isRequest:
        #     raise KiwoomProcessingError("sendCondition(): 조건검색 요청 실패")

        # receiveTrCondition() 이벤트 메서드에서 루프 종료
        self.conditionLoop = QEventLoop()
        self.conditionLoop.exec_()

    def sendConditionStop(self, screenNo, conditionName, conditionIndex):

        print("sendConditionStop executed")
        """ 종목 조건검색 중지 메서드 """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(screenNo, str) and isinstance(conditionName, str)
                and isinstance(conditionIndex, int)):
            raise ParameterTypeError()

        self.dynamicCall("SendConditionStop(QString, QString, int)", screenNo,
                         conditionName, conditionIndex)

    ###############################################################
    # 메서드 정의: 주문과 잔고처리 관련 메서드                              #
    # 1초에 5회까지 주문 허용                                          #
    ###############################################################

    @SyncRequestDecorator.kiwoom_sync_request
    def sendOrder(self, requestName, screenNo, accountNo, orderType, code, qty,
                  price, hogaType, originOrderNo):
        """
        주식 주문 메서드

        sendOrder() 메소드 실행시,
        OnReceiveMsg, OnReceiveTrData, OnReceiveChejanData 이벤트가 발생한다.
        이 중, 주문에 대한 결과 데이터를 얻기 위해서는 OnReceiveChejanData 이벤트를 통해서 처리한다.
        OnReceiveTrData 이벤트를 통해서는 주문번호를 얻을 수 있는데, 주문후 이 이벤트에서 주문번호가 ''공백으로 전달되면,
        주문접수 실패를 의미한다.

        :param requestName: string - 주문 요청명(사용자 정의)
        :param screenNo: string - 화면번호(4자리)
        :param accountNo: string - 계좌번호(10자리)
        :param orderType: int - 주문유형(1: 신규매수, 2: 신규매도, 3: 매수취소, 4: 매도취소, 5: 매수정정, 6: 매도정정)
        :param code: string - 종목코드
        :param qty: int - 주문수량
        :param price: int - 주문단가
        :param hogaType: string - 거래구분(00: 지정가, 03: 시장가, 05: 조건부지정가, 06: 최유리지정가, 그외에는 api 문서참조)
        :param originOrderNo: string - 원주문번호(신규주문에는 공백, 정정및 취소주문시 원주문번호르 입력합니다.)
        """
        print("kiwoom/sendorder executed")
        if not self.getConnectState():
            raise KiwoomConnectError()

        if not (isinstance(requestName, str) and isinstance(screenNo, str)
                and isinstance(accountNo, str) and isinstance(orderType, int)
                and isinstance(code, str) and isinstance(qty, int)
                and isinstance(price, int) and isinstance(hogaType, str)
                and isinstance(originOrderNo, str)):

            raise ParameterTypeError()

        returnCode = self.dynamicCall(
            "SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
            [
                requestName, screenNo, accountNo, orderType, code, qty, price,
                hogaType, originOrderNo
            ])

        if returnCode != ReturnCode.OP_ERR_NONE:
            raise KiwoomProcessingError("sendOrder(): " +
                                        ReturnCode.CAUSE[returnCode])

        print("kiwoom/sendorder finished")
        # receiveTrData() 에서 루프종료
        self.orderLoop = QEventLoop()
        self.orderLoop.exec_()

    def getChejanData(self, fid):
        """
        주문접수, 주문체결, 잔고정보를 얻어오는 메서드

        이 메서드는 receiveChejanData() 이벤트 메서드가 호출될 때 그 안에서 사용해야 합니다.

        :param fid: int
        :return: string
        """

        if not isinstance(fid, int):
            raise ParameterTypeError()

        cmd = 'GetChejanData("%s")' % fid
        data = self.dynamicCall(cmd)
        return data

    ###############################################################
    # 기타 메서드 정의                                                #
    ###############################################################

    def getCodeListByMarket(self, market):
        """
        시장 구분에 따른 종목코드의 목록을 List로 반환한다.

        market에 올 수 있는 값은 아래와 같다.
        '0': 장내, '3': ELW, '4': 뮤추얼펀드, '5': 신주인수권, '6': 리츠, '8': ETF, '9': 하이일드펀드, '10': 코스닥, '30': 제3시장

        :param market: string
        :return: List
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not isinstance(market, str):
            raise ParameterTypeError()

        if market not in ['0', '3', '4', '5', '6', '8', '9', '10', '30']:
            raise ParameterValueError()

        cmd = 'GetCodeListByMarket("%s")' % market
        codeList = self.dynamicCall(cmd)
        return codeList.split(';')

    def getCodeList(self, *market):
        """
        여러 시장의 종목코드를 List 형태로 반환하는 헬퍼 메서드.

        :param market: Tuple - 여러 개의 문자열을 매개변수로 받아 Tuple로 처리한다.
        :return: List
        """

        codeList = []

        for m in market:
            tmpList = self.getCodeListByMarket(m)
            codeList += tmpList

        return codeList

    def getMasterCodeName(self, code):
        """
        종목코드의 한글명을 반환한다.

        :param code: string - 종목코드
        :return: string - 종목코드의 한글명
        """

        if not self.getConnectState():
            raise KiwoomConnectError()

        if not isinstance(code, str):
            raise ParameterTypeError()

        cmd = 'GetMasterCodeName("%s")' % code
        name = self.dynamicCall(cmd)
        return name

    def changeFormat(self, data, percent=0):

        if percent == 0:
            d = int(data)
            formatData = '{:-,d}'.format(d)

        elif percent == 1:
            try:
                f = int(data) / 100
            except:
                f = float(data) / 100
            formatData = '{:-,.2f}'.format(f)

        elif percent == 2:
            f = float(data)
            formatData = '{:-,.2f}'.format(f)

        return formatData

    def opwDataReset(self):
        """ 잔고 및 보유종목 데이터 초기화 """
        self.opw00001Data = 0
        self.opw00018Data = {'accountEvaluation': [], 'stocks': []}
        self.opt10001Data = {'code': [], 'high': [], 'low': []}
Beispiel #42
0
class MatrixDialog(WindowModalDialog):
    def __init__(self, parent):
        super(MatrixDialog, self).__init__(parent)
        self.setWindowTitle(_("Trezor Matrix Recovery"))
        self.num = 9
        self.loop = QEventLoop()

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(MATRIX_RECOVERY))

        grid = QGridLayout()
        grid.setSpacing(0)
        self.char_buttons = []
        for y in range(3):
            for x in range(3):
                button = QPushButton('?')
                button.clicked.connect(
                    partial(self.process_key,
                            ord('1') + y * 3 + x))
                grid.addWidget(button, 3 - y, x)
                self.char_buttons.append(button)
        vbox.addLayout(grid)

        self.backspace_button = QPushButton("<=")
        self.backspace_button.clicked.connect(
            partial(self.process_key, Qt.Key_Backspace))
        self.cancel_button = QPushButton(_("Cancel"))
        self.cancel_button.clicked.connect(
            partial(self.process_key, Qt.Key_Escape))
        buttons = Buttons(self.backspace_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()

    def refresh(self):
        for y in range(3):
            self.char_buttons[3 * y + 1].setEnabled(self.num == 9)

    def is_valid(self, key):
        return key >= ord('1') and key <= ord('9')

    def process_key(self, key):
        self.data = None
        if key == Qt.Key_Backspace:
            self.data = '\010'
        elif key == Qt.Key_Escape:
            self.data = 'x'
        elif self.is_valid(key):
            self.char_buttons[key - ord('1')].setFocus()
            self.data = '%c' % key
        if self.data:
            self.loop.exit(0)

    def keyPressEvent(self, event):
        self.process_key(event.key())
        if not self.data:
            QDialog.keyPressEvent(self, event)

    def get_matrix(self, num):
        self.num = num
        self.refresh()
        self.loop.exec_()
Beispiel #43
0
 def exec_(self):
     if hasattr(self, 'qApp'):
         mainWindow = MainWindow()
         mainWindow.show()
         self.mainEventLoop = QEventLoop()
         self.mainEventLoop.exec_()
Beispiel #44
0
class QtReactor(posixbase.PosixReactorBase):
    # implements(IReactorFDSet)

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        self._timer.timeout.connect(self.iterate_qt)
        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp = QCoreApplication([])
            self._ownApp = True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp = False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)

    def _add(self, xer, primary, type):
        """
        Private method for adding a descriptor from the event loop.

        It takes care of adding it if  new or modifying it if already added
        for another state (read -> read/write for example).
        """
        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)

    def addReader(self, reader):
        """Add a FileDescriptor for notification of data available to read."""
        self._add(reader, self._reads, QSocketNotifier.Read)

    def addWriter(self, writer):
        """Add a FileDescriptor for notification of data available to write."""
        self._add(writer, self._writes, QSocketNotifier.Write)

    def _remove(self, xer, primary):
        """
        Private method for removing a descriptor from the event loop.

        It does the inverse job of _add, and also add a check in case of the fd
        has gone away.
        """
        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

    def removeReader(self, reader):
        """Remove a Selectable for notification of data available to read."""
        self._remove(reader, self._reads)

    def removeWriter(self, writer):
        """Remove a Selectable for notification of data available to write."""
        self._remove(writer, self._writes)

    def removeAll(self):
        """Remove all selectables, and return a list of them."""
        return self._removeAll(self._reads, self._writes)

    def getReaders(self):
        return self._reads.keys()

    def getWriters(self):
        return self._writes.keys()

    def callLater(self, howlong, *args, **kargs):
        rval = super(QtReactor, self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval

    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()

    def _iterate(self, delay=None, fromqt=False):
        """See twisted.internet.interfaces.IReactorCore.iterate."""
        self.runUntilCurrent()
        self.doIteration(delay, fromqt=fromqt)

    iterate = _iterate

    def iterate_qt(self, delay=None):
        self.iterate(delay=delay, fromqt=True)

    def doIteration(self, delay=None, fromqt=False):
        """This method is called by a Qt timer or by network activity on a file descriptor"""
        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        if delay is None:
            delay = 0
        delay = max(delay, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        timeout = self.timeout()
        if timeout is not None:
            self._timer.setInterval(timeout * 1000)
            self._timer.start()

    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()

    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
        if self.running:
            self.stop()
            self.runUntilCurrent()
 def write(self, text):
     self.textWritten.emit(str(text))
     loop = QEventLoop()
     QTimer.singleShot(1000, loop.quit)
     loop.exec_()
class InstallWizard(QtWidgets.QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    def __init__(self, config, app, plugins):
        BaseWizard.__init__(self, config)
        QtWidgets.QDialog.__init__(self, None)
        self.setWindowTitle(f'{PROJECT_NAME}  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        # Set for base base class
        self.plugins = plugins
        self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QtWidgets.QLabel()
        self.main_widget = QtWidgets.QWidget()
        self.back_button = QtWidgets.QPushButton(_("Back"), self)
        self.back_button.setText(_('Back') if self.can_go_back() else _('Cancel'))
        self.next_button = QtWidgets.QPushButton(_("Next"), self)
        self.next_button.setDefault(True)
        self.logo = QtWidgets.QLabel()
        self.please_wait = QtWidgets.QLabel(_("Please wait..."))
        self.please_wait.setAlignment(Qt.AlignCenter)
        self.icon_filename = None
        self.loop = QEventLoop()
        self.rejected.connect(lambda: self.loop.exit(0))
        self.back_button.clicked.connect(lambda: self.loop.exit(1))
        self.next_button.clicked.connect(lambda: self.loop.exit(2))
        outer_vbox = QtWidgets.QVBoxLayout(self)
        inner_vbox = QtWidgets.QVBoxLayout()
        inner_vbox.addWidget(self.title)
        inner_vbox.addWidget(self.main_widget)
        inner_vbox.addStretch(1)
        inner_vbox.addWidget(self.please_wait)
        inner_vbox.addStretch(1)
        scroll_widget = QtWidgets.QWidget()
        scroll_widget.setLayout(inner_vbox)
        scroll = QtWidgets.QScrollArea()
        scroll.setWidget(scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        icon_vbox = QtWidgets.QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QtWidgets.QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addWidget(scroll)
        hbox.setStretchFactor(scroll, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon(':icons/electrumABC.svg')
        self.show()
        self.raise_()

        # Track object lifecycle
        finalization_print_error(self)

    def select_storage(
        self, path, get_wallet_from_daemon
    ) -> Tuple[str, Optional[WalletStorage]]:
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(
            QtWidgets.QLabel(
                _('Create a new wallet or load an existing wallet from file.'))
        )
        vbox.addSpacing(20)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(QtWidgets.QLabel(_('Wallet name') + ':'))
        self.name_e = QtWidgets.QLineEdit()
        self.name_e.setToolTip(
            _("Enter a wallet name (new or existing), or the path to a wallet file.")
        )
        hbox.addWidget(self.name_e)
        button = QtWidgets.QPushButton(_('Load...'))
        button.setToolTip(_("Open a file selection dialog to load a wallet file."))
        hbox.addWidget(button)
        vbox.addLayout(hbox)
        vbox.addSpacing(20)

        self.msg_label = QtWidgets.QLabel('')
        vbox.addWidget(self.msg_label)
        hbox2 = QtWidgets.QHBoxLayout()
        self.pw_e = PasswordLineEdit('', self)
        self.pw_e.setFixedWidth(17 * char_width_in_lineedit())
        self.pw_label = QtWidgets.QLabel(_('Password') + ':')
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self.set_layout(vbox, title=_(f'{PROJECT_NAME} wallet'))

        self.temp_storage = WalletStorage(path, manual_upgrades=True)
        wallet_folder = os.path.dirname(self.temp_storage.path)

        def on_choose():
            path, __ = QtWidgets.QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder)
            if path:
                self.name_e.setText(path)

        def on_filename(filename):
            path = os.path.join(wallet_folder, filename)
            wallet_from_memory = get_wallet_from_daemon(path)
            try:
                if wallet_from_memory:
                    self.temp_storage = wallet_from_memory.storage
                else:
                    self.temp_storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except IOError:
                self.temp_storage = None
                self.next_button.setEnabled(False)
            if self.temp_storage:
                if not self.temp_storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                    pw = False
                elif not wallet_from_memory:
                    if self.temp_storage.is_encrypted_with_user_pw():
                        msg = _("This file is encrypted with a password.") + '\n' \
                              + _('Enter your password or choose another file.')
                        pw = True
                    elif self.temp_storage.is_encrypted_with_hw_device():
                        msg = _("This file is encrypted using a hardware device.") + '\n' \
                              + _("Press 'Next' to choose device to decrypt.")
                        pw = False
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                        pw = False
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
                    pw = False
            else:
                msg = _('Cannot read file')
                pw = False
            self.msg_label.setText(msg)
            if pw:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        button.clicked.connect(on_choose)
        self.name_e.textChanged.connect(on_filename)
        n = os.path.basename(self.temp_storage.path)
        self.name_e.setText(n)

        while True:
            if self.loop.exec_() != 2:  # 2 = next
                raise UserCancelled
            if self.temp_storage.file_exists() and not self.temp_storage.is_encrypted():
                break
            if not self.temp_storage.file_exists():
                break
            wallet_from_memory = get_wallet_from_daemon(self.temp_storage.path)
            if wallet_from_memory:
                raise WalletAlreadyOpenInMemory(wallet_from_memory)
            if self.temp_storage.file_exists() and self.temp_storage.is_encrypted():
                if self.temp_storage.is_encrypted_with_user_pw():
                    password = self.pw_e.text()
                    try:
                        self.temp_storage.decrypt(password)
                        break
                    except InvalidPassword as e:
                        QtWidgets.QMessageBox.information(None, _('Error'), str(e))
                        continue
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QtWidgets.QMessageBox.information(None, _('Error'), str(e))
                        raise UserCancelled()
                elif self.temp_storage.is_encrypted_with_hw_device():
                    try:
                        self.run(
                            'choose_hw_device',
                            HWD_SETUP_DECRYPT_WALLET,
                            storage=self.temp_storage
                        )
                    except InvalidPassword as e:
                        QtWidgets.QMessageBox.information(
                            None, _('Error'),
                            _('Failed to decrypt using this hardware device.') + '\n' +
                            _('If you use a passphrase, make sure it is correct.'))
                        self.reset_stack()
                        return self.select_storage(path, get_wallet_from_daemon)
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QtWidgets.QMessageBox.information(None, _('Error'), str(e))
                        raise UserCancelled()
                    if self.temp_storage.is_past_initial_decryption():
                        break
                    else:
                        raise UserCancelled()
                else:
                    raise Exception('Unexpected encryption version')
        return self.temp_storage.path, (self.temp_storage if self.temp_storage.file_exists() else None)

    def run_upgrades(self, storage):
        path = storage.path
        if storage.requires_split():
            self.hide()
            msg = _("The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n"
                    "Do you want to split your wallet into multiple files?").format(path)
            if not self.question(msg):
                return
            file_list = '\n'.join(storage.split_accounts())
            msg = _('Your accounts have been moved to') + ':\n' + file_list + '\n\n'+ _('Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            # raise now, to avoid having the old storage opened
            raise UserCancelled()

        action = storage.get_action()
        if action and storage.requires_upgrade():
            raise WalletFileException('Incomplete wallet files cannot be upgraded.')
        if action:
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(_("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
            self.data = storage.db.data

            self.run(action)
            for k, v in self.data.items():
                storage.put(k, v)
            storage.write()
            return

        if storage.requires_upgrade():
            self.upgrade_storage(storage)

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            traceback.print_exception(*exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(QIcon(filename).pixmap(60))
        return prior_filename

    def set_layout(self, layout, title=None, next_enabled=True):
        self.title.setText("<b>%s</b>"%title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QtWidgets.QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)

    def exec_layout(self, layout, title=None, raise_on_cancel=True,
                        next_enabled=True):
        self.set_layout(layout, title, next_enabled)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack from None
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid, allow_multi=False):
        slayout = KeysLayout(parent=self, title=message, is_valid=is_valid,
                             allow_multi=allow_multi)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_text()

    def seed_input(self, title, message, is_seed, options):
        slayout = SeedLayout(title=message, is_seed=is_seed, options=options, parent=self, editable=True)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    def bip38_prompt_for_pw(self, bip38_keys):
        ''' Reimplemented from basewizard superclass. Expected to return the pw
        dict or None. '''
        d = Bip38Importer(bip38_keys, parent=self.top_level_window())
        res = d.exec_()
        d.setParent(None)  # python GC quicker if this happens
        return d.decoded_keys  # dict will be empty if user cancelled

    @wizard_dialog
    def add_xpub_dialog(self, title, message, is_valid, run_next, allow_multi=False):
        return self.text_input(title, message, is_valid, allow_multi)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d"%index
        message = ' '.join([
            _('Please enter the master public key (xpub) of your cosigner.'),
            _('Enter their master private key (xprv) if you want to be able to sign for them.')
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, test):
        options = []
        if self.opt_ext:
            options.append('ext')
        if self.opt_bip39:
            options.append('bip39')
        title = _('Enter Seed')
        message = _('Please enter your seed phrase in order to restore your wallet.')
        return self.seed_input(title, message, test, options)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, test):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('If you lose your seed, your money will be permanently lost.'),
            _('To make sure that you have properly saved your seed, please retype it here.')
        ])
        seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
        return seed

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text, editable=True):
        title =  _("Your wallet generation seed is:")
        slayout = SeedLayout(seed=seed_text, title=title, msg=True, options=['ext'], editable=False)
        self.exec_layout(slayout)
        return slayout.is_ext

    def pw_layout(self, msg, kind, force_disable_encrypt_cb):
        playout = PasswordLayout(msg=msg, kind=kind, OK_button=self.next_button,
                                 force_disable_encrypt_cb=force_disable_encrypt_cb)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.new_password(), playout.encrypt_cb.isChecked()

    @wizard_dialog
    def request_password(self, run_next, force_disable_encrypt_cb=False):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password.  Note that this dialog screen
        cannot go back, and instead the user can only cancel."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW, force_disable_encrypt_cb)

    @wizard_dialog
    def request_storage_encryption(self, run_next):
        playout = PasswordLayoutForHW(MSG_HW_STORAGE_ENCRYPTION)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.encrypt_cb.isChecked()

    @staticmethod
    def _add_extra_button_to_layout(extra_button, layout):
        if (not isinstance(extra_button, (list, tuple))
                or not len(extra_button) == 2):
            return
        but_title, but_action = extra_button
        hbox = QtWidgets.QHBoxLayout()
        hbox.setContentsMargins(12,24,12,12)
        but = QtWidgets.QPushButton(but_title)
        hbox.addStretch(1)
        hbox.addWidget(but)
        layout.addLayout(hbox)
        but.clicked.connect(but_action)

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next, extra_button=None):
        self.confirm(message, title, extra_button=extra_button)

    def confirm(self, message, title, extra_button=None):
        label = WWLabel(message)

        textInteractionFlags = (Qt.LinksAccessibleByMouse
                                | Qt.TextSelectableByMouse
                                | Qt.TextSelectableByKeyboard
                                | Qt.LinksAccessibleByKeyboard)
        label.setTextInteractionFlags(textInteractionFlags)
        label.setOpenExternalLinks(True)

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(label)
        if extra_button:
            self._add_extra_button_to_layout(extra_button, vbox)
        self.exec_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self, **kwargs):
        self.accept_signal.emit()

    def run_task_without_blocking_gui(self, task, *, msg=None):
        assert self.gui_thread == threading.current_thread(), 'must be called from GUI thread'
        if msg is None:
            msg = _("Please wait...")

        exc: Optional[Exception] = None
        res = None
        def task_wrapper():
            nonlocal exc
            nonlocal res
            try:
                task()
            except Exception as e:
                exc = e
        self.waiting_dialog(task_wrapper, msg=msg)
        if exc is None:
            return res
        else:
            raise exc

    def waiting_dialog(self, task, msg, on_finished=None):
        label = WWLabel(msg)
        vbox = QtWidgets.QVBoxLayout()
        vbox.addSpacing(100)
        label.setMinimumWidth(300)
        label.setAlignment(Qt.AlignCenter)
        vbox.addWidget(label)
        self.set_layout(vbox, next_enabled=False)
        self.back_button.setEnabled(False)

        t = threading.Thread(target=task)
        t.start()
        while True:
            t.join(1.0/60)
            if t.is_alive():
                self.refresh_gui()
            else:
                break
        if on_finished:
            on_finished()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next, extra_button=None):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(clayout.layout())
        if extra_button:
            self._add_extra_button_to_layout(extra_button, vbox)
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QtWidgets.QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def line_dialog(self, run_next, title, message, default, test, warning=''):
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QtWidgets.QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))
        self.exec_layout(vbox, title, next_enabled=test(default))
        return ' '.join(line.text().split())

    @wizard_dialog
    def derivation_path_dialog(self, run_next, title, message, default, test, warning='', seed='', scannable=False):
        def on_derivation_scan(derivation_line, seed):
            derivation_scan_dialog = DerivationDialog(self, seed, DerivationPathScanner.DERIVATION_PATHS)
            destroyed_print_error(derivation_scan_dialog)
            selected_path = derivation_scan_dialog.get_selected_path()
            if selected_path:
                derivation_line.setText(selected_path)
            derivation_scan_dialog.deleteLater()

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QtWidgets.QLineEdit()
        line.setText(default)
        def f(text):
            self.next_button.setEnabled(test(text))
        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        if scannable:
            hbox = QtWidgets.QHBoxLayout()
            hbox.setContentsMargins(12,24,12,12)
            but = QtWidgets.QPushButton(_("Scan Derivation Paths..."))
            hbox.addStretch(1)
            hbox.addWidget(but)
            vbox.addLayout(hbox)
            but.clicked.connect(lambda: on_derivation_scan(line, seed))

        self.exec_layout(vbox, title, next_enabled=test(default))
        return ' '.join(line.text().split())

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QtWidgets.QVBoxLayout()
        layout = SeedLayout(xpub, title=msg, icon=False)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _(
            f"{PROJECT_NAME} communicates with remote servers to get "
            "information about your transactions and addresses. The "
            "servers all fulfil the same purpose only differing in "
            f"hardware. In most cases you simply want to let {PROJECT_NAME} "
            "pick one at random.  However if you prefer feel free to "
            "select a server manually.")
        choices = [_("Auto connect"), _("Select server manually")]
        title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(message, choices)
        self.back_button.setText(_('Cancel'))
        self.exec_layout(clayout.layout(), title)
        r = clayout.selected_index()
        network.auto_connect = (r == 0)
        self.config.set_key('auto_connect', network.auto_connect, True)
        if r == 1:
            nlayout = NetworkChoiceLayout(self, network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QtWidgets.QSlider(Qt.Horizontal, self)
        n_edit = QtWidgets.QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(1)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QtWidgets.QLabel()
        m_label = QtWidgets.QLabel()
        grid = QtWidgets.QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)
        def on_m(m):
            m_label.setText(_('Require %d signatures')%m)
            cw.set_m(m)
        def on_n(n):
            n_label.setText(_('From %d cosigners')%n)
            cw.set_n(n)
            m_edit.setMaximum(n)
        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        on_n(2)
        on_m(2)
        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(WWLabel(_("Choose the number of signatures needed to unlock funds in your wallet:")))
        vbox.addLayout(grid)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)

    linux_hw_wallet_support_dialog = None

    def on_hw_wallet_support(self):
        ''' Overrides base wizard's noop impl. '''
        if sys.platform.startswith("linux"):
            if self.linux_hw_wallet_support_dialog:
                self.linux_hw_wallet_support_dialog.raise_()
                return
            # NB: this should only be imported from Linux
            from . import udev_installer
            self.linux_hw_wallet_support_dialog = udev_installer.InstallHardwareWalletSupportDialog(self.top_level_window(), self.plugins)
            self.linux_hw_wallet_support_dialog.exec_()
            self.linux_hw_wallet_support_dialog.setParent(None)
            self.linux_hw_wallet_support_dialog = None
        else:
            self.show_error("Linux only facility. FIXME!")
Beispiel #47
0
class ConverterThread(QThread):
    """Thread converts markdown to HTML.
    """

    # This signal is emitted by the converter thread when a file has been
    # converted to HTML.
    htmlReady = pyqtSignal(
      # Path to the file which should be converted to / displayed as HTML.
      str,
      # HTML rendering of the file; empty if the HTML is provided in a file
      # specified by the URL below.
      str,
      # Error text resulting from the conversion process.
      str,
      # A reference to a file containing HTML rendering. Empty if the second
      # parameter above contains the HTML instead.
      QUrl)

    # This signal clears the context of the log window.
    logWindowClear = pyqtSignal()

    # This signal emits messages for the log window.
    logWindowText = pyqtSignal(
      # A string to append to the log window.
      str)

    _Task = collections.namedtuple("Task", ["filePath", "language", "text"])

    def __init__(self):
        QThread.__init__(self)
        self._queue = queue.Queue()
        self.start(QThread.LowPriority)
        self._ac = AsyncController('QThread', self)
        self._ac.defaultPriority = QThread.LowPriority
        self._SphinxInvocationCount = 1

    def process(self, filePath, language, text):
        """Convert data and emit result.
        """
        self._queue.put(self._Task(filePath, language, text))

    def stop_async(self):
        self._queue.put(None)

    def _getHtml(self, language, text, filePath):
        """Get HTML for document
        """
        if language == 'Markdown':
            return self._convertMarkdown(text), None, QUrl()
        # For ReST, use docutils only if Sphinx isn't available.
        elif language == 'Restructured Text' and not sphinxEnabledForFile(filePath):
            htmlUnicode, errString = self._convertReST(text)
            return htmlUnicode, errString, QUrl()
        elif filePath and sphinxEnabledForFile(filePath):  # Use Sphinx to generate the HTML if possible.
            return self._convertSphinx(filePath)
        elif filePath and canUseCodeChat(filePath):  # Otherwise, fall back to using CodeChat+docutils.
            return self._convertCodeChat(text, filePath)
        else:
            return 'No preview for this type of file', None, QUrl()

    def _convertMarkdown(self, text):
        """Convert Markdown to HTML
        """
        try:
            import markdown
        except ImportError:
            return 'Markdown preview requires <i>python-markdown</i> package<br/>' \
                   'Install it with your package manager or see ' \
                   '<a href="http://packages.python.org/Markdown/install.html">installation instructions</a>'

        extensions = ['fenced_code', 'nl2br', 'tables', 'enki.plugins.preview.mdx_math']

        # version 2.0 supports only extension names, not instances
        if markdown.version_info[0] > 2 or \
           (markdown.version_info[0] == 2 and markdown.version_info[1] > 0):

            class _StrikeThroughExtension(markdown.Extension):
                """http://achinghead.com/python-markdown-adding-insert-delete.html
                Class is placed here, because depends on imported markdown, and markdown import is lazy
                """
                DEL_RE = r'(~~)(.*?)~~'

                def extendMarkdown(self, md, md_globals):
                    # Create the del pattern
                    delTag = markdown.inlinepatterns.SimpleTagPattern(self.DEL_RE, 'del')
                    # Insert del pattern into markdown parser
                    md.inlinePatterns.add('del', delTag, '>not_strong')

            extensions.append(_StrikeThroughExtension())

        return markdown.markdown(text, extensions)

    def _convertReST(self, text):
        """Convert ReST
        """
        try:
            import docutils.core
            import docutils.writers.html4css1
        except ImportError:
            return 'Restructured Text preview requires the <i>python-docutils</i> package.<br/>' \
                   'Install it with your package manager or see ' \
                   '<a href="http://pypi.python.org/pypi/docutils"/>this page.</a>', None

        errStream = io.StringIO()
        settingsDict = {
          # Make sure to use Unicode everywhere.
          'output_encoding': 'unicode',
          'input_encoding' : 'unicode',
          # Don't stop processing, no matter what.
          'halt_level'     : 5,
          # Capture errors to a string and return it.
          'warning_stream' : errStream }
        # Frozen-specific settings.
        if isFrozen:
            settingsDict['template'] = (
              # The default docutils stylesheet and template uses a relative path,
              # which doesn't work when frozen ???. Under Unix when not frozen,
              # it produces:
              # ``IOError: [Errno 2] No such file or directory:
              # '/usr/lib/python2.7/dist-packages/docutils/writers/html4css1/template.txt'``.
              os.path.join(os.path.dirname(docutils.writers.html4css1.__file__),
                           docutils.writers.html4css1.Writer.default_template) )
            settingsDict['stylesheet_dirs'] = ['.',
                                               os.path.dirname(docutils.writers.html4css1.__file__)]
        htmlString = docutils.core.publish_string(text, writer_name='html',
                                                  settings_overrides=settingsDict)
        errString = errStream.getvalue()
        errStream.close()
        return htmlString, errString

    def _convertSphinx(self, filePath):
        # Run the builder.
        errString = self._runHtmlBuilder()

        # Look for the HTML output.
        #
        # Get an absolute path to the output path, which could be relative.
        outputPath = core.config()['Sphinx']['OutputPath']
        projectPath = core.config()['Sphinx']['ProjectPath']
        if not os.path.isabs(outputPath):
            outputPath = os.path.join(projectPath, outputPath)
        # Create an htmlPath as OutputPath + remainder of filePath.
        htmlPath = os.path.join(outputPath + filePath[len(projectPath):])
        html_file_suffix = '.html'
        try:
            with codecs.open(os.path.join(projectPath, 'sphinx-enki-info.txt')) as f:
                hfs = f.read()
                # If the file is empty, then html_file_suffix wasn't defined
                # or is None. In this case, use the default extension.
                # Otherwise, use the extension read from the file.
                if hfs:
                    html_file_suffix = hfs
        except:
            errString = "Warning: assuming .html extension. Use " + \
                "the conf.py template to set the extension.\n" + errString
            pass
        # First place to look: file.html. For example, look for foo.py
        # in foo.py.html.
        htmlFile = htmlPath + html_file_suffix
        # Second place to look: file without extension.html. For
        # example, look for foo.html for foo.rst.
        htmlFileAlter = os.path.splitext(htmlPath)[0] + html_file_suffix
        # Check that the output file produced by Sphinx is newer than
        # the source file it was built from.
        if os.path.exists(htmlFile):
            return _checkModificationTime(filePath, htmlFile, errString)
        elif os.path.exists(htmlFileAlter):
            return _checkModificationTime(filePath, htmlFileAlter, errString)
        else:
            return ('No preview for this type of file.<br>Expected ' +
                    htmlFile + " or " + htmlFileAlter, errString, QUrl())

    def _convertCodeChat(self, text, filePath):
        # Use StringIO to pass CodeChat compilation information back to
        # the UI.
        errStream = io.StringIO()
        try:
            htmlString = CodeToRest.code_to_html_string(text, errStream,
                                                        filename=filePath)
        except KeyError:
            # Although the file extension may be in the list of supported
            # extensions, CodeChat may not support the lexer chosen by Pygments.
            # For example, a ``.v`` file may be Verilog (supported by CodeChat)
            # or Coq (not supported). In this case, provide an error messsage
            errStream.write('Error: this file is not supported by CodeChat.')
            htmlString = ''
        errString = errStream.getvalue()
        errStream.close()
        return htmlString, errString, QUrl()

    def _runHtmlBuilder(self):
        # Build the commond line for Sphinx.
        if core.config()['Sphinx']['AdvancedMode']:
            htmlBuilderCommandLine = core.config()['Sphinx']['Cmdline']
            if sys.platform.startswith('linux'):
                # If Linux is used, then subprocess cannot take the whole
                # commandline as the name of an executable file. Module shlex
                # has to be used to parse commandline.
                htmlBuilderCommandLine = shlex.split(htmlBuilderCommandLine)
        else:
            # For available builder options, refer to: http://sphinx-doc.org/builders.html
            htmlBuilderCommandLine = [core.config()['Sphinx']['Executable'],
              # Place doctrees in the ``_build`` directory; by default, Sphinx
              # places this in _build/html/.doctrees.
              '-d', os.path.join('_build', 'doctrees'),
              # Source directory -- the current directory, since we'll chdir to
              # the project directory before executing this.
              '.',
              # Build directory
              core.config()['Sphinx']['OutputPath']]

        # Invoke it.
        try:
            # Clear the log at the beginning of a Sphinx build.
            self.logWindowClear.emit()

            cwd = core.config()['Sphinx']['ProjectPath']
            # If the command line is already a string (advanced mode), just print it.
            # Otherwise, it's a list that should be transformed to a string.
            if isinstance(htmlBuilderCommandLine, str):
                htmlBuilderCommandLineStr = htmlBuilderCommandLine
            else:
                htmlBuilderCommandLineStr = ' '.join(htmlBuilderCommandLine)
            self.logWindowText.emit('{} : {}\n\n'.format(cwd,
                                                         htmlBuilderCommandLineStr))

            # Run Sphinx, reading stdout in a separate thread.
            self._qe = QEventLoop()
            # Sphinx will output just a carriage return (0x0D) to simulate a
            # single line being updated by build status and the build
            # progresses. Without universal newline support here, we'll wait
            # until the build is complete (with a \n\r) to report any build
            # progress! So, enable universal newlines, so that each \r will be
            # treated as a separate line, providing immediate feedback on build
            # progress.
            popen = open_console_output(htmlBuilderCommandLine, cwd=cwd,
                                        universal_newlines=True)
            # Perform reads in an event loop. The loop is exit when all reads
            # have completed. We can't simply start the _stderr_read thread
            # here, because calls to self._qe_exit() will be ignored until
            # we're inside the event loop.
            QTimer.singleShot(0, lambda: self._popen_read(popen))
            self._qe.exec_()
        except OSError as ex:
            return (
                'Failed to execute HTML builder:\n'
                '{}\n'.format(str(ex)) +
                'Go to Settings -> Settings -> CodeChat to set HTML'
                ' builder configurations.')

        return self._stderr

    # Read from stdout (in this thread) and stderr (in another thread),
    # so that the user sees output as the build progresses, rather than only
    # producing output after the build is complete.
    def _popen_read(self, popen):
        # Read are blocking; we can't read from both stdout and stderr in the
        # same thread without possible buffer overflows. So, use this thread to
        # read from and immediately report progress from stdout. In another
        # thread, read all stderr and report that after the build finishes.
        self._ac.start(None, self._stderr_read, popen.stderr)

        # Read a line of stdout then report it to the user immediately.
        s = popen.stdout.readline()
        while s:
            self.logWindowText.emit(s.rstrip('\n'))
            s = popen.stdout.readline()
        self._SphinxInvocationCount += 1
        # I would expect the following code to do the same thing. It doesn't:
        # instead, it waits until Sphinx completes before returning anything.
        # ???
        #
        # .. code-block: python
        #    :linenos:
        #
        #    for s in popen.stdout:
        #        self.logWindowText.emit(s)

    # Runs in a separate thread to read stdout. It then exits the QEventLoop as
    # a way to signal that stderr reads have completed.
    def _stderr_read(self, stderr):
        self._stderr = stderr.read()
        self._qe.exit()

    def run(self):
        """Thread function
        """
        while True:  # exits with break
            # wait task
            task = self._queue.get()
            # take the last task
            while self._queue.qsize():
                task = self._queue.get()

            if task is None:  # None is a quit command
                self._ac.terminate()
                break

            # TODO: This is ugly. Should pass this exception back to the main
            # thread and re-raise it there, or use a QFuture like approach which
            # does this automaticlaly.
            try:
                html, errString, url = self._getHtml(task.language, task.text,
                                                     task.filePath)
            except Exception:
                traceback.print_exc()

            self.htmlReady.emit(task.filePath, html, errString, url)

        # Free resources.
        self._ac.terminate()
Beispiel #48
0
    def handleUnsupportedContent(self, reply, preset_filename=None, page_url=None):
        """ This is called when url content is a downloadable file. e.g- pdf,mp3,mp4 """
        if reply.rawHeaderList() == []:
            loop = QEventLoop()
            reply.metaDataChanged.connect(loop.quit)
            QTimer.singleShot(5000, loop.quit)
            loop.exec_()
        if reply.hasRawHeader(b'Location'):
            URL = QUrl.fromUserInput(str_(reply.rawHeader(b'Location')))
            reply.abort()
            reply = networkmanager.get(QNetworkRequest(URL))
            self.handleUnsupportedContent(reply, preset_filename)
            return
        for (title, header) in reply.rawHeaderPairs():
            print( str_(title) + "-> " + str_(header) )
        # copy url to clipboard
        QApplication.clipboard().setText(reply.url().toString())
        # Get filename and mimetype
        mimetype = None
        if reply.hasRawHeader(b'Content-Type'):
            mimetype = str_(reply.rawHeader(b'Content-Type')).split(';')[0] # eg - audio/mpeg; name=""
        content_name = str_(reply.rawHeader(b'Content-Disposition'))
        if preset_filename:
            filename = preset_filename
        else:
            filename = filenameFromHeader(content_name)
            if filename == '':
                filename = filenameFromUrl(reply.url().toString())
        filename = validateFileName(filename, mimetype)
        # Create downld Confirmation dialog
        dlDialog = DownloadDialog(self)
        dlDialog.filenameEdit.setText(filename)
        # Get filesize
        if reply.hasRawHeader(b'Content-Length'):
            filesize = reply.header(1)
            if filesize >= 1048576 :
                file_size = "{} M".format(round(float(filesize)/1048576, 2))
            elif 1023 < filesize < 1048576 :
                file_size = "{} k".format(round(float(filesize)/1024, 1))
            else:
                file_size = "{} B".format(filesize)
            dlDialog.labelFileSize.setText(file_size)
        # Get filetype and resume support info
        if mimetype:
            dlDialog.labelFileType.setText(mimetype)
        if reply.hasRawHeader(b'Accept-Ranges') or reply.hasRawHeader(b'Content-Range'):
            dlDialog.labelResume.setText("True")
        # Execute dialog and show confirmation
        if dlDialog.exec_()== QDialog.Accepted:
            filepath = dlDialog.folder + dlDialog.filenameEdit.text()
            url = reply.url().toString()
            if self.useexternaldownloader:
                download_externally(url, self.externaldownloader)
                reply.abort()
                reply.deleteLater()
                return

            global downloads_list_file
            newdownload = Download(networkmanager, page_url)
            newdownload.startDownload(reply, filepath)
            newdownload.datachanged.connect(self.dwnldsmodel.datachanged)
            self.downloads.insert(0, newdownload)
            imported_downloads = importDownloads(downloads_list_file)
            imported_downloads.insert(0, [filepath, url, str(newdownload.totalsize), newdownload.timestamp])
            exportDownloads(downloads_list_file, imported_downloads)
        else:
            reply.abort()
            reply.deleteLater()
Beispiel #49
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    def __init__(self, config, app, plugins, storage):
        BaseWizard.__init__(self, config, plugins, storage)
        QDialog.__init__(self, None)
        self.setWindowTitle('Electrum  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        # Set for base base class
        self.language_for_seed = config.get('language')
        self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(_("Back"), self)
        self.back_button.setText(
            _('Back') if self.can_go_back() else _('Cancel'))
        self.next_button = QPushButton(_("Next"), self)
        self.next_button.setDefault(True)
        self.logo = QLabel()
        self.please_wait = QLabel(_("Please wait..."))
        self.please_wait.setAlignment(Qt.AlignCenter)
        self.icon_filename = None
        self.loop = QEventLoop()
        self.rejected.connect(lambda: self.loop.exit(0))
        self.back_button.clicked.connect(lambda: self.loop.exit(1))
        self.next_button.clicked.connect(lambda: self.loop.exit(2))
        outer_vbox = QVBoxLayout(self)
        inner_vbox = QVBoxLayout()
        inner_vbox.addWidget(self.title)
        inner_vbox.addWidget(self.main_widget)
        inner_vbox.addStretch(1)
        inner_vbox.addWidget(self.please_wait)
        inner_vbox.addStretch(1)
        scroll_widget = QWidget()
        scroll_widget.setLayout(inner_vbox)
        scroll = QScrollArea()
        scroll.setWidget(scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        icon_vbox = QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addWidget(scroll)
        hbox.setStretchFactor(scroll, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon('electrum.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def select_storage(self, path, get_wallet_from_daemon):

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(_('Wallet') + ':'))
        self.name_e = QLineEdit()
        hbox.addWidget(self.name_e)
        button = QPushButton(_('Choose...'))
        hbox.addWidget(button)
        vbox.addLayout(hbox)

        self.msg_label = QLabel('')
        vbox.addWidget(self.msg_label)
        hbox2 = QHBoxLayout()
        self.pw_e = QLineEdit('', self)
        self.pw_e.setFixedWidth(150)
        self.pw_e.setEchoMode(2)
        self.pw_label = QLabel(_('Password') + ':')
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self.set_layout(vbox, title=_('Electrum wallet'))

        self.storage = WalletStorage(path, manual_upgrades=True)
        wallet_folder = os.path.dirname(self.storage.path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self,
                                                   "Select your wallet file",
                                                   wallet_folder)
            if path:
                self.name_e.setText(path)

        def on_filename(filename):
            path = os.path.join(wallet_folder, filename)
            wallet_from_memory = get_wallet_from_daemon(path)
            try:
                if wallet_from_memory:
                    self.storage = wallet_from_memory.storage
                else:
                    self.storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except BaseException:
                traceback.print_exc(file=sys.stderr)
                self.storage = None
                self.next_button.setEnabled(False)
            if self.storage:
                if not self.storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                    pw = False
                elif not wallet_from_memory:
                    if self.storage.is_encrypted_with_user_pw():
                        msg = _("This file is encrypted with a password.") + '\n' \
                              + _('Enter your password or choose another file.')
                        pw = True
                    elif self.storage.is_encrypted_with_hw_device():
                        msg = _("This file is encrypted using a hardware device.") + '\n' \
                              + _("Press 'Next' to choose device to decrypt.")
                        pw = False
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                        pw = False
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
                    pw = False
            else:
                msg = _('Cannot read file')
                pw = False
            self.msg_label.setText(msg)
            if pw:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        button.clicked.connect(on_choose)
        self.name_e.textChanged.connect(on_filename)
        n = os.path.basename(self.storage.path)
        self.name_e.setText(n)

        while True:
            if self.loop.exec_() != 2:  # 2 = next
                return
            if self.storage.file_exists() and not self.storage.is_encrypted():
                break
            if not self.storage.file_exists():
                break
            wallet_from_memory = get_wallet_from_daemon(self.storage.path)
            if wallet_from_memory:
                return wallet_from_memory
            if self.storage.file_exists() and self.storage.is_encrypted():
                if self.storage.is_encrypted_with_user_pw():
                    password = self.pw_e.text()
                    try:
                        self.storage.decrypt(password)
                        break
                    except InvalidPassword as e:
                        QMessageBox.information(None, _('Error'), str(e))
                        continue
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QMessageBox.information(None, _('Error'), str(e))
                        return
                elif self.storage.is_encrypted_with_hw_device():
                    try:
                        self.run('choose_hw_device', HWD_SETUP_DECRYPT_WALLET)
                    except InvalidPassword as e:
                        QMessageBox.information(
                            None, _('Error'),
                            _('Failed to decrypt using this hardware device.')
                            + '\n' +
                            _('If you use a passphrase, make sure it is correct.'
                              ))
                        self.reset_stack()
                        return self.run_and_get_wallet(get_wallet_from_daemon)
                    except BaseException as e:
                        traceback.print_exc(file=sys.stdout)
                        QMessageBox.information(None, _('Error'), str(e))
                        return
                    if self.storage.is_past_initial_decryption():
                        break
                    else:
                        return
                else:
                    raise Exception('Unexpected encryption version')
        return True

    def run_and_get_wallet(self):
        path = self.storage.path
        if self.storage.requires_split():
            self.hide()
            msg = _(
                "The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n"
                "Do you want to split your wallet into multiple files?"
            ).format(path)
            if not self.question(msg):
                return
            file_list = '\n'.join(self.storage.split_accounts())
            msg = _('Your accounts have been moved to'
                    ) + ':\n' + file_list + '\n\n' + _(
                        'Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            return

        action = self.storage.get_action()
        if action and action not in ('new', 'upgrade_storage'):
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(
                        _("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
        if action:
            # self.wallet is set in run
            self.run(action)
            return self.wallet

        self.wallet = Wallet(self.storage)
        return self.wallet

    def finished(self):
        """Called in hardware client wrapper, in order to close popups."""
        return

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            traceback.print_exception(*exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(
            QPixmap(icon_path(filename)).scaledToWidth(
                60, mode=Qt.SmoothTransformation))
        return prior_filename

    def set_layout(self, layout, title=None, next_enabled=True):
        self.title.setText("<b>%s</b>" % title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)

    def exec_layout(self,
                    layout,
                    title=None,
                    raise_on_cancel=True,
                    next_enabled=True):
        self.set_layout(layout, title, next_enabled)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack from None
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid, allow_multi=False):
        slayout = KeysLayout(parent=self,
                             header_layout=message,
                             is_valid=is_valid,
                             allow_multi=allow_multi)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_text()

    def seed_input(self, title, message, is_seed, options):
        slayout = SeedLayout(title=message,
                             is_seed=is_seed,
                             options=options,
                             parent=self)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    @wizard_dialog
    def add_xpub_dialog(self,
                        title,
                        message,
                        is_valid,
                        run_next,
                        allow_multi=False,
                        show_wif_help=False):
        header_layout = QHBoxLayout()
        label = WWLabel(message)
        label.setMinimumWidth(400)
        header_layout.addWidget(label)
        if show_wif_help:
            header_layout.addWidget(InfoButton(WIF_HELP_TEXT),
                                    alignment=Qt.AlignRight)
        return self.text_input(title, header_layout, is_valid, allow_multi)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d" % index
        message = ' '.join([
            _('Please enter the master public key (xpub) of your cosigner.'),
            _('Enter their master private key (xprv) if you want to be able to sign for them.'
              )
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, test):
        options = []
        if self.opt_ext:
            options.append('ext')
        if self.opt_bip39:
            options.append('bip39')
        title = _('Enter Seed')
        message = _(
            'Please enter your seed phrase in order to restore your wallet.')
        return self.seed_input(title, message, test, options)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, test):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('If you lose your seed, your money will be permanently lost.'),
            _('To make sure that you have properly saved your seed, please retype it here.'
              )
        ])
        seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
        return seed

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        title = _("Your wallet generation seed is:")
        slayout = SeedLayout(seed=seed_text,
                             title=title,
                             msg=True,
                             options=['ext'])
        self.exec_layout(slayout)
        return slayout.is_ext

    def pw_layout(self, msg, kind, force_disable_encrypt_cb):
        playout = PasswordLayout(
            msg=msg,
            kind=kind,
            OK_button=self.next_button,
            force_disable_encrypt_cb=force_disable_encrypt_cb)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.new_password(), playout.encrypt_cb.isChecked()

    @wizard_dialog
    def request_password(self, run_next, force_disable_encrypt_cb=False):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW,
                              force_disable_encrypt_cb)

    @wizard_dialog
    def request_storage_encryption(self, run_next):
        playout = PasswordLayoutForHW(MSG_HW_STORAGE_ENCRYPTION)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.encrypt_cb.isChecked()

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next):
        self.confirm(message, title)

    def confirm(self, message, title):
        label = WWLabel(message)
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        self.exec_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self):
        self.accept_signal.emit()

    def waiting_dialog(self, task, msg, on_finished=None):
        label = WWLabel(msg)
        vbox = QVBoxLayout()
        vbox.addSpacing(100)
        label.setMinimumWidth(300)
        label.setAlignment(Qt.AlignCenter)
        vbox.addWidget(label)
        self.set_layout(vbox, next_enabled=False)
        self.back_button.setEnabled(False)

        t = threading.Thread(target=task)
        t.start()
        while True:
            t.join(1.0 / 60)
            if t.is_alive():
                self.refresh_gui()
            else:
                break
        if on_finished:
            on_finished()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def choice_and_line_dialog(self,
                               title: str,
                               message1: str,
                               choices: List[Tuple[str, str, str]],
                               message2: str,
                               test_text: Callable[[str], int],
                               run_next,
                               default_choice_idx: int = 0) -> Tuple[str, str]:
        vbox = QVBoxLayout()

        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        c_default_text = [x[2] for x in choices]

        def on_choice_click(clayout):
            idx = clayout.selected_index()
            line.setText(c_default_text[idx])

        clayout = ChoicesLayout(message1,
                                c_titles,
                                on_choice_click,
                                checked_index=default_choice_idx)
        vbox.addLayout(clayout.layout())

        vbox.addSpacing(50)
        vbox.addWidget(WWLabel(message2))

        line = QLineEdit()

        def on_text_change(text):
            self.next_button.setEnabled(test_text(text))

        line.textEdited.connect(on_text_change)
        on_choice_click(clayout)  # set default text for "line"
        vbox.addWidget(line)

        self.exec_layout(vbox, title)
        choice = c_values[clayout.selected_index()]
        return str(line.text()), choice

    @wizard_dialog
    def line_dialog(self,
                    run_next,
                    title,
                    message,
                    default,
                    test,
                    warning='',
                    presets=(),
                    warn_issue4566=False):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)

        def f(text):
            self.next_button.setEnabled(test(text))
            if warn_issue4566:
                text_whitespace_normalised = ' '.join(text.split())
                warn_issue4566_label.setVisible(
                    text != text_whitespace_normalised)

        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        warn_issue4566_label = WWLabel(MSG_PASSPHRASE_WARN_ISSUE4566)
        warn_issue4566_label.setVisible(False)
        vbox.addWidget(warn_issue4566_label)

        for preset in presets:
            button = QPushButton(preset[0])
            button.clicked.connect(
                lambda __, text=preset[1]: line.setText(text))
            button.setMinimumWidth(150)
            hbox = QHBoxLayout()
            hbox.addWidget(button, alignment=Qt.AlignCenter)
            vbox.addLayout(hbox)

        self.exec_layout(vbox, title, next_enabled=test(default))
        return line.text()

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedLayout(xpub, title=msg, icon=False, for_seed_words=False)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _("Electrum communicates with remote servers to get "
                    "information about your transactions and addresses. The "
                    "servers all fulfill the same purpose only differing in "
                    "hardware. In most cases you simply want to let Electrum "
                    "pick one at random.  However if you prefer feel free to "
                    "select a server manually.")
        choices = [_("Auto connect"), _("Select server manually")]
        title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(message, choices)
        self.back_button.setText(_('Cancel'))
        self.exec_layout(clayout.layout(), title)
        r = clayout.selected_index()
        if r == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()
        else:
            network.auto_connect = True
            self.config.set_key('auto_connect', True, True)

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QLabel()
        m_label = QLabel()
        grid = QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)

        def on_m(m):
            m_label.setText(_('Require {0} signatures').format(m))
            cw.set_m(m)

        def on_n(n):
            n_label.setText(_('From {0} cosigners').format(n))
            cw.set_n(n)
            m_edit.setMaximum(n)

        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        on_n(2)
        on_m(2)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(
            WWLabel(
                _("Choose the number of signatures needed to unlock funds in your wallet:"
                  )))
        vbox.addLayout(grid)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)
Beispiel #50
0
 def run(self):
     log.info("starting beat ..")
     self.beat_timer.start(self.interval)
     loop = QEventLoop()
     loop.exec_()
Beispiel #51
0
        pixmap = self.movie.currentPixmap()
        self.setPixmap(pixmap)
        self.setMask(pixmap.mask())

# Put your initialization code here
def longInitialization(arg):
    time.sleep(arg)
    return 0
if __name__ == "__main__":
    import sys, time

    app = QApplication(sys.argv)

    # Create and display the splash screen
#   splash_pix = QPixmap('a.gif')
    splash = MySplashScreen("images/giphy2.gif", Qt.Qt.WindowStaysOnTopHint)
#   splash.setMask(splash_pix.mask())
    #splash.raise_()
    splash.show()
    app.processEvents()

    # this event loop is needed for dispatching of Qt events
    initLoop = QEventLoop()
    pool = Pool(processes=1)
    pool.apply_async(longInitialization, [2], callback=lambda exitCode: initLoop.exit(exitCode))
    initLoop.exec_()

    form = Form()
    form.show()
    splash.finish(form)
    app.exec_()
Beispiel #52
0
class Debugger(QObject, ComponentMixin):

    name = 'Debugger'

    preferences = Parameter.create(name='Preferences',
                                   children=[{
                                       'name': 'Reload CQ',
                                       'type': 'bool',
                                       'value': True
                                   }, {
                                       'name': 'Add script dir to path',
                                       'type': 'bool',
                                       'value': True
                                   }, {
                                       'name':
                                       'Change working dir to script dir',
                                       'type': 'bool',
                                       'value': True
                                   }])

    sigRendered = pyqtSignal(dict)
    sigLocals = pyqtSignal(dict)
    sigTraceback = pyqtSignal(object, str)

    sigFrameChanged = pyqtSignal(object)
    sigLineChanged = pyqtSignal(int)
    sigLocalsChanged = pyqtSignal(dict)
    sigCQChanged = pyqtSignal(dict, bool)
    sigDebugging = pyqtSignal(bool)

    def __init__(self, parent):

        super(Debugger, self).__init__(parent)
        ComponentMixin.__init__(self)

        self.inner_event_loop = QEventLoop(self)

        self._actions =  \
            {'Run' : [QAction(icon('run'),
                              'Render',
                               self,
                               shortcut='F5',
                               triggered=self.render),
                      QAction(icon('debug'),
                             'Debug',
                             self,
                             checkable=True,
                             shortcut='ctrl+F5',
                             triggered=self.debug),
                      QAction(icon('arrow-step-over'),
                             'Step',
                             self,
                             shortcut='ctrl+F10',
                             triggered=lambda: self.debug_cmd(DbgState.STEP)),
                      QAction(icon('arrow-step-in'),
                             'Step in',
                             self,
                             shortcut='ctrl+F11',
                             triggered=lambda: None),
                      QAction(icon('arrow-continue'),
                              'Continue',
                              self,
                              shortcut='ctrl+F12',
                              triggered=lambda: self.debug_cmd(DbgState.CONT))
                      ]}

    def get_current_script(self):

        return self.parent().components['editor'].get_text_with_eol()

    def get_breakpoints(self):

        return self.parent().components['editor'].get_breakpoints()

    def compile_code(self, cq_script):

        try:
            module = imp.new_module('temp')
            cq_code = compile(cq_script, '<string>', 'exec')
            return cq_code, module
        except Exception:
            self.sigTraceback.emit(sys.exc_info(), cq_script)
            return None, None

    def _exec(self, code, locals_dict, globals_dict):

        with ExitStack() as stack:
            p = Path(self.parent().components['editor'].filename).abspath(
            ).dirname()
            if self.preferences['Add script dir to path'] and p:
                sys.path.insert(0, p)
                stack.callback(sys.path.remove, p)
            if self.preferences['Change working dir to script dir'] and p:
                stack.enter_context(p)
            exec(code, locals_dict, globals_dict)

    def _inject_locals(self, module):

        cq_objects = {}

        def _show_object(obj, name=None, options={}):

            if name:
                cq_objects.update(
                    {name: SimpleNamespace(shape=obj, options=options)})
            else:
                cq_objects.update({
                    str(id(obj)):
                    SimpleNamespace(shape=obj, options=options)
                })

        def _debug(obj, name=None):

            _show_object(obj, name, options=dict(color='red', alpha=0.2))

        module.__dict__['show_object'] = _show_object
        module.__dict__['debug'] = _debug
        module.__dict__['log'] = lambda x: info(str(x))
        module.__dict__['cq'] = cq

        return cq_objects, set(module.__dict__) - {'cq'}

    def _cleanup_locals(self, module, injected_names):

        for name in injected_names:
            module.__dict__.pop(name)

    @pyqtSlot(bool)
    def render(self):

        if self.preferences['Reload CQ']:
            reload_cq()

        cq_script = self.get_current_script()
        cq_code, module = self.compile_code(cq_script)

        if cq_code is None: return

        cq_objects, injected_names = self._inject_locals(module)

        try:
            self._exec(cq_code, module.__dict__, module.__dict__)

            #remove the special methods
            self._cleanup_locals(module, injected_names)

            #collect all CQ objects if no explicit show_object was called
            if len(cq_objects) == 0:
                cq_objects = find_cq_objects(module.__dict__)
            self.sigRendered.emit(cq_objects)
            self.sigTraceback.emit(None, cq_script)
            self.sigLocals.emit(module.__dict__)
        except Exception:
            self.sigTraceback.emit(sys.exc_info(), cq_script)

    @pyqtSlot(bool)
    def debug(self, value):
        if value:
            self.sigDebugging.emit(True)
            self.state = DbgState.STEP

            self.script = self.get_current_script()
            code, module = self.compile_code(self.script)

            if code is None:
                self.sigDebugging.emit(False)
                self._actions['Run'][1].setChecked(False)
                return

            cq_objects, injected_names = self._inject_locals(module)

            self.breakpoints = [el[0] for el in self.get_breakpoints()]

            #clear possible traceback
            self.sigTraceback.emit(None, self.script)
            try:
                sys.settrace(self.trace_callback)
                exec(code, module.__dict__, module.__dict__)
            except Exception:
                self.sigTraceback.emit(sys.exc_info(), self.script)
            finally:
                sys.settrace(None)
                self.sigDebugging.emit(False)
                self._actions['Run'][1].setChecked(False)

                if len(cq_objects) == 0:
                    cq_objects = find_cq_objects(module.__dict__)
                self.sigRendered.emit(cq_objects)

                self._cleanup_locals(module, injected_names)
                self.sigLocals.emit(module.__dict__)
        else:
            sys.settrace(None)
            self.inner_event_loop.exit(0)

    def debug_cmd(self, state=DbgState.STEP):

        self.state = state
        self.inner_event_loop.exit(0)

    def trace_callback(self, frame, event, arg):

        filename = frame.f_code.co_filename

        if filename == DUMMY_FILE:
            self.trace_local(frame, event, arg)
            return self.trace_callback

        else:
            return None

    def trace_local(self, frame, event, arg):

        lineno = frame.f_lineno
        line = self.script.splitlines()[lineno - 1]
        f_id = id(frame)

        if event in (DbgEevent.LINE, DbgEevent.RETURN):
            if (self.state in (DbgState.STEP, DbgState.STEP_IN)) \
            or (lineno in self.breakpoints):
                self.sigLineChanged.emit(lineno)
                self.sigFrameChanged.emit(frame)
                self.sigLocalsChanged.emit(frame.f_locals)
                self.sigCQChanged.emit(find_cq_objects(frame.f_locals), True)

                self.inner_event_loop.exec_()

        elif event in (DbgEevent.RETURN):
            self.sigLocalsChanged.emit(frame.f_locals)

        elif event == DbgEevent.CALL:
            func_filename = frame.f_code.co_filename

            if self.state == DbgState.STEP_IN and func_filename == DUMMY_FILE:
                self.sigLineChanged.emit(lineno)
                self.sigFrameChanged.emit(frame)
                self.state = DbgState.STEP
    def request(self,
                url,
                method="GET",
                body=None,
                headers=None,
                redirections=DEFAULT_MAX_REDIRECTS,
                connection_type=None,
                blocking=True):
        """
        Make a network request by calling QgsNetworkAccessManager.
        redirections argument is ignored and is here only for httplib2 compatibility.
        """
        self.http_call_result.url = url
        self.msg_log(u'http_call request: {0}'.format(url))

        self.blocking_mode = blocking
        req = QNetworkRequest()
        # Avoid double quoting form QUrl
        url = urllib.parse.unquote(url)
        req.setUrl(QUrl(url))
        if headers is not None:
            # This fixes a weird error with compressed content not being correctly
            # inflated.
            # If you set the header on the QNetworkRequest you are basically telling
            # QNetworkAccessManager "I know what I'm doing, please don't do any content
            # encoding processing".
            # See: https://bugs.webkit.org/show_bug.cgi?id=63696#c1
            try:
                del headers['Accept-Encoding']
            except KeyError:
                pass
            for k, v in list(headers.items()):
                self.msg_log("Setting header %s to %s" % (k, v))
                req.setRawHeader(k, v)
        if self.authid:
            self.msg_log("Update request w/ authid: {0}".format(self.authid))
            QgsAuthManager.instance().updateNetworkRequest(req, self.authid)
        if self.reply is not None and self.reply.isRunning():
            self.reply.close()
        if method.lower() == 'delete':
            func = getattr(QgsNetworkAccessManager.instance(),
                           'deleteResource')
        else:
            func = getattr(QgsNetworkAccessManager.instance(), method.lower())
        # Calling the server ...
        # Let's log the whole call for debugging purposes:
        self.msg_log("Sending %s request to %s" %
                     (method.upper(), req.url().toString()))
        self.on_abort = False
        headers = {str(h): str(req.rawHeader(h)) for h in req.rawHeaderList()}
        for k, v in list(headers.items()):
            self.msg_log("%s: %s" % (k, v))
        if method.lower() in ['post', 'put']:
            if isinstance(body, file):
                body = body.read()
            self.reply = func(req, body)
        else:
            self.reply = func(req)
        if self.authid:
            self.msg_log("Update reply w/ authid: {0}".format(self.authid))
            QgsAuthManager.instance().updateNetworkReply(
                self.reply, self.authid)

        # necessary to trap local timeout manage by QgsNetworkAccessManager
        # calling QgsNetworkAccessManager::abortRequest
        QgsNetworkAccessManager.instance().requestTimedOut.connect(
            self.requestTimedOut)

        self.reply.sslErrors.connect(self.sslErrors)
        self.reply.finished.connect(self.replyFinished)
        self.reply.downloadProgress.connect(self.downloadProgress)

        # block if blocking mode otherwise return immediately
        # it's up to the caller to manage listeners in case of no blocking mode
        if not self.blocking_mode:
            return None, None

        # Call and block
        self.el = QEventLoop()
        self.reply.finished.connect(self.el.quit)

        # Catch all exceptions (and clean up requests)
        try:
            self.el.exec_(QEventLoop.ExcludeUserInputEvents)
        except Exception as e:
            raise e

        if self.reply:
            self.reply.finished.disconnect(self.el.quit)

        # emit exception in case of error
        if not self.http_call_result.ok:
            if self.http_call_result.exception and not self.exception_class:
                raise self.http_call_result.exception
            elif self.exception_class:
                raise self.exception_class(self.http_call_result.reason)
            else:
                raise RequestsException('Unknown reason')

        return self.http_call_result, self.http_call_result.content
Beispiel #54
0
class CharacterDialog(WindowModalDialog):
    def __init__(self, parent):
        super(CharacterDialog, self).__init__(parent)
        self.setWindowTitle(_("KeepKey Seed Recovery"))
        self.character_pos = 0
        self.word_pos = 0
        self.loop = QEventLoop()
        self.word_help = QLabel()
        self.char_buttons = []

        vbox = QVBoxLayout(self)
        vbox.addWidget(WWLabel(CHARACTER_RECOVERY))
        hbox = QHBoxLayout()
        hbox.addWidget(self.word_help)
        for i in range(4):
            char_button = CharacterButton('*')
            char_button.setMaximumWidth(36)
            self.char_buttons.append(char_button)
            hbox.addWidget(char_button)
        self.accept_button = CharacterButton(_("Accept Word"))
        self.accept_button.clicked.connect(partial(self.process_key, 32))
        self.rejected.connect(partial(self.loop.exit, 1))
        hbox.addWidget(self.accept_button)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        self.finished_button = QPushButton(_("Seed Entered"))
        self.cancel_button = QPushButton(_("Cancel"))
        self.finished_button.clicked.connect(
            partial(self.process_key, Qt.Key_Return))
        self.cancel_button.clicked.connect(self.rejected)
        buttons = Buttons(self.finished_button, self.cancel_button)
        vbox.addSpacing(40)
        vbox.addLayout(buttons)
        self.refresh()
        self.show()

    def refresh(self):
        self.word_help.setText("Enter seed word %2d:" % (self.word_pos + 1))
        self.accept_button.setEnabled(self.character_pos >= 3)
        self.finished_button.setEnabled((self.word_pos in (11, 17, 23)
                                         and self.character_pos >= 3))
        for n, button in enumerate(self.char_buttons):
            button.setEnabled(n == self.character_pos)
            if n == self.character_pos:
                button.setFocus()

    def is_valid_alpha_space(self, key):
        # Auto-completion requires at least 3 characters
        if key == ord(' ') and self.character_pos >= 3:
            return True
        # Firmware aborts protocol if the 5th character is non-space
        if self.character_pos >= 4:
            return False
        return (key >= ord('a') and key <= ord('z')
                or (key >= ord('A') and key <= ord('Z')))

    def process_key(self, key):
        self.data = None
        if key == Qt.Key_Return and self.finished_button.isEnabled():
            self.data = {'done': True}
        elif key == Qt.Key_Backspace and (self.word_pos or self.character_pos):
            self.data = {'delete': True}
        elif self.is_valid_alpha_space(key):
            self.data = {'character': chr(key).lower()}
        if self.data:
            self.loop.exit(0)

    def keyPressEvent(self, event):
        self.process_key(event.key())
        if not self.data:
            QDialog.keyPressEvent(self, event)

    def get_char(self, word_pos, character_pos):
        self.word_pos = word_pos
        self.character_pos = character_pos
        self.refresh()
        if self.loop.exec_():
            self.data = None  # User cancelled
class NetworkAccessManager(QObject):
    """
    This class mimicks httplib2 by using QgsNetworkAccessManager for all
    network calls.
    The return value is a tuple of (response, content), the first being and
    instance of the Response class, the second being a string that contains
    the response entity body.
    Parameters
    ----------
    debug : bool
        verbose logging if True
    exception_class : Exception
        Custom exception class
    Usage 1 (blocking mode)
    -----
    ::
        nam = NetworkAccessManager(authcgf)
        try:
            (response, content) = nam.request('http://www.example.com')
        except RequestsException as e:
            # Handle exception
            pass
    Usage 2 (Non blocking mode)
    -------------------------
    ::
        NOTE! if blocking mode returns immediatly
              it's up to the caller to manage listeners in case
              of non blocking mode
        nam = NetworkAccessManager(authcgf)
        try:
            nam.request('http://www.example.com', blocking=False)
            nam.reply.finished.connect(a_signal_listener)
        except RequestsException as e:
            # Handle exception
            pass
        Get response using method:
        nam.httpResult() that return a dictionary with keys:
            'status' - http code result come from reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
            'status_code' - http code result come from reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
            'status_message' - reply message string from reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute)
            'content' - bytearray returned from reply
            'ok' - request success [True, False]
            'headers' - Dicionary containing the reply header
            'reason' - fomatted message string with reply.errorString()
            'exception' - the exception returne dduring execution
    """

    finished = pyqtSignal(Response)

    def __init__(self,
                 authid=None,
                 disable_ssl_certificate_validation=False,
                 exception_class=None,
                 debug=False):
        QObject.__init__(self)
        self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
        self.authid = authid
        self.reply = None
        self.debug = debug
        self.exception_class = exception_class
        self.on_abort = False
        self.blocking_mode = False
        self.http_call_result = Response({
            'status': 0,
            'status_code': 0,
            'status_message': '',
            'content': '',
            'ok': False,
            'headers': {},
            'reason': '',
            'exception': None,
            'url': ''
        })

    def msg_log(self, msg):
        if self.debug:
            QgsMessageLog.logMessage(msg, "NetworkAccessManager")

    def httpResult(self):
        return self.http_call_result

    def request(self,
                url,
                method="GET",
                body=None,
                headers=None,
                redirections=DEFAULT_MAX_REDIRECTS,
                connection_type=None,
                blocking=True):
        """
        Make a network request by calling QgsNetworkAccessManager.
        redirections argument is ignored and is here only for httplib2 compatibility.
        """
        self.http_call_result.url = url
        self.msg_log(u'http_call request: {0}'.format(url))

        self.blocking_mode = blocking
        req = QNetworkRequest()
        # Avoid double quoting form QUrl
        url = urllib.parse.unquote(url)
        req.setUrl(QUrl(url))
        if headers is not None:
            # This fixes a weird error with compressed content not being correctly
            # inflated.
            # If you set the header on the QNetworkRequest you are basically telling
            # QNetworkAccessManager "I know what I'm doing, please don't do any content
            # encoding processing".
            # See: https://bugs.webkit.org/show_bug.cgi?id=63696#c1
            try:
                del headers['Accept-Encoding']
            except KeyError:
                pass
            for k, v in list(headers.items()):
                self.msg_log("Setting header %s to %s" % (k, v))
                req.setRawHeader(k, v)
        if self.authid:
            self.msg_log("Update request w/ authid: {0}".format(self.authid))
            QgsAuthManager.instance().updateNetworkRequest(req, self.authid)
        if self.reply is not None and self.reply.isRunning():
            self.reply.close()
        if method.lower() == 'delete':
            func = getattr(QgsNetworkAccessManager.instance(),
                           'deleteResource')
        else:
            func = getattr(QgsNetworkAccessManager.instance(), method.lower())
        # Calling the server ...
        # Let's log the whole call for debugging purposes:
        self.msg_log("Sending %s request to %s" %
                     (method.upper(), req.url().toString()))
        self.on_abort = False
        headers = {str(h): str(req.rawHeader(h)) for h in req.rawHeaderList()}
        for k, v in list(headers.items()):
            self.msg_log("%s: %s" % (k, v))
        if method.lower() in ['post', 'put']:
            if isinstance(body, file):
                body = body.read()
            self.reply = func(req, body)
        else:
            self.reply = func(req)
        if self.authid:
            self.msg_log("Update reply w/ authid: {0}".format(self.authid))
            QgsAuthManager.instance().updateNetworkReply(
                self.reply, self.authid)

        # necessary to trap local timeout manage by QgsNetworkAccessManager
        # calling QgsNetworkAccessManager::abortRequest
        QgsNetworkAccessManager.instance().requestTimedOut.connect(
            self.requestTimedOut)

        self.reply.sslErrors.connect(self.sslErrors)
        self.reply.finished.connect(self.replyFinished)
        self.reply.downloadProgress.connect(self.downloadProgress)

        # block if blocking mode otherwise return immediately
        # it's up to the caller to manage listeners in case of no blocking mode
        if not self.blocking_mode:
            return None, None

        # Call and block
        self.el = QEventLoop()
        self.reply.finished.connect(self.el.quit)

        # Catch all exceptions (and clean up requests)
        try:
            self.el.exec_(QEventLoop.ExcludeUserInputEvents)
        except Exception as e:
            raise e

        if self.reply:
            self.reply.finished.disconnect(self.el.quit)

        # emit exception in case of error
        if not self.http_call_result.ok:
            if self.http_call_result.exception and not self.exception_class:
                raise self.http_call_result.exception
            elif self.exception_class:
                raise self.exception_class(self.http_call_result.reason)
            else:
                raise RequestsException('Unknown reason')

        return self.http_call_result, self.http_call_result.content

    # @pyqtSlot()
    def downloadProgress(self, bytesReceived, bytesTotal):
        """Keep track of the download progress"""
        #self.msg_log("downloadProgress %s of %s ..." % (bytesReceived, bytesTotal))
        pass

    # @pyqtSlot(QNetworkReply)
    def requestTimedOut(self, QNetworkReply):
        """Trap the timeout. In Async mode requestTimedOut is called after replyFinished"""
        # adapt http_call_result basing on receiving qgs timer timout signal
        self.exception_class = RequestsExceptionTimeout
        self.http_call_result.exception = RequestsExceptionTimeout(
            "Timeout error")

    # @pyqtSlot(QObject)
    def replyFinished(self):
        err = self.reply.error()
        httpStatus = self.reply.attribute(
            QNetworkRequest.HttpStatusCodeAttribute)
        httpStatusMessage = self.reply.attribute(
            QNetworkRequest.HttpReasonPhraseAttribute)
        self.http_call_result.status_code = httpStatus
        self.http_call_result.status = httpStatus
        self.http_call_result.status_message = httpStatusMessage
        for k, v in self.reply.rawHeaderPairs():
            self.http_call_result.headers[str(k)] = str(v)
            self.http_call_result.headers[str(k).lower()] = str(v)

        if err != QNetworkReply.NoError:
            # handle error
            # check if errorString is empty, if so, then set err string as
            # reply dump
            if re.match('(.)*server replied: $', self.reply.errorString()):
                errString = self.reply.errorString(
                ) + self.http_call_result.content
            else:
                errString = self.reply.errorString()
            # check if self.http_call_result.status_code is available (client abort
            # does not produce http.status_code)
            if self.http_call_result.status_code:
                msg = "Network error #{0}: {1}".format(
                    self.http_call_result.status_code, errString)
            else:
                msg = "Network error: {0}".format(errString)

            self.http_call_result.reason = msg
            self.http_call_result.ok = False
            self.msg_log(msg)
            # set return exception
            if err == QNetworkReply.TimeoutError:
                self.http_call_result.exception = RequestsExceptionTimeout(msg)

            elif err == QNetworkReply.ConnectionRefusedError:
                self.http_call_result.exception = RequestsExceptionConnectionError(
                    msg)

            elif err == QNetworkReply.OperationCanceledError:
                # request abort by calling NAM.abort() => cancelled by the user
                if self.on_abort:
                    self.http_call_result.exception = RequestsExceptionUserAbort(
                        msg)
                else:
                    self.http_call_result.exception = RequestsException(msg)

            else:
                self.http_call_result.exception = RequestsException(msg)

            # overload exception to the custom exception if available
            if self.exception_class:
                self.http_call_result.exception = self.exception_class(msg)

        else:
            # Handle redirections
            redirection_url = self.reply.attribute(
                QNetworkRequest.RedirectionTargetAttribute)
            if redirection_url is not None and redirection_url != self.reply.url(
            ):
                if redirection_url.isRelative():
                    redirection_url = self.reply.url().resolved(
                        redirection_url)

                msg = "Redirected from '{}' to '{}'".format(
                    self.reply.url().toString(), redirection_url.toString())
                self.msg_log(msg)

                self.reply.deleteLater()
                self.reply = None
                self.request(redirection_url.toString())

            # really end request
            else:
                msg = "Network success #{0}".format(self.reply.error())
                self.http_call_result.reason = msg
                self.msg_log(msg)

                ba = self.reply.readAll()
                self.http_call_result.content = bytes(ba)
                self.http_call_result.ok = True

        # Let's log the whole response for debugging purposes:
        self.msg_log(
            "Got response %s %s from %s" %
            (self.http_call_result.status_code,
             self.http_call_result.status_message, self.reply.url().toString()
             if self.reply else 'reply has been deleted'))
        for k, v in list(self.http_call_result.headers.items()):
            self.msg_log("%s: %s" % (k, v))
        if len(self.http_call_result.content) < 1024:
            self.msg_log("Payload :\n%s" % self.http_call_result.content)
        else:
            self.msg_log("Payload is > 1 KB ...")

        # clean reply
        if self.reply is not None:
            if self.reply.isRunning():
                self.reply.close()
            self.msg_log("Deleting reply ...")
            # Disconnect all slots
            self.reply.sslErrors.disconnect(self.sslErrors)
            self.reply.finished.disconnect(self.replyFinished)
            self.reply.downloadProgress.disconnect(self.downloadProgress)
            self.reply.deleteLater()
            self.reply = None
        else:
            self.msg_log("Reply was already deleted ...")

        self.finished.emit(self.http_call_result)

    # @pyqtSlot()
    def sslErrors(self, ssl_errors):
        """
        Handle SSL errors, logging them if debug is on and ignoring them
        if disable_ssl_certificate_validation is set.
        """
        if ssl_errors:
            for v in ssl_errors:
                self.msg_log("SSL Error: %s" % v.errorString())
        if self.disable_ssl_certificate_validation:
            self.reply.ignoreSslErrors()

    # @pyqtSlot()
    def abort(self):
        """
        Handle request to cancel HTTP call
        """
        if self.reply and self.reply.isRunning():
            self.on_abort = True
            self.reply.abort()
Beispiel #56
0
class CategoryPopupMenu(FramelessWindow):
    triggered = Signal(QAction)
    hovered = Signal(QAction)

    def __init__(self, parent=None, **kwargs):
        FramelessWindow.__init__(self, parent, **kwargs)
        self.setWindowFlags(self.windowFlags() | Qt.Popup)

        layout = QVBoxLayout()
        layout.setContentsMargins(6, 6, 6, 6)

        self.__menu = MenuPage()
        self.__menu.setActionRole(QtWidgetRegistry.WIDGET_ACTION_ROLE)

        if sys.platform == "darwin":
            self.__menu.view().setAttribute(Qt.WA_MacShowFocusRect, False)

        self.__menu.triggered.connect(self.__onTriggered)
        self.__menu.hovered.connect(self.hovered)

        self.__dragListener = ItemViewDragStartEventListener(self)
        self.__dragListener.dragStarted.connect(self.__onDragStarted)

        self.__menu.view().viewport().installEventFilter(self.__dragListener)

        layout.addWidget(self.__menu)

        self.setLayout(layout)

        self.__action = None
        self.__loop = None
        self.__item = None

    def setCategoryItem(self, item):
        """
        Set the category root item (:class:`QStandardItem`).
        """
        self.__item = item
        model = item.model()
        self.__menu.setModel(model)
        self.__menu.setRootIndex(item.index())

    def popup(self, pos=None):
        if pos is None:
            pos = self.pos()
        geom = widget_popup_geometry(pos, self)
        self.setGeometry(geom)
        self.show()

    def exec_(self, pos=None):
        self.popup(pos)
        self.__loop = QEventLoop()

        self.__action = None
        self.__loop.exec_()
        self.__loop = None

        if self.__action is not None:
            action = self.__action
        else:
            action = None
        return action

    def hideEvent(self, event):
        if self.__loop is not None:
            self.__loop.exit(0)

        return FramelessWindow.hideEvent(self, event)

    def __onTriggered(self, action):
        self.__action = action
        self.triggered.emit(action)
        self.hide()

        if self.__loop:
            self.__loop.exit(0)

    def __onDragStarted(self, index):
        desc = qunwrap(index.data(QtWidgetRegistry.WIDGET_DESC_ROLE))
        icon = qunwrap(index.data(Qt.DecorationRole))

        drag_data = QMimeData()
        drag_data.setData(
            "application/vnv.orange-canvas.registry.qualified-name",
            desc.qualified_name)
        drag = QDrag(self)
        drag.setPixmap(icon.pixmap(38))
        drag.setMimeData(drag_data)

        # TODO: Should animate (accept) hide.
        self.hide()

        # When a drag is started and the menu hidden the item's tool tip
        # can still show for a short time UNDER the cursor preventing a
        # drop.
        viewport = self.__menu.view().viewport()
        filter = ToolTipEventFilter()
        viewport.installEventFilter(filter)

        drag.exec_(Qt.CopyAction)

        viewport.removeEventFilter(filter)
Beispiel #57
0
board = chess.Board()
board.reset_board()

app = QApplication(sys.argv)
svgWidget = QSvgWidget()
svgWidget.setGeometry(400, 300, 400, 400)
svgWidget.show()
board_picture = chess.svg.board(board)

svg_bytes = bytearray(board_picture, encoding='utf-8')
svgWidget.renderer().load(svg_bytes)
svgWidget.update()
svgWidget.show()

loop = QEventLoop()
QTimer.singleShot(1000, loop.quit)
loop.exec_()

source_cell_index = chess.square(
    chess.FILE_NAMES.index('a'),
    chess.RANK_NAMES.index('1'))  #example: A1 is cell index = 0 (out of 63)
dest_cell_index = chess.square(chess.FILE_NAMES.index('a'),
                               chess.RANK_NAMES.index('3'))

print('source_cell_index:', source_cell_index)
print('dest_cell_index:', dest_cell_index)

board_picture = chess.svg.board(board,
                                arrows=[
                                    (source_cell_index, dest_cell_index)
Beispiel #58
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()
    synchronized_signal = pyqtSignal(str)

    def __init__(self):
        BaseWizard.__init__(self)
        QDialog.__init__(self, None)

        self.setWindowTitle('ElectrumSV')
        self.language_for_seed = app_state.config.get('language')
        self.setMinimumSize(600, 420)
        self.accept_signal.connect(self.accept)
        self.back_button = QPushButton(_(MSG_BUTTON_BACK), self)
        self.back_button.setText(
            _(MSG_BUTTON_BACK) if self.can_go_back() else _(MSG_BUTTON_CANCEL))
        self.next_button = QPushButton(_(MSG_BUTTON_NEXT), self)
        self.next_button.setDefault(True)
        self.icon_filename = None
        self.loop = QEventLoop()
        self.rejected.connect(lambda: self.loop.exit(0))
        self.back_button.clicked.connect(lambda: self.loop.exit(1))
        self.next_button.clicked.connect(lambda: self.loop.exit(2))
        self.scroll_widget = QWidget()
        self.scroll_widget.setLayout(self.create_template_layout())
        scroll = QScrollArea()
        scroll.setWidget(self.scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        outer_vbox = QVBoxLayout(self)
        outer_vbox.addWidget(scroll)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def create_template_layout(self):
        """
        The standard layout divides creates a three part template.
        """
        self.title = QLabel()
        self.main_widget = QWidget()
        self.please_wait = QLabel(_("Please wait..."))
        self.please_wait.setAlignment(Qt.AlignCenter)

        vbox = QVBoxLayout()
        vbox.addWidget(self.title)
        vbox.addWidget(self.main_widget)
        vbox.addStretch(1)
        vbox.addWidget(self.please_wait)
        vbox.addStretch(1)
        self.template_hbox = QHBoxLayout()
        vbox.addLayout(self.template_hbox)
        return vbox

    def select_storage(self, initial_path: str, is_startup=False):
        if is_startup:
            self._copy_electron_cash_wallets()

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(_('Wallet') + ':'))
        self.name_e = QLineEdit()
        hbox.addWidget(self.name_e)
        button = QPushButton(_('Choose...'))
        hbox.addWidget(button)
        vbox.addLayout(hbox)

        self.msg_label = QLabel('')
        vbox.addWidget(self.msg_label)

        hbox2 = QHBoxLayout()
        self.pw_e = PasswordLineEdit()
        self.pw_e.setMinimumWidth(200)
        self.pw_label = QLabel(_('Password') + ':')
        self.pw_label.setAlignment(Qt.AlignTop)
        hbox2.addWidget(self.pw_label)
        hbox2.addWidget(self.pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)
        self._set_standard_layout(vbox,
                                  title=_('ElectrumSV wallet'),
                                  back_text=_(MSG_BUTTON_CANCEL))

        esv_wallets_dir = os.path.join(app_state.config.electrum_path(),
                                       "wallets")

        if is_startup:

            def _show_copy_electron_cash_wallets_dialog(*args):
                nonlocal esv_wallets_dir, ec_wallets_dir

                d = WindowModalDialog(self, _("Copy Electron Cash Wallets"))
                d.setMinimumWidth(400)

                vbox, file_list = self._create_copy_electron_cash_wallets_layout(
                    ec_wallets_dir)

                bbox = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
                bbox.rejected.connect(d.reject)
                bbox.accepted.connect(d.accept)
                vbox.addWidget(bbox)

                d.setLayout(vbox)

                result = d.exec()
                if result == QDialog.Accepted:
                    self._do_copy_electron_cash_wallets(
                        file_list, esv_wallets_dir, ec_wallets_dir)

                _update_selected_wallet()

            ec_import_text = ("<p>" + _(
                "You have previously run Electron Cash and created at " +
                "least one wallet with it. ElectrumSV should not be used to open these wallets "
                +
                "directly using the 'Choose' button, and you should instead use the 'Import' "
                + "button to help you copy them."
            ) + "</p>" + "<p>" + _(
                "There are many reasons for this, and the simplest is that if Electron Cash "
                +
                "and ElectrumSV were to operate on the same wallet at the same time, then the "
                +
                "wallet will most likely become corrupted. It's simpler in every way to just "
                +
                "copy the wallet over to ElectrumSV and use a separate wallet file for each."
            ) + "</p>")
            ec_import_icon = HelpLabel("label text", ec_import_text)
            ec_import_icon.setPixmap(
                QPixmap(icon_path("icons8-info.svg")).scaledToWidth(
                    16, Qt.SmoothTransformation))
            ec_import_label = QLabel(
                _("Existing Electron Cash wallets detected"))
            ec_import_button = QPushButton(_("Import..."))
            ec_import_button.clicked.connect(
                _show_copy_electron_cash_wallets_dialog)
            self.template_hbox.addWidget(ec_import_icon)
            self.template_hbox.addWidget(ec_import_label)
            self.template_hbox.addWidget(ec_import_button)
            self.template_hbox.addStretch(1)

            ec_wallets_dir = get_electron_cash_user_dir(esv_wallets_dir)
            if len(self._list_user_wallets(ec_wallets_dir)) == 0:
                ec_import_icon.set_help_text("<p>" + _(
                    "This feature is aimed at users who were " +
                    "already using Electron Cash and have existing wallets associated with it. "
                    +
                    "None were detected on this computer, but if you do have some stored in "
                    +
                    "places ElectrumSV does not know about you may need to copy them "
                    + "yourself."
                ) + "</p>" + "<p>" + _(
                    "You should never open your existing Electron Cash wallets directly in "
                    +
                    "ElectrumSV as this can lead to them being opened in both applications at "
                    + "the same time, and can result in corruption.") + "</p>")
                ec_import_button.setEnabled(False)
                ec_import_button.setToolTip(_("Nothing to import"))
                ec_import_label.setText(_("No Electron Cash wallets detected"))

        if WalletStorage.files_are_matched_by_path(initial_path):
            self._storage_existing = WalletStorage(initial_path,
                                                   manual_upgrades=True)

        wallet_folder = os.path.dirname(initial_path)

        def _on_choose() -> None:
            path, __ = QFileDialog.getOpenFileName(self,
                                                   "Select your wallet file",
                                                   wallet_folder)
            if path:
                self.name_e.setText(path)

        def _on_filename(filename: str) -> None:
            pw = False
            self._storage_existing = None

            # A relative path will be relative to the folder we offered in the choose dialog.
            # An absolute path will not get joined to the dialog folder (no-op).
            path = os.path.join(wallet_folder, filename)
            if WalletStorage.files_are_matched_by_path(path):
                try:
                    self._storage_existing = WalletStorage(
                        path, manual_upgrades=True)
                except IOError:
                    self.next_button.setEnabled(False)
                    msg = _('Cannot read file')
                else:
                    self.next_button.setEnabled(True)
                    if self._storage_existing.is_encrypted():
                        msg = '\n'.join([
                            _("This file is encrypted."),
                            _('Enter your password or choose another file.'),
                        ])
                        pw = True
                    else:
                        msg = _("Press 'Next' to open this wallet.")
            else:
                msg = _("This file does not exist.")
                if os.access(wallet_folder, os.W_OK):
                    self.next_button.setEnabled(True)
                    msg += "\n" + _(
                        "Press 'Next' to create this wallet, or choose another file."
                    )
                    self._path_new = path
                else:
                    self.next_button.setEnabled(False)
                    msg += "\n" + _("You do not have write access " +
                                    "to this folder to create a new wallet.")

            self.msg_label.setText(msg)
            if pw:
                self.pw_label.show()
                self.pw_e.show()
                self.pw_e.setFocus()
            else:
                self.pw_label.hide()
                self.pw_e.hide()

        def _update_selected_wallet(
                skip_pick_most_recent: bool = False) -> None:
            wallet_filename = None
            if is_startup and not skip_pick_most_recent and self._storage_existing is None:
                esv_wallet_names = self._list_user_wallets(esv_wallets_dir)
                if len(esv_wallet_names):
                    wallet_filename = esv_wallet_names[0]
            if wallet_filename is None:
                if self._storage_existing is not None:
                    wallet_filename = os.path.basename(
                        self._storage_existing.get_path())
                else:
                    wallet_filename = os.path.basename(initial_path)
            self.name_e.setText(wallet_filename)

        button.clicked.connect(_on_choose)
        self.name_e.textChanged.connect(_on_filename)

        # We do not pick the most recent when first displaying the wizard because we want to
        # treat the preselected wallet as the user's explicit choice. So a non-existent name
        # should be a possible wallet creation.
        _update_selected_wallet(skip_pick_most_recent=True)

        while True:
            if self._storage_existing is not None and not self._storage_existing.is_encrypted(
            ):
                break
            if self.loop.exec_() != 2:  # 2 = next
                return
            if self._storage_existing is None:
                break
            if self._storage_existing is not None and self._storage_existing.is_encrypted(
            ):
                password = self.pw_e.text()
                try:
                    self._storage_existing.decrypt(password)
                    self.pw_e.setText('')
                    break
                except DecryptionError:
                    QMessageBox.information(None, _('Error'),
                                            _("Incorrect password"))
                    continue
                except Exception as e:
                    logger.exception("decrypting storage")
                    QMessageBox.information(None, _('Error'), str(e))
                    return

        return True

    def _copy_electron_cash_wallets(self):
        """
        Work out whether we should show UI to offer to copy the user's
        Electron Cash wallets to their ElectrumSV wallet directory, and
        if so, show it and give them the chance.
        """
        # If the user has ElectrumSV wallets already, we do not offer to copy the one's
        # Electron Cash has.
        esv_wallets_dir = os.path.join(app_state.config.electrum_path(),
                                       "wallets")
        if len(self._list_user_wallets(esv_wallets_dir)) > 0:
            return

        ec_wallets_dir = get_electron_cash_user_dir(esv_wallets_dir)
        ec_wallet_count = len(self._list_user_wallets(ec_wallets_dir))
        # If the user does not have Electron Cash wallets to copy, there's no point in offering.
        if ec_wallet_count == 0:
            return

        vbox, file_list = self._create_copy_electron_cash_wallets_layout(
            ec_wallets_dir)
        self._set_standard_layout(vbox,
                                  title=_('Import Electron Cash wallets'))

        v = self.loop.exec_()
        # Cancel, exit application.
        if v == -1:
            raise UserCancelled()
        if v != 2:
            raise GoBack()

        self._do_copy_electron_cash_wallets(file_list, esv_wallets_dir,
                                            ec_wallets_dir)

    def _do_copy_electron_cash_wallets(self, file_list, esv_wallets_dir,
                                       ec_wallets_dir):
        # If the user selected any files, then we copy them before exiting to the next page.
        copy_count = 0
        for item in file_list.selectedItems():
            filename = item.text()
            source_path = os.path.join(ec_wallets_dir, filename)
            target_path = os.path.join(esv_wallets_dir, filename)
            # If they are copying an Electron Cash wallet over an ElectrumSV wallet, make sure
            # they confirm they are going to replace/overwrite it.
            if os.path.exists(target_path):
                if self.question(
                        _("You already have a wallet named '{}' for ElectrumSV. "
                          + "Replace/overwrite it?").format(filename), self,
                        _("Delete Wallet?")):
                    os.remove(target_path)
                else:
                    continue
            try:
                shutil.copyfile(source_path, target_path)
                copy_count += 1
            except shutil.Error:
                # For now we ignore copy errors.
                pass

        if copy_count == 1:
            self.show_message(_("1 wallet copied."))
        elif copy_count > 1:
            self.show_message(_("%d wallets copied.") % copy_count)

    def _create_copy_electron_cash_wallets_layout(self, ec_wallets_dir):
        def update_summary_label():
            selection_count = len(file_list.selectedItems())
            if selection_count == 0:
                summary_label.setText(
                    _("No wallets are selected / will be copied."))
            elif selection_count == 1:
                summary_label.setText(
                    _("1 wallet is selected / will be copied."))
            else:
                summary_label.setText(
                    _("%d wallets are selected / will be copied.") %
                    selection_count)

        wallet_filenames = sorted(os.listdir(ec_wallets_dir),
                                  key=lambda s: s.lower())

        file_list = QListWidget()
        file_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        for filename in wallet_filenames:
            if not self._ignore_wallet_file(
                    os.path.join(ec_wallets_dir, filename)):
                file_list.addItem(QListWidgetItem(filename))
        file_list.itemSelectionChanged.connect(update_summary_label)

        vbox = QVBoxLayout()
        introduction_label = QLabel(
            _("Your Electron Cash wallet directory was found. If you want ElectrumSV to import "
              "any of them on your behalf, select the ones you want copied from the list below "
              "before clicking the Next button."))
        introduction_label.setWordWrap(True)
        vbox.setSpacing(20)
        vbox.addWidget(introduction_label)
        vbox.addWidget(file_list)
        summary_label = QLabel()
        update_summary_label()
        vbox.addWidget(summary_label)
        return vbox, file_list

    def _list_user_wallets(self, wallets_path):
        if os.path.exists(wallets_path):
            from stat import ST_MTIME
            l = [(os.stat(os.path.join(wallets_path,
                                       filename))[ST_MTIME], filename)
                 for filename in os.listdir(wallets_path)
                 if not self._ignore_wallet_file(
                     os.path.join(wallets_path, filename))]
            l = sorted(l, reverse=True)
            return [entry[1] for entry in l]
        return []

    def _ignore_wallet_file(self, wallet_path):
        if os.path.isdir(wallet_path):
            return True
        if wallet_path.startswith("."):
            return True
        return False

    def run_and_get_wallet(self) -> Optional[Abstract_Wallet]:
        # NOTE(rt12): This used to have unused code related to resuming incompletely created
        # wallets. This is worth supporting, but not at this stage where we will eventually
        # rewrite for a new wallet wizard and multiple accounts and legacy wallets so on.
        if self._storage_existing is not None:
            path = self._storage_existing.get_path()
            if self._storage_existing.requires_split():
                msg = _(
                    "The wallet '{}' contains multiple accounts, which are not supported.\n\n"
                    "Do you want to split your wallet "
                    "into multiple files?").format(path)
                if not MessageBox.question(msg):
                    return
                file_list = '\n'.join(self._storage_existing.split_accounts())
                msg = (_('Your accounts have been moved to') + ':\n' +
                       file_list + '\n\n' +
                       _('Do you want to delete the old file') + ':\n' + path)
                if self.question(msg):
                    # We know that this will be the only relevant path and will not require
                    # extensions, as it predates the use of the database.
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return

            if self._storage_existing.requires_upgrade():
                msg = _(
                    "The format of your wallet '%s' must be upgraded for ElectrumSV. "
                    "This change will not be backward compatible, " +
                    "and your existing wallet will be backed up. Proceed?"
                ) % path
                if not MessageBox.question(msg):
                    return
                self._storage_existing.upgrade()

            self._parent_wallet = ParentWallet(self._storage_existing)
        else:
            assert self._path_new is not None, "user should have selected storage already"

            wallet_filename = os.path.basename(self._path_new)
            # Make a temporary directory as securely as possible, where the new wallet will be
            # created.
            creation_path = tempfile.mkdtemp()
            try:
                path_new = os.path.join(creation_path, wallet_filename)
                self._storage_new = WalletStorage(path_new)
                # Ensure the path is full and includes the extension.
                path_new = self._storage_new.get_path()

                self.run("new")

                if self._parent_wallet is not None:
                    self._parent_wallet.save_storage()

                if self._parent_wallet is not None:
                    self._parent_wallet.move_to(self._path_new)
                else:
                    # We want to make sure we are not keeping the database open, before deleting.
                    self._storage_new.close()
                    del self._storage_new
            finally:
                shutil.rmtree(creation_path)
        return self._parent_wallet

    def finished(self):
        """Called in hardware client wrapper, in order to close popups."""
        return

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            logger.exception("")
            self.show_error(str(exc_info[1]))

    def _remove_layout_from_widget(self, widget):
        """
        The only way to remove a layout from a first widget, is to transfer it to a second one.
        This needs to be done, to be able to set a new layout on the first widget.
        """
        existing_layout = widget.layout()
        QWidget().setLayout(existing_layout)

    def _set_layout(self, layout, next_enabled=True, back_text=None):
        """
        Set a layout that is in control of the whole display area.
        """
        self._remove_layout_from_widget(self.scroll_widget)
        self.scroll_widget.setLayout(layout)

        self.back_button.setEnabled(True)
        if back_text is not None:
            self.back_button.setText(back_text)
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()

    def _set_standard_layout(self,
                             layout,
                             title=None,
                             next_enabled=True,
                             back_text=None):
        """
        Ensure the standard template layout is in place.
        And put the current stage's sub-layout in the defined place.
        """
        self._remove_layout_from_widget(self.scroll_widget)
        self.scroll_widget.setLayout(self.create_template_layout())

        self.title.setText("<b>%s</b>" % title if title else "")
        self.title.setVisible(bool(title))
        self.main_widget.setLayout(layout)

        if back_text is None:
            self.back_button.setText(_(MSG_BUTTON_BACK))
        else:
            self.back_button.setText(back_text)
        self.back_button.setEnabled(True)
        self.next_button.setText(_(MSG_BUTTON_NEXT))
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)

    def exec_layout(self,
                    layout,
                    title=None,
                    raise_on_cancel=True,
                    next_enabled=True):
        self._set_standard_layout(layout, title, next_enabled)
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled
        if result == 1:
            raise GoBack
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        app_state.app.processEvents()
        app_state.app.processEvents()

    def text_input(self, title, message, is_valid, allow_multi=False):
        slayout = KeysLayout(parent=self,
                             title=message,
                             is_valid=is_valid,
                             allow_multi=allow_multi)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_text()

    def seed_input(self, title, message, is_seed, options):
        slayout = SeedLayout(title=message,
                             is_seed=is_seed,
                             options=options,
                             parent=self)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    @wizard_dialog
    def add_xpub_dialog(self,
                        title,
                        message,
                        is_valid,
                        run_next,
                        allow_multi=False):
        return self.text_input(title, message, is_valid, allow_multi)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d" % index
        message = ' '.join([
            _('Please enter the master public key (xpub) of your cosigner.'),
            _('Enter their master private key (xprv) if you want to be able to sign for them.'
              )
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, test):
        options = []
        if self.opt_ext:
            options.append('ext')
        if self.opt_bip39:
            options.append('bip39')
        title = _('Enter Seed')
        message = _(
            'Please enter your seed phrase in order to restore your wallet.')
        return self.seed_input(title, message, test, options)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, test):
        app_state.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('If you lose your seed, your money will be permanently lost.'),
            _('To make sure that you have properly saved your seed, please retype it here.'
              )
        ])
        seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
        return seed

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        title = _("Your wallet generation seed is:")
        slayout = SeedLayout(seed=seed_text,
                             title=title,
                             msg=True,
                             options=['ext'])
        self.exec_layout(slayout)
        return slayout.is_ext

    def pw_layout(self, msg, kind):
        playout = PasswordLayout(None, msg, kind, self.next_button)
        self.exec_layout(playout.layout())
        return playout.new_password()

    @wizard_dialog
    def request_password(self, run_next):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW)

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next):
        self.confirm(message, title)

    def confirm(self, message, title):
        label = WWLabel(message)
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        self.exec_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self):
        self.accept_signal.emit()

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def line_dialog(self, run_next, title, message, default, test, warning=''):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)

        def f(text):
            self.next_button.setEnabled(test(text))

        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))
        self.exec_layout(vbox, title, next_enabled=test(default))
        return ' '.join(line.text().split())

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedLayout(xpub, title=msg, icon=False)
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network):
        message = _(
            "ElectrumSV communicates with remote servers to get "
            "information about your transactions and addresses. The "
            "servers all fulfil the same purpose only differing in "
            "hardware. In most cases you simply want to let ElectrumSV "
            "pick one at random.  However if you prefer feel free to "
            "select a server manually.")
        choices = [_("Auto connect"), _("Select server manually")]
        title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(message, choices)
        self.back_button.setText(_(MSG_BUTTON_CANCEL))
        self.exec_layout(clayout.layout(), title)
        r = clayout.selected_index()
        app_state.config.set_key('auto_connect', r == 0, True)
        if r == 1:
            nlayout = NetworkChoiceLayout(network,
                                          app_state.config,
                                          wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QLabel()
        m_label = QLabel()
        grid = QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)

        def on_m(m):
            m_label.setText(_('Require %d signatures') % m)
            cw.set_m(m)

        def on_n(n):
            n_label.setText(_('From %d cosigners') % n)
            cw.set_n(n)
            m_edit.setMaximum(n)

        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        on_n(2)
        on_m(2)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(
            WWLabel(
                _("Choose the number of signatures needed to unlock "
                  "funds in your wallet:")))
        vbox.addLayout(grid)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)
Beispiel #59
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    def __init__(self, config: 'SimpleConfig', app: QApplication,
                 plugins: 'Plugins', *, gui_object: 'ElectrumGui'):
        QDialog.__init__(self, None)
        BaseWizard.__init__(self, config, plugins)
        self.setWindowTitle('Electrum-LTC  -  ' + _('Install Wizard'))
        self.app = app
        self.config = config
        self.gui_thread = gui_object.gui_thread
        self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(_("Back"), self)
        self.back_button.setText(
            _('Back') if self.can_go_back() else _('Cancel'))
        self.next_button = QPushButton(_("Next"), self)
        self.next_button.setDefault(True)
        self.logo = QLabel()
        self.please_wait = QLabel(_("Please wait..."))
        self.please_wait.setAlignment(Qt.AlignCenter)
        self.icon_filename = None
        self.loop = QEventLoop()
        self.rejected.connect(lambda: self.loop.exit(0))
        self.back_button.clicked.connect(lambda: self.loop.exit(1))
        self.next_button.clicked.connect(lambda: self.loop.exit(2))
        outer_vbox = QVBoxLayout(self)
        inner_vbox = QVBoxLayout()
        inner_vbox.addWidget(self.title)
        inner_vbox.addWidget(self.main_widget)
        inner_vbox.addStretch(1)
        inner_vbox.addWidget(self.please_wait)
        inner_vbox.addStretch(1)
        scroll_widget = QWidget()
        scroll_widget.setLayout(inner_vbox)
        scroll = QScrollArea()
        scroll.setWidget(scroll_widget)
        scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        scroll.setWidgetResizable(True)
        icon_vbox = QVBoxLayout()
        icon_vbox.addWidget(self.logo)
        icon_vbox.addStretch(1)
        hbox = QHBoxLayout()
        hbox.addLayout(icon_vbox)
        hbox.addSpacing(5)
        hbox.addWidget(scroll)
        hbox.setStretchFactor(scroll, 1)
        outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self.set_icon('electrum-ltc.png')
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.

    def select_storage(
            self, path,
            get_wallet_from_daemon) -> Tuple[str, Optional[WalletStorage]]:

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(_('Wallet') + ':'))
        name_e = QLineEdit()
        hbox.addWidget(name_e)
        button = QPushButton(_('Choose...'))
        hbox.addWidget(button)
        vbox.addLayout(hbox)

        msg_label = WWLabel('')
        vbox.addWidget(msg_label)
        hbox2 = QHBoxLayout()
        pw_e = PasswordLineEdit('', self)
        pw_e.setFixedWidth(17 * char_width_in_lineedit())
        pw_label = QLabel(_('Password') + ':')
        hbox2.addWidget(pw_label)
        hbox2.addWidget(pw_e)
        hbox2.addStretch()
        vbox.addLayout(hbox2)

        vbox.addSpacing(50)
        vbox_create_new = QVBoxLayout()
        vbox_create_new.addWidget(QLabel(_('Alternatively') + ':'),
                                  alignment=Qt.AlignLeft)
        button_create_new = QPushButton(_('Create New Wallet'))
        button_create_new.setMinimumWidth(120)
        vbox_create_new.addWidget(button_create_new, alignment=Qt.AlignLeft)
        widget_create_new = QWidget()
        widget_create_new.setLayout(vbox_create_new)
        vbox_create_new.setContentsMargins(0, 0, 0, 0)
        vbox.addWidget(widget_create_new)

        self.set_layout(vbox, title=_('Electrum-LTC wallet'))

        temp_storage = None  # type: Optional[WalletStorage]
        wallet_folder = os.path.dirname(path)

        def on_choose():
            path, __ = QFileDialog.getOpenFileName(self,
                                                   "Select your wallet file",
                                                   wallet_folder)
            if path:
                name_e.setText(path)

        def on_filename(filename):
            # FIXME? "filename" might contain ".." (etc) and hence sketchy path traversals are possible
            nonlocal temp_storage
            temp_storage = None
            msg = None
            if filename:
                path = os.path.join(wallet_folder, filename)
                wallet_from_memory = get_wallet_from_daemon(path)
                try:
                    if wallet_from_memory:
                        temp_storage = wallet_from_memory.storage  # type: Optional[WalletStorage]
                    else:
                        temp_storage = WalletStorage(path)
                except (StorageReadWriteError, WalletFileException) as e:
                    msg = _('Cannot read file') + f'\n{repr(e)}'
                except Exception as e:
                    self.logger.exception('')
                    msg = _('Cannot read file') + f'\n{repr(e)}'
            else:
                msg = _('')
            self.next_button.setEnabled(temp_storage is not None)
            user_needs_to_enter_password = False
            if temp_storage:
                if not temp_storage.file_exists():
                    msg =_("This file does not exist.") + '\n' \
                          + _("Press 'Next' to create this wallet, or choose another file.")
                elif not wallet_from_memory:
                    if temp_storage.is_encrypted_with_user_pw():
                        msg = _("This file is encrypted with a password.") + '\n' \
                              + _('Enter your password or choose another file.')
                        user_needs_to_enter_password = True
                    elif temp_storage.is_encrypted_with_hw_device():
                        msg = _("This file is encrypted using a hardware device.") + '\n' \
                              + _("Press 'Next' to choose device to decrypt.")
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
            if msg is None:
                msg = _('Cannot read file')
            msg_label.setText(msg)
            widget_create_new.setVisible(
                bool(temp_storage and temp_storage.file_exists()))
            if user_needs_to_enter_password:
                pw_label.show()
                pw_e.show()
                pw_e.setFocus()
            else:
                pw_label.hide()
                pw_e.hide()

        button.clicked.connect(on_choose)
        button_create_new.clicked.connect(
            partial(name_e.setText, get_new_wallet_name(wallet_folder)))
        name_e.textChanged.connect(on_filename)
        name_e.setText(os.path.basename(path))

        def run_user_interaction_loop():
            while True:
                if self.loop.exec_() != 2:  # 2 = next
                    raise UserCancelled()
                assert temp_storage
                if temp_storage.file_exists(
                ) and not temp_storage.is_encrypted():
                    break
                if not temp_storage.file_exists():
                    break
                wallet_from_memory = get_wallet_from_daemon(temp_storage.path)
                if wallet_from_memory:
                    raise WalletAlreadyOpenInMemory(wallet_from_memory)
                if temp_storage.file_exists() and temp_storage.is_encrypted():
                    if temp_storage.is_encrypted_with_user_pw():
                        password = pw_e.text()
                        try:
                            temp_storage.decrypt(password)
                            break
                        except InvalidPassword as e:
                            self.show_message(title=_('Error'), msg=str(e))
                            continue
                        except BaseException as e:
                            self.logger.exception('')
                            self.show_message(title=_('Error'), msg=repr(e))
                            raise UserCancelled()
                    elif temp_storage.is_encrypted_with_hw_device():
                        try:
                            self.run('choose_hw_device',
                                     HWD_SETUP_DECRYPT_WALLET,
                                     storage=temp_storage)
                        except InvalidPassword as e:
                            self.show_message(
                                title=_('Error'),
                                msg=
                                _('Failed to decrypt using this hardware device.'
                                  ) + '\n' +
                                _('If you use a passphrase, make sure it is correct.'
                                  ))
                            self.reset_stack()
                            return self.select_storage(path,
                                                       get_wallet_from_daemon)
                        except (UserCancelled, GoBack):
                            raise
                        except BaseException as e:
                            self.logger.exception('')
                            self.show_message(title=_('Error'), msg=repr(e))
                            raise UserCancelled()
                        if temp_storage.is_past_initial_decryption():
                            break
                        else:
                            raise UserCancelled()
                    else:
                        raise Exception('Unexpected encryption version')

        try:
            run_user_interaction_loop()
        finally:
            try:
                pw_e.clear()
            except RuntimeError:  # wrapped C/C++ object has been deleted.
                pass  # happens when decrypting with hw device

        return temp_storage.path, (temp_storage
                                   if temp_storage.file_exists() else None)

    def run_upgrades(self, storage: WalletStorage, db: 'WalletDB') -> None:
        path = storage.path
        if db.requires_split():
            self.hide()
            msg = _(
                "The wallet '{}' contains multiple accounts, which are no longer supported since Electrum 2.7.\n\n"
                "Do you want to split your wallet into multiple files?"
            ).format(path)
            if not self.question(msg):
                return
            file_list = db.split_accounts(path)
            msg = _('Your accounts have been moved to') + ':\n' + '\n'.join(
                file_list) + '\n\n' + _(
                    'Do you want to delete the old file') + ':\n' + path
            if self.question(msg):
                os.remove(path)
                self.show_warning(_('The file was removed'))
            # raise now, to avoid having the old storage opened
            raise UserCancelled()

        action = db.get_action()
        if action and db.requires_upgrade():
            raise WalletFileException(
                'Incomplete wallet files cannot be upgraded.')
        if action:
            self.hide()
            msg = _("The file '{}' contains an incompletely created wallet.\n"
                    "Do you want to complete its creation now?").format(path)
            if not self.question(msg):
                if self.question(
                        _("Do you want to delete '{}'?").format(path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
            self.data = json.loads(storage.read())
            self.run(action)
            for k, v in self.data.items():
                db.put(k, v)
            db.write(storage)
            return

        if db.requires_upgrade():
            self.upgrade_db(storage, db)

    def on_error(self, exc_info):
        if not isinstance(exc_info[1], UserCancelled):
            self.logger.error("on_error", exc_info=exc_info)
            self.show_error(str(exc_info[1]))

    def set_icon(self, filename):
        prior_filename, self.icon_filename = self.icon_filename, filename
        self.logo.setPixmap(
            QPixmap(icon_path(filename)).scaledToWidth(
                60, mode=Qt.SmoothTransformation))
        return prior_filename

    def set_layout(self, layout, title=None, next_enabled=True):
        self.title.setText("<b>%s</b>" % title if title else "")
        self.title.setVisible(bool(title))
        # Get rid of any prior layout by assigning it to a temporary widget
        prior_layout = self.main_widget.layout()
        if prior_layout:
            QWidget().setLayout(prior_layout)
        self.main_widget.setLayout(layout)
        self.back_button.setEnabled(True)
        self.next_button.setEnabled(next_enabled)
        if next_enabled:
            self.next_button.setFocus()
        self.main_widget.setVisible(True)
        self.please_wait.setVisible(False)

    def exec_layout(self,
                    layout,
                    title=None,
                    raise_on_cancel=True,
                    next_enabled=True,
                    focused_widget=None):
        self.set_layout(layout, title, next_enabled)
        if focused_widget:
            focused_widget.setFocus()
        result = self.loop.exec_()
        if not result and raise_on_cancel:
            raise UserCancelled()
        if result == 1:
            raise GoBack from None
        self.title.setVisible(False)
        self.back_button.setEnabled(False)
        self.next_button.setEnabled(False)
        self.main_widget.setVisible(False)
        self.please_wait.setVisible(True)
        self.refresh_gui()
        return result

    def refresh_gui(self):
        # For some reason, to refresh the GUI this needs to be called twice
        self.app.processEvents()
        self.app.processEvents()

    def remove_from_recently_open(self, filename):
        self.config.remove_from_recently_open(filename)

    def text_input(self, title, message, is_valid, allow_multi=False):
        slayout = KeysLayout(parent=self,
                             header_layout=message,
                             is_valid=is_valid,
                             allow_multi=allow_multi,
                             config=self.config)
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_text()

    def seed_input(self, title, message, is_seed, options):
        slayout = SeedLayout(
            title=message,
            is_seed=is_seed,
            options=options,
            parent=self,
            config=self.config,
        )
        self.exec_layout(slayout, title, next_enabled=False)
        return slayout.get_seed(), slayout.is_bip39, slayout.is_ext

    @wizard_dialog
    def add_xpub_dialog(self,
                        title,
                        message,
                        is_valid,
                        run_next,
                        allow_multi=False,
                        show_wif_help=False):
        header_layout = QHBoxLayout()
        label = WWLabel(message)
        label.setMinimumWidth(400)
        header_layout.addWidget(label)
        if show_wif_help:
            header_layout.addWidget(InfoButton(WIF_HELP_TEXT),
                                    alignment=Qt.AlignRight)
        return self.text_input(title, header_layout, is_valid, allow_multi)

    @wizard_dialog
    def add_cosigner_dialog(self, run_next, index, is_valid):
        title = _("Add Cosigner") + " %d" % index
        message = ' '.join([
            _('Please enter the master public key (xpub) of your cosigner.'),
            _('Enter their master private key (xprv) if you want to be able to sign for them.'
              )
        ])
        return self.text_input(title, message, is_valid)

    @wizard_dialog
    def restore_seed_dialog(self, run_next, test):
        options = []
        if self.opt_ext:
            options.append('ext')
        if self.opt_bip39:
            options.append('bip39')
        title = _('Enter Seed')
        message = _(
            'Please enter your seed phrase in order to restore your wallet.')
        return self.seed_input(title, message, test, options)

    @wizard_dialog
    def confirm_seed_dialog(self, run_next, seed, test):
        self.app.clipboard().clear()
        title = _('Confirm Seed')
        message = ' '.join([
            _('Your seed is important!'),
            _('If you lose your seed, your money will be permanently lost.'),
            _('To make sure that you have properly saved your seed, please retype it here.'
              )
        ])
        seed, is_bip39, is_ext = self.seed_input(title, message, test, None)
        return seed

    @wizard_dialog
    def show_seed_dialog(self, run_next, seed_text):
        title = _("Your wallet generation seed is:")
        slayout = SeedLayout(
            seed=seed_text,
            title=title,
            msg=True,
            options=['ext'],
            config=self.config,
        )
        self.exec_layout(slayout)
        return slayout.is_ext

    def pw_layout(self, msg, kind, force_disable_encrypt_cb):
        pw_layout = PasswordLayout(
            msg=msg,
            kind=kind,
            OK_button=self.next_button,
            force_disable_encrypt_cb=force_disable_encrypt_cb)
        pw_layout.encrypt_cb.setChecked(True)
        try:
            self.exec_layout(pw_layout.layout(),
                             focused_widget=pw_layout.new_pw)
            return pw_layout.new_password(), pw_layout.encrypt_cb.isChecked()
        finally:
            pw_layout.clear_password_fields()

    @wizard_dialog
    def request_password(self, run_next, force_disable_encrypt_cb=False):
        """Request the user enter a new password and confirm it.  Return
        the password or None for no password."""
        return self.pw_layout(MSG_ENTER_PASSWORD, PW_NEW,
                              force_disable_encrypt_cb)

    @wizard_dialog
    def request_storage_encryption(self, run_next):
        playout = PasswordLayoutForHW(MSG_HW_STORAGE_ENCRYPTION)
        playout.encrypt_cb.setChecked(True)
        self.exec_layout(playout.layout())
        return playout.encrypt_cb.isChecked()

    @wizard_dialog
    def confirm_dialog(self, title, message, run_next):
        self.confirm(message, title)

    def confirm(self, message, title):
        label = WWLabel(message)
        vbox = QVBoxLayout()
        vbox.addWidget(label)
        self.exec_layout(vbox, title)

    @wizard_dialog
    def action_dialog(self, action, run_next):
        self.run(action)

    def terminate(self, **kwargs):
        self.accept_signal.emit()

    def waiting_dialog(self, task, msg, on_finished=None):
        label = WWLabel(msg)
        vbox = QVBoxLayout()
        vbox.addSpacing(100)
        label.setMinimumWidth(300)
        label.setAlignment(Qt.AlignCenter)
        vbox.addWidget(label)
        self.set_layout(vbox, next_enabled=False)
        self.back_button.setEnabled(False)

        t = threading.Thread(target=task)
        t.start()
        while True:
            t.join(1.0 / 60)
            if t.is_alive():
                self.refresh_gui()
            else:
                break
        if on_finished:
            on_finished()

    def run_task_without_blocking_gui(self, task, *, msg=None):
        assert self.gui_thread == threading.current_thread(
        ), 'must be called from GUI thread'
        if msg is None:
            msg = _("Please wait...")

        exc = None  # type: Optional[Exception]
        res = None

        def task_wrapper():
            nonlocal exc
            nonlocal res
            try:
                res = task()
            except Exception as e:
                exc = e

        self.waiting_dialog(task_wrapper, msg=msg)
        if exc is None:
            return res
        else:
            raise exc

    @wizard_dialog
    def choice_dialog(self, title, message, choices, run_next):
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        clayout = ChoicesLayout(message, c_titles)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        return action

    def query_choice(self, msg, choices):
        """called by hardware wallets"""
        clayout = ChoicesLayout(msg, choices)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, '')
        return clayout.selected_index()

    @wizard_dialog
    def derivation_and_script_type_gui_specific_dialog(
        self,
        *,
        title: str,
        message1: str,
        choices: List[Tuple[str, str, str]],
        hide_choices: bool = False,
        message2: str,
        test_text: Callable[[str], int],
        run_next,
        default_choice_idx: int = 0,
        get_account_xpub=None,
    ) -> Tuple[str, str]:
        vbox = QVBoxLayout()

        if get_account_xpub:
            button = QPushButton(_("Detect Existing Accounts"))

            def on_account_select(account):
                script_type = account["script_type"]
                if script_type == "p2pkh":
                    script_type = "standard"
                button_index = c_values.index(script_type)
                button = clayout.group.buttons()[button_index]
                button.setChecked(True)
                line.setText(account["derivation_path"])

            button.clicked.connect(lambda: Bip39RecoveryDialog(
                self, get_account_xpub, on_account_select))
            vbox.addWidget(button, alignment=Qt.AlignLeft)
            vbox.addWidget(QLabel(_("Or")))

        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        c_default_text = [x[2] for x in choices]

        def on_choice_click(clayout):
            idx = clayout.selected_index()
            line.setText(c_default_text[idx])

        clayout = ChoicesLayout(message1,
                                c_titles,
                                on_choice_click,
                                checked_index=default_choice_idx)
        if not hide_choices:
            vbox.addLayout(clayout.layout())

        vbox.addWidget(WWLabel(message2))

        line = QLineEdit()

        def on_text_change(text):
            self.next_button.setEnabled(test_text(text))

        line.textEdited.connect(on_text_change)
        on_choice_click(clayout)  # set default text for "line"
        vbox.addWidget(line)

        self.exec_layout(vbox, title)
        choice = c_values[clayout.selected_index()]
        return str(line.text()), choice

    @wizard_dialog
    def line_dialog(self,
                    run_next,
                    title,
                    message,
                    default,
                    test,
                    warning='',
                    presets=(),
                    warn_issue4566=False):
        vbox = QVBoxLayout()
        vbox.addWidget(WWLabel(message))
        line = QLineEdit()
        line.setText(default)

        def f(text):
            self.next_button.setEnabled(test(text))
            if warn_issue4566:
                text_whitespace_normalised = ' '.join(text.split())
                warn_issue4566_label.setVisible(
                    text != text_whitespace_normalised)

        line.textEdited.connect(f)
        vbox.addWidget(line)
        vbox.addWidget(WWLabel(warning))

        warn_issue4566_label = WWLabel(MSG_PASSPHRASE_WARN_ISSUE4566)
        warn_issue4566_label.setVisible(False)
        vbox.addWidget(warn_issue4566_label)

        for preset in presets:
            button = QPushButton(preset[0])
            button.clicked.connect(
                lambda __, text=preset[1]: line.setText(text))
            button.setMinimumWidth(150)
            hbox = QHBoxLayout()
            hbox.addWidget(button, alignment=Qt.AlignCenter)
            vbox.addLayout(hbox)

        self.exec_layout(vbox, title, next_enabled=test(default))
        return line.text()

    @wizard_dialog
    def show_xpub_dialog(self, xpub, run_next):
        msg = ' '.join([
            _("Here is your master public key."),
            _("Please share it with your cosigners.")
        ])
        vbox = QVBoxLayout()
        layout = SeedLayout(
            xpub,
            title=msg,
            icon=False,
            for_seed_words=False,
            config=self.config,
        )
        vbox.addLayout(layout.layout())
        self.exec_layout(vbox, _('Master Public Key'))
        return None

    def init_network(self, network: 'Network'):
        message = _("Electrum communicates with remote servers to get "
                    "information about your transactions and addresses. The "
                    "servers all fulfill the same purpose only differing in "
                    "hardware. In most cases you simply want to let Electrum "
                    "pick one at random.  However if you prefer feel free to "
                    "select a server manually.")
        choices = [_("Auto connect"), _("Select server manually")]
        title = _("How do you want to connect to a server? ")
        clayout = ChoicesLayout(message, choices)
        self.back_button.setText(_('Cancel'))
        self.exec_layout(clayout.layout(), title)
        r = clayout.selected_index()
        if r == 1:
            nlayout = NetworkChoiceLayout(network, self.config, wizard=True)
            if self.exec_layout(nlayout.layout()):
                nlayout.accept()
                self.config.set_key('auto_connect', network.auto_connect, True)
        else:
            network.auto_connect = True
            self.config.set_key('auto_connect', True, True)

    @wizard_dialog
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QLabel()
        m_label = QLabel()
        grid = QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)

        def on_m(m):
            m_label.setText(_('Require {0} signatures').format(m))
            cw.set_m(m)
            backup_warning_label.setVisible(cw.m != cw.n)

        def on_n(n):
            n_label.setText(_('From {0} cosigners').format(n))
            cw.set_n(n)
            m_edit.setMaximum(n)
            backup_warning_label.setVisible(cw.m != cw.n)

        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(
            WWLabel(
                _("Choose the number of signatures needed to unlock funds in your wallet:"
                  )))
        vbox.addLayout(grid)
        vbox.addSpacing(2 * char_width_in_lineedit())
        backup_warning_label = WWLabel(
            _("Warning: to be able to restore a multisig wallet, "
              "you should include the master public key for each cosigner "
              "in all of your backups."))
        vbox.addWidget(backup_warning_label)
        on_n(2)
        on_m(2)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)
Beispiel #60
-1
 def send(params):
     url = self.formatUrl(endpoint, params)
     request = QNetworkRequest(url)
     headers['User-Agent'] = 'Divi QGIS Plugin/%s' % PLUGIN_VERSION
     QgsMessageLog.logMessage(str(headers), 'DIVI')
     for key, value in headers.items():
         request.setRawHeader(key.encode('utf-8'), value.encode('utf-8'))
     if method == 'delete':
         reply = manager.sendCustomRequest(request, 'DELETE'.encode('utf-8'), data)
     else:
         if not data:
             reply = getattr(manager, method)(request)
         elif isinstance(data, QHttpMultiPart) == True:
             reply = getattr(manager, method)(request, data)
         elif isinstance(data, str) == True:
             reply = getattr(manager, method)(request, data.encode('utf-8'))
     loop = QEventLoop()
     reply.uploadProgress.connect(self.uploadProgress)
     reply.downloadProgress.connect(self.downloadProgress)
     reply.metaDataChanged.connect(self.metaDataChanged)
     #reply.error.connect(self._error)
     reply.finished.connect(loop.exit)
     self.abort_sig.connect( reply.abort )
     loop.exec_()
     self.abort_sig.disconnect( reply.abort )
     return reply