def train_model(self, UI=None):
        if self.k <= 0 and UI is not None:
            UI.msg = QMessageBox()
            UI.msg.setIcon(QMessageBox.Warning)
            UI.msg.setWindowTitle("Warning")
            UI.msg.setText(
                "Anda harus membagi data menggunakan k-fold terlebih dahulu")
            UI.msg.setStandardButtons(QMessageBox.Ok)
            UI.msg.show()
            UI.statusBar().showMessage("Train and test failed")
            return False

        self.attrs, self.clfs, self.tfidfs = ([0 for i in range(self.k)
                                               ], [0 for i in range(self.k)],
                                              [0 for i in range(self.k)])
        el = QEventLoop()
        for i in range(self.k):
            if UI is not None:
                UI.logOutput.append(f"Train tree {i + 1}")
            params = {'i': i, 'remove_zero_tfidf': True, 'UI': UI}
            worker = Worker(self.mltrain_fn, params)
            worker.signals.result.connect(self.mltrain_result)
            self.threadpool.start(worker)

        self.threadpool.waitForDone()
        el.processEvents()
        el.exit()
        if UI is not None:
            UI.logOutput.append("Training completed")
        return self.attrs
Exemple #2
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_()
Exemple #3
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_()
Exemple #4
0
class ang(QAxWidget):
    def __init__(self):
        # print("kiwoom() class start")
        print("hello")
        super().__init__()
        self.login_event_loop = None
        self.calculator_event_loop = QEventLoop()
        self.all_stock_dict = {}

        self.account_stock_dict = {}

        self.get_ocx_instance()
        self.event_slots()
        self.signal_login_commConnect()

        min_data = []

    def get_ocx_instance(self):
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

    def event_slots(self):
        self.OnEventConnect.connect(self.login_slot)
        self.OnReceiveMsg.connect(self.msg_slot)

    def signal_login_commConnect(self):
        self.dynamicCall("CommConnect()")
        self.login_event_loop = QEventLoop()
        self.login_event_loop.exec_()

    def login_slot(self, err_code):
        self.login_event_loop.exit()

    def stop_screen_cancel(self, sScrNo=None):
        self.dynamicCall("DisconnectRealData(QString)", sScrNo)

    def msg_slot(self, sScrNo, sRQName, sTrCode, msg):
        pass

    def get_min_data(self, code, tick, sPrevNext):
        self.dynamicCall("SetInputValue(QString, QString)", "종목코드", code)
        self.dynamicCall("SetInputValue(QString, QString)", "틱범위", tick)
        self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")
        self.dynamicCall("CommRqData(QString,QString,int,QString)", "주식분봉차트조회",
                         "opt10080", sPrevNext, "2000")
        self.calculator_event_loop.exec_()
        return self.min_data

    def tr_data_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        if sRQName == "주식분봉차트조회":
            code = self.dynamicCall(
                "GetCommData(QString, QString, int , QString)", sTrCode,
                sRQName, 0, "종목코드")
            data = self.dynamicCall("GetCommDataEx(QString, QString)", sTrCode,
                                    sRQName)
            self.min_data = data
            self.calculator_event_loop.exit()
Exemple #5
0
class TheReal(QAxWidget):
    def __init__(self):
        super().__init__()
        print(">> class TheReal() Start.")

        self.realType = RealType()
        self.real_screen_number = "1000"
        self.login_event_loop = QEventLoop()

        self.get_ocx_instance()
        self.event_slots()
        self.real_event_slot()
        self.login_signal()
        self.real_signal()

    def get_ocx_instance(self):
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

    def event_slots(self):
        self.OnEventConnect.connect(self.login_slot)

    def real_event_slot(self):
        self.OnReceiveRealData.connect(self.realdata_slot)

    def login_signal(self):
        self.dynamicCall("CommConnect()")
        self.login_event_loop.exec_()

    def login_slot(self, error_code):
        print(errors(error_code)[1])
        login_status = self.dynamicCall("GetConnectState()")
        print(">> 로그인 상태(0:연결안됨, 1:연결): %s" % login_status)
        self.login_event_loop.exit()

    def real_signal(self):
        print(">>>>> real_signal %s" %
              self.realType.REAL_TYPE["장시작시간"]["장운영구분"])
        value = self.dynamicCall("SetRealReg(QString,QString,QString,QString)",
                                 self.real_screen_number, ' ',
                                 self.realType.REAL_TYPE["장시작시간"]["장운영구분"],
                                 "0")
        print(value)

    def realdata_slot(self, stock_code, real_type, real_data):
        print(">> realdata_slot start.")
        print(">> stock_code: [%s]" % stock_code)
        print(">> real_type: [%s]" % real_type)
        print(">> real_data: [%s]" % real_data)

        value = self.dynamicCall("GetCommRealData(QString,int)", stock_code,
                                 self.realType.REAL_TYPE[real_type]["장운영구분"])
        print(">> value: %" % value)
Exemple #6
0
 def procedure(self, spc_path=None, opened_window=None):
     if opened_window is None:
         return None, "Error in 'set cell free position' action. No window is opened."
     if opened_window.window_type == "ms":
         # cfp 設定(イベントループ)
         continue_process = ["continue"]   # closeEvent関数内で、参照渡しできるように、リストにしておく。
         loop = QEventLoop()
         def closeEvent1(self, event=None):
             bool_popup = popups.WarningPopup("Closing the window will stop the batch.\nDo you really want to close?", title="warning", p_type="Bool")
             done = bool_popup.exec_()
             if done == 0x00004000:      # Yes
                 continue_process[0] = "Cell free position was not set."
                 self.toolbar_layout.cfp_setting_popup.close()
                 event.accept()
                 loop.exit()
             elif done == 0x00010000:    # No
                 event.ignore()
         def closeEvent2(self, event=None):
             close_or_not = opened_window.close()
             if close_or_not:
                 event.accept()
             else:
                 event.ignore()
         def cancelEvent():
             close_or_not = opened_window.close()
             if close_or_not:
                 loop.exit()
             else:
                 opened_window.toolbar_layout.set_CellFreePosition()
         opened_window.closeEvent = closeEvent1.__get__(opened_window, type(opened_window))
         result = opened_window.toolbar_layout.set_CellFreePosition()
         # cell free position が指定された時
         if result:
             pass
         # cell free position の window で cancel された時
         else:
             opened_window.closeEvent = closeEvent.__get__(opened_window, type(opened_window))
             loop.exit(0)
             return None, "Cell free position was not set."
         opened_window.toolbar_layout.cfp_setting_popup.closeEvent = closeEvent2.__get__(opened_window.toolbar_layout.cfp_setting_popup, type(opened_window.toolbar_layout.cfp_setting_popup))
         opened_window.toolbar_layout.cfp_setting_popup.btnOK.clicked.connect(lambda future: loop.exit())
         opened_window.toolbar_layout.cfp_setting_popup.btnCancel.clicked.connect(cancelEvent)
         loop.exec_()
         opened_window.closeEvent = closeEvent.__get__(opened_window, type(opened_window))
         return opened_window, continue_process[0]
     else:
         # opened_window.close()
         return None, "Invalid window type."
Exemple #7
0
class Test(QAxWidget):
    ge = None

    def __init__(self):
        super().__init__()

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

        self.OnEventConnect.connect(self.login_slot)
        self.OnReceiveConditionVer.connect(self.ce_slot)
        self.dynamicCall("CommConnect()")
        self.ge = QEventLoop()
        self.ge.exec_()

        self.dynamicCall("GetConditionLoad()")

    def ce_slot(self, f, a):
        print(self.dynamicCall("GetConditionNameList()"))

    def login_slot(self, a):
        self.ge.exit()
Exemple #8
0
class Main():
    __kiwoom = None
    __event_loop = None
    __condition_list = []
    __condition_stock_list = []

    def __init__(self):
        self.__kiwoom = QAxWidget("KHOPENAPI.KHOpenAPICtrl.1")
        self.__event_loop = QEventLoop()

        self.__kiwoom.OnEventConnect.connect(self.__login_slot)
        self.__kiwoom.OnReceiveConditionVer.connect(self.__condition_ver_slot)
        self.__kiwoom.OnReceiveTrCondition.connect(self.__send_condition_slot)

    def login(self):
        self.__kiwoom.dynamicCall("CommConnect()")
        self.__event_loop.exec_()

    def get_condition_list(self):
        self.__kiwoom.dynamicCall("GetConditionLoad()")
        self.__event_loop.exec_()
        return self.__condition_list

    def print_condition_list(self, cond):
        self.__kiwoom.dynamicCall("SendCondition(QString, QString, int, int)", "2000", cond[1], cond[0], 0)
        self.__event_loop.exec_()

        print("{} 조건식의 검색 결과".format(cond[1]))
        i = 1
        for stock in self.__condition_stock_list:
            print("{} ".format(stock), end="")
            if i % 10 == 0:
                print()
            i += 1

    def __condition_ver_slot(self, lRet, sMsg):
        condition_name_list: str
        condition_name_list = self.__kiwoom.dynamicCall("GetConditionNameList()")
        '''
        condition_name_list
        =
        인덱스^조건식이름;인덱스^조건식이름;
        '''

        for cond_index_name in condition_name_list[:-1].split(";"):
            self.__condition_list.append(cond_index_name.split("^"))

        self.__event_loop.exit()

    def __login_slot(self, errNo):
        self.__event_loop.exit()

    def __send_condition_slot(self, sScrNo, strCodeList, strConditionName, nIndex, nNext):
        self.__condition_stock_list = strCodeList.split(";")
        self.__event_loop.exit()
Exemple #9
0
def main():

    #initializing the GUI
    app = QApplication(sys.argv)

    # Create and display the splash screen
    splash = splashscreen.ArcaneSplashScreen('image/diamondgif.gif')
    splash.show()
    splash.showMessage("Arcane")

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

    # create logger
    logger = logging.getLogger('mainApp')
    logger.setLevel(logging.DEBUG)
    # create file handler
    fhandler = logging.FileHandler(LOG_FILENAME)
    fhandler.setLevel(logging.DEBUG)
    # create formatter
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    # add formatter to fhandler
    fhandler.setFormatter(formatter)
    # add fhandler to logger
    logger.addHandler(fhandler)

    ###############################
    logger.info('Program started.')

    mainObj = MainApplication()
    mainObj.show()

    splash.finish(
        mainObj
    )  #Makes the splash screen wait until the widget mainObj is displayed before calling close() on itself.

    sys.exit(app.exec_())

    pass
Exemple #10
0
class SphinxConverter(QObject):
    """This class converts Sphinx input to HTML. It is run in a separate
    thread.
    """
    # 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)

    def __init__(self, parent):
        super().__init__(parent)
        # Use an additional thread to process Sphinx output.
        self._ac = AsyncController('QThread', self)
        self._ac.defaultPriority = QThread.LowPriority
        self._SphinxInvocationCount = 1

    def terminate(self):
        # Free resources.
        self._ac.terminate()

    def convert(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 (filePath,
                    'No preview for this type of file.<br>Expected ' +
                    htmlFile + " or " + htmlFileAlter, 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()
Exemple #11
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)
Exemple #12
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
Exemple #13
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)
Exemple #14
0
class MainWindowDialog(MainWrappedUI):

    # Signal used when user close app after clicked esc or cros to close app.
    # If user clicked Yes on message box return True other way return False.

    closingSignal = pyqtSignal(QEvent)
    callSignal = pyqtSignal(bool)
    creditsSignal = pyqtSignal(bool)

    def __init__(self, client):
        super(MainWindowDialog, self).__init__()

        self.client = client
        self.prefix = ":/avatars/"

        self.loop = QEventLoop()

        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(lambda: self.loop.exit(1))

        self.table_widget_list_of_users.setIconSize(QSize(72, 72))
        self.table_widget_list_of_users.setSelectionBehavior(QTableView.SelectRows)


        self.set_menubar_about(self.showCredits)
        self.set_push_button_logout(self.closeApp)
        self.set_push_button_call(self.call_someone)

        """
        self.setWindowFlags(Qt.CustomizeWindowHint)


        self.setWindowFlags(
                QtCore.Qt.Window |
                QtCore.Qt.CustomizeWindowHint |
                QtCore.Qt.WindowTitleHint |
                QtCore.Qt.WindowCloseButtonHint |
                QtCore.Qt.WindowStaysOnTopHint
        )
        self.setAttribute(Qt.WA_TranslucentBackground)
        """
        # connect(self.close_event_message_box)
    def showCredits(self):
        self.creditsSignal.emit(True)

    def closeApp(self, event):
        print(event)
        self.close()

    def closeEvent(self, event):
        self.closingSignal.emit(event)

    def setUserName(self, user_name):
        self.userName = user_name

    def setUserAvatar(self, user_avatar):
        try:
            self.label_avatar.setPixmap(
                QtGui.QPixmap(self.prefix + str(user_avatar))
            )
        except Exception as err:
            print(err)

    def getList(self):
        payload = {"type": "d", "description": "GET"}
        data = json.dumps(payload).encode("utf-8")
        print("{*} MainWindow info : Sended data to server:", data)
        self.client.sendMessage(data)
        self.read()

    def call_someone(self):
        row = int(self.table_widget_list_of_users.currentRow())

        if row != None:
            try:
                where = self.table_widget_list_of_users.item(row, 0).text()
            except Exception as err:
                print(err)

            if where != None:
                print("Wybrano dzwonienie do ", where)
                payload = {"type": "d", "description": "INVITE", "call_to": where}
                data = json.dumps(payload).encode("utf-8")
                print(data)
                self.client.sendMessage(data)
                self.timer.start(5000)
                self.read()

    def waiting_for_signal(self):
        self.timer.start(10000)  # 10 second time-out

        print('{*} MainWindow info:  waiting for response')

        if self.loop.exec_() == 0:
            self.timer.stop()
            print("{*} MainWindow info: stop timer")
            return True
        else:
            print('{!} MainWindow error: time-out :(')
            return False

    def showConnectionStatus(self, status):
        # self.addWidget(self.statusBar)
        self.statusBar.showMessage(status)

    def read(self):
        if self.waiting_for_signal():
            print("{*} MainWindow getting from Server:", self.client.received)
            if self.client.status == "202 USERS":
                print("{*} MainWindow users: ", self.client.users)
                self.set_who_is_signed(self.client.who_signed)
                print("{*} MainWindow info: Users - ",self.client.users)
                self.add_row_to_list_of_users(self.client.users)

            elif self.client.status == "200 INVITE":
                status = "Nawiązywanie polaczenia"
                self.showConnectionStatus(status)
                print(status)
            elif self.client.status == "406 INVITE":
                status = "Nie można się połączyć z wybranym użytkownikiem"
                self.showConnectionStatus(status)
                print(status)
            elif self.client.status == "200 END":
                status = "Zakonczono polaczenie"
                self.showConnectionStatus(status)
                self.client.send_ack_on_close_conn()
                print(status)
            elif self.client.status == "402 NOT ACCEPTABLE":
                status = "Aktywuj swoje konto"
                self.showConnectionStatus(status)
                print(status)

        else:
            status = "Serwer nie odpowiada"
            self.showConnectionStatus(status)
            print(status)
            print("{!} MainWindow error: Didn't get response")

    def keyPressEvent(self, event):
        """
            Close application from escape key.
        """
        if event.key() == Qt.Key_Escape:
            self.close()
Exemple #15
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_()
Exemple #16
0
class OPT30009(QAxWidget):
    def __init__(self):
        super().__init__()

        # 전역변수 모음
        self.account_number = None
        self.total_days = 0
        self.stock_day_list = []
        self.target_code_list = []
        self.market_type = None
        self.stock_day_table = "stock_day_opt30009"

        # 종목 일봉 데이터 수집 화면번호 지정
        self.stock_day_screen_number = "4000"
        
        # 짤려서 현재 실행할 순번 1001, 569
        # 2020-10-05 23:27
        # 2020-10-06 22:16 ~
        # 2020-10-08 22:58 ~ 2020-10-09 00:30 완료
        # 2020-10-12 MON
        # 2020-10-13 TUE 20:01 ~
        self.current_day = 0

        # 수집 대상 기간 일수
        self.days = 0

        # 로그인 Event Loop 실행
        self.login_event_loop = QEventLoop()

        
        self.day_info_event_loop = QEventLoop()

        # Kiwoom OCX 사용 함수 호출
        self.get_ocx_instance()

        # Event Slots
        self.event_slots()

        # 로그인 signal 함수 호출
        self.signal_login_commConnect()

        # 계좌정보 가져오기
        self.get_login_info()

        # 전체 종목 수집 및 분석을 위한 전체 종복 정보 가져오기
        self.get_day_info_by_stock(sPrevNext="0")

    def get_ocx_instance(self):
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

    def event_slots(self):
        self.OnEventConnect.connect(self.login_event_slot)
        self.OnReceiveTrData.connect(self.trdata_slot)

    def signal_login_commConnect(self):
        self.dynamicCall("CommConnect()")
        self.login_event_loop.exec_()

    def login_event_slot(self, error_code):
        self.login_event_loop.exit()

    def get_login_info(self):
        accounts = self.dynamicCall("GetLoginInfo(QString)", "ACCNO")
        self.account_number = accounts.split(";")[0]

    def get_day_info_by_stock(self, sPrevNext="0"):
        QTest.qWait(1000) # 3600 Delay Time

        if(sPrevNext == '0'):
            print("순위,종목코드,종목명,현재가,대비기호,전일대비,등락률,매도잔량,매수잔량,거래량,거래대금")

        ############################## opt30009 : ELW등락율순위요청 ##############################
        # 정렬구분 = 1:상승률, 2:상승폭, 3:하락률, 4:하락폭
        self.dynamicCall("SetInputValue(QString,QString)", "정렬구분", "1")

        # 권리구분 = 000:전체, 001:콜, 002:풋, 003:DC, 004:DP, 006:조기종료콜, 007:조기종료풋
        self.dynamicCall("SetInputValue(QString,QString)", "권리구분", "000")

        # 거래종료제외 = 1:거래종료제외, 0:거래종료포함
        self.dynamicCall("SetInputValue(QString,QString)", "거래종료제외", "1")

        self.dynamicCall("CommRqData(QString,QString,int,QString)", "ELW등락율순위요청", "opt30009", sPrevNext, self.stock_day_screen_number)
        self.day_info_event_loop.exec_()

    def trdata_slot(self, screen_number, tr_name, tr_code, record_name, sPrevNext):

        # 멀티데이터 일봉 차트 가져오기
        multi_cnt = self.dynamicCall("GetRepeatCnt(QString,QString)", tr_code, tr_name)

        for i in range(multi_cnt):
            data = []
            output01 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "순위").strip()
            output02 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "종목코드").strip()
            output03 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "종목명").strip()
            output04 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "현재가").strip()
            output05 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "대비기호").strip()
            output06 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "전일대비").strip()
            output07 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "등락률").strip()
            output08 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "매도잔량").strip()
            output09 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "매수잔량").strip()
            output10 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "거래량").strip()
            output11 = self.dynamicCall("GetCommData(QString,QString,int,QString)", tr_code, tr_name, i, "거래대금").strip()

            self.total_days = self.total_days + 1

            data.append("")
            data.append(output01)
            data.append(output02)
            data.append(output03)
            data.append(output04)
            data.append(output05)
            data.append(output06)
            data.append(output07)
            data.append(output08)
            data.append(output09)
            data.append(output10)
            data.append(output11)
            data.append("")

            self.stock_day_list.append(data.copy())

            if int(output11) > 0:
                print("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s" % (output01, output02, output03, output04, output05, output06, output07, output08, output09, output10, output11))

            if self.total_days == self.days:
#                 print(">> %s,%s,%s,%s,%s,%s,%s,%s,%s " % ((i+1),stock_code,date,close_price,open_price,high_price,low_price,volume,trading_value))
                sPrevNext = "0"
                break

        if sPrevNext == "2":
            self.get_day_info_by_stock(sPrevNext=sPrevNext)
        else:
            self.day_info_event_loop.exit()

    def insert_stock_day_list(self):
        conn_string = "host='localhost' dbname='postgres' user='******' password='******' port='5432'"
        conn = psycopg2.connect(conn_string)
        cur = conn.cursor()

        day_num = 0
        for stock_day in self.stock_day_list:
            
            delete_day_sql = "delete from "+self.stock_day_table+" where stock_code = %s and dt = %s"
            cur.execute(delete_day_sql, (stock_day[1], stock_day[3],))
            
            day_num = day_num + 1
            sql = "insert into "+self.stock_day_table+" (stock_code,stock_name,day_num,dt,volume,trading_value,close_price,open_price,high_price,low_price,market_type) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
            cur.execute(sql,(stock_day[1],stock_day[2],day_num,stock_day[3],stock_day[4],stock_day[5],stock_day[6],stock_day[7],stock_day[8],stock_day[9],stock_day[10]))

        conn.commit()
        cur.close()
        conn.close()
        
    def update_day_num(self):
        conn_string = "host='localhost' dbname='postgres' user='******' password='******' port='5432'"
        conn = psycopg2.connect(conn_string)
        cur = conn.cursor()
        
        # 시장유형의 모든 종목 입력 후 시장유형 별 일봉 순번 갱신
        update_day_sql  = " update "+self.stock_day_table+" s              "
        update_day_sql += "    set day_num = a.day_num                     "
        update_day_sql += "   from                                         "
        update_day_sql += "      (                                         "
        update_day_sql += "        select row_number() over(partition by stock_code order by dt desc) as day_num  "
        update_day_sql += "             , stock_code                       "
        update_day_sql += "             , dt                               "
        update_day_sql += "             , market_type                      "
        update_day_sql += "          from "+self.stock_day_table+"         "
        update_day_sql += "         where market_type = %s                 "
        update_day_sql += "      ) as a                                    "
        update_day_sql += "  where s.stock_code = a.stock_code             "
        update_day_sql += "    and s.dt = a.dt                             "
        update_day_sql += "    and s.market_type = a.market_type           "
        update_day_sql += "    and s.market_type = %s                      "
        
        cur.execute(update_day_sql, (self.market_type, self.market_type,))

        conn.commit()
        cur.close()
        conn.close()        
Exemple #17
0
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard,
                    TermsAndConditionsMixin):
    accept_signal = pyqtSignal()

    def __init__(self, config: 'SimpleConfig', app: QApplication,
                 plugins: 'Plugins', **kwargs):
        QDialog.__init__(self, parent=kwargs.get('parent', None))
        if kwargs.get('parent', None):
            self.setWindowModality(QtCore.Qt.ApplicationModal)
        BaseWizard.__init__(self, config, plugins)
        self.app = app
        self.config = config
        if kwargs.get('minimum_size', None):
            self.setMinimumSize(*kwargs.get('minimum_size'))
        else:
            self.setMinimumSize(600, 400)
        self.accept_signal.connect(self.accept)
        self.title = QLabel()
        self.main_widget = QWidget()
        self.back_button = QPushButton(self)
        self.next_button = QPushButton(self)
        self.next_button.text()
        self.next_button.setDefault(True)
        self.logo = QLabel()
        self.please_wait = QLabel()
        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()
        if not kwargs.get('turn_off_icon', None):
            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)
        if kwargs.get('turn_off_icon', None):
            outer_vbox.addLayout(inner_vbox)
        else:
            outer_vbox.addLayout(hbox)
        outer_vbox.addLayout(Buttons(self.back_button, self.next_button))
        self._add_advanced_button()
        self.set_icon('electrum.png')
        self._set_gui_text()
        self.show()
        self.raise_()
        self.refresh_gui()  # Need for QT on MacOSX.  Lame.
        LastChosenState.clear()

    def _set_gui_text(self):
        self.setWindowTitle('Electrum  -  ' + _('Install Wizard'))
        self.back_button.setText(
            _('Back') if self.can_go_back() else _('Cancel'))
        self.next_button.setText(_("Next"))
        self.please_wait.setText(_("Please wait..."))
        self._translate_advanced_options()

    def select_and_save_language(self):
        """Method for selecting and saving language in config file as {'language': <language-abbreviation: str>}"""
        filtered_languages = dict(
            filter(lambda item: item[1] != 'Default', languages.items()))
        language_abbreviations = list(filtered_languages.keys())
        default_language = get_default_language()
        vbox = QVBoxLayout()
        cb = QComboBox()
        cb.addItems(filtered_languages.values())
        cb.setCurrentIndex(language_abbreviations.index(default_language))
        vbox.addWidget(cb)

        def on_change():
            language_abbreviation = language_abbreviations[cb.currentIndex()]
            set_language(language_abbreviation)
            self._set_gui_text()
            self.refresh_gui()

        cb.currentIndexChanged.connect(on_change)
        # refresh config language
        on_change()
        pushed_button = self.exec_layout(
            vbox, title=_('Select installation language'))
        if pushed_button == PushedButton.NEXT:
            self.config.set_key('language',
                                language_abbreviations[cb.currentIndex()])

    def select_storage(
            self, path,
            get_wallet_from_daemon) -> Tuple[str, Optional[WalletStorage]]:

        vbox = QVBoxLayout()
        hbox = QHBoxLayout()
        label = QLabel(
            _('Enter the name of the new wallet or load an existing wallet by clicking "Choose".'
              ))
        label.setWordWrap(True)
        vbox.addWidget(label)
        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 = WWLabel('')
        vbox.addWidget(self.msg_label)
        hbox2 = QHBoxLayout()
        self.pw_e = QLineEdit('', self)
        self.pw_e.setFixedWidth(17 * char_width_in_lineedit())
        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'))

        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:
                self.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
            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, manual_upgrades=True)
            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)}'
            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')
            self.msg_label.setText(msg)
            if user_needs_to_enter_password:
                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)
        self.name_e.setText(os.path.basename(path))

        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 = self.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 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')

        return temp_storage.path, (temp_storage
                                   if temp_storage.file_exists() else None)

    def run_upgrades(self, storage):
        path = storage.path
        if storage.requires_split():
            self.hide()
            msg = _(
                "The wallet '{path}' 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=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 '{path}' contains an incompletely created wallet.\n"
                "Do you want to complete its creation now?").format(path=path)
            if not self.question(msg):
                if self.question(
                        _("Do you want to delete '{path}'?").format(
                            path=path)):
                    os.remove(path)
                    self.show_warning(_('The file was removed'))
                return
            self.show()
            self.data = storage.db.data  # FIXME
            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 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):
            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):
        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, on_import=False):
        imported_wallet = self.wallet_type if on_import else None
        slayout = SeedLayout(title=message,
                             is_seed=is_seed,
                             options=options,
                             parent=self,
                             imported_wallet=imported_wallet)
        if imported_wallet == 'standard':
            slayout.show_default_options()
            self.exec_advanced_layout(
                layout=slayout,
                default_show_function=slayout.show_default_options,
                advanced_show_function=slayout.show_advanced_options,
                title=title,
                next_enabled=False)
            return slayout.get_seed(
            ), slayout.is_bip39, slayout.is_ext, slayout.is_gold_wallet_import
        else:
            self.exec_layout(slayout, title, next_enabled=False)
        if self.wallet_type in ['2-key', '3-key']:
            return slayout.get_seed(
            ), slayout.is_gold_wallet_import, slayout.is_ext
        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(get_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, on_import=True)

    @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's generated 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."""
        msg_enter_password = (
            _("Choose a password to encrypt your wallet keys.") + '\n' +
            _("Leave this field empty if you want to disable encryption."))
        return self.pw_layout(msg_enter_password, PW_NEW,
                              force_disable_encrypt_cb)

    @wizard_dialog
    def request_storage_encryption(self, run_next):
        msg_hw_storage_encryption = (_(
            "Set wallet file encryption."
        ) + '\n' + _(
            "Your wallet file does not contain secrets, mostly just metadata. "
        ) + _(
            "It also contains your master public key that allows watching your addresses."
        ) + '\n\n' + _(
            "Note: If you enable this setting, you will need your hardware device to open your wallet."
        ))
        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()

    @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]
        id_ = self.compute_window_id(message, c_titles,
                                     list(self.data.values()))
        checked_index = LastChosenState.get_index(id_)
        clayout = ChoicesLayout(message, c_titles, checked_index=checked_index)
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_layout(vbox, title)
        action = c_values[clayout.selected_index()]
        LastChosenState.set_state(id_, clayout.selected_index())
        return action

    @wizard_dialog
    def choice_dialog_with_advanced_options(self, title, message, base_choices,
                                            advanced_choices, run_next):
        choices = base_choices + advanced_choices
        c_values = [x[0] for x in choices]
        c_titles = [x[1] for x in choices]
        id_ = self.compute_window_id(message, c_titles,
                                     list(self.data.values()))
        checked_index = LastChosenState.get_index(id_)
        clayout = ChoicesLayout(message, c_titles, checked_index=checked_index)
        if checked_index < len(base_choices):
            clayout.show_index(len(base_choices))
            self._show_advanced_text()
        else:
            clayout.show_index(len(choices))
            self._hide_advanced_text()
        vbox = QVBoxLayout()
        vbox.addLayout(clayout.layout())
        self.exec_advanced_layout(
            layout=vbox,
            default_show_function=lambda: clayout.show_index(len(base_choices)
                                                             ),
            advanced_show_function=lambda: clayout.show_index(len(choices)),
            title=title,
        )
        action = c_values[clayout.selected_index()]
        LastChosenState.set_state(id_, clayout.selected_index())
        return action

    @wizard_dialog
    def get_recovery_pubkey(self, run_next, instant_key=None):
        # todo move it to some global settings ?
        web_generator_url = 'https://keygenerator.bitcoinvault.global/'
        label = QLabel()
        message = _(
            'Please paste a Cancel Transaction Key. Use an existing one, if you are importing a wallet, '
            'or generate a new one at')
        message += f' <a href="{web_generator_url}">{web_generator_url}</a>'
        label.setText(message)
        label.setOpenExternalLinks(True)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setWordWrap(True)

        disallowed_keys = [instant_key] if instant_key else []
        layout = InsertPubKeyDialog(self,
                                    message_label=label,
                                    disallowed_keys=disallowed_keys)
        self.exec_layout(layout,
                         _('Cancel Transaction Key'),
                         next_enabled=False)
        return layout.get_compressed_pubkey()

    @wizard_dialog
    def get_instant_pubkey(self, run_next, recovery_key=None):
        # todo move it to some global settings ?
        web_generator_url = 'https://keygenerator.bitcoinvault.global/'
        label = QLabel()
        message = _(
            'Please paste a Fast Transaction Key. Use an existing one, if you are importing a wallet, '
            'or generate a new one at')
        message += f' <a href="{web_generator_url}">{web_generator_url}</a>.'
        label.setText(message)
        label.setOpenExternalLinks(True)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setWordWrap(True)

        disallowed_keys = [recovery_key] if recovery_key else []
        layout = InsertPubKeyDialog(self,
                                    message_label=label,
                                    disallowed_keys=disallowed_keys)
        self.exec_layout(layout, _('Fast Transaction Key'), next_enabled=False)
        return layout.get_compressed_pubkey()

    @wizard_dialog
    def get_authenticator_pubkey(self, run_next, disallowed_key=None):
        # todo move it to some global settings ?
        label = QLabel()
        message = _(
            'Please paste authenticator public key exported from your Gold Wallet application'
        )
        label.setText(message)
        label.setOpenExternalLinks(True)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setWordWrap(True)

        disallowed_keys = [disallowed_key] if disallowed_key else []
        layout = InsertPubKeyDialog(self,
                                    message_label=label,
                                    disallowed_keys=disallowed_keys)
        self.exec_layout(layout,
                         _('Gold Wallet authenticator public key'),
                         next_enabled=False)
        return layout.get_compressed_pubkey()

    @wizard_dialog
    def display_2fa_pairing_qr(self, run_next, entropy: bytes):
        title_label = QLabel()
        title_message = _(
            'Generate an authenticator on Gold Wallet and scan the following QR code: '
        )
        title_label.setText(title_message)
        title_label.setWordWrap(True)

        pin_label = QLabel()
        pin_message = _('Confirmation PIN from Gold Wallet:')
        pin_label.setText(pin_message)

        qr_data = {
            "name": os.path.basename(self.path),
            "entropy": entropy,
        }
        layout = Qr2FaDialog(self,
                             title_label=title_label,
                             pin_label=pin_label,
                             qr_data=qr_data)
        if self.wallet_type == '2-key':
            title = _('Pair your 2-Key Vault wallet with Gold Wallet')
        elif self.wallet_type == '3-key':
            title = _('Pair your 3-Key Vault wallet with Gold Wallet')
        else:
            raise ValueError(f'Wrong wallet type {self.wallet_type}')
        self.exec_layout(layout, title, next_enabled=False)
        return layout.get_pubkey()

    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(
            _("Warning") + ": " +
            _("You have multiple consecutive whitespaces or leading/trailing "
              "whitespaces in your passphrase.") + " " +
            _("This is discouraged.") + " " +
            _("Due to a bug, old versions of Electrum will NOT be creating the "
              "same wallet as newer versions or other software."))
        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 Standard"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)
Exemple #18
0
        ).setSectionResizeMode(QHeaderView.Stretch)
        # self.home_page.ui.members_list.resizeRowsToContents()
        self.all_payments.ui.all_payments_view.setSelectionBehavior(
            QTableView.SelectRows)
        self.all_payments.ui.all_payments_view.font().setPointSize(42)
        self.all_payments.ui.all_payments_view.setSortingEnabled(True)


if __name__ == "__main__":
    import sys, time

    app = QApplication(sys.argv)

    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, [4],
                     callback=lambda exitCode: initLoop.exit(exitCode))
    initLoop.exec_()

    a = TheApp()
    a.home_page.show()
    splash.finish(a)
    app.exec_()
Exemple #19
0
class Kiwoom(QAxWidget):
    """ 싱글톤 패턴 적용 """

    __instance = None

    @classmethod
    def __getInstance(cls):
        return cls.__instance

    @classmethod
    def instance(cls, *args, **kwargs):
        cls.__instance = cls(*args, **kwargs)
        cls.instance = cls.__getInstance
        return cls.__instance

    def __init__(self, API_SERVER_URL, API_SERVER_PORT):

        super().__init__()
        self.API_SERVER_URL = API_SERVER_URL
        self.API_SERVER_PORT = API_SERVER_PORT
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
        self.connectState = 0  # 최초에는 미접속
        # old process kill
        # self.__killOldProcess()

        # Loop 변수: 비동기 방식으로 동작되는 이벤트를 동기화
        self.logingLoop = None
        self.requestLoop = None
        self.orderLoop = None
        self.conditionLoop = None

        # 서버구분
        self.serverStatus = None

        # 연속조회구분
        self.isNext = 0

        # logging 클래스
        self.homepath = os.environ.get('userprofile')
        self.logger = Logger(path=self.log_path, name="Kiwoom")

        # API 요청 제한 관리 Queue (1초 5회, 1시간 1,000회)
        self.requestDelayCheck = APIDelayCheck(logger=self.logger)
        self.orderDelayCheck = APIDelayCheck(logger=self.logger)

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

        # Event 처리
        self.OnEventConnect.connect(self.eventConnect)
        self.OnReceiveTrData.connect(self.eventReceiveTrData)
        self.OnReceiveRealData.connect(self.eventReceiveRealData)
        self.OnReceiveConditionVer.connect(self.eventReceiveConditionVer)
        self.OnReceiveTrCondition.connect(self.eventReceiveTrCondition)
        self.OnReceiveRealCondition.connect(self.eventReceiveRealCondition)
        self.OnReceiveChejanData.connect(self.eventReceiveChejanData)
        self.OnReceiveMsg.connect(self.eventReceiveMsg)

    @property
    def log_path(self):
        path = os.path.join(self.homepath, '.kiwoom_log')
        if not os.path.exists(path):
            os.mkdir(path)
        return path

    @property
    def order_log_path(self):
        path = os.path.join(self.homepath, '.kiwoom_order_log')
        if not os.path.exists(path):
            os.mkdir(path)
        return path

    ###############################################################
    ################### Send log to api server ####################
    ###############################################################
    def send_log(self, log):
        response = requests.post(
            'http://{}:{}/kiwoom_info/receive_response'.format(
                self.API_SERVER_URL, self.API_SERVER_PORT),
            json=json.dumps(log, ensure_ascii=False),
            headers=headers)
        if response.status_code == 200:
            print('Send log success')
        else:
            print('Send log failed = {}'.format('!'))

    ###############################################################
    ################### 이벤트 발생 시 메서드   #####################
    ###############################################################

    def eventConnect(self, returnCode):
        """ 통신 연결 상태 변경시 이벤트
        returnCode가 0이면 로그인 성공
        그 외에는 ReturnCode 클래스 참조.

        Parameters
        ----------
        returnCode: int 
            0이면 로그인 성공, 이외에는 로그인 실패
        """

        if returnCode == 0:
            msg = "{} Connection Successful".format(dt.now())
            self.connectState = 1
        else:
            errorName = getattr(ReturnCode, "CAUSE").get(returnCode)
            msg = "{} Connection Failed : {}".format(dt.now(), errorName)
            self.connectState = 0

        self.logger.debug(msg)

        try:
            self.loginLoop.exit()
            print('loginLoop Exited')
        except AttributeError as e:
            print('Attribute error after tr receive = {}', e)
            return

    def eventReceiveMsg(self, scrNo, rqName, trCode, msg):
        """ 수신 메시지 이벤트
        서버로 어떤 요청을 했을 때(로그인, 주문, 조회 등),
        그 요청에 대한 처리내용을 전달해준다.

        Parameters
        ----------
        scrNo: str
            화면번호(4자리, 사용자 정의,
            서버에 조회나 주문을 요청할 때 이 요청을 구별하기 위한 키값)
        rqName: str 
            TR 요청명(사용자 정의)
        trCode: str
        msg: str 
            서버로 부터의 메시지
        """
        if hasattr(self, "orderResponse"):
            self.orderResponse.update({"msg": msg})

        self.logger.debug(msg)

    def eventReceiveTrData(self, scrNo, rqName, trCode, recordName, inquiry,
                           **kwargs):
        """
        TR 수신 이벤트시 실행되는 매서드

        조회요청 응답을 받거나 조회데이터를 수신했을 때 호출됩니다.
        rqName과 trCode는 getCommData() 메소드의 매개변수와 매핑되는 값 입니다.

        요청한 TR에 대해 수신된 데이터는 self.{trCode}에 저장됩니다.
        수신된 데이터는 모두 str 타입으로 사용자가 원하는 형태로 2차 가공이 필요합니다.

        trCode 및 rqName과 관련된 자세한 내용은 OPEN API+ 개발가이드 및
        KOA StudioSA를 참고하시기 바랍니다.

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

        # 주문 이벤트인 경우
        if "ORD" in trCode:
            # 주문번호 획득, 주문번호가 존재하면 주문 성공
            orderNo = self.getCommData(trCode, "", 0, "주문번호")
            self.orderResponse.update({"orderNo": orderNo})
            try:
                self.orderLoop.exit()
                print('orderLoop Exited')
            except AttributeError as e:
                print('Attribute error after tr receive = {}', e)
                pass
            return

        # 취소 이벤트인 경우
        if "KOA_NORMAL_KP_CANCEL" in trCode:
            # 주문번호 획득, 주문번호가 존재하면 주문 성공
            orderNo = self.getCommData(trCode, "", 0, "주문번호")
            self.orderResponse.update({"orderNo": orderNo})
            try:
                self.orderLoop.exit()
                print('orderLoop Exited')
            except AttributeError as e:
                print('Attribute error after tr receive = {}', e)
                pass
            return

        # 수정 이벤트인 경우
        if "KOA_NORMAL_KP_MODIFY" in trCode:
            # 주문번호 획득, 주문번호가 존재하면 주문 성공
            orderNo = self.getCommData(trCode, "", 0, "주문번호")
            self.orderResponse.update({"orderNo": orderNo})
            try:
                self.orderLoop.exit()
                print('orderLoop Exited')
            except AttributeError as e:
                print('Attribute error after tr receive = {}', e)
                pass
            return

        # TR 이벤트인 경우, orderResponse를 삭제
        if hasattr(self, "orderResponse"):
            delattr(self, "orderResponse")

        # TR Data 수신
        if trCode == "OPTKWFID":
            data = self.__getOPTKWFID(trCode, rqName)
        else:
            data = self.__getData(trCode, rqName)
        setattr(self, trCode, data)

        self.isNext = 0 if ((inquiry == "0") or
                            (inquiry == "")) else 2  # 추가조회 여부

        # TR loop 탈출
        try:
            self.requestLoop.exit()
            print('requestLoop Exited')
        except AttributeError as e:
            print('Attribute error after tr receive = {}', e)
            pass

        # TR 이벤트 logging
        # eventDetail = {
        #     "TIME": dt.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
        #     "BASC_DT": dt.now().strftime("%Y-%m-%d"),
        #     "EVENT": "eventReceiveTrData",
        #     "REQUEST_NAME": rqName,
        #     "TR_CODE": trCode,
        # }
        # print(eventDetail)

    def eventReceiveRealData(self, code, realType, realData):
        """ 설정된 종목에 대한 실시간 정보 수신시 발생하는 이벤트

        Parameters
        ----------
        code: str
            종목코드
        realType: str
            fid의 갯수
        realData: str
            fidList 구분은 \t 이다.
        """
        print('code = ', code)
        print('realType = ', realType)

        if realType in RealType.FIDS:
            fidList = getattr(RealType, realType)

            resultDict = {}
            for (k, v) in fidList.items():
                resultDict[v] = self.getCommRealData(realType, k)

            print('realType = {}, return = {}'.format(realType, resultDict))
            resultDict["BASC_DT"] = dt.now().strftime("%Y-%m-%d")
            self.send_log({
                'log_type': 'realtime',
                'code': code,
                'realType': realType,
                'log': resultDict
            })
            # print('log response in realtype = ', response)

    def eventReceiveChejanData(self, gubun, itemCnt, fidList):
        """ 주문 접수/확인 수신시 이벤트
        주문요청후 주문접수, 체결통보, 잔고통보를 수신할 때 마다 호출됩니다.

        Parameters
        ----------
        gubun: str
            체결구분('0': 주문접수/주문체결, '1': 잔고통보, '3': 특이신호)
        itemCnt: int
            fid의 갯수
        fidList: str
            fidList 구분은 ;(세미콜론) 이다.
        """
        if gubun != '0':  # 주문접수/주문체결이 아니면 logging 안함
            return

        print('fid List in receive chejan data = ', fidList)
        orderStatus = self.getChejanData(
            '913').strip()  # 주문상태 "접수" or "체결" or "확인"
        print('orderStatus in chejan data = ', orderStatus)
        if orderStatus == '접수':
            fidDict = getattr(FidList, 'SUBMITTED')
        elif orderStatus == '체결':
            fidDict = getattr(FidList, 'EXECUTED')
        elif orderStatus == '확인':  #주문취소
            fidDict = getattr(FidList, 'CANCELLED')
        else:
            fidDict = getattr(FidList, 'ALL')

        print('fid dict for chejan data = ', fidDict)

        resultDict = {"BASC_DT": dt.now().strftime("%Y-%m-%d")}
        fids = fidList.split(";")

        for fid in fids:
            fidName = fidDict.get(fid)
            if fidName is None:
                continue
            data = self.getChejanData(fid).strip()
            resultDict[fidName] = data

        self.send_log({'log_type': 'chejan', 'log': resultDict})

    ###############################################################
    #################### 로그인 관련 메서드   ######################
    ###############################################################

    def commConnect(self):
        """ 로그인 시도 """

        if not self.connectState:
            self.dynamicCall("CommConnect()")
            self.loginLoop = QEventLoop()
            self.loginLoop.exec_()  # eventConnect에서 loop를 종료

    def commTerminate(self):
        """ 로그인 시도 """

        if not self.connectState:
            self.dynamicCall("CommTerminate()")
            self.loginLoop = QEventLoop()
            self.loginLoop.exec_()  # eventConnect에서 loop를 종료

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

        Returns
        ----------
        int
            0(미연결), 1(연결)
        """
        return self.dynamicCall("GetConnectState()")

    @property
    def accNo(self):
        return self.getLoginInfo("ACCNO").rstrip(";")

    def getLoginInfo(self, tag):
        """ 사용자의 tag에 해당하는 정보를 반환한다.
        tag에 올 수 있는 값은 아래와 같다.
        ACCOUNT_CNT, ACCNO, USER_ID, USER_NAME, GetServerGubun

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

        Returns
        -----------
        info : str
            입력한 tag에 대응하는 정보
        """

        if not self.connectState:  # 1: 연결, 0: 미연결
            raise KiwoomConnectError()

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

        if tag == "GetServerGubun":
            info = self.getServerGubun()
        else:
            info = self.dynamicCall('GetLoginInfo("{}")'.format(tag))
        return info

    def getServerGubun(self):
        """ 서버구분 정보를 반환한다.

        Returns
        ----------
        server_status: str
            "1": 모의투자서버 else: 실서버
        """

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

    ###############################################################
    ################### TR(조회) 관련 메서드   #####################
    ## 시세조회, 관심종목 조회, 조건검색 등 합산 조회수 1초 5회 제한 ##
    ###############################################################

    # 점검방법은 CommRqData()함수와 CommKwRqData()함수를 이용한 조회횟수를
    # 합산하는 것으로 연속조회 역시 CommRqData()를 이용므로 합산됩니다.

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

        Parameters
        ----------
        key: str
            TR에 명시된 input 이름, ex) 계좌번호, 종목코드
        value: str
            key에 해당하는 값, ex) 88231524, 005930
        """

        if not isinstance(key, str):
            key = str(key)

        if not isinstance(value, str):
            value = str(value)

        if (key == "계좌번호") and (value not in self.accNo.split(';')):
            raise KiwoomProcessingError("ERROR: Invalid 계좌번호")

        if (key == "종목코드") and (value not in self.codes):
            raise KiwoomProcessingError("ERROR: Invalid 종목코드")

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

    def commRqData(self, rqName, trCode, inquiry, scrNo):
        """ 키움서버에 TR 요청을 한다.
        요청한 데이터는 데이터 수신 이벤트 발생 시 eventReceiveTrData 매서드에서 처리

        1초에 5회 제한

        Parameters
        ----------
        rqName: str
            TR 요청명(사용자 정의)
        trCode: str
        inquiry: int
            조회(0: 조회, 2: 남은 데이터 이어서 요청)
        scrNo: str
            화면번호(4자리)

        Returns
        ----------
        returnCode: str
            0(정상), -200(시세과부하), -201(조회전문작성 에러)
        """

        if not self.connectState:
            raise KiwoomConnectError()

        if not (isinstance(rqName, str) and isinstance(trCode, str)
                and isinstance(inquiry, int) and isinstance(scrNo, str)):
            raise ParameterTypeError()

        # API 제한 확인
        self.requestDelayCheck.checkDelay()

        returnCode = self.dynamicCall(
            "CommRqData(QString, QString, int, QString)",
            rqName,
            trCode,
            inquiry,
            scrNo,
        )

        if returnCode != 0:  # 0이외엔 실패
            self.logger.error(
                "{} commRqData {} Request Failed!, CAUSE: {}".format(
                    dt.now(), rqName,
                    getattr(ReturnCode, "CAUSE").get(returnCode)))
            raise KiwoomProcessingError()

        # 루프 생성: eventReceiveTrData() 메서드에서 루프를 종료시킨다.
        self.logger.debug("{}  commRqData {}".format(dt.now(), rqName))
        self.requestLoop = QEventLoop()
        self.requestLoop.exec_()
        print('requestLoop entered')

    def getRepeatCnt(self, trCode, rqName):
        """ 서버로 부터 전달받은 데이터의 갯수를 리턴합니다.(멀티데이터의 갯수)
        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        키움 OpenApi+에서는 데이터를 싱글데이터와 멀티데이터로 구분합니다.
        싱글데이터란, 서버로 부터 전달받은 데이터 내에서, 중복되는 키(항목이름)가
        하나도 없을 경우. 예를들면, 데이터가 '종목코드', '종목명', '상장일',
        '상장주식수' 처럼 키(항목이름)가 중복되지 않는 경우를 말합니다.

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

        차트조회는 한번에 최대 900개 데이터를 수신할 수 있습니다.

        Parameters
        ----------
        trCode: str
        rqName: str

        Returns
        ----------
        cnt : int
            서버에서 전달받은 데이터 갯수
        """

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

        cnt = self.dynamicCall("GetRepeatCnt(QString, QString)", trCode,
                               rqName)
        return cnt

    def getCommData(self, trCode, rqName, index, key):
        """ 데이터 획득 메서드
        evnetReceiveTrData() 이벤트 메서드가 호출될 때, 그 안에서
        조회데이터를 얻어오는 메서드입니다. 이 함수는 반드시 OnReceiveTRData()
        이벤트가 호출될때 그 안에서 사용해야 합니다.

        싱글데이터는 index=0
        멀티데이터는 getRepeatCnt 매서드로 데이터 수를 확인한 후,
        loop문으로 index를 1씩 늘리며 접근

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

        Returns
        ----------
        data: str
        """

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

        data = self.dynamicCall("GetCommData(QString, QString, int, QString)",
                                trCode, rqName, index, key)

        return data.strip()

    def getCommDataEx(self, trCode, multiDataName):
        """ 멀티 데이터 획득
        조회 수신데이터 크기가 큰 차트데이터를 한번에 가져올 목적으로 만든 전용함수입니다.
        receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다.

        Parameters
        ----------
        trCode: str
        multiDataName: str
            KOA에 명시된 멀티데이터명

        Returns
        ----------
        data: 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,
                     arrCode,
                     next,
                     codeCount,
                     rqName,
                     scrNo,
                     typeFlag=0):
        """ 복수종목조회 메서드(관심종목조회 메서드라고도 함).

        이 메서드는 setInputValue() 메서드를 이용하여, 사전에 필요한 값을 지정하지 않는다.
        단지, 메서드의 매개변수에서 직접 종목코드를 지정하여 호출한다.

        데이터 수신은 receiveTrData() 이벤트에서 아래 명시한 항목들을 1회 수신하며,
        이후 receiveRealData() 이벤트를 통해 실시간 데이터를 얻을 수 있다.
        복수종목조회 TR 코드는 OPTKWFID 이며, 요청 성공시 아래 항목들의 정보를 얻을 수 있다.

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

        1초에 5회 제한

        Parameters
        ----------
        arrCode: str
            종목코드, 세미콜론(;)으로 구분, 한번에 100종목까지 조회가능
        next: int 
            (0: 조회, 1: 남은 데이터 이어서 조회)
            기존 API 문서는 boolean type
        codeCount: int 
            codes에 지정한 종목의 갯수.
        rqName: str
        scrNo: str
        typeFlag: int
          주식과 선물옵션 구분(0: 주식, 3: 선물옵션),
          기존 API 문서에서는 가운데 위치하지만, 맨 뒤로 이동시켰음

        return
        ----------
        str 
            0(정상), -200(시세과부하), -201(조회전문작성 에러)
        """

        if not self.connectState:
            raise KiwoomConnectError()

        if not (isinstance(arrCode, str) and isinstance(next, int)
                and isinstance(codeCount, int) and isinstance(rqName, str)
                and isinstance(scrNo, str) and isinstance(typeFlag, int)):
            raise ParameterTypeError()

        # API 제한 확인
        self.requestDelayCheck.checkDelay()

        returnCode = self.dynamicCall(
            "CommKwRqData(QString, QBoolean, int, int, QString, QString)",
            arrCode,
            next,
            codeCount,
            typeFlag,
            rqName,
            scrNo,
        )

        if returnCode != ReturnCode.OP_ERR_NONE:
            self.logger.error("{} commKwRqData {} Request Failed!".format(
                dt.now(), rqName))
            raise KiwoomProcessingError()

        # logging
        self.logger.debug("{}  commKwRqData {}".format(dt.now(), rqName))

        # eventReceiveTrData()에서 loop 종료 or timeout
        self.requestLoop = QEventLoop()
        QTimer.singleShot(1000, self.requestLoop.exit)  # timout in 1000 ms
        self.requestLoop.exec_()
        print('requestLoop entered')

    # strRealType – 실시간 구분
    # nFid – 실시간 아이템
    # Ex) 현재가출력 - openApi.GetCommRealData(“주식시세”, 10);
    # 참고)실시간 현재가는 주식시세, 주식체결 등 다른 실시간타입(RealType)으로도 수신가능
    # https://programtalk.com/vs2/python/11670/QWebview-plus/plus/kiwoom.py/
    def getCommRealData(self, realType, fid):
        if not (isinstance(realType, str) and isinstance(fid, str)):
            raise ParameterTypeError()

        data = self.dynamicCall("GetCommRealData(QString, int)", realType, fid)
        return data.strip()

    ###############################################################
    ################### 조건식 관련 메서드 #################
    ########################## 1초 5회 제한 ########################
    ###############################################################

    def getCondition(self):
        returnCode = self.dynamicCall("GetConditionLoad()")
        if returnCode != 1:
            self.logger.error(
                "{} GetConditionLoad {} Request Failed!, CAUSE: {}".format(
                    dt.now(), conditionName,
                    getattr(ReturnCode, "CAUSE").get(returnCode)))
            raise KiwoomProcessingError()

        # 루프 생성: eventReceiveTrData() 메서드에서 루프를 종료시킨다.
        self.logger.debug("{}  condition name list requested.".format(
            dt.now()))
        self.conditionLoop = QEventLoop()
        self.conditionLoop.exec_()
        print('conditionLoop entered')

    def eventReceiveConditionVer(self, IRet):
        print('on receive condition ver = ', IRet)
        if IRet != 1:
            self.logger.error(
                "{} sendCondition {} Request Failed!, CAUSE: {}".format(
                    dt.now(), conditionName,
                    getattr(ReturnCode, "CAUSE").get(returnCode)))
            raise KiwoomProcessingError()

        print('Successfully got condition version!')
        try:
            self.conditionLoop.exit()
            print('conditionLoop Exited')
        except AttributeError as e:
            print('Attribute error after tr receive = {}', e)
            pass

    def getConditionList(self):
        return self.dynamicCall("GetConditionNameList()")

    def getCompByCondition(self, scrNo, conditionName, nIndex, nSearch):

        self.sendCondition(scrNo, conditionName, nIndex, nSearch)
        return getattr(self, conditionName)

    def sendCondition(self, scrNo, conditionName, nIndex, nSearch):
        '''
        # Params
        BSTR strScrNo,    // 화면번호
        BSTR strConditionName,  // 조건식 이름
        int nIndex,     // 조건명 인덱스
        int nSearch   // 조회구분, 0:조건검색, 1:실시간 조건검색
        '''
        if not isinstance(scrNo, str):
            strScrNo = str(scrNo)

        if not isinstance(conditionName, str):
            conditionName = str(conditionName)

        if not isinstance(nIndex, int):
            nIndex = int(nIndex)

        if not isinstance(nSearch, int):
            nSearch = int(nSearch)

        returnCode = self.dynamicCall(
            "SendCondition(QString, QString, int, int)", scrNo, conditionName,
            nIndex, nSearch)
        print('returnCode for cond = {}'.format(returnCode))
        if returnCode != 1:  # 1이외엔 실패
            self.logger.error(
                "{} sendCondition {} Request Failed!, CAUSE: {}".format(
                    dt.now(), conditionName,
                    getattr(ReturnCode, "CAUSE").get(returnCode)))
            raise KiwoomProcessingError()
        self.logger.info("{} sendCondition {} Realtime: {}".format(
            dt.now(), conditionName, nSearch))
        # 루프 생성: eventReceiveTrCondition() 메서드에서 루프를 종료시킨다.
        self.conditionLoop = QEventLoop()
        self.conditionLoop.exec_()

# 실시간 조건검색을 중지합니다.
# ※ 화면당 실시간 조건검색은 최대 10개로 제한되어 있어서 더 이상 실시간 조건검색을 원하지 않는 조건은 중지해야만 카운트 되지 않습니다.

    def sendConditionStop(self, scrNo, conditionName, index):
        self.dynamicCall("SendConditionStop(QString, QString, int)", scrNo,
                         conditionName, index)

    def eventReceiveTrCondition(self, scrNo, codeList, conditionName, nIndex,
                                nNext):
        setattr(self, conditionName,
                [x for x in codeList.split(';') if len(x) > 0])
        try:
            self.conditionLoop.exit()
            print('conditionLoop Exited')
        except AttributeError as e:
            print('Attribute error after tr receive = {}', e)
            return

    # https://devshj.tistory.com/14
    def eventReceiveRealCondition(
            self, code, type, conditionName,
            conditionIndex):  # 조건검색 실시간 조회시 반환되는 값을 받는 함수
        '''실시간 편입/이탈 종목이 발생될 때마다 호출됩니다.'''
        print("receive_real_condition strCode: " + str(code) + ", Type: " +
              str(type) + ", ConditionName: " + str(conditionName) +
              ", ConditionIndex: " + str(conditionIndex))

        try:
            print("receive_real_condition strCode: " + str(code) + ", Type: " +
                  str(type) + ", ConditionName: " + str(conditionName) +
                  ", ConditionIndex: " + str(conditionIndex))
            logDateTime = dt.today().strftime(
                "%Y-%m-%d %H:%M:%S")  # 화면에 노출할 날짜를 만듬 (YYYY-mm-dd HH:MM:SS 형태)
            codeName = self.dynamicCall("GetMasterCodeName(QString)",
                                        [code]).strip()  # 종목 코드로 종목 이름을 가져옴

            if str(type) == "I":  # 편입 종목이라면
                print(
                    str(logDateTime) + " 편입 신호 : " + str(code) + ", " +
                    str(codeName))  # 트레이딩 화면 내역에 로그를 남김
            elif str(type) == "D":  # 이탈 종목이라면
                print(
                    str(logDateTime) + " 이탈 신호 : " + str(code) + ", " +
                    str(codeName))  # 트레이딩 화면 내역에 로그를 남김

            resultDict = {
                'code': code,
                'name': codeName,
                'type': type,
                'conditionName': conditionName,
                'conditionIndex': conditionIndex
            }

            self.send_log({
                'log_type': 'realtime_condition',
                'log': resultDict
            })

        except Exception as e:
            print('Realtime Condition error = {}'.format(e))
            return

    ###############################################################
    ############### 실시간 조회와 관련된 메서드 #####################
    # https://programtalk.com/vs2/python/11670/QWebview-plus/plus/kiwoom.py/
    # 실시간 등록을 한다.
    # strScreenNo : 화면번호
    # strCodeList : 종목코드리스트(ex: 039490;005930;…)
    # strFidList : FID번호(ex:9001;10;13;…)
    #   9001 – 종목코드
    #   10 - 현재가
    #   13 - 누적거래량
    # strOptType : 타입(“0”, “1”)
    # 타입 “0”은 항상 마지막에 등록한 종목들만 실시간등록이 됩니다.
    # 타입 “1”은 이전에 실시간 등록한 종목들과 함께 실시간을 받고 싶은 종목을 추가로 등록할 때 사용합니다.
    # ※ 종목, FID는 각각 한번에 실시간 등록 할 수 있는 개수는 100개 입니다.
    def setRealReg(self, screenNo, codeList, fidList, optType):
        return self.dynamicCall(
            "SetRealReg(QString, QString, QString, QString)", screenNo,
            codeList, fidList, optType)

    # 리얼 시세를 끊는다.
    # 화면 내 모든 리얼데이터 요청을 제거한다.
    # 화면을 종료할 때 반드시 위 함수를 호출해야 한다.
    # Ex) openApi.DisconnectRealData(“0101”);

    def disconnectRealData(self, scnNo):
        self.dynamicCall("DisconnectRealData(QString)", scnNo)

    ###############################################################
    ################### 주문과 잔고처리 관련 메서드 #################
    ########################## 1초 5회 제한 ########################
    ###############################################################

    def sendOrder(
        self,
        rqName,
        scrNo,
        accNo,
        orderType,
        code,
        qty,
        price,
        hogaType,
        originOrderNo,
    ):
        """ 주식 주문 메서드

        sendOrder() 메소드 실행시,
        OnReceiveMsg, OnReceiveTrData, OnReceiveChejanData 이벤트가 발생한다.
        이 중, 주문에 대한 결과 데이터를 얻기 위해서는 OnReceiveChejanData 이벤트를 통해서 처리한다.

        OnReceiveTrData 이벤트를 통해서는 주문번호를 얻을 수 있는데,
        주문후 OnReceiveTrData에서 주문번호가 ''공백으로 전달되면 주문접수 실패를 의미한다.

        ※  시장가, 최유리지정가, 최우선지정가, 시장가IOC, 최유리IOC,시장가FOK,
            최유리FOK, 장전시간외, 장후시간외 주문시 
            주문가격(price)을 0으로 입력.
        
        Paramters
        ----------
        rqName: str
            주문 요청명(사용자 정의)
        scrNo: str
            화면번호(4자리)
        accNo: str
            계좌번호(10자리)
        orderType: int
            주문유형(1: 신규매수, 2: 신규매도, 3: 매수취소, 4: 매도취소, 5: 매수정정, 6: 매도정정)
        code: str
            종목코드
        qty: int
            주문수량
        price: int
            주문단가
        hogaType: str
            거래구분(00: 지정가, 03: 시장가, 05: 조건부지정가, 06: 최유리지정가, 그외에는 api 문서참조)
        originOrderNo: str
            원주문번호(신규주문에는 공백, 정정및 취소주문시 원주문번호를 입력합니다.)

        """
        orderParams = {
            "rqName": rqName,
            "scrNo": scrNo,
            "accNo": accNo,
            "orderType": orderType,
            "code": code,
            "qty": qty,
            "price": price,
            "hogaType": hogaType,
            "originOrderNo": originOrderNo,
        }

        # order response data
        self.orderResponse = {
            "time": dt.now().strftime("%Y-%m-%d %H:%M:%S.%f"),
            "orderNo": "",
        }
        self.orderResponse.update(orderParams)

        # server connection check
        if not self.connectState:
            msg = "Server not connected"
            self.orderResponse.update({"msg": msg})
            raise KiwoomConnectError(msg)

        # Error: code not supported
        if not code in self.codes:

            msg = f"Code not supported: {code}"
            self.orderResponse.update({"msg": msg})
            raise KiwoomProcessingError("ERROR: sendOrder() : {}".format(msg))

        # API 제한 확인
        self.orderDelayCheck.checkDelay()

        # 주문 전송
        try:
            returnCode = self.dynamicCall(
                "SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
                list(orderParams.values()),
            )

        except Exception as msg:
            self.orderResponse.update({"msg": msg})
            raise KiwoomProcessingError("ERROR: sendOrder() : {}".format(msg))

        if returnCode != 0:
            msg = getattr(ReturnCode, "CAUSE").get(returnCode)
            self.orderResponse.update({"msg": msg})
            raise KiwoomProcessingError("ERROR: sendOrder() : {}".format(msg))

        # eventReceiveTrData() 에서 루프종료 or timeout
        self.orderLoop = QEventLoop()
        #QTimer.singleShot(1000, self.orderLoop.exit)  # timout in 1000 ms
        self.orderLoop.exec_()
        print('orderLoop entered')

    def getChejanData(self, fid):
        """ 주문접수, 주문체결, 잔고정보를 얻어오는 메서드
        이 메서드는 receiveChejanData() 이벤트 메서드가 호출될 때
        그 안에서 사용해야 합니다.

        Parameters
        ===========================
        fid: str

        return
        ===========================
        data: str
        """

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

        data = self.dynamicCall(f'GetChejanData("{fid}")')
        return data

    def __getData(self, trCode, rqName):

        returnDict = {}
        if getattr(TRKeys, trCode).get("멀티데이터", False):
            returnDict["멀티데이터"] = self.__getMultiData(trCode, rqName)
        if getattr(TRKeys, trCode).get("싱글데이터", False):
            returnDict["싱글데이터"] = self.__getSingleData(trCode, rqName)
        return returnDict

    def __getSingleData(self, trCode, rqName):

        data = {}

        keyList = getattr(TRKeys, trCode).get("싱글데이터")
        for key in keyList:
            val = self.getCommData(trCode, rqName, 0, key)
            if key.endswith("호가") or key in getattr(TRKeys, "NOSIGNKEY"):
                val = removeSign(val)
            data[key] = val
        return data

    def __getMultiData(self, trCode, rqName):

        data = []
        cnt = self.getRepeatCnt(trCode, rqName)
        keyList = getattr(TRKeys, trCode).get("멀티데이터")

        for i in range(cnt):
            tmpDict = {}
            for key in keyList:
                val = self.getCommData(trCode, rqName, i, key)
                if key.endswith("호가") or key in getattr(TRKeys, "NOSIGNKEY"):
                    val = removeSign(val)
                tmpDict[key] = val
            data.append(tmpDict)
        return data

    def __getOPTKWFID(self, trCode, rqName):

        data = {}

        tmpData = self.getCommDataEx(trCode, rqName)
        keyList = getattr(TRKeys, trCode).get("멀티데이터")

        for key, ls in zip(keyList, zip(*tmpData)):
            if key.endswith("호가") or key in getattr(TRKeys, "NOSIGNKEY"):
                ls = map(removeSign, ls)
            data[key] = list(ls)
        data = dictListToListDict(data)  # dict of list to list of dict
        data = {"멀티데이터": data}
        return data

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

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

        Parameters
        ----------
        market: str

        Returns
        ----------
        codeList: list
            조회한 시장에 소속된 종목 코드를 담은 list
        """

        if not self.connectState:
            raise KiwoomConnectError()

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

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

        codes = self.dynamicCall('GetCodeListByMarket("{}")'.format(market))
        return codes.split(";")

    @property
    def codes(self):
        if not self.connectState:
            raise KiwoomConnectError()

        codes = self.__getCodeListByMarket("0")  # KOSPI
        codes += self.__getCodeListByMarket("10")  # KOSDAQ
        codes += self.__getCodeListByMarket("8")  # ETF
        return codes

    """
Exemple #20
0
class PrepareNextDay(ParentKiwoom):
    def __init__(self):
        super().__init__()

        self.logging.logger.info("ETF PrepareNextDay() class start.")
        self.line.notification("ETF PrepareNextDay() class start.")

        self.all_etc_info_event_loop = QEventLoop()
        self.etf_info_event_loop = QEventLoop()
        self.etf_day_info_event_loop = QEventLoop()

        self.screen_all_etf_stock = "4000"
        self.screen_etf_stock = "5000"
        self.screen_etf_day_stock = "4050"

        self.target_etf_stock_dict = {}
        self.target_etf_day_info_dict = []
        self.event_slots()

        self.line.notification("ETF PREPARE AUTO TRADE START")
        self.prepare_next_day()

    def event_slots(self):
        self.OnReceiveTrData.connect(self.trdata_slot)
        self.OnReceiveMsg.connect(self.msg_slot)

    def prepare_next_day(self):
        self.logging.logger.info("다음날 위한 준비 시작")
        self.file_delete()
        self.get_all_etf_stock()
        self.get_etf_stock_info()
        self.get_etf_day_stock_info()

        QTest.qWait(5000)
        self.createTargetEtfStockFile()

        self.line.notification("시스템 종료")
        QTest.qWait(5000)
        sys.exit()

    def file_delete(self):
        self.logging.logger.info("file_delete")
        if os.path.isfile(self.target_etf_file_path):
            os.remove(self.target_etf_file_path)
            self.logging.logger.info("remove %s" % self.target_etf_file_path)

    def get_all_etf_stock(self, sPrevNext="0"):
        self.logging.logger.info("get_all_etf_stock")
        self.dynamicCall("SetInputValue(QString, QString)", self.customType.TAXATION_TYPE, "0")
        self.dynamicCall("SetInputValue(QString, QString)", self.customType.COMPARED_TO_NAV, "0")
        self.dynamicCall("SetInputValue(QString, QString)", self.customType.MANAGER, "0000")
        self.dynamicCall("CommRqData(QString, QString, int, QString)", self.customType.OPT40004, "opt40004", sPrevNext, self.screen_all_etf_stock)

        self.all_etc_info_event_loop.exec_()

    def trdata_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        if sRQName == self.customType.OPT40004:
            self.trdata_slot_opt40004(sScrNo, sRQName, sTrCode, sRecordName, sPrevNext)
        elif sRQName == self.customType.OPT10001:
            self.trdata_slot_opt10001(sScrNo, sRQName, sTrCode, sRecordName, sPrevNext)
        elif sRQName == self.customType.OPT40003:
            self.trdata_slot_opt40003(sScrNo, sRQName, sTrCode, sRecordName, sPrevNext)

    def trdata_slot_opt40003(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
        compareLastDayList = []
        for i in range(rows):
            compareLastDay = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, self.customType.THE_DAY_BEFORE)
            compareLastDay = compareLastDay.strip()
            if int(compareLastDay) > 0:
                compareLastDayList.append(int(compareLastDay))
        avgValue = round(int(np.mean(compareLastDayList)))
        maxValue = np.max(compareLastDayList)
        minValue = np.min(compareLastDayList)
        for code in self.target_etf_day_info_dict:
            self.target_etf_stock_dict[code].update({self.customType.THE_DAY_BEFORE_AVG: avgValue})
            self.target_etf_stock_dict[code].update({self.customType.THE_DAY_BEFORE_MAX: maxValue})
            self.target_etf_stock_dict[code].update({self.customType.THE_DAY_BEFORE_MIN: minValue})
            self.logging.logger.info(self.logType.OPT40003_STATUS_LOG % (code, avgValue, maxValue, minValue))
        del self.target_etf_day_info_dict[:]

        self.etf_day_info_event_loop.exit()

    def trdata_slot_opt40004(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        rows = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode, sRQName)
        for i in range(rows):
            volume = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, self.customType.VOLUME)
            volume = volume.strip()
            code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, self.customType.STOCK_CODE)
            code = code.strip()
            last_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, self.customType.LAST_PRICE)
            last_price = last_price.strip()
            if int(volume) >= 50000 and int(last_price) <= 50000:
                code_nm = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, i, self.customType.STOCK_NAME)
                code_nm = code_nm.strip()

                if code in self.target_etf_stock_dict:
                    pass
                else:
                    self.target_etf_stock_dict[code] = {}

        if sPrevNext == "2":  # 다음페이지 존재
            self.get_all_etf_stock(sPrevNext="2")
        else:
            self.stop_screen_cancel(self.screen_all_etf_stock)
            self.all_etc_info_event_loop.exit()

    def trdata_slot_opt10001(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        code = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.STOCK_CODE)
        code = code.strip()
        code_nm = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.STOCK_NAME)
        highest_stock_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.HIGHEST_PRICE)
        lowest_stock_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.LOWEST_PRICE)
        last_stock_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.CURRENT_PRICE)
        change_price = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.THE_DAY_BEFORE)

        market_cap = self.dynamicCall("GetCommData(QString, QString, int, QString)", sTrCode, sRQName, 0, self.customType.MARTKET_CAP)
        market_cap = market_cap.strip()

        self.logging.logger.info(self.logType.OPT10001_STATUS_LOG % (
            code, highest_stock_price.strip(), lowest_stock_price.strip(), last_stock_price.strip(), change_price.strip(), market_cap)
        )

        self.target_etf_stock_dict[code].update({self.customType.STOCK_NAME: code_nm})
        self.target_etf_stock_dict[code].update({self.customType.LAST_DAY_HIGHEST_PRICE: abs(int(highest_stock_price.strip()))})
        self.target_etf_stock_dict[code].update({self.customType.LAST_DAY_LOWEST_PRICE: abs(int(lowest_stock_price.strip()))})
        self.target_etf_stock_dict[code].update({self.customType.LAST_DAY_LAST_PRICE: abs(int(last_stock_price.strip()))})
        self.target_etf_stock_dict[code].update({self.customType.MARTKET_CAP: int(market_cap)})

        self.etf_info_event_loop.exit()

    def isTragetEtfStock(self, value):
        return int(value[self.customType.MARTKET_CAP]) >= 65

    def createTargetEtfStockFile(self):
        self.logging.logger.info("create_target_etf_stock_file")
        for sCode in self.target_etf_stock_dict.keys():
            value = self.target_etf_stock_dict[sCode]
            if self.isTragetEtfStock(value):
                f = open(self.target_etf_file_path, "a", encoding="utf8")
                f.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" %
                        (sCode, value[self.customType.STOCK_NAME], value[self.customType.LAST_DAY_HIGHEST_PRICE],
                         value[self.customType.LAST_DAY_LOWEST_PRICE], value[self.customType.LAST_DAY_LAST_PRICE],
                         value[self.customType.THE_DAY_BEFORE_AVG], value[self.customType.THE_DAY_BEFORE_MAX],
                         value[self.customType.THE_DAY_BEFORE_MIN]))
                f.close()

    def get_etf_stock_info(self):
        self.logging.logger.info("get_etf_stock_info")

        for sCode in self.target_etf_stock_dict.keys():
            QTest.qWait(4000)
            self.dynamicCall("SetInputValue(QString, QString)", self.customType.STOCK_CODE, sCode)
            self.dynamicCall("CommRqData(QString, QString, int, QString)", self.customType.OPT10001, "opt10001", 0, self.screen_etf_stock)
            self.etf_info_event_loop.exec_()

    def get_etf_day_stock_info(self):
        self.logging.logger.info("get_etf_day_stock_info")

        for sCode in self.target_etf_stock_dict.keys():
            QTest.qWait(4000)
            self.target_etf_day_info_dict.append(sCode)
            self.dynamicCall("SetInputValue(QString, QString)", self.customType.STOCK_CODE, sCode)
            self.dynamicCall("CommRqData(QString, QString, int, QString)", self.customType.OPT40003, "OPT40003", 0, self.screen_etf_day_stock)
            self.etf_day_info_event_loop.exec_()
Exemple #21
0
class Kiwoom(QAxWidget):
    '''
    키움 통신 클래스
    =============
    키움 API와 직접적인 통신을 진행한다.

    Thread_safe함.

    TODO
    -----------
    이벤트루프를 적용하여 get_tr_data 등의 메소드가 thread safe 해야함
    '''

    __global_eventloop: QEventLoop = None  # 동기처리를 위한 전역 EventLoop
    __tr_data_temp: Dict[str, Dict[str,
                                   str]] = None  # GetTrData의 결과가 임시적으로 담기는 곳
    __condition_name_list: List[Tuple[str,
                                      str]] = None  # 조건식 이름과 인덱스가 임시적으로 담기는 곳
    __condition_stock_list: List[str] = None  # 조건식 필터링 결과가 임시적으로 담기는 곳
    __tr_rq_single_data: Dict[str, str] = None  # 사용자 요청 싱글데이터
    __tr_rq_multi_data: Dict[str, str] = None  # 사용자 요청 멀티데이터
    __realtime_data_callback: Callable = None  # 실시간 데이터 콜백
    __server_msg_callback: Callable = None
    __chejan_data_callback: Callable = None

    def __init__(self):
        super().__init__()
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
        self.__global_eventloop = QEventLoop()
        self.__reg_all_slot()  # 이벤트 슬롯 등록

    def do_login(self):
        self.dynamicCall("CommConnect()")
        self.__global_eventloop.exec_()

    def get_tr_data(self, inputValue: Dict[str, str], trEnum: TrCode,
                    nPrevNext: int, sScrNo: str, rqSingleData: Dict[str, str],
                    rqMultiData: Dict[str, str]):
        '''키움 API서버에 TR 데이터를 요청한다.

        Parameters
        ----------
        inputValue :
            KOA Studio 기준으로, SetInputValue에 들어갈 값

        trEnum : TrCode
            KOA Studio 기준, Tr 코드

        nPrevNext :
            prevNext

        sScrNo :
            스크린 번호

        rqSingleData :
            받아오고자 하는 싱글데이터 목록

        rqMultiData :
            받아오고자 하는 멀티데이터 목록

        Returns
        -------
        Dict[str, Dict[str, str]]
        '''
        self.__tr_rq_single_data = rqSingleData
        self.__tr_rq_multi_data = rqMultiData

        self._set_input_values(inputValue)  # inputvalue 대입
        r_value = self.dynamicCall(
            "CommRqData(Qstring, QString, int, QString)", trEnum.value,
            trEnum.name, nPrevNext, sScrNo)

        print(ErrCode(r_value))  # dynamic call 반환 값 출력
        self.__global_eventloop.exec_()
        return self.__tr_data_temp

    def get_condition_list(self):
        '''키움 증권 HTS에 저장되어 있는 조건식 목록들을 반환

        Returns
        -------
        (index, name)의 리스트를 반환한다.
        '''
        r_value = self.dynamicCall("GetConditionLoad()")
        # print(ErrCode(r_value))     # dynamic call 반환 값 출력
        self.__global_eventloop.exec_()
        return self.__condition_name_list

    def get_condition_stock(self, sScrNo: str, cond_name: str,
                            index: str) -> List[str]:
        '''
        해당 조건식을 만족하는 종목 코드를 리스트 형태로 반환

        Parameters
        ----------
        sScrNo : 스크린번호

        cond_name : 조건식 이름

        index : 조건식 인덱스 번호

        Returns
        -------
        Stock 객체들의 리스트를 반환한다.
        '''
        r_value = self.dynamicCall("SendCondition(QString, QString, int, int)",
                                   sScrNo, cond_name, index, 0)
        print(ErrCode(r_value))  # dynamic call 반환 값 출력
        self.__global_eventloop.exec_()
        return self.__condition_stock_list

    def set_realtime_reg(self, sScrNo: str, stockList: List[str],
                         realtimeDataList: List[RealTimeDataEnum]):
        '''
        realtime data를 받아올 수 있도록 레지스터링 한다.

        Parameters
        ----------
        stockList : Stock 객체가 list 형태로 들어와야함

        realTimeDataList : RealTimeDataEnum 객체가 list 형태로 들어와야함
        '''
        if self.__realtime_data_callback is None:
            raise RuntimeError("realtime data callback 함수가 설정되지 않았습니다.")

        fid_list = "".join("{};".format(fid.value)
                           for fid in realtimeDataList)[:-1]  # fid str list
        stock_code_list = "".join(
            "{};".format(stock)
            for stock in stockList)[:-1]  # stock code str list

        r_value = self.dynamicCall(
            "SetRealReg(QString, QString, QString, QString)", sScrNo,
            stock_code_list, fid_list, 1)
        print(ErrCode(r_value))

    def set_realtime_callback(self, callback: Callable):
        '''
        realtime data 이벤트가 발생했을 때, 이를 받아들일 콜백을 설정한다.
        '''
        self.__realtime_data_callback = callback

    def set_server_msg_callback(self, callback: Callable):
        '''서버로 부터 메시지가 도착했을 때, 이를 출력할 콜백을 설정한다.
        '''
        self.__server_msg_callback = callback

    def set_chejan_data_callback(self, callback: Callable):
        self.__chejan_data_callback = callback

    def send_order(self, sScrNo: str, sAccNo: str,
                   nOrderType: TrClassification, sCode: str, nQty: int,
                   nPrice: int, sHogaGb, sOrgOrderNo: str):
        '''주식 주문

        Parameters
        ----------
        sScrNo :
            스크린번호

        sAccNo :
            계좌번호

        nOrderType :
            주문 유형

        sCode :
            종목코드

        nQty :
            주문수량

        nPrice :
            주문가격

        sHogaGb :
            거래구분

        sOrgOrderNo :
            원주문번호
        '''
        self.dynamicCall(
            "SendOrder(QString, QString, QString, QString, QString, QString, QString, QString, QString)",
            TrCode.SEND_ORDER.value, sScrNo, sAccNo, nOrderType.value, sCode,
            nQty, nPrice, sHogaGb, sOrgOrderNo)

    def parse_realtime(
        self, sCode: str, realtimeDataList: List[RealTimeDataEnum]
    ) -> Dict[RealTimeDataEnum, Any]:
        '''
        realtime_callback이 호출 되었을 경우에만 사용이 가능하다.

        실시간 데이터 이벤트가 발생했을 때, FID 들을 파싱하여 가져오는 메소드
        '''
        realtime_data = {}

        for realtime_enum in realtimeDataList:
            realtime_data[realtime_enum] = self.dynamicCall(
                "GetCommRealData(QString, QString)", sCode,
                realtime_enum.value)

        return realtime_data

    def get_account_info(self):
        account_list = self.dynamicCall("GetLoginInfo(QString)", "ACCNO")
        account_num = account_list.split(';')[0]
        return account_num

    def _set_input_values(self, input_value: Dict[str, str]):
        '''
        SetInputVlaue() 동적 호출 iteration 용도
        '''
        for k, v in input_value.items():
            self.dynamicCall("SetInputValue(QString, QString)", k, v)

    def _tr_data_slot(self, sScrNo, sRQName, sTrCode, sPrevNext):
        '''
        CommRqData 처리용 슬롯
        '''
        _ = (sScrNo, sPrevNext)  # warning avoid

        n_record = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode,
                                    sRQName)  # tr데이터 중, 멀티데이터의 레코드 개수를 받아옴.

        self.__tr_data_temp = {}  # 이전에 저장되어 있던 임시 tr_data 삭제.
        self.__tr_data_temp["single_data"] = {}  # empty dict 선언
        for s_data in self.__tr_rq_single_data:
            self.__tr_data_temp["single_data"][s_data] = self.dynamicCall(
                "GetCommData(QString, QString, int, QString)", sTrCode,
                sRQName, 0, s_data).replace(" ", "")

        self.__tr_data_temp["multi_data"] = []
        for i in range(n_record):
            m_data_dict_temp = {}  # 멀티데이터에서 레코드 하나에 담길 딕셔너리 선언
            for m_data in self.__tr_rq_multi_data:
                m_data_dict_temp[m_data] = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, m_data)
            self.__tr_data_temp["multi_data"].append(m_data_dict_temp)
        self.__global_eventloop.exit()

    def _realtime_data_slot(self, sCode, sRealType, sRealData):
        '''
        realtime data 처리용 슬롯
        '''
        self.__realtime_data_callback(sCode, sRealType, sRealData)

    def _login_slot(self, errNo):
        print(ErrCode(errNo))
        self.__global_eventloop.exit()

    def _send_condition_slot(self, _sScrNo, sCodeList, _sCondName, _nIndex,
                             _nNext):
        '''
        SendCondition 처리용 슬롯
        '''
        self.__condition_stock_list = sCodeList[:-1].split(";")
        self.__global_eventloop.exit()

    def _condition_ver_slot(self, _lRet, _sMsg):
        '''
        GetConditionLoad 처리용 슬롯
        '''
        print(_sMsg)
        self.__condition_name_list = []  # 이전에 저장되어 있던 조건식 들을 삭제한다.
        condition_name_list = self.dynamicCall("GetConditionNameList()")

        for cond_index_name in condition_name_list[:-1].split(";"):
            self.__condition_name_list.append(cond_index_name.split("^"))

        self.__global_eventloop.exit()

    def _server_msg_slot(self, _sScrNo, sRQName, sTrCode, sMsg):
        '''
        서버 통신 응답 처리용 슬롯
        '''
        if self.__server_msg_callback is not None:
            self.__server_msg_callback(TrCode(sRQName.replace(" ", ""), sMsg))

    def _chejan_data_slot(self, sGubun, nItemCnt, sFidList):
        '''체결 잔고데이터 이벤트 처리용 슬롯
        '''
        fid_list = sFidList[:-1].split(";")

        chejan_fid_data = {}
        for fid in fid_list:
            chejan_fid_data[fid] = self.dynamicCall("GetChejanData(QString)",
                                                    fid)

        if self.__chejan_data_callback is not None:
            self.__chejan_data_callback(chejan_fid_data)

    def __reg_all_slot(self):
        '''
        키움 API의 이벤트 슬롯을 전부 다 등록
        '''
        self.OnEventConnect.connect(self._login_slot)  # 로그인 슬롯
        self.OnReceiveTrData.connect(self._tr_data_slot)  # Tr 데이터 슬롯
        self.OnReceiveConditionVer.connect(
            self._condition_ver_slot)  # 조건식 데이터 슬롯
        self.OnReceiveTrCondition.connect(
            self._send_condition_slot)  # 조건식 종목 슬롯
        self.OnReceiveRealData.connect(self._realtime_data_slot)  # 실시간 데이터 슬롯
        self.OnReceiveMsg.connect(self._server_msg_slot)
        self.OnReceiveChejanData.connect(self._chejan_data_slot)
Exemple #22
0
class SphinxConverter(QObject):
    """This class converts Sphinx input to HTML. It is run in a separate
    thread.
    """
    # 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)

    def __init__(self, parent):
        super().__init__(parent)
        # Use an additional thread to process Sphinx output.
        self._ac = AsyncController('QThread', self)
        self._ac.defaultPriority = QThread.LowPriority
        self._SphinxInvocationCount = 1

    def terminate(self):
        # Free resources.
        self._ac.terminate()

    def convert(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 (filePath, 'No preview for this type of file.<br>Expected ' +
                    htmlFile + " or " + htmlFileAlter, 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()
Exemple #23
0
class Kiwoom(QAxWidget):
    def __init__(self):
        super().__init__()
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

        # variables
        self.login_event_loop = None
        self.tr_event_loop = None
        self.prev_next = None
        self.ohlcv = {
            'date': [],
            'open': [],
            'high': [],
            'low': [],
            'close': [],
            'volume': []
        }
        self.opw00001_data = ""
        self.opw00018_data = {'single': [], 'multi': []}

        # signal & slot
        self.OnEventConnect.connect(self.onEventConnect)
        self.OnReceiveTrData.connect(self.onReceiveTrData)
        self.OnReceiveChejanData.connect(self.onReceiveTradeData)

    """ ======= METHODS ======= """

    def changeFormat(self, strData, percent=0):
        """
		Change string to currency format
		:param strData: 바꿀 문자열
		:param percent: 0=정수, 1=소수점, 2=백분율
		"""
        is_minus = False

        if strData.startswith('-'):
            is_minus = True

        strip_str = strData.lstrip('-0')

        if strip_str == '':
            if percent == 1:
                return '0.00'
            else:
                return '0'

        if percent == 1:
            ret = format(int(strip_str) / 100, ',.2f')
        elif percent == 2:
            ret = format(float(strip_str), ',.2f')
        else:
            ret = format(int(strip_str), ',d')

        if ret.startswith('.'):
            ret = '0' + ret
        if is_minus:
            ret = '-' + ret

        return ret

    """ ======= OpenAPI+ METHODS ======= """

    def commConnect(self):
        """ execute login window """
        self.dynamicCall("CommConnect()")

        self.login_event_loop = QEventLoop()
        self.login_event_loop.exec_()

    def commRqData(self, sRQName, sTrCode, nPrevNext, sScreenNo):
        """
		transmit Transaction to server
		:param nPrevNext: 0=조회, 2=연속
		"""
        self.dynamicCall("CommRqData(QString, QString, int, QString)", sRQName,
                         sTrCode, nPrevNext, sScreenNo)
        self.tr_event_loop = QEventLoop()
        self.tr_event_loop.exec_()

    def getLoginInfo(self, sTag):
        ret = self.dynamicCall("GetLoginInfo(QString)", sTag)
        return ret

    def sendOrder(self, sRQName, sScreenNo, sAccountNo, nOrderType, sItemCode,
                  nQty, nPrice, sBid, sOrgOrderNo):
        ret = self.dynamicCall(
            "SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)",
            [
                sRQName, sScreenNo, sAccountNo, nOrderType, sItemCode, nQty,
                nPrice, sBid, sOrgOrderNo
            ])
        print("SEND ORDER ERROR : " + str(ret))
        return ret

    def setInputValue(self, sID, sValue):
        self.dynamicCall("SetInputValue(QString, QString)", sID, sValue)

    def commGetData(self, sItemCode, sRealType, sFieldName, nIndex,
                    sInnerFieldName):
        """
		- Transaction 데이터
		:param sItemCode        : Tr명
		:param sRealType        : not used
		:param sFieldName       : 레코드 명
		:param nIndex           : 반복 인뎃스
		:param sInnerFieldName  : 아이템 명
		
		- 실시간 데이터
		:param sItemCode        : Key Code
		:param sRealType        : Real Type
		:param sFieldName       : Item Index
		:param nIndex           : not used
		:param sInnerFieldName  : not used
		
		- 체결잔고 데이터
		:param sItemCode        : 체결구분
		:param sRealType        : "-1"
		:param sFieldName       : not used
		:param nIndex           : Item Index
		:param sInnerFieldName  : not used
		"""
        ret = self.dynamicCall(
            "CommGetData(QString, QString, QString, int, QString)", sItemCode,
            sRealType, sFieldName, nIndex, sInnerFieldName)
        return ret.strip()

    def disconnectRealData(self, sScreenNo):
        pass

    def getRepeatCnt(self, sTrCode, sRecordName):
        """ 레코드 반복 횟수 리턴"""
        ret = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode,
                               sRecordName)
        return ret

    def getCodeListByMarket(self, sMarket):
        """ 시장구분에 따른 종목코드 리턴"""
        ret = self.dynamicCall("GetCodeListByMarket(QString)", sMarket)
        item_code = ret.split(';')
        return item_code

    def getConnectState(self):
        ret = self.dynamicCall("GetConnectState()")
        return ret

    def getMasterCodeName(self, sItemCode):
        """ 종목코드 한글명 리턴 """
        ret = self.dynamicCall("GetMasterCodeName(QString)", sItemCode)
        return ret

    def getMasterListedStockCnt(self, sItemCode):
        """ 종목코드의 상장주식 수를 리턴"""
        pass

    def getCommData(self, sTrCode, sRecordName, nIndex, sItemName):
        """
		cf) commGetData
		개발가이드에서 CommGetData보다 제약이 적은 GetCommData를 쓰라고 명시
		ex)현재가출력 : openAPI.GetCommData("opt00001_req", "주식기본정보", 0, "현재가")
		"""
        ret = self.dynamicCall("GetCommData(QString, QString, int, QString)",
                               sTrCode, sRecordName, nIndex, sItemName)
        return ret

    def getTradeData(self, nFid):
        """ 체결잔고 데이터 리턴"""
        ret = self.dynamicCall("GetChejanData(int)", nFid)
        return ret

    """
	======= OpenAPI+ EVENT FUNCTIONS
        시세조회나 조건검색요청등 서버에 데이터를 요청해서 키움OpenAPI가 데이터를 수신하게 되면 사용자 프로그램에 특정함수를 자동으로 호출해서 알려줍니다.
        이렇게 어떤 조건이 맞으면 키움OpenAPI에서 자동으로 호출해서 알려주며 OnReceiveTRData(), OnEventConnect(), OnReceiveRealData()
        처럼 함수이름 앞에 On~이 붙어있어서 일반적인 함수와 구별됩니다.
        로그인, 시세조회, 주문등 각 요청별로 요청함수, 이벤트와 데이터획득함수가 분류되어 있으니 용도에 맞는 함수를 사용해야 합니다.
	"""

    def onReceiveTrData(self, sScreenNo, sRQname, sTrCode, sRecordName,
                        sPrevNext, notUsed1, notUsed2, notUsed3, notUsed4):
        """
		서버로부터 event를 받으면 자동 실행된다.
		수신 받은 데이터는 commGetData()를 통해 받는다.
		GetCommData()를 쓰라고 하는데 자세한건 나중에 수정
		sPrevNext: 연속 조회가 필요하면 2를 리턴 받음
		"""
        self.prev_next = sPrevNext
        print("RqName : " + sRQname)
        print("Trcode : " + sTrCode + "\n")

        if sRQname == "opt10081_req":  # opt10081 : 주식일봉차트조회요청
            cnt = self.getRepeatCnt(sTrCode, sRQname)

            for i in range(cnt):
                date = self.commGetData(sTrCode, "", sRQname, i, "일자")
                open = self.commGetData(sTrCode, "", sRQname, i, "시가")
                high = self.commGetData(sTrCode, "", sRQname, i, "고가")
                low = self.commGetData(sTrCode, "", sRQname, i, "저가")
                close = self.commGetData(sTrCode, "", sRQname, 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))
        elif sRQname == "opw00001_req":  # opw00001 : 예수금상세현황요청
            estimated_day2_deposit = self.commGetData(sTrCode, "", sRQname, 0,
                                                      "d+2추정예수금")
            self.opw00001_data = self.changeFormat(estimated_day2_deposit)
        elif sRQname == "opw00018_req":  # opw00018 : 계좌평가잔고내역
            """
			single : 개별
			multi : 개별합산
			한 번의 TR요청으로 최대 20개 보유 종목 가져옴
			"""
            # single data
            single_data = []

            total_purchase_price = self.commGetData(sTrCode, "", sRQname, 0,
                                                    "총매입금액")
            single_data.append(self.changeFormat(total_purchase_price))

            total_eval_price = self.commGetData(sTrCode, "", sRQname, 0,
                                                "총평가금액")
            single_data.append(self.changeFormat(total_eval_price))

            total_eval_profit_loss_price = self.commGetData(
                sTrCode, "", sRQname, 0, "총평가손익금액")
            single_data.append(self.changeFormat(total_eval_profit_loss_price))

            total_profit_rate = self.commGetData(sTrCode, "", sRQname, 0,
                                                 "총수익률(%)")
            single_data.append(self.changeFormat(total_profit_rate, 1))

            estimated_deposit = self.commGetData(sTrCode, "", sRQname, 0,
                                                 "추정예탁자산")
            single_data.append(self.changeFormat(estimated_deposit))

            self.opw00018_data['single'] = single_data

            # multi data
            cnt = self.getRepeatCnt(sTrCode, sRQname)
            for i in range(cnt):
                multi_data = []

                item_name = self.commGetData(sTrCode, "", sRQname, i, "종목명")
                multi_data.append(item_name)

                quantity = self.commGetData(sTrCode, "", sRQname, i, "보유수량")
                multi_data.append(self.changeFormat(quantity))

                purchase_price = self.commGetData(sTrCode, "", sRQname, i,
                                                  "매입가")
                multi_data.append(self.changeFormat(purchase_price))

                current_price = self.commGetData(sTrCode, "", sRQname, i,
                                                 "현재가")
                multi_data.append(self.changeFormat(current_price))

                eval_profit_loss_price = self.commGetData(
                    sTrCode, "", sRQname, i, "평가손익")
                multi_data.append(self.changeFormat(eval_profit_loss_price))

                profit_rate = self.commGetData(sTrCode, "", sRQname, i,
                                               "수익률(%)")
                multi_data.append(self.changeFormat(profit_rate, 2))

                self.opw00018_data['multi'].append(multi_data)
        self.tr_event_loop.exit()

    def onReceiveRealData(self, sItemCode):
        pass

    def onReceiveMsg(self, sScrNo, sRQname, sTrCode, sMsg):
        pass

    def onReceiveTradeData(self, sTradeType, nItemCnt, sFidList):
        print("sTradeType : ", sTradeType)
        print(self.getTradeData(9203))
        print(self.getTradeData(302))
        print(self.getTradeData(900))
        print(self.getTradeData(909))

    def onEventConnect(self, nErrCode):
        if nErrCode == 0:
            print("connected")
        else:
            print("disconnected")

        self.login_event_loop.exit()

    def onReceiveCondition(self, sItemCode, sType, sConditionName,
                           sConditionIndex):
        """
		조건검색 실시간 편입, 이탈 종목을 받을 시점을 알려준다
		:param sItemCode:
		:param sType: 'I' = 편입, 'D' = 이탈
		:return:
		"""
        pass
Exemple #24
0
class KFOpenAPI(QAxWidget):
    def __init__(self):
        super().__init__()
        self.setControl("KFOPENAPI.KFOpenAPICtrl.1")
        self.loginLoop = None
        self.requestLoop = None
        self.orderLoop = None
        self.error = None
        self.orderNo = ""
        self.inquiry = 0
        self.msg = ""
        self.screenNo = 1000
        self.dicScrNo = {}
        self.OnReceiveTrData.connect(self.ReceiveTrData)
        self.OnReceiveRealData.connect(self.ReceiveRealData)
        self.OnReceiveMsg.connect(self.ReceiveMsg)
        self.OnReceiveChejanData.connect(self.ReceiveChejanData)
        self.OnEventConnect.connect(self.EventConnect)
        print("Initialized Kiwoom OpenAPI-W")

    ###############################################################
    # Method
    ###############################################################

    def CommConnect(self):
        """
        1) CommConnect
        원형    LONG CommConnect(LONG nAutoUpgrade)
        설명    로그인 윈도우를 실행한다.
        입력값  0 – 버전 수동처리, 1 – 버전 자동처리
                *로그인창 및 OCX 파일을 버전처리 받는 경우에,
                수동처리시, 고객 프로그램(ocx포함)을 직접 수동으로 Close하고 버전처리 진행
                자동처리시, 고객 프로그램(ocx포함)을 자동으로 Close하고 버전처리 및 자동
                재실행을 함.
        반환값  0 - 성공, 음수값은 실패
        비고    로그인이 성공하거나 실패하는 경우 OnEventConnect 이벤트가 발생하고
                이벤트의 인자 값으로 로그인 성공 여부를 알 수 있다.
                구분자 자동여부는 고객 프로그램(ocx포함)이 단독으로 사용되어지는 경우에,
                구분자를 자동으로 선택하시고, 고객 프로그램(ocx포함)이 다른프로그램과
                연동되어져 실행시 데이터를 수신해야하는 경우에는 구분자를 수동으로 선택.
        """
        self.dynamicCall("CommConnect(1)")
        self.loginEventLoop = QEventLoop()
        self.loginEventLoop.exec_()

    def CommRqData(self, sRQName, sTrCode, sPrevNext, sScreenNo):
        """
        2) CommRqData
        원형    LONG CommRqData (BSTR sRQName, BSTR sTrCode, BSTR sPrevNext, BSTR sScreenNo )
        설명    Tran을 서버로 송신한다.
        입력값  BSTR sRQName  BSTR sTrCode  long nPrevNext  BSTR sScreenNo
        반환값  OP_ERR_SISE_OVERFLOW – 과도한 시세조회로 인한 통신불가
                OP_ERR_RQ_STRUCT_FAIL – 입력 구조체 생성 실패
                OP_ERR_RQ_STRING_FAIL – 요청전문 작성 실패
                OP_ERR_NONE – 정상처리
        비고    sRQName – 사용자구분 명
                sTrCode - Tran명 입력
                nPrevNext – 서버에서 내려준 Next키값 입력(샘플참조)
                sScreenNo - 4자리의 화면번호(1~9999 :숫자값으로만 가능)
                Ex) openApi.CommRqData( “RQ_1”, “OPT00001”, “”, “0101”);
        """
        if not self.GetConnectState():
            print("Error : KiwoomConnectError by CommRqData")
            raise KiwoomConnectError()

        if not (isinstance(sRQName, str) and isinstance(sTrCode, str)
                and isinstance(sPrevNext, str) and isinstance(sScreenNo, str)):
            print("Error : ParameterTypeError by CommRqData")
            raise ParameterTypeError()

        errorCode = self.dynamicCall(
            'CommRqData(QString, QString, QString, QString)', sRQName, sTrCode,
            sPrevNext, sScreenNo)
        if errorCode != ErrorCode.OP_ERR_NONE:
            raise KiwoomProcessingError("CommRqData(): " +
                                        ErrorCode.CAUSE[errorCode])
        else:
            self.requestLoop = QEventLoop()
            self.requestLoop.exec_()

    def SetInputValue(self, sID, sValue):
        """
        3) SetInputValue
        원형    void SetInputValue(BSTR sID, BSTR sValue)
        설명    Tran 입력 값을 서버통신 전에 입력한다.
        입력값  sID – 아이템명  sValue – 입력 값
        반환값  없음
        비고    통신 Tran 매뉴얼 참고
                Ex) openApi.SetInputValue(“종목코드”, “6AH16”);
                openApi.SetInputValue(“계좌번호”, “5015123401”);
        """
        if not (isinstance(sID, str) and isinstance(sValue, str)):
            print("Error : ParameterTypeError by SetInputValue")
            raise ParameterTypeError()

        self.dynamicCall("SetInputValue(QString, QString)", sID, sValue)

    def GetCommData(self, strTrCode, strRecordName, nIndex, strItemName):
        """
        4) GetCommData
        원형    BSTR GetCommData(BSTR strTrCode, BSTR strRecordName, long nIndex, BSTR strItemName)
        설명    수신 데이터를 반환한다.
        입력값  strTrCode – Tran 코드
                strRecordName – 레코드명
                nIndex – 복수데이터 인덱스
                strItemName – 아이템명
        반환값  수신 데이터
        비고    Ex)현재가출력 - openApi.GetCommData(“OPT00001”, “해외선물기본정보”, 0, “현재가”);
        """
        if not (isinstance(strTrCode, str) and isinstance(strRecordName, str)
                and isinstance(nIndex, int) and isinstance(strItemName, str)):
            print("Error : ParameterTypeError by GetCommData")
            raise ParameterTypeError()

        return self.dynamicCall("GetCommData(QString, QString, int, QString)",
                                strTrCode, strRecordName, nIndex,
                                strItemName).strip()

    def CommTerminate(self):
        """
        5) CommTerminate
        원형    void CommTerminate()
        설명    OpenAPI의 서버 접속을 해제한다.
        입력값  없음
        반환값  없음
        비고    통신 연결 상태는 GetConnectState 메소드로 알 수 있다.
        """
        self.dynamicCall("CommTerminate()")

    def GetRepeatCnt(self, sTrCode, sRecordName):
        """
        6) GetRepeatCnt
        원형    LONG GetRepeatCnt(BSTR sTrCode, BSTR sRecordName)
        설명    레코드 반복횟수를 반환한다.
        입력값  sTrCode – Tran 명
                sRecordName – 레코드 명
        반환값  레코드의 반복횟수
        비고    Ex) openApi.GetRepeatCnt(“OPT00001”, “해외선물체결데이타”);
        """
        if not (isinstance(sTrCode, str) and isinstance(sRecordName, str)):
            print("Error : ParameterTypeError by GetRepeatCnt")
            raise ParameterTypeError()

        return self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode,
                                sRecordName)

    def DisconnectRealData(self, sScrNo):
        """
        7) DisconnectRealData
        원형    void DisconnectRealData(BSTR sScnNo)
        설명    화면 내 모든 리얼데이터 요청을 제거한다.
        입력값  sScrNo – 화면번호[4]
        반환값  없음
        비고    화면을 종료할 때 반드시 위 함수를 호출해야 한다.
                Ex) openApi.DisconnectRealData(“0101”);
        """
        if not (isinstance(sScrNo, str)):
            print("Error : ParameterTypeError by DisconnectRealData")
            raise ParameterTypeError()

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

    def GetCommRealData(self, strRealType, nFid):
        """
        8) GetCommRealData
        원형    BSTR GetCommRealData(BSTR strRealType, long nFid)
        설명    실시간데이터를 반환한다.
        입력값  strRealType – 실시간 구분
                nFid – 실시간 아이템
        반환값  수신 데이터
        비고    Ex) 현재가출력 - openApi.GetCommRealData(“해외선물시세”, 10);
        """
        if not (isinstance(strRealType, str) and isinstance(nFid, int)):
            print("Error : ParameterTypeError by GetCommRealData")
            raise ParameterTypeError()

        return self.dynamicCall("GetCommRealData(QString, int)", strRealType,
                                nFid)

    def GetChejanData(self, nFid):
        """
        9) GetChejanData
        원형    BSTR GetChjanData(long nFid)
        설명    체결잔고 데이터를 반환한다.
        입력값  nFid – 체결잔고 아이템
        반환값  수신 데이터
        비고    Ex) 현재가출력 – openApi.GetChejanData(910); //체결가격
        """
        if not (isinstance(nFid, int)):
            print("Error : ParameterTypeError by GetChejanData")
            raise ParameterTypeError()

        return self.dynamicCall("GetChejanData(int)", nFid)

    def SendOrder(self, sRQName, sScreenNo, sAccNo, nOrderType, sCode, nQty,
                  sPrice, sStop, sHogaGb, sOrgOrderNo):
        """
        10) SendOrder
        원형    LONG SendOrder(BSTR sRQName, BSTR sScreenNo, BSTR sAccNo, LONG nOrderType, BSTR sCode, LONG nQty,
                BSTR sPrice, BSTR sStop, BSTR sHogaGb, BSTR sOrgOrderNo  )
        설명    주문을 서버로 전송한다.
        입력값  sRQName - 사용자 구분 요청 명
                sScreenNo - 화면번호[4] (1~9999 :숫자값으로만 가능)
                sAccNo - 계좌번호[10]
                nOrderType - 주문유형 (1:신규매도, 2:신규매수, 3:매도취소, 4:매수취소, 5:매도정정, 6:매수정정)
                sCode  - 종목코드
                nQty – 주문수량
                sPrice – 주문단가
                sStop - Stop단가
                sHogaGb - 거래구분
                sOrgOrderNo – 원주문번호
        반환값  에러코드 <7.에러코드표 참고>
        비고    sHogaGb – 1:시장가, 2:지정가, 3:STOP, 4:STOP LIMIT
                ex) 지정가 매수 - openApi.SendOrder(“RQ_1”, “0101”, “5015123410”, 2, “6AH16”, 10, “0.7900”, “2”, “”);
                    시장가 매수 - openApi.SendOrder(“RQ_1”, “0101”, “5015123410”, 2, “6AH16”, 10, “0”, “1”, “”);
                    매수 정정 - openApi.SendOrder(“RQ_1”,“0101”, “5015123410”, 6, “6AH16”, 10, “0.7800”, “0”, “200060”);
                    매수 취소 - openApi.SendOrder(“RQ_1”, “0101”, “5015123410”, 4, “6AH16”, 10, “0”, “0”, “200061”);
        """
        if not self.getConnectState():
            print("Error : KiwoomConnectError by SendOrder")
            raise KiwoomConnectError()

        if not (isinstance(sRQName, str) and isinstance(sScreenNo, str)
                and isinstance(sAccNo, str) and isinstance(nOrderType, int)
                and isinstance(sCode, str) and isinstance(nQty, int)
                and isinstance(sPrice, str) and isinstance(sStop, str)
                and isinstance(sHogaGb, str) and isinstance(sOrgOrderNo, str)):
            print("Error : ParameterTypeError by SendOrder")
            raise ParameterTypeError()

        ErrorCode = self.dynamicCall(
            "SendOrder(QString, QString, QString, int, QString, int, QString, QString, QString, QString)",
            sRQName, sScreenNo, sAccNo, nOrderType, sCode, nQty, sPrice, sStop,
            sHogaGb, sOrgOrderNo)

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

        self.orderLoop = QEventLoop()
        self.orderLoop.exec_()

    def GetLoginInfo(self, sTag):
        """
        11) GetLoginInfo
        원형    BSTR GetLoginInfo(BSTR sTag)
        설명    로그인한 사용자 정보를 반환한다.
        입력값  BSTR sTag : 사용자 정보 구분 TAG값 (비고)
        반환값  TAG값에 따른 데이터 반환
        비고    BSTR sTag에 들어 갈 수 있는 값은 아래와 같음
                “ACCOUNT_CNT” – 전체 계좌 개수를 반환한다.
                "ACCNO" – 전체 계좌를 반환한다. 계좌별 구분은 ‘;’이다.
                “USER_ID” - 사용자 ID를 반환한다.
                “USER_NAME” – 사용자명을 반환한다.
                “KEY_BSECGB” – 키보드보안 해지여부. 0:정상, 1:해지
                “FIREW_SECGB” – 방화벽 설정 여부. 0:미설정, 1:설정, 2:해지
                Ex) openApi.GetLoginInfo(“ACCOUNT_CNT”);
        """
        if not self.GetConnectState():
            print("Error : KiwoomConnectError by GetLoginInfo")
            raise KiwoomConnectError()

        if not isinstance(sTag, str):
            print("Error : ParameterTypeError by GetLoginInfo")
            raise ParameterTypeError()

        if sTag not in [
                "ACCOUNT_CNT", "ACCNO", "USER_ID", "USER_NAME",
                "GetServerGubun"
        ]:
            print("Error : ParameterValueError by GetLoginInfo")
            raise ParameterValueError()

        return self.dynamicCall("GetLoginInfo(QString)", sTag)

    def GetGlobalFutureItemlist(self):
        """
        12) GetGlobalFutureItemlist
        원형    BSTR GetGlobalFutureItemlist()
        설명    해외선물 상품리스트를 반환한다.
        입력값  없음
        반환값  해외선물 상품리스트, 상품간 구분은 ‘;’이다.
        비고    해외선물 상품리스트(6A, 6B, 6C, ES…..)
        """
        return self.dynamicCall("GetGlobalFutureItemlist()")

    def GetGlobalOptionItemlist(self):
        """
        13) GetGlobalOptionItemlist
        원형 BSTR GetGlobalOptionItemlist()
        설명 해외옵션 상품리스트를 반환한다.
        입력값 없음
        반환값 해외옵션 상품리스트, 상품간 구분은 ‘;’이다.
        비고 해외옵션 상품리스트(6A, 6B, 6C, ES…..)
        """
        return self.dynamicCall("GetGlobalOptionItemlist()")

    def GetGlobalFutureCodelist(self, sItem):
        """
        14) GetGlobalFutureCodelist
        원형    BSTR GetGlobalFutureCodelist(BSTR sItem)
        설명    해외상품별 해외선물 종목코드리스트를 반환한다.
        입력값  해외상품
        반환값  해외선물 종목코드리스트, 종목간 구분은 ‘;’이다.
        비고    해외선물 종목코드리스트
        """
        if not (isinstance(sItem, str)):
            print("Error : ParameterTypeError by GetGlobalFutureCodelist")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalFutureCodelist(QString)", sItem)

    def GetGlobalOptionCodelist(self, sItem):
        """
        15) GetGlobalOptionCodelist
        원형    BSTR GetGlobalOptionCodelist(BSTR sItem)
        설명    해외상품별 해외선물 종목코드리스트를 반환한다.
        입력값  해외상품
        반환값  해외옵션 종목코드리스트, 종목간 구분은 ‘;’이다.
        비고    해외옵션 종목코드리스트
        """
        if not (isinstance(sItem, str)):
            print("Error : ParameterTypeError by GetGlobalOptionCodelist")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalOptionCodelist(QString)", sItem)

    def GetConnectState(self):
        """
        16) GetConnectState
        원형    LONG GetConnectState()
        설명    현재접속상태를 반환한다.
        입력값  없음
        반환값  접속상태
        비고    0:미연결, 1:연결완료
        """
        return self.dynamicCall("GetConnectState()")

    def GetAPIModulePath(self):
        """
        17) GetAPIModulePath
        원형    BSTR GetAPIModulePath()
        설명    OpenAPI모듈의 경로를 반환한다.
        입력값  없음
        반환값  경로
        비고
        """
        return self.dynamicCall("GetAPIModulePath()")

    def GetCommonFunc(self, sFuncName, sParam):
        """
        18) GetCommonFunc
        원형    BSTR GetCommonFunc(BSTR sFuncName, BSTR sParam)
        설명    공통함수로 추후 추가함수가 필요시 사용할 함수이다.
        입력값  함수명, 인자값
        반환값  문자값으로 반환한다.
        비고
        """
        if not (isinstance(sFuncName, str) and isinstance(sParam, str)):
            print("Error : ParameterTypeError by GetCommonFunc")
            raise ParameterTypeError()

        return self.dynamicCall("GetCommonFunc(QString, QString)", sFuncName,
                                sParam)

    def GetConvertPrice(self, sCode, sPrice, nType):
        """
        19) GetConvertPrice
        원형    BSTR GetConvertPrice(BSTR sCode, BSTR sPrice, LONG nType)
        설명    가격 진법에 따라 변환된 가격을 반환한다.
        입력값  종목코드, 가격, 타입(0 : 진법->10진수, 1 : 10진수->진법)
        반환값  문자값으로 반환한다.
        비고
        """
        if not (isinstance(sCode, str) and isinstance(sPrice, str)
                and isinstance(nType, str)):
            print("Error : ParameterTypeError by GetConvertPrice")
            raise ParameterTypeError()

        return self.dynamicCall("GetConvertPrice(QString, QString, int)",
                                sCode, sPrice, nType)

    def GetGlobalFutOpCodeInfoByType(self, nGubun, sType):
        """
        20) GetGlobalFutOpCodeInfoByType
        원형    BSTR GetGlobalFutCodeInfoByType(LONG nGubun, BSTR sType)
        설명    해외선물옵션종목코드정보를 타입별로 반환한다.
        입력값  nGubun : 0(해외선물), 1(해외옵션)
                sType : IDX(지수), CUR(통화), INT(금리), MLT(금속), ENG(에너지), CMD(농산물)
        반환값  종목코드정보리스트들을 문자값으로 반환한다.(아래 종목마스터파일 참조)
        비고    전체는 “”으로 보내면 된다.
        """
        if not (isinstance(nGubun, int) and isinstance(sType, str)):
            print("Error : ParameterTypeError by GetGlobalFutOpCodeInfoByType")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalFutOpCodeInfoByType(int, QString)",
                                nGubun, sType)

    def GetGlobalFutOpCodeInfoByCode(self, sCode):
        """
        21) GetGlobalFutOpCodeInfoByCode
        원형    BSTR GetGlobalFutCodeInfoByCode(BSTR sCode)
        설명    해외선물옵션종목코드정보를 종목코드별로 반환한다.
        입력값  sCode : 해외선물옵션 종목코드 입력
        반환값  종목코드정보를 문자값으로 반환한다.(아래 종목마스터파일 참조)
        비고
        """
        if not (isinstance(sCode, str)):
            print("Error : ParameterTypeError by GetGlobalFutOpCodeInfoByCode")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalFutOpCodeInfoByCode(QString)", sCode)

    def GetGlobalFutureItemlistByType(self, sType):
        """
        22) GetGlobalFutureItemlistByType
        원형    BSTR GetGlobalFutureItemlistByType (BSTR sType)
        설명    해외선물상품리스트를 타입별로 반환한다.
        입력값  sType : IDX(지수), CUR(통화), INT(금리), MLT(금속), ENG(에너지), CMD(농산물)
        반환값  상품리스트를 문자값으로 반환한다.
        비고
        """
        if not (isinstance(sType, str)):
            print(
                "Error : ParameterTypeError by GetGlobalFutureItemlistByType")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalFutureItemlistByType(QString)",
                                sType)

    def GetGlobalFutureCodeByItemMonth(self, sItem, sMonth):
        """
        23) GetGlobalFutureCodeByItemMonth
        원형    BSTR GetGlobalFutureCodeByItemMonth(BSTR sItem, BSTR sMonth)
        설명    해외선물종목코드를 상품/월물별로 반환한다.
        입력값  sItem: 상품코드(6A, ES..),
                sMonth: “201606”
        반환값  종목코드를 문자값으로 반환한다.
        비고
        """
        if not (isinstance(sItem, str) and isinstance(sMonth, str)):
            print(
                "Error : ParameterTypeError by GetGlobalFutureCodeByItemMonth")
            raise ParameterTypeError()

        return self.dynamicCall(
            "GetGlobalFutureCodeByItemMonth(QString, QString)", sItem, sMonth)

    def GetGlobalOptionCodeByMonth(self, sItem, sCPGubun, sActPrice, sMonth):
        """
        24) GetGlobalOptionCodeByMonth
        원형    BSTR GetGlobalOptionCodeByMonth(BSTR sItem, BSTR sCPGubun, BSTR sActPrice, BSTR sMonth)
        설명    해외옵션종목코드를 상품/콜풋/행사가/월물별로 반환한다.
        입력값  sItem: 상품코드(6A, ES..),
                sCPGubun: C(콜)/P(풋), sActPrice: 0.760,
                sMonth: “201606”
        반환값  종목코드를 문자값으로 반환한다.
        비고
        """
        if not (isinstance(sItem, str) and isinstance(sCPGubun, str)
                and isinstance(sActPrice, str) and isinstance(sMonth, str)):
            print("Error : ParameterTypeError by GetGlobalOptionCodeByMonth")
            raise ParameterTypeError()

        return self.dynamicCall(
            "GetGlobalOptionCodeByMonth(QString, QString, QString, QString)",
            sItem, sCPGubun, sActPrice, sMonth)

    def GetGlobalOptionMonthByItem(self, sItem):
        """
        25) GetGlobalOptionMonthByItem
        원형    BSTR GetGlobalOptionMonthByItem(BSTR sItem)
        설명    해외옵션월물리스트를 상품별로 반환한다.
        입력값  sItem: 상품코드(6A, ES..)
        반환값  월물리스트를 문자값으로 반환한다.
        비고
        """
        if not (isinstance(sItem, str)):
            print("Error : ParameterTypeError by GetGlobalOptionMonthByItem")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalOptionMonthByItem(QString", sItem)

    def GetGlobalOptionActPriceByItem(self, sItem):
        """
        26) GetGlobalOptionActPriceByItem
        원형    BSTR GetGlobalOptionActPriceByItem(BSTR sItem)
        설명    해외옵션행사가리스트를 상품별로 반환한다.
        입력값  sItem: 상품코드(6A, ES..)
        반환값  행사가리스트를 문자값으로 반환한다.
        비고
        """
        if not (isinstance(sItem, str)):
            print(
                "Error : ParameterTypeError by GetGlobalOptionActPriceByItem")
            raise ParameterTypeError()

        return self.dynamicCall("GetGlobalOptionActPriceByItem(QString", sItem)

    def GetGlobalFutureItemTypelist(self):
        """
        27) GetGlobalFutureItemTypelist
        원형    BSTR GetGlobalFutureItemTypelist()
        설명    해외선물상품타입리스트를 반환한다.
        입력값  없음
        반환값  상품타입리스트를 문자값으로 반환한다.
        비고    IDX;CUR;INT;MLT;ENG;CMD;  반환
        """
        return self.dynamicCall("GetGlobalFutureItemTypelist()")

    def GetCommFullData(self, strTrCode, strRecordName, nGubun):
        """
        28) GetCommFullData
        원형    BSTR GetCommFullData(BSTR strTrCode, BSTR strRecordName, LONG nGubun )
        설명    수신된 전체데이터를 반환한다.
        입력값  strTrCode – Tran 코드
                strRecordName – 레코드명
                nGubun–  0 : 전체(싱글+멀티),  1 : 싱글데이타, 2 : 멀티데이타
        반환값  수신 전체데이터를 문자값으로 반환한다.
        비고    WKOAStudio의 TR목록탭에서 필드 사이즈 참조.(필드명 옆 가로안의 값들)
                모든 시세/원장 조회에 사용 가능하며, 특히 차트데이타 같은 대용량 데이터를
                한번에 받아서 처리가능.
        """
        if not (isinstance(strTrCode, str) and isinstance(strRecordName, str)):
            print("Error : ParameterTypeError by GetCommFullData")
            raise ParameterTypeError()

        return self.dynamicCall("GetCommFullData(QString, QString", strTrCode,
                                strRecordName)

    def GetScreenNumber(self):
        ret = "{0:04d}".format(self.screenNo)
        if self.screenNo == 9999:
            raise MaxScreenNumber()
        self.dicScrNo.update({ret: self.screenNo})
        self.screenNo += 1
        return ret

    ###############################################################
    # Event
    # 각종 수신 Event를 처리
    ###############################################################

    # OnReceiveTrData
    def ReceiveTrData(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        """
        1) OnReceiveTrData
        원형    void OnReceiveTrData(BSTR sScrNo, BSTR sRQName, BSTR sTrCode, BSTR sRecordName, BSTR sPreNext)
        설명    서버통신 후 데이터를 받은 시점을 알려준다.
        입력값  sScrNo – 화면번호
                sRQName – 사용자구분 명
                sTrCode – Tran 명
                sRecordName – Record 명
                sPreNext – 연속조회 유무
        반환값  없음
        비고    sRQName – CommRqData의 sRQName과 매핑되는 이름이다.
                sTrCode – CommRqData의 sTrCode과 매핑되는 이름이다.
        """
        print("OnReceiveTrData")
        self.requestLoop.exit()

    # OnReceiveRealData
    def ReceiveRealData(self, sJongmokCode, sRealType, sRealData):
        """
        2) OnReceiveRealData
        원형    void OnReceiveRealData(BSTR sJongmokCode, BSTR sRealType, BSTR sRealData)
        설명    실시간데이터를 받은 시점을 알려준다.
        입력값  sJongmokCode – 종목코드
                sRealType – 리얼타입
                sRealData – 실시간 데이터전문
        반환값  없음
        비고
        """
        #print("OnReceiveRealData")

    # OnReceiveMsg
    def ReceiveMsg(self, sScrNo, sRQName, sTrCode, sMsg):
        """
        3) OnReceiveMsg
        원형    void OnReceiveMsg(BSTR sScrNo BSTR sRQName, BSTR sTrCode, BSTR sMsg)
        설명    서버통신 후 메시지를 받은 시점을 알려준다.
        입력값  sScrNo – 화면번호
                sRQName – 사용자구분 명
                sTrCode – Tran 명
                sMsg – 서버메시지
        반환값  없음
        비고    sScrNo – CommRqData의 sScrNo와 매핑된다.
                sRQName – CommRqData의 sRQName 와 매핑된다.
                sTrCode – CommRqData의 sTrCode 와 매핑된다
        """
        print("OnReceiveMsg")
        self.msg += sRQName + ": " + sMsg + "\r\n\r\n"

    # OnReceiveChejanData
    def ReceiveChejanData(self, sGubun, nItemCnt, sFidList):
        """
        4) OnReceiveChejanData
        원형    void OnReceiveChejanData(BSTR sGubun, LONG nItemCnt, BSTR sFidList)
        설명    체결데이터를 받은 시점을 알려준다.
        입력값  sGubun – 체결구분
                nItemCnt - 아이템갯수
                sFidList – 데이터리스트
                sSplmMsg - 1.0.0.1 버전 이후 사용하지 않음.
        반환값  없음
        비고    sGubun – 0:주문체결통보, 1:잔고통보, 3:특이신호
                sFidList – 데이터 구분은 ‘;’ 이다
        """
        print("OnReceiveChejanData")
        if self.requestLoop:
            self.requestLoop.exit()
        if self.orderLoop:
            self.orderLoop.exit()

    # 로그인 이벤트 응답
    def EventConnect(self, nErrCode):
        """
        5) OnEventConnect
        원형    void OnEventConnect(LONG nErrCode)
        설명    서버 접속 관련 이벤트
        입력값  LONG nErrCode : 에러 코드
        반환값  없음
        비고    nErrCode가 0이면 로그인 성공, 음수면 실패
                음수인 경우는 에러 코드 참조
        """
        print("OnEventConnect")
        self.loginEventLoop.exit()
Exemple #25
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
Exemple #26
0
class WaitForSignal(unittest.TestCase):

    def __init__(self,
                 # The signal to wait for.
                 signal,
                 # The maximum time to wait for the signal to be emitted, in ms.
                 timeoutMs,
                 # True to self.assertif the signal wasn't emitted.
                 assertIfNotRaised=True,
                 # Expected parameters which this signal must supply.
                 expectedSignalParams=None,
                 # True to print exceptions raised in the event loop
                 printExcTraceback=True,
                 # Number of times this signal must be emitted
                 numEmittedExpected=1):

        self.signal = signal
        self.timeoutMs = timeoutMs
        self.expectedSignalParams = expectedSignalParams
        self.assertIfNotRaised = assertIfNotRaised
        self.printExcTraceback = printExcTraceback
        self.numEmittedExpected = numEmittedExpected

        # Stores the result of comparing self.expectedSignalParams with the
        # actual params.
        self.areSenderSignalArgsWrong = False
        # The number of times this signal was emitted.
        self.numEmitted = 0

    # Create a slot which receives a senderSignal with any number
    # of arguments. Check the arguments against their expected
    # values, if requested, storing the result in senderSignalArgsWrong[0].
    # (I can't use senderSignalArgsWrong = True/False, since
    # non-local variables cannot be assigned in another scope).
    def signalSlot(self, *args):
        # If the senderSignal args should be checked and they
        # don't match, then they're wrong. In all other cases,
        # they're right.
        if self.expectedSignalParams:
            self.areSenderSignalArgsWrong = (self.expectedSignalParams != args)
        self.numEmitted += 1
        if self._gotSignal():
            # We received the requested signal, so exit the event loop or never
            # enter it (exit won't exit an event loop that hasn't been run). When
            # this is nested inside other WaitForSignal clauses, signals may be
            # received in another QEventLoop, even before this object's QEventLoop
            # starts.
            self.qe.exit()

    # True of the signal was emitted the expected number of times.
    def _gotSignal(self):
        return self.numEmitted == self.numEmittedExpected

    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

    def __exit__(self, exc_type, exc_value, traceback):
        # Create a single-shot timer. Could use QTimer.singleShot(),
        # but can't cancel this / disconnect it.
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.qe.quit)
        self.timer.start(self.timeoutMs)

        # Catch any exceptions which the EventLoop would otherwise catch
        # and not re-raise.
        self.exceptions = None

        def excepthook(type_, value, tracebackObj):
            self.exceptions = (value, tracebackObj)
            if self.printExcTraceback:
                oldExcHook(type_, value, tracebackObj)
            self.qe.exit()
        oldExcHook = sys.excepthook
        sys.excepthook = excepthook

        # Wait for an emitted signal, unless it already occurred.
        if not self._gotSignal():
            self.qe.exec_()
        # Restore the old exception hook
        sys.excepthook = oldExcHook
        # Clean up: don't allow the timer to call qe.quit after this
        # function exits, which would produce "interesting" behavior.
        timerIsActive = self.timer.isActive()
        self.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.
        # Likewise, disconnect the senderSignal for the same reason.
        self.signal.disconnect(self.signalSlot)
        self.timer.timeout.disconnect(self.qe.quit)

        # If an exception occurred in the event loop, re-raise it.
        if self.exceptions:
            value, tracebackObj = self.exceptions
            raise value.with_traceback(tracebackObj)

        # Check that the signal occurred.
        self.sawSignal = timerIsActive and not self.areSenderSignalArgsWrong
        if self.assertIfNotRaised:
            self.assertTrue(self.sawSignal)

        # Don't mask exceptions.
        return False
Exemple #27
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)
Exemple #28
0
class Kiwoom(QAxWidget):
    """
    https://wikidocs.net/5755
    키움증권에서로 부터 설치한것은 OCX 방식에 컨포넌트 객체로 설치가 되었다.
    응용프로그램에서 키움Open API를 실행 할 수 있게 한거다.
    덕분에 제어가 된다.
    제어 할수 있는 함수는 PytQ5
    시그널 = 요청
    키움 API는 싱글데이터 멀티데이터 Key제목을 꼭 보고 가져와야 된다.
    """
    def __init__(self):

        super().__init__()  # 분모에있는 초기값들을 사용하기위해 분모에 있는 init을 사용한다.

        print("키움 API 연동")

        self.realType = RealType()

        ############event_loop
        self.login_exec = None
        ######################

        ############변수모음###
        self.account_BankNum = None  # 계좌번호
        self.sPrevNext = None  # 페이지 번호
        self.checkBalanceBox = {}  # 계좌잔고 계좌종목
        self.not_tighteningBox = {}  # 미체결
        #####################

        ###########종합일봉리스트########
        self.onedaypayListData = []  # 모든 데이터 리스트(중점데이터)
        ###############################

        #############스크린 번호 모음
        self.QscreenNumberT = 2000
        self.QscreenNumberF = 4000
        self.QscreenNumberFi = 5000
        self.QscreenNumberS = 6000
        self.realTime_start_stop_QscreenNumberO = 1000

        ############유저 계좌 접근 변수
        self.userMoney = None
        self.deposit_persent = 0.5
        self.deposit_division = 4
        ###########################

        #############이벤트 루프 모음
        self.defult_account_info_event_loop = QEventLoop()
        ###########################################

        ##############조건선별 file변수###
        self.files_stock_diet = {}
        ###############################

        self.get_Ocx_Install()
        self.event_slot_List()

        self.signal_login_CommConnect()
        self.dynamicCall("KOA_Functions(QString, QString)",
                         "ShowAccountWindow", "")
        self.get_account_info()

        # 예수금 조회
        self.deposit_acount_info()

        # 계좌평가잔고내역요청 조회
        self.checkBalance_acount_info()

        # 종목분석용, 임시용으로 실행
        self.calculator_Fn()

        # condition_stock.txt에 저장된 종목들을 불러옵니다.
        self.read_code()

        print("계좌번호 :%s" % self.account_BankNum)
        print("페이지 번호 :%s" % self.sPrevNext)
        print("계좌잔고 계좌종목 :%s" % self.checkBalanceBox)
        print("미체결 :%s" % self.not_tighteningBox)
        print("조건선별딕셔너리 :%s" % self.files_stock_diet)

        # 각각의 스크린번호 Setting
        self.screen_number_settings()
        """
        0 : 처음 RealTime 등록했을시
        1 : 이후 추가 RealTime 등록시
        """

        # realTypeData Test
        print(self.realType.REALTYPE['장시작시간']['장운영구분'])
        print(self.realType.REALTYPE['장시작시간']['시간'])

        self.real_slot_call()
        # RealTime_slot_Insert
        self.real_slot_event()

    def get_Ocx_Install(self):
        """
        키움 API를 설치함으로써 응용프로그램들이 우리 window 레지스트리에 저장된다.
        레지스트리 편집기에 찾을수 있다.
        KHOPENAPI.KHOpenAPICtrl.1 폴더명으로 저장되며 DKHOPENAPI 기본값으로 설정되어있다.
        """

        # 응용프로그램을 제어하기 위한 그 설치된 경로를 지정해주어야됨
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")

    def event_slot_List(self):

        # 로그인용 이벤트 슬롯연결
        self.OnEventConnect.connect(self.login_slot)

        # TR용 이벤트 슬롯연결
        self.OnReceiveTrData.connect(self.trData_slot)

    # 0503 Testing Error
    def real_slot_event(self):

        self.OnReceiveRealData.connect(self.realData_slot)

    # 로그인 성공시 이벤트
    def login_Function(self, nErrCode):
        print('로그인처리 %d' % nErrCode)

        self.login_exec.exit()

    # 키움 로그인 기능창 출력 or 자동설정 (로그인 시도)
    def signal_login_CommConnect(self):
        """
         로그인 성공시에 대한 이벤트 지정을 안해주면 로그인 성공시에 이벤트값이 없기에
         이벤트 핸들러 오류 발생할수 있음
        """

        # 네트워크적이거나 다른 서버 응용프로그램에다가 데이터를 전송할수 있음음
        self.dynamicCall("CommConnect()")

        # QtCore 이벤트 루프 초기화
        self.login_exec = QEventLoop()

        # 이벤트 루프 지정
        self.login_exec.exec_()

    # 키움 에러처리 key
    def login_slot(self, errCode):
        print(errors(errCode))

        self.login_exec.exit()

    # 계좌번호가져오기
    def get_account_info(self):
        account_list = self.dynamicCall("GetLoginInfo(QString)", "ACCLIST")

        # split : 문자열 자르기 (계좌번호 가져올때 18545454545;45447878785; 이런식으로 출력됨)
        self.account_BankNum = account_list.split(';')[0]

        # 현재 계좌번호
        print('현재 계좌 번호 %s' % self.account_BankNum)

    # 예수금 요청내역
    def deposit_acount_info(self):
        print('예수금 조회 요청')

        # 조회 함수입력
        self.dynamicCall("SetInputValue(QString, QString)", "계좌번호",
                         self.account_BankNum)  # 계좌번호
        self.dynamicCall("SetInputValue(QString, QString)", "비밀번호", "0000")
        self.dynamicCall("SetInputValue(QString, QString)", "비밀번호입력매체구분", "00")
        self.dynamicCall("SetInputValue(QString, QString)", "조회구분",
                         '2')  #2는 일반조회 3은 추정조회

        # 예수금 상세현황요청 조회함수 호출하여 서버전송
        self.dynamicCall("CommRqData(QString, QString, QString, QString)",
                         "예수금상세현황요청", "opw00001", '0', self.QscreenNumberT)

        # 다른데이터를 처리하는동안 다른작업을 할수있게 만드는것 이벤트 루프
        self.defult_account_info_event_loop
        self.defult_account_info_event_loop.exec_()

    # 계좌평가잔고내역요청내역, sPrevNext값이 0으로 설정되었기에 싱글데이터값을 요청함함
    def checkBalance_acount_info(self, setsPrevNext="0"):
        print("계좌평가잔고내역요청")

        self.dynamicCall("SetInputValue(QString, QString)", "계좌번호",
                         self.account_BankNum)
        self.dynamicCall("SetInputValue(QString, QString)", "비밀번호", "0000")
        self.dynamicCall("SetInputValue(QString, QString)", "비밀번호입력매체구분", "00")
        self.dynamicCall("SetInputValue(QString, QString)", "조회구분", '2')

        # 계좌평가잔고내역요청 조회함수 호출하여 서버전송
        self.dynamicCall("CommRqData(QString, QString, QString, QString)",
                         "계좌평가잔고내역요청", "opw00018", setsPrevNext,
                         self.QscreenNumberF)

        self.defult_account_info_event_loop.exec_()

    # 미체결요청
    def not_tightening_acount_info(self):
        print("미체결요청")

        # 조회하고 싶은것만 받아오게 할수있다.
        self.dynamicCall("SetInputValue(QString, QString)", "계좌번호",
                         self.account_BankNum)
        self.dynamicCall("SetInputValue(QString, QString)", "매매구분", 0)
        self.dynamicCall("SetInputValue(QString, QString)", "체결구분", 1)

        # Kiwoom서버에게 해당 데이터값 요청
        self.dynamicCall("CommRqData(QString, QString, QString, QString)",
                         "미체결요청", "opt10075", 0, self.QscreenNumber)

        self.defult_account_info_event_loop.exec_()

    # 주식일봉차트조회요청
    def request_to_check_the_stock_pay_chart(self,
                                             eventCode=None,
                                             date=None,
                                             setsPrevNext=0):
        print("주식일봉차트조회")
        """
        위 네트워크적인 프로세스를 멈추진 않고 대신에 이상태에서 다음 코드실행하기 전에 4초 지연합니다. (보안상 3.5초 이상 지연)
        너무 느리지만 이점을 보완할 방법이 딱히 없다.
        그리고 키움은 매일 새벽마다 업데이트를 한다.
        """
        QTest.qWait(4000)

        self.dynamicCall("SetInputValue(QString, QString)", "종목코드", eventCode)
        self.dynamicCall("SetInputValue(QString, QString)", "수정주가구분", "1")

        if date != None:
            self.dynamicCall("SetInputValue(QString, QString)", "기준일자", date)

        self.dynamicCall("CommRqData(QString, QString, int, QString)",
                         "주식일봉차트조회", "opt10081", setsPrevNext,
                         self.QscreenNumberF)  #TR서버로 전송

        self.defult_account_info_event_loop.exec_()

    def get_code_list_by_market(self, market_code):
        '''
        종목코드들 반환
        :param market_code:
        :return:
        '''

        code_list = self.dynamicCall("GetCodeListByMarket(QString)",
                                     market_code)
        code_list = code_list.split(";")[:-1]

        return code_list

    def calculator_Fn(self):

        code_list = self.get_code_list_by_market("10")
        print("코스닥 갯수 %s", len(code_list))

        # 데이터가 너무 많아 1449이기에 3개로 줄여서 테스팅실시함.
        for index, code in enumerate(code_list[0:3]):
            """
            추가 : 스크린번호를 한번이라도 요청하면 그룹이 만들어 진것이다.
            그래서 끊어주는건 개인의 선택이다.
            """
            # 화면번호 끊기
            self.dynamicCall("DisconnectRealData(QString)",
                             self.QscreenNumberF)
            print("%s / %s : Kosdaq code_list : %s" %
                  (index + 1, len(code_list), code))
            self.request_to_check_the_stock_pay_chart(eventCode=code)

    # Tr슬롯

    def trData_slot(self, sScrNo, sRQName, sTrCode, sRecordName, sPrevNext):
        '''
        TR 요청을 받는 구역 (슬롯)
        :param sScrNo: 화면번호
        :param sRQName: 사용자 구분명
        :param sTrCode: TR이름
        :param sRecordName: 레코드이름
        :param sPrevNext: 연속조회 유무를 판단하는 값 0: 연속(추가조회)데이터 없음, 2:연속(추가조회) 데이터 있음 (다음페이지가 있는지)
        sPrevNext는 다음페이지가 활성화가 되면 2로 반환됩니다. 만약 다음페이지가 비활성화 상태라면 첫페이지인 0 or ""이 반환됩니다.
        다음페이지 활성화조건은 계좌평가잔고내역요청에 종목코드가 20개 초과일시 생성되어야합니다.
        :return:
        '''

        print(sTrCode)

        if (sRQName == "예수금상세현황요청"):
            """
            GetCommData
            - OnReceiveTRData()이벤트가 발생될때 수신한 데이터를 얻어오는 함수입니다.
            이 함수는 OnReceiveTRData()이벤트가 발생될때 그 안에서 사용해야 합니다.
            """

            # TR이름, 레코드이름인데 사용자 구분명으로 대체함;, 0은 아직도 잘 모르겠다, 필드항목이름이 예수금임
            deposit = self.dynamicCall(
                "GetCommData(QString, QString, int, QString)", sTrCode,
                sRQName, 0, "예수금")
            print(deposit)
            print("예수금 : %s" % deposit)
            print("예수금 형변환 : %s" % int(deposit))

            ###### 유저 한번 구입할때 조절하는 수치 (고객이 직접조절하게 GUI 개선해야됨)
            self.userMoney = int(deposit) * self.deposit_persent
            self.userMoney = self.userMoney / self.deposit_division

            stock_evidence_cash = self.dynamicCall(
                "GetCommData(QString, QString, int, QString)", sTrCode,
                sRQName, 0, "주식증거금현금")
            print("주식증거금현금 : %s" % stock_evidence_cash)
            print("주식증거금현금 형변환 : %s" % int(stock_evidence_cash))

            ture_deposit = self.dynamicCall(
                "GetCommData(QString, QString, QString, QString)", sTrCode,
                sRQName, 0, "출금가능금액")
            print("출금가능금액 : %s" % ture_deposit)
            print("출금가능금액 형변환 : %d" % int(ture_deposit))

            self.defult_account_info_event_loop.exit()

        elif (sRQName == "계좌평가잔고내역요청"):

            # 계좌평가잔고내역요청_총매입금액
            checkBalance_total_purchase_amount = self.dynamicCall(
                "GetCommData(QString, QString, QString, QString)", sTrCode,
                sRQName, 0, "총매입금액")
            print('총매입금액 : %s' % checkBalance_total_purchase_amount)
            print('총매입금액 형변환 : %s' % int(checkBalance_total_purchase_amount))

            # 계좌평가잔고내역요청_총평가금액
            checkBalance_total_evaluation_amount = self.dynamicCall(
                "GetCommData(QString, QString, QString, QString)", sTrCode,
                sRQName, 0, "총평가금액")
            print('총평가금액 : %s' % checkBalance_total_evaluation_amount)
            print('총평가금액 형변환 : %s' % int(checkBalance_total_evaluation_amount))

            # 계좌평가잔고내역요청_총수익률
            checkBalance_yield = self.dynamicCall(
                "GetCommData(QString, QString, QString, QString)", sTrCode,
                sRQName, 0, "총수익률(%)")
            print('총수익률(%%) : %s' % checkBalance_yield)
            print('총수익률(%%) 형변환 : %s' % float(checkBalance_yield), '%')

            # 현재 주식 매매 카운트 (GetRepeatCnt를 사용하면 카운트가 멀티데이터로 됨)
            cint = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode,
                                    sRQName)

            # 종목이 몇개 인지 뽑아보기 위해서 함

            # 600일의 관한 일봉 데이터
            for i in range(cint):

                balance_id = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "종목번호")
                balance_item_nm = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "종목명")
                balance_retained_quantity = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "보유수량")
                balance_purchase_price = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "매입가")
                balance_yield = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "수익률(%)")
                balance_current_price = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "현재가")
                balance_purchase_amount = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "매입금액")
                balance_tradeable_quantity = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "매매가능수량")

                # checkBalance 안에 cint이 있다면
                if (cint in self.checkBalanceBox):
                    pass

                else:

                    # self.checkBalanceBox[balance_id] = {} 이렇게 사용해도됨
                    balance_id = balance_id.strip()[1:]
                    self.checkBalanceBox.update({balance_id: {}})

                # strip(공백없애기) 하지만 [1:] 이면 2번째요소(인덱스자리 1)부터 시작한다는뜻
                balance_item_nm = balance_item_nm.strip()
                balance_retained_quantity = int(
                    balance_retained_quantity.strip())
                balance_purchase_price = int(balance_purchase_price.strip())
                balance_yield = float(balance_yield.strip())
                balance_current_price = int(balance_current_price.strip())
                balance_purchase_amount = int(balance_purchase_amount.strip())
                balance_tradeable_quantity = int(
                    balance_tradeable_quantity.strip())

                checkBalanceBoxSkip = self.checkBalanceBox[balance_id]

                # 종목 코드 해당 id 딕셔너리 안에 그 안에 딕셔너리 추가함
                checkBalanceBoxSkip.update({"종목명": balance_item_nm})
                checkBalanceBoxSkip.update({"보유수량": balance_retained_quantity})
                checkBalanceBoxSkip.update({"매입가": balance_purchase_price})
                checkBalanceBoxSkip.update({"수익률(%)": balance_yield})
                checkBalanceBoxSkip.update({"현재가": balance_current_price})
                checkBalanceBoxSkip.update({"매입금액": balance_purchase_amount})
                checkBalanceBoxSkip.update(
                    {"매매가능수량": balance_tradeable_quantity})

                print(
                    "종목코드: %s - 종목명: %s - 보유수량: %s - 매입가:%s - 수익률: %s - 현재가: %s"
                    % (balance_id, balance_item_nm, balance_retained_quantity,
                       balance_purchase_price, balance_yield,
                       balance_current_price))

                print("계좌 가지고 있는종목 : %d" % len(self.checkBalanceBox))

                if (sPrevNext == "2"):

                    self.checkBalance_acount_info(setsPrevNext="2")

                else:

                    self.defult_account_info_event_loop.exit()

            print(self.checkBalanceBox.keys())
            print(self.checkBalanceBox.values())

        elif (sRQName == "미체결요청"):

            # 미체결요청에 GetRepeatCnt은 최대 100Count
            not_tightening_List = self.dynamicCall(
                "GetRepeatCnt(QStirng, QString)", sTrCode, sRQName)

            for i in range(not_tightening_List):
                not_tightening_id = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "종목코드")
                not_tightening_nm = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "종목명")

                # -매도 +매수, -매도정리
                not_tightening_order_sortation = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "주문구분")

                # 접수 -> 확인 -> 체결
                not_tightening_order_state = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "주문상태")

                not_tightening_order_quantity = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "주문수량")
                not_tightening_order_price = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "주문가격")

                # 기본키
                not_tightening_order_no = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "주문번호")

                not_tightening_quantity = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "미체결수량")
                tightening_quantity = self.dynamicCall(
                    "GetCommData(QString QString int QString)", sTrCode,
                    sRQName, i, "체결량")

                not_tightening_id = not_tightening_id.strip()
                not_tightening_nm = not_tightening_nm.strip()

                # 첫문자 + or - 를 제거
                not_tightening_order_sortation = not_tightening_order_sortation.strip(
                ).lstrip('+').lstrip('-')
                not_tightening_order_state = not_tightening_order_state.strip()
                not_tightening_order_quantity = int(
                    not_tightening_order_quantity.strip())
                not_tightening_order_price = int(
                    not_tightening_order_price.strip())
                not_tightening_order_no = int(not_tightening_order_no.strip())
                not_tightening_quantity = int(not_tightening_quantity.strip())
                tightening_quantity = int(tightening_quantity.strip())

                if not_tightening_order_no in self.not_tighteningBox:
                    pass

                else:

                    self.not_tighteningBox.update(
                        {not_tightening_order_no, {}})

                not_tighteningBox_skip = self.not_tighteningBox[
                    not_tightening_order_no]

                not_tighteningBox_skip.update({"종목코드", not_tightening_id})
                not_tighteningBox_skip.update({"종목명", not_tightening_nm})
                not_tighteningBox_skip.update(
                    {"주문구분", not_tightening_order_sortation})
                not_tighteningBox_skip.update(
                    {"주문상태", not_tightening_order_state})
                not_tighteningBox_skip.update(
                    {"주문수량", not_tightening_order_quantity})
                not_tighteningBox_skip.update(
                    {"주문가격", not_tightening_order_price})
                not_tighteningBox_skip.update(
                    {"미체결수량", not_tightening_quantity})
                not_tighteningBox_skip.update({"체결량", tightening_quantity})

                print("미체결 관련 데이터 %s" % self.not_tighteningBox)

        elif "주식일봉차트조회" == sRQName:
            """
            장기적인 투자면 이방법이 편하지만 단기적으로 해야된다면 이방법은 추천드리지 않는다.
            문제점 방대한 데이터를 전부 instoll하기엔 너무 오래걸린다.
            그렇기에 특정 종목코드를 지정하거나 데이터수를 리미트를 걸어서 가져온다.
            """

            # 싱글데이터
            code = self.dynamicCall(
                "GetCommData(QString, QString, int, QString)", sTrCode,
                sRQName, 0, "종목코드")
            code = code.strip()
            print("%s 일봉데이터 요청" % code)

            # 최대 호출 카운트
            cint = self.dynamicCall("GetRepeatCnt(QString, QString)", sTrCode,
                                    sRQName)
            print("데이터 일수 %s" % cint)

            # 하루치들의 데이터들 600번 반복
            for i in range(cint):

                data = []

                current_price = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "현재가")  #종가
                transaction_volume = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "거래량")
                transaction_amount = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "거래대금")
                date = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "일자")
                market_value = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "시가")
                high_value = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "고가")
                low_value = self.dynamicCall(
                    "GetCommData(QString, QString, int, QString)", sTrCode,
                    sRQName, i, "저가")

                data.append(current_price.strip())
                data.append(transaction_volume.strip())
                data.append(transaction_amount.strip())
                data.append(date.strip())
                data.append(market_value.strip())
                data.append(high_value.strip())
                data.append(low_value.strip())

                self.onedaypayListData.append(data.copy())

            print("현재 일봉 데이터량 %s" % len(self.onedaypayListData))
            #print("현재 데이터 리스트 %s" % self.onedaypayListData)

            if sPrevNext == "2":
                self.request_to_check_the_stock_pay_chart(
                    eventCode=code, setsPrevNext=sPrevNext)
            else:

                print("총 일수 %s" % len(self.onedaypayListData))

                pass_success = None

                # 아직은 분석을 더해야됨 자세히 분석하지 못하였음
                # 120일 이평선을 그릴만큼의 데이터가 있는지 체크해야됨 (120일 이평선을 보기위한 행위)
                if self.onedaypayListData == None or len(
                        self.onedaypayListData) < 120:

                    pass_success = False

                # 120일 이상 일시
                else:
                    total_price = 0
                    # 리스트 슬라이싱
                    for value in self.onedaypayListData[:120]:

                        # 120일차에 1일현재가들을 전부 더한 값 total_price
                        total_price += int(value[0])

                        # 120일치 이평선 평균
                        average_horizontal = total_price / 120

                        # 오늘자 주기기 120일 이평선에 걸쳐있는지 확인
                        bottom_stock_price = False
                        check_price = None

                        # 현재 봉(양봉,음봉 구별은 아직안함)이 평균이평선 안에 속해있을때
                        if int(
                                self.onedaypayListData[0][6]
                        ) <= average_horizontal and average_horizontal <= int(
                                self.onedaypayListData[0][5]):
                            print("오늘 주가 120이평선 확인")

                            # 이평선이 저가가 평균값보다 낮고 고가가 평균값보다 높아야합니다.
                            bottom_stock_price = True
                            price_top_move = False

                            # 현재 고가 가격
                            check_price = int(self.onedaypayListData[0][5])

                        # 과거 일봉들이 120일 이평선보다 밑에 있는지 확인
                        # 그렇게 확인을 하다가 일봉이 120일 이평선보다 위에 있으면 계산 진행

                        if bottom_stock_price == True:

                            average_horizontal = 0

                            price_top_move = False

                            index = 1

                            while True:

                                # 120일치가 있는지 계속 확인
                                if len(self.onedaypayListData[index:]) < 120:
                                    print("120일치가 없음!")
                                    break

                                # 120일치 이평선이 있다는것이 확인된후 다시 구하기 위해 0 설정
                                total_price = 0

                                # 이해불능?
                                for value in self.onedaypayListData[index:120 +
                                                                    index]:

                                    # 전날 120일차 이평선 현 시점 평균값을 구하기위한 전날 120일 총 현재가
                                    total_price += int(value[0])

                                # 전날 평균 이평선 값
                                move_average_horizontal = total_price / 120

                                # 전날 평균 이평선값이 전날 고가보다 낮고 이 상태로 20일보다 작아야 합니다.
                                if move_average_horizontal <= int(
                                        self.onedaypayListData[index]
                                    [5]) and index <= 20:
                                    print(
                                        "20일 동안 주기가 120일 이평선과 같거나 위에 있으면 조건 통과 못함"
                                    )
                                    price_top_move = False
                                    break

                                # 전날 평균 이평선 값이 전날 저가보다 더 작고 20일차보다 커야됩니다.
                                elif int(
                                        self.onedaypayListData[index][6]
                                ) > move_average_horizontal and index > 20:
                                    print("120일 이평선 위에 있는 일봉 확인됨")
                                    price_top_move = True

                                    # 전날 저가 가격
                                    prev_price = int(
                                        self.onedaypayListData[index][6])
                                    break

                                else:
                                    index += 1

                            # 해당 부분 이평선이 가장 최근 일자의 이평선보다 가격이 낮은지 확인 하면서 현재 저가값이 전날 저가값보다
                            # 더 커야됩니다.
                            if price_top_move == True:
                                if average_horizontal > move_average_horizontal and check_price > prev_price:
                                    print(
                                        "포착된 이평선의 가격이 오늘자(최근일자) 이평선 가격보다 낮은 것 확인됨"
                                    )
                                    print(
                                        "포착된 부분의 일봉 저가가 오늘자 일봉의 고가보다 낮은지 확인됨")
                                    pass_success = True

                    if pass_success == True:
                        print("조건부 통과됨")

                        # 종목코드로 코드네임을 가져옵니다.
                        code_nm = self.dynamicCall(
                            "GetMasterCodeName(QString)", code)

                        # open이라는 파이썬 내장함수가 있습니다.
                        files_direction = open("files/condition_stock.txt",
                                               "a",
                                               encoding="utf8")

                        files_direction.write(
                            "%s\t%s\t%s\n" %
                            (code, code_nm, str(self.onedaypayListData[0][0])))

                        files_direction.close()

                    elif pass_success == False:
                        print("조건부 통과 못함")

                    self.onedaypayListData.clear()
                    self.defult_account_info_event_loop.exit()

                self.defult_account_info_event_loop.exit()

    def read_code(self):

        if os.path.exists("S:/Python/Project_Yanajo/files/condition_stock.txt"
                          ):  # 있으면 Ture 없으면 False

            # r = reading
            files_direction = open(
                "S:/Python/Project_Yanajo/files/condition_stock.txt",
                "r",
                encoding="utf8")

            lines = files_direction.readlines()

            for line in lines:
                if line != "":
                    line_list = line.split("\t")

                    stock_code = line_list[0]
                    stock_name = line_list[1]
                    stock_price = int(line_list[2].split("\n")[0])
                    stock_price = abs(stock_price)

                    # {"2027": {"종목명":"삼성 ....}....}]
                    self.files_stock_diet.update(
                        {stock_code: {
                            "종목명": stock_name,
                            "현재가": stock_price
                        }})

            # 메모리를 잡아먹지 않기위해 file을 닫아줍니다.
            files_direction.close()

            print(self.files_stock_diet)

    def screen_number_settings(self):

        # 겹치는 종목들을 알기위함
        screemOvering = []

        # 계좌잔고내역 종목 getter
        for getValueList in self.checkBalanceBox.keys():

            if getValueList not in screemOvering:

                # 0429(58-24:19)
                # 겹치는종목이 없다면은 해당 종목을 추가해줍니다.
                screemOvering.append(getValueList)

        # 미체결에 있는 종목들
        for getValueList in self.not_tighteningBox.keys():

            code = self.not_tighteningBox[getValueList]['종목코드']

            if code not in getValueList:
                screemOvering.append(code)

        # 리스트에 담겨있는 목록들
        for getValueList in self.files_stock_diet.keys():

            if getValueList not in screemOvering:

                screemOvering.append(getValueList)

        # 스크린번호 할당
        count = 0
        for getValueList in screemOvering:

            temp_screen = int(self.QscreenNumberFi)
            meme_screen = int(self.QscreenNumberS)

            # 100번째에 끊음
            if (count % 100) == 0:
                temp_screen += 1  #스크린 번호 하나당 데이터(종목코드) 100개를 넣어줌
                self.QscreenNumberFi = str(temp_screen)

                meme_screen += 1  # 스크린 번호 하나당 데이터(종목코드) 100개를 넣어줌
                self.QscreenNumberS = str(meme_screen)

            # 포함될때

            if getValueList in self.files_stock_diet.keys():

                # 각 종목들 한번에 모으기
                self.files_stock_diet[getValueList].update({
                    "스크린번호":
                    self.QscreenNumberFi,
                    "주문용스크린번호":
                    self.QscreenNumberS
                })

            # 포함안될때
            elif getValueList not in self.files_stock_diet.keys():

                # 각 종목들 한번에 모으기
                self.files_stock_diet.update({
                    getValueList: {
                        "스크린번호": self.QscreenNumberFi,
                        "주문용스크린번호": self.QscreenNumberS
                    }
                })

            count += 1
        print("files Array : %s" % self.files_stock_diet)
        """
        programer -> (삼성) -> 증권서버
        프로그래머가 삼성을 등록해놓음
        미리 이벤트랑 슬롯이랑 연결해놓은 구간이있습니다.
        그전에는 OnReceiveTrData를 통해서 슬롯과 연결했지만
        이번에는 OnReceiveRealData.connet(실시간수신슬롯)을 등록합니다.
        삼성이라는 데이터가 바뀌면은 바뀔때마다 서버에서 슬롯을 던져줍니다.
        여러가지를 불러오면서 files_stock_diet를 실시간 업데이트를 해줄겁니다.
        그리고 매수매도를 여기서 확인을 합니다.
        
        """

    # 0503 Testing Error
    def real_slot_call(self):

        self.dynamicCall("SetRealReg(QString, QString, QString, QString",
                         self.realTime_start_stop_QscreenNumberO, '',
                         self.realType.REALTYPE['장시작시간']['장운영구분'], "0")
        self.defult_account_info_event_loop.exec_()

    # 0503 Error : 실행안됨
    def realData_slot(self, sCode, sRealType, sRealData):
        """

        :param sCode: 종목코드
        :param sRealType: 실시간타입
        :param sRealData: 실시간 데이터 전문 (사용불능)
        :return: respons Data
        """

        print("RealTimeCode : %s" % sCode)
        print("RealTimeType : %s" % sRealType)
        print("RealTimeData : %s" % sRealData)

        if sRealType == "장시작시간":
            fid = self.realType.REALTYPE[sRealType]['장운영구분']
            values = self.dynamicCall("GetCommRealData(QString, QString)",
                                      sCode, fid)

            # Test
            print(values)
            if values == 0:
                print('현재 시장이 닫은 시간이지만 시작 전 상태 입니다.')

            elif values == 3:
                print('현재 시장 운영중 입니다.')

            elif values == 2:
                print('현재 장 종료 10분 전 동시 호가입니다.')

            elif values == 4:
                print('현재 시장이 종료 되어 있습니다..')
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!")
Exemple #30
0
class PasswordChangeDialog(PasswordChangeWrappedUI):
    # Signal used when user close app after clicked esc or cros to close app.
    # If user clicked Yes on message box return True other way return False.

    closingSignal = pyqtSignal(QEvent)

    callSignal = pyqtSignal(bool)

    def __init__(self, client):
        super(PasswordChangeDialog, self).__init__()

        self.client = client
        # self.__session_id = None
        self.dialog = SelectAvatar(self)
        self.loop = QEventLoop()
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(lambda: self.loop.exit(1))

        self.set_change_password_button(self.change_password)

        self.push_button_avatar.clicked.connect(self.selectAvatar)

    def selectAvatar(self):
        self.dialog.show()

    def validatePasswords(self, password, repeat_password):
        # TO DO
        if len(password) < 8:
            raise PasswdLengthError("Passwords is too short")
            return False

        if len(password) > 20:
            raise PasswdLengthError("Passwords is too long")
            return False

        if password != repeat_password:
            raise PasswdEqualError("Passwords are not equal")
            return False

        return True

    def validateLogin(self, login):
        # TO DO and CHECK
        if login is None or login == "":
            raise TypeError("Login is empty!")
            return False

        if type(login) != str:
            raise TypeError("Login is not text!")
            return False

        return True

    def validateData(self, login, password, repeat_password):

        try:
            if self.validateLogin(login):

                if self.validatePasswords(password, repeat_password):
                    return True

        except Exception as e:
            # TO DO SHOW PASSWORD CHANGE STATUS

            #self.showRegisterStatus(str(e.args[0]))
            return False

    def getActivationStatus(self):
        print("[*] ActivationDialog info: Get response from server ",
              self.client.received)

        if self.client.received == "200 CHANGED":
            status = "Status zmiany hasła | " + str(self.client.received)
            self.showRegisterStatus(status)
            return True

        elif self.client.received == "406 NOT CHANGED":
            status = "Status zmiany hasła | " + str(self.client.received)
            self.showRegisterStatus(status)
            return False

    def change_password(self):
        login = self.get_login()
        avatar = self.get_avatar_name()
        print("<*> Client info: avatar is:", avatar)
        passwd = self.get_password_activate()
        passwd_hash = hashlib.sha256(passwd.encode()).hexdigest()
        repeat_passwd = self.get_repeat_password_activate()

        if self.validateData(login, passwd, repeat_passwd):

            payload = {
                "type": "d",
                "description": "CHANGE",
                "NICKNAME": login,
                "PASSWORD": passwd_hash,
                "AVATAR": avatar
            }

            self.client.sendMessage(json.dumps(payload).encode("utf-8"))

            return self.getActivationStatus()
        else:
            return False

    def closeApp(self, event):
        print(event)
        self.close()

    def closeEvent(self, event):
        self.closingSignal.emit(event)

    def setUserName(self, user_name):
        self.username = user_name



    def waiting_for_signal(self):
        self.timer.start(10000)  # 10 second time-out

        print('{*} MainWindow info:  waiting for response')

        if self.loop.exec_() == 0:
            self.timer.stop()
            print("{*} MainWindow info: stop timer")
            return True
        else:
            print('{!} MainWindow error: time-out :(')
            return False



    def keyPressEvent(self, event):
        """
            Close application from escape key.
        """
        if event.key() == Qt.Key_Escape:
            self.close()
Exemple #31
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
Exemple #32
0
class EquityLinkedWarrant(QAxWidget):
    def __init__(self):
        super().__init__()

        # 전역변수 모음
        self.account_number = None
        self.total_days = 0
        self.stock_day_list = []
        #         self.target_code_list = ["032190","263750","091990","056190","032500","034950","225190","049520","225330","040420"]
        self.target_code_list = []
        self.market_type = None
        self.stock_day_table = "stock_day_elw"

        # 종목 일봉 데이터 수집 화면번호 지정
        self.stock_day_screen_number = "4000"

        # 짤려서 현재 실행할 순번 1001, 569
        # 2020-10-05 23:27
        # 2020-10-06 22:16 ~
        # 2020-10-08 22:58 ~ 2020-10-09 00:30 완료
        # 2020-10-12 MON
        # 2020-10-13 TUE 20:01 ~
        self.current_day = 0

        # 수집 대상 기간 일수
        self.days = 0

        # 로그인 Event Loop 실행
        self.login_event_loop = QEventLoop()

        self.day_info_event_loop = QEventLoop()

        # Kiwoom OCX 사용 함수 호출
        self.get_ocx_instance()

        # Event Slots
        self.event_slots()

        # 로그인 signal 함수 호출
        self.signal_login_commConnect()

        # 계좌정보 가져오기
        self.get_login_info()

        # 전체 종목 수집 및 분석을 위한 전체 종복 정보 가져오기
        self.get_day_info_by_stock(sPrevNext="0")

    def get_ocx_instance(self):
        self.setControl("KHOPENAPI.KHOpenAPICtrl.1")
        print(">> Kiwoom OCX KHOPENAPI.KHOpenAPICtrl.1 setControl")

    def event_slots(self):
        self.OnEventConnect.connect(self.login_event_slot)
        self.OnReceiveTrData.connect(self.trdata_slot)

    def signal_login_commConnect(self):
        self.dynamicCall("CommConnect()")
        self.login_event_loop.exec_()

    def login_event_slot(self, error_code):
        self.login_event_loop.exit()

    def get_login_info(self):
        accounts = self.dynamicCall("GetLoginInfo(QString)", "ACCNO")
        self.account_number = accounts.split(";")[0]
        print(">> 계좌번호: %s" % self.account_number)

    def get_day_info_by_stock(self, sPrevNext="0"):
        QTest.qWait(1000)  # 3600 Delay Time

        if (sPrevNext == '0'):
            print(
                "종목코드,종목명,발행사명,회차,기초자산명,권리구분,만기일,행사가격,현재가,이론가,대비구분,전일대비,등락율,거래량"
            )

        ############################## opt30005 : ELW조건검색요청 ##############################
        # 000000000000:전체, 3:한국투자증권, 5:미래대우, 6:신영, 12:NK투자, 17:KB증권
        self.dynamicCall("SetInputValue(QString,QString)", "발행사코드",
                         "000000000000")

        # 000000000000:전체, 201:KOSPI200, 150:KOSDAQ150, 005930:삼성전자, ...
        self.dynamicCall("SetInputValue(QString,QString)", "기초자산코드",
                         "000000000000")

        # 0:전체, 1:콜, 2:풋, 3:DC, 4:DP, 5:EX, 6:조기종료콜, 7:조기종료풋
        self.dynamicCall("SetInputValue(QString,QString)", "권리구분", "0")

        # 000000000000:전체, 3:한국투자증, 5:미래대우, 6:신영, 12:NK투자, 17:KB증권
        self.dynamicCall("SetInputValue(QString,QString)", "LP코드",
                         "000000000000")

        # 0:정렬없음, 1:상승율, 2:상승폭, 3:하락율, 4:하락폭, 5:거래량, 6:거래대금, 7:잔존일
        self.dynamicCall("SetInputValue(QString,QString)", "정렬구분", "6")

        self.dynamicCall("CommRqData(QString,QString,int,QString)",
                         "ELW조건검색요청", "opt30005", sPrevNext,
                         self.stock_day_screen_number)
        self.day_info_event_loop.exec_()

    def trdata_slot(self, screen_number, tr_name, tr_code, record_name,
                    sPrevNext):

        # 멀티데이터 일봉 차트 가져오기
        multi_cnt = self.dynamicCall("GetRepeatCnt(QString,QString)", tr_code,
                                     tr_name)

        for i in range(multi_cnt):
            data = []
            output01 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "종목코드").strip()
            output02 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "발행사명").strip()
            output03 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "회차").strip()
            output04 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "기초자산명").strip()
            output05 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "권리구분").strip()
            output06 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "만기일").strip()
            output07 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "현재가").strip()
            output08 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "대비구분").strip()
            output09 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "전일대비").strip()
            output10 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "등락율").strip()
            output11 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "거래량").strip()
            output12 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "거래량대비").strip()
            output13 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "거래대금").strip()
            output14 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "전일거래량").strip()
            output15 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "매도호가").strip()
            output16 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "매수호가").strip()
            output17 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "패리티").strip()
            output18 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "기어링비율").strip()
            output19 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "손일분기율").strip()
            output20 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "자본지지점").strip()
            output21 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "이론가").strip()
            output22 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "내재변동성").strip()
            output23 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "델타").strip()
            output24 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "레버리지").strip()
            output25 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "행사가격").strip()
            output26 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "전환비율").strip()
            output27 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "LP보유비율").strip()
            output28 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "손익분기점").strip()
            output29 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "최종거래일").strip()
            output30 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "상장일").strip()
            output31 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "LP초종공급일").strip()
            output32 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "종목명").strip()
            output33 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "잔존일수").strip()
            output34 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "괴리율").strip()
            output35 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "LP회원사명").strip()
            output36 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "LP회원사명1").strip()
            output37 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "LP회원사명2").strip()
            output38 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "Xray순간체결량정리매매구분").strip()
            output39 = self.dynamicCall(
                "GetCommData(QString,QString,int,QString)", tr_code, tr_name,
                i, "Xray순간체결량증거금100구분").strip()

            self.total_days = self.total_days + 1

            data.append("")
            data.append(output01)
            data.append(output02)
            data.append(output03)
            data.append(output04)
            data.append(output05)
            data.append(output06)
            data.append(output07)
            data.append(output08)
            data.append(output09)
            data.append(output10)
            data.append(output11)
            data.append(output12)
            data.append(output13)
            data.append(output14)
            data.append(output15)
            data.append(output16)
            data.append(output17)
            data.append(output18)
            data.append(output19)
            data.append(output20)
            data.append(output21)
            data.append(output22)
            data.append(output23)
            data.append(output24)
            data.append(output25)
            data.append(output26)
            data.append(output27)
            data.append(output28)
            data.append(output29)
            data.append(output30)
            data.append(output31)
            data.append(output32)
            data.append(output33)
            data.append(output34)
            data.append(output35)
            data.append(output36)
            data.append(output37)
            data.append(output38)
            data.append(output39)
            data.append("")

            self.stock_day_list.append(data.copy())

            output06 = output06[:6]
            is_lp = False
            if output02 == '한국투자증' or output02 == '미래대우':
                is_lp = True

            if output04 == 'KOSPI200' and is_lp and (
                    output06 == '202104' or output06 == '202105'
                    or output06 == '202106') and int(output11) > 0:
                # if output04 == 'KOSPI200' and output02 == '한국투자증' and (output06 == '20210412' or output06 == '20210517' or output06 == '20210614') and int(output11) > 0:
                # if output04 == 'KOSPI200' and (output02 == '한국투자증' or output02 == '미래대우') and (output06 == '20210118' or output06 == '20210216'):
                # if output04 == 'KOSPI200' and int(output11) > 100000:
                print("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s" %
                      (output01, output32, output02, output03, output04,
                       output05, output06, output25, output07, output21,
                       output08, output09, output10, output11))

            if self.total_days == self.days:
                #                 print(">> %s,%s,%s,%s,%s,%s,%s,%s,%s " % ((i+1),stock_code,date,close_price,open_price,high_price,low_price,volume,trading_value))
                sPrevNext = "0"
                break

        if sPrevNext == "2":
            self.get_day_info_by_stock(sPrevNext=sPrevNext)
        else:
            self.day_info_event_loop.exit()

    def insert_stock_day_list(self):
        conn_string = "host='localhost' dbname='postgres' user='******' password='******' port='5432'"
        conn = psycopg2.connect(conn_string)
        cur = conn.cursor()

        day_num = 0
        for stock_day in self.stock_day_list:

            delete_day_sql = "delete from " + self.stock_day_table + " where stock_code = %s and dt = %s"
            cur.execute(delete_day_sql, (
                stock_day[1],
                stock_day[3],
            ))

            day_num = day_num + 1
            sql = "insert into " + self.stock_day_table + " (stock_code,stock_name,day_num,dt,volume,trading_value,close_price,open_price,high_price,low_price,market_type) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
            cur.execute(
                sql, (stock_day[1], stock_day[2], day_num, stock_day[3],
                      stock_day[4], stock_day[5], stock_day[6], stock_day[7],
                      stock_day[8], stock_day[9], stock_day[10]))

        conn.commit()
        cur.close()
        conn.close()

    def update_day_num(self):
        conn_string = "host='localhost' dbname='postgres' user='******' password='******' port='5432'"
        conn = psycopg2.connect(conn_string)
        cur = conn.cursor()

        # 시장유형의 모든 종목 입력 후 시장유형 별 일봉 순번 갱신
        update_day_sql = " update " + self.stock_day_table + " s              "
        update_day_sql += "    set day_num = a.day_num                     "
        update_day_sql += "   from                                         "
        update_day_sql += "      (                                         "
        update_day_sql += "        select row_number() over(partition by stock_code order by dt desc) as day_num  "
        update_day_sql += "             , stock_code                       "
        update_day_sql += "             , dt                               "
        update_day_sql += "             , market_type                      "
        update_day_sql += "          from " + self.stock_day_table + "         "
        update_day_sql += "         where market_type = %s                 "
        update_day_sql += "      ) as a                                    "
        update_day_sql += "  where s.stock_code = a.stock_code             "
        update_day_sql += "    and s.dt = a.dt                             "
        update_day_sql += "    and s.market_type = a.market_type           "
        update_day_sql += "    and s.market_type = %s                      "

        cur.execute(update_day_sql, (
            self.market_type,
            self.market_type,
        ))

        conn.commit()
        cur.close()
        conn.close()
Exemple #33
0
class Browser(QObject):

    comment_postdata_example = {
            'ft_ent_identifier': '735272899920440', # ИД сообщения
            'comment_text': 'Cool))', # Текст коментария
            'source': '22',
            'client_id': '1429632677205%3A3425397009',
            'reply_fbid': '',
            'parent_comment_id': '',
            'rootid': 'u_ps_0_0_k',
            'clp': '',
            'attached_sticker_fbid': '0',
            'attached_photo_fbid': '0',
            'feed_context': '%7B%22fbfeed_context%22%3Atrue%2C%22location_type%22%3A36%2C%22is_starred%22%3Afalse%2C%22is_pinned_post%22%3Afalse%2C%22can_moderate_timeline_story%22%3Afalse%2C%22profile_id%22%3A308106089303792%2C%22outer_object_element_id%22%3A%22u_ps_0_0_0%22%2C%22object_element_id%22%3A%22u_ps_0_0_0%22%2C%22is_ad_preview%22%3Afalse%2C%22is_editable%22%3Afalse%7D',
            'ft[tn]': '[]',
            'ft[top_level_post_id]': '750869418360788',
            'ft[fbfeed_location]': '36',
            'nctr[_mod]': 'pagelet_timeline_main_column',
            'av': '100009110845526',
            '__user': '******',
            '__a': '1',
            '__dyn': '', #пустой
            '__req': 'c',
            'fb_dtsg': 'AQEkxiOYhtrJ', # инпут в теле документа
            'ttstamp': '26581716611911872109105876676',
            '__rev': '1713404',
        }

    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

    def _create_request(self, operation, request, data):
        # print(data)
        reply = QNetworkAccessManager.createRequest(self.conn,
                                                    operation,
                                                    request,
                                                    data)
        self.conn.new_reply = reply
        self.wv_reply = reply
        return reply

    def set_url(self, url):
        if isinstance(url, QByteArray):
            self.url = QUrl().fromEncoded(url)
        else:
            self.url = QUrl(url)

    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()

    def prepare_responce(self):
        # self.check_redirect()
        self.responce = self.reply_post.readAll()#.data().decode('utf-8')
        pprint(self.responce)
        sys.exit()

    def check_redirect(self):
        print(self.url)
        a = self.reply.rawHeader('Location')

        if len(a) > 0:
            self.set_url(a)
            self.send_request()
        else:
            self.loop.exit()

    def test(self):
        self.wv.auth('https://www.facebook.com/freelanceuidesignerdeveloper')
        self.wv.authentication.connect(self.webview_login)

    def _urlencode_post_data(self, post_data):
        post_params = []
        for (key, value) in post_data.items():
            print(key, value)
            post_params.append(key+'='+value)
        return '&'.join(post_params)

    def webview_login(self):
        text_page = self.wv.page().mainFrame().toHtml()
        data = {
            'ft_ent_identifier': '735272899920440',
            'comment_text': 'amazing',
            'source': '22',
            'client_id': '1429632677205%3A3425397009',
            'reply_fbid': '',
            'parent_comment_id': '',
            'rootid': 'u_ps_0_0_o',
            'clp': '',
            'attached_sticker_fbid': '0',
            'attached_photo_fbid': '0',
            'feed_context': '%7B%22fbfeed_context%22%3Atrue%2C%22location_type%22%3A36%2C%22is_starred%22%3Afalse%2C%22is_pinned_post%22%3Afalse%2C%22can_moderate_timeline_story%22%3Afalse%2C%22profile_id%22%3A308106089303792%2C%22outer_object_element_id%22%3A%22u_ps_0_0_0%22%2C%22object_element_id%22%3A%22u_ps_0_0_0%22%2C%22is_ad_preview%22%3Afalse%2C%22is_editable%22%3Afalse%7D',
            'ft[tn]': '[]',
            'ft[top_level_post_id]': '',
            'ft[fbfeed_location]': '36',
            'nctr[_mod]': 'pagelet_timeline_main_column',
            'av': '100009110845526',
            '__user': '******',
            '__a': '1',
            '__dyn': '', #пустой
            '__req': 'c',
            'fb_dtsg': 'AQEkxiOYhtrJ', # инпут в теле документа
            'ttstamp': '26581716611911872109105876676',
            '__rev': '1713404',
        }
        dtsg_index = text_page.find('"token":"AQ')
        data['fb_dtsg'] = text_page[dtsg_index+9:dtsg_index+21]
        data['ttstamp'] = self.ttstamp_gen(data['fb_dtsg'])
        data['ft_ent_identifier'] = '849713745045784' # self.get_post_id(post_object)
        data['comment_text'] = random_number.choice(config.comments_list)
        pprint(data['comment_text'])


        # data['av'] = data['__user'] = from_user
        # self.applyMetaData()
        self.url = QUrl('https://www.facebook.com/ajax/ufi/add_comment.php')
        self.send_request(True, data)
        sys.exit()


    def applyMetaData(self):
        print('applyMetaData')
        raw_header = {
            'Host': 'www.facebook.com',
            'User-Agent': ' Mozilla/5.0 (X11; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0',
            'Accept': ' text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Language': ' en-US,en;q=0.5',
            'Content-Type': ' application/x-www-form-urlencoded; charset=UTF-8',
            'Referer': ' https://www.facebook.com/SoftProposal?fref=nf',
        }
        for (key, header) in raw_header.items():
            self.r.setRawHeader(key, header)

    @staticmethod
    def ttstamp_gen(fb_dtsg):
        u = ''
        for v in fb_dtsg:
            u += str(ord(v))
        return '2'+u
    @staticmethod
    def get_post_id(post):
        if post.object_id:
            return post.object_id
        else:
            str = post.id.split('_')
            if len(str) > 1:
                return str[0]
            else:
                post.id
Exemple #34
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()
Exemple #35
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)
class InstallWizard(QDialog, MessageBoxMixin, BaseWizard):

    accept_signal = pyqtSignal()

    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.

    def select_storage(self, path, get_wallet_from_daemon) -> Tuple[str, Optional[WalletStorage]]:

        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=_('Vialectrum wallet'))

        self.temp_storage = WalletStorage(path, manual_upgrades=True)
        wallet_folder = os.path.dirname(self.temp_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.temp_storage = wallet_from_memory.storage
                else:
                    self.temp_storage = WalletStorage(path, manual_upgrades=True)
                self.next_button.setEnabled(True)
            except BaseException:
                self.logger.exception('')
                self.temp_storage = None
                self.next_button.setEnabled(False)
            user_needs_to_enter_password = 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.")
                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.')
                        user_needs_to_enter_password = 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.")
                    else:
                        msg = _("Press 'Next' to open this wallet.")
                else:
                    msg = _("This file is already open in memory.") + "\n" \
                        + _("Press 'Next' to create/focus window.")
            else:
                msg = _('Cannot read file')
            self.msg_label.setText(msg)
            if user_needs_to_enter_password:
                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:
                        self.show_message(title=_('Error'), msg=str(e))
                        continue
                    except BaseException as e:
                        self.logger.exception('')
                        self.show_message(title=_('Error'), msg=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:
                        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 BaseException as e:
                        self.logger.exception('')
                        self.show_message(title=_('Error'), msg=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 # FIXME
            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 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):
            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):
        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, **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()

    @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)