def run_program(self): self.photo.setPixmap(QtGui.QPixmap("./images/wait.png")) loop = QEventLoop() QTimer.singleShot(50, loop.quit) loop.exec_() self.runButton.setEnabled(False) self.pushButton_2.setEnabled(False) self.pushButton_3.setEnabled(False) self.pushButton_4.setEnabled(True) self.pushButton_6.setEnabled(True) self.pushButton_5.setEnabled(True) global cutoff_values global number_of_states global path_queue global sol global initial_state global n sol = solution(initial_state) plot_board(sol, "Final State", "final_state", n) self.photo.setPixmap(QtGui.QPixmap("./images/final_state.png")) self.photo.setStyleSheet("border: 3px solid red;") number_of_states = len(path_queue) # print(number_of_states) for i in range(1, number_of_states + 1): plot_board(path_queue.pop(), "State - {}".format(i), "{}".format(i), n)
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_()
def openCanvas(self): self.getData() loop = QEventLoop() QTimer.singleShot(100, loop.quit) loop.exec_() self.close() self.open = CanvasClass()
def _runJavaScriptSync(self, js, timeout=500): """ 同步执行 , 不能与 qwebchannel一起使用 :param js: :param timeout: :return: """ result = None eventLoop = QEventLoop() called = False page = self def callback(val): nonlocal result, called result = val called = True eventLoop.quit() page.runJavaScript(js, callback) if not called: timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(eventLoop.quit) timer.start(timeout) eventLoop.exec_() if not called: print('runJavaScriptSync() timed out') return result
def execInBg(self, command): self.addToConsole("Executing command:") self.addToConsole(command, QtCore.Qt.green) self.proc.setArguments(("-c", command)) eloop = QEventLoop(self) self.proc.finished.connect(eloop.quit) self.proc.readyReadStandardOutput.connect(eloop.quit) self.proc.readyReadStandardError.connect(eloop.quit) self.proc.start() self.proc.waitForStarted(500) accOut = "" accErr = "" while True: rdstr = self.proc.readAllStandardOutput().data().decode(sys.getdefaultencoding()) accOut += rdstr self.addOutToConsole(rdstr) rdstr = self.proc.readAllStandardError().data().decode(sys.getdefaultencoding()) self.addErrToConsole(rdstr) accErr += rdstr if self.proc.state(): eloop.exec_() else: break self.addToConsole("Stopped with exit status %i" % self.proc.exitCode()) return self.proc.exitCode(), accOut, accErr
def execJavaScript(self, script, worldId=QWebEngineScript.MainWorld, timeout=500): """ Public method to execute a JavaScript function synchroneously. @param script JavaScript script source to be executed @type str @param worldId ID to run the script under @type int @param timeout max. time the script is given to execute @type int @return result of the script @rtype depending upon script result """ loop = QEventLoop() resultDict = {"res": None} QTimer.singleShot(timeout, loop.quit) def resultCallback(res, resDict=resultDict): if loop and loop.isRunning(): resDict["res"] = res loop.quit() self.runJavaScript(script, worldId, resultCallback) loop.exec_() return resultDict["res"]
def add_webview_state(self, data): # Add webview state to data dictionary, synchronously. # # You have got to be kidding me - Johnny Mac # JavaScript callbacks are executed asynchronously, # and it looks like (in Qt 5.9) it is handled as # part of event processing. So we cannot simply # use a semaphore and wait for the callback to # happen, since it will never happen because we # are not processing events. So we use a busy # wait until the data we expect to get shows up. # Using a semaphore is overkill, since we can just # check for the presence of the key to be added, # but it does generalize if we want to call other # JS functions and get the value back synchronously. from PyQt5.QtCore import QEventLoop from threading import Semaphore event_loop = QEventLoop() js = "%s.get_state();" % self.CUSTOM_SCHEME def add(state): data[self.html_state] = state event_loop.quit() self.html_view.runJavaScript(js, add) while self.html_state not in data: event_loop.exec_()
def _processPendingEvents(): """Process pending application events.""" # Create an event loop to run in. Otherwise, we need to use the # QApplication main loop, which may already be running and therefore # unusable. qe = QEventLoop() # Create a single-shot timer. Could use QTimer.singleShot(), # but can't cancel this / disconnect it. timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(qe.quit) timer.start(1) # Wait for an emitted signal. qe.exec_() # Clean up: don't allow the timer to call qe.quit after this # function exits, which would produce "interesting" behavior. timer.stop() # Stopping the timer may not cancel timeout signals in the # event queue. Disconnect the signal to be sure that loop # will never receive a timeout after the function exits. timer.timeout.disconnect(qe.quit)
def terminar_juego(self): """Este método se encarga de determinar si luego de terminar las tareas ganas o pierdes. Puedes ignorarlo.""" self.area_tareas.hide() self.setFixedSize(*self.size) numero_random = random.random() if numero_random <= p.PROB_GANAR: self.mostrar_imagen_final(True) else: gif_label = QLabel(self) gif_label.resize(*self.size) gif = QMovie(os.path.join("frontend", "assets", "kill.gif")) gif_label.setMovie(gif) gif_label.setScaledContents(True) gif_label.show() gif.start() loop = QEventLoop() QTimer.singleShot(self.gif_time * 1000, loop.quit) loop.exec_() self.mostrar_imagen_final(False)
def loadPage(url): page = QWebEnginePage() loop = QEventLoop() # Create event loop page.loadFinished.connect(loop.quit) # Connect loadFinished to loop quit page.load(QUrl(url)) loop.exec_() # Run event loop, it will end on loadFinished return page.toHtml(get_html)
def run(self): self.now = 0 self.hard_stop = False self.update_timer() self.timer.start(10) loop = QEventLoop() loop.exec_()
def printDocument(self, printer): loop = QEventLoop() result = False def printPreview(success): nonlocal result result = success loop.quit() progressbar = QProgressDialog(self.m_page.view()) progressbar.findChild(QProgressBar).setTextVisible(False) progressbar.setLabelText("Wait please...") progressbar.setRange(0, 0) progressbar.show() progressbar.canceled.connect(loop.quit) self.m_page.print(printer, printPreview) loop.exec_() progressbar.close() if not result: painter = QPainter() if painter.begin(printer): font = painter.font() font.setPixelSize(20) painter.setFont(font) painter.drawText(QPointF(10, 25), "Could not generate print preview.") painter.end()
def run(self): self.channelset.connect(self.dmx_thread.set_channel) self.dmx_thread.start() self.send_artnet_all.connect(self.dmx_thread.send_artnet_all_sock) loop = QEventLoop() loop.exec_()
def done(self): with open('settings.py', 'rb') as f: data = pickle.load(f) data['MODULE_SETTINGS']['graphs'].update(self.settings) with open('settings.py', 'wb') as f: pickle.dump(data, f) with open('settings.py', 'rb') as f: settings = pickle.load(f) module_starter = ModuleManipulator(settings) threading.Thread(target=module_starter.start, daemon=True).start() self.spinner = QtWaitingSpinner(self) self.layout().addWidget(self.spinner) self.spinner.start() #QTimer.singleShot(10000, self.spinner.stop) loop = QEventLoop() QTimer.singleShot(10000, loop.quit) loop.exec_() self.spinner.stop() remove_if_exists() self.hide() self.child.show()
def img(self): imgName, imgType = QFileDialog.getOpenFileName( None, "打开图片", "", "*.jpg;;*.png;;All Files(*)") jpg = QtGui.QPixmap(imgName) self.label6.setPixmap(jpg) #c = ['python', 'query_online.py', '-query', imgName, '-index' ,'add_after.h5' ,'-result' ,'Download'] c = [ 'python query_online.py -query', imgName, '-index add_after.h5 -result Download' ] str = ' '.join(c) print(str) p = os.system(str) for line in f: p.append(line[:-1]) pix = QtGui.QPixmap(p[0]) pix1 = QtGui.QPixmap(p[1]) pix2 = QtGui.QPixmap(p[2]) #pix3 = QtGui.QPixmap(imgName) self.label1.setPixmap(pix) self.label2.setPixmap(pix1) self.label3.setPixmap(pix2) #self.label4.setPixmap(pix3) file = open('./fs.txt') file_text = file.read() self.label5.setText(file_text) loop = QEventLoop() QTimer.singleShot(2000, loop.quit) loop.exec_()
def f2(): future = ac.start(em2.g, lambda x: x, QThread.currentThread()) # The doneSignal won't be processed without an event loop. A # thread pool doesn't create one, so make our own to run ``g``. qe = QEventLoop() future._signalInvoker.doneSignal.connect(qe.exit) qe.exec_()
def wait_signal(self, signal, timeout=1000): ''' Block loop until signal received, or until timeout (ms) elapsed. ''' loop = QEventLoop() signal.connect(loop.quit) # only quit is a slot of QEventLoop QTimer.singleShot(timeout, loop.exit) loop.exec_()
def genMastClicked(self): """Runs the main function.""" print('Running...') loop = QEventLoop() QTimer.singleShot(5000, loop.quit) loop.exec_() print('Done.')
def consulta_cep(): retorno.show() cep_input = front.lineEdit.text() if len(cep_input) != 8: retorno.listWidget.clear() retorno.listWidget.addItem('CEP incorreto, tente novamente.') loop = QEventLoop() QTimer.singleShot(5000, loop.quit) loop.exec_() retorno.close() else: () request = requests.get( 'https://viacep.com.br/ws/{}/json/'.format(cep_input)) address_data = request.json() if 'erro' not in address_data: retorno.listWidget.clear() retorno.listWidget.addItem(' CEP: {} '.format(address_data['cep'])) retorno.listWidget.addItem(' Logradouro: {} '.format( address_data['logradouro'])) retorno.listWidget.addItem(' Bairro: {} '.format( address_data['bairro'])) retorno.listWidget.addItem(' Cidade: {} '.format( address_data['localidade'])) retorno.listWidget.addItem(' Estado: {} '.format( address_data['uf'])) else: retorno.listWidget.clear() retorno.listWidget.addItem(' CEP inválido, tente novamente.')
def waitForSignal(signal, message="", timeout=0): """Waits (max timeout msecs if given) for a signal to be emitted. It the waiting lasts more than 2 seconds, a progress dialog is displayed with the message. Returns True if the signal was emitted. Return False if the wait timed out or the dialog was canceled by the user. """ loop = QEventLoop() dlg = QProgressDialog(minimum=0, maximum=0, labelText=message) dlg.setWindowTitle(appinfo.appname) dlg.setWindowModality(Qt.ApplicationModal) QTimer.singleShot(2000, dlg.show) dlg.canceled.connect(loop.quit) if timeout: QTimer.singleShot(timeout, dlg.cancel) stop = lambda: loop.quit() signal.connect(stop) loop.exec_() signal.disconnect(stop) dlg.hide() dlg.deleteLater() return not dlg.wasCanceled()
def ssh2(self): ip = self.three.text() username = self.four.text() passwd = self.five.text() self.cmd = "sed -i \'2,3c user \"{user}\"\\npassword \"{password}\"\' /tmp/ppp/options.wan0&/usr/sbin/pppd file /tmp/ppp/options.wan0&/usr/sbin/pppd plugin xinjiang_qinghai_sxplugin.so".format( user=self.c, password=self.two.text()) try: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(ip, 22, username, passwd, timeout=5) stdin, stdout, stderr = ssh.exec_command(self.cmd) # print(stdout.read()) self._signal.emit("路由连接成功!,开始执行so文件") ssh.close() loop = QEventLoop() QTimer.singleShot(500, loop.quit) loop.exec_() self._signal.emit("执行完毕,请检查网络!") # self.getip() except: self._signal.emit("连接路由失败!请检查网络连接,和连接信息!")
class SyncRequestDecorator: """키움 API 비동기 함수 데코레이터 """ @staticmethod def kiwoom_sync_request(func): def func_wrapper(self, *args, **kwargs): if kwargs.get('nPrevNext', 0) == 0: logger.debug('초기 요청 준비') self.params = {} self.result = {} # self.request_thread_worker.request_queue.append((func, args, kwargs)) logger.debug("요청 실행: %s %s %s" % (func.__name__, args, kwargs)) func(self, *args, **kwargs) self.event = QEventLoop() self.event.exec_() return self.result # 콜백 결과 반환 return func_wrapper @staticmethod def kiwoom_sync_callback(func): def func_wrapper(self, *args, **kwargs): logger.debug("요청 콜백: %s %s %s" % (func.__name__, args, kwargs)) func(self, *args, **kwargs) # 콜백 함수 호출 return func_wrapper
def execInBg(self, proc): self.addToConsole("Executing command:") self.addToConsole(proc.program() + " " + ' '.join([ar for ar in proc.arguments()]), QtCore.Qt.green) if proc.workingDirectory() and \ not os.path.samefile(proc.workingDirectory(), os.getcwd()): self.addToConsole("in ") self.addToConsole(os.path.realpath(proc.workingDirectory()), QtCore.Qt.green) eloop = QEventLoop(self) proc.finished.connect(eloop.quit) proc.readyReadStandardOutput.connect(eloop.quit) proc.readyReadStandardError.connect(eloop.quit) proc.start() proc.waitForStarted(500) while True: self.addOutToConsole(proc.readAllStandardOutput() .data().decode(sys.getdefaultencoding())) self.addErrToConsole(proc.readAllStandardError() .data().decode(sys.getdefaultencoding())) if proc.state(): eloop.exec_() else: break self.addToConsole("Stopped with exit status %i" % proc.exitCode())
def fileLister(self, url, dir): try: self.macRequest = QtNetwork.QNetworkRequest( QUrl(url + 'rr_filelist?dir=' + dir)) self.macRequest.setRawHeader(b'User-Agent', b'Cura Plugin Nautilus') self.macRequest.setRawHeader(b'Accept', b'application/json, text/javascript') self.macRequest.setRawHeader(b'Connection', b'keep-alive') #self.gitRequest.setRawHeader(b"User-Agent", b"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7 (.NET CLR 3.5.30729)") self.macroReply = self._qnam.get(self.macRequest) loop = QEventLoop() self.macroReply.finished.connect(loop.quit) loop.exec_() reply_body = bytes(self.macroReply.readAll()).decode() Logger.log("d", self._name_id + " | Status received | " + reply_body) return reply_body except: Logger.log( "i", "couldn't connect to the printer: " + str(traceback.format_exc())) return '0'
class WidgetPicker(QWidget): """Widget for letting user point at another widget.""" selected = Signal() def __init__(self): super(WidgetPicker, self).__init__() self.band = QRubberBand(QRubberBand.Rectangle) self.setMouseTracking(True) self.el = QEventLoop() def mousePressEvent(self, ev): self.el.quit() self.widget = QApplication.widgetAt(ev.globalPos()) self.band.hide() def mouseMoveEvent(self, ev): widget = QApplication.widgetAt(ev.globalPos()) if widget: rect = widget.frameGeometry() if widget.parent(): rect.moveTo(widget.parent().mapToGlobal(rect.topLeft())) self.band.setGeometry(rect) self.band.show() else: self.band.hide() def run(self): self.grabMouse() try: self.el.exec_() finally: self.releaseMouse() return self.widget
def onConnected(self): self._send('status', [("type", '3')]) loop = QEventLoop() self._reply.finished.connect(loop.quit) loop.exec_() reply_body = bytes(self._reply.readAll()).decode() Logger.log("d", str(len(reply_body)) + " | The reply is: | " + reply_body) if len(reply_body) == 0: self._onTimeout() else: status = json.loads(reply_body)["status"] if 'i' in status.lower(): Logger.log('d', 'update under normal conditions. Status: ' + status) self._send('gcode', [( "gcode", 'M291 P\"Do not power off your printer or close Cura until updates complete\" R\"Update Alert\" S0 T0' )]) #self.githubRequest() else: message = Message( catalog.i18nc("@info:status", "{} is busy, unable to update").format( self._name)) message.show()
def done(self): if self.checkBox_2.isChecked() != True: self.settings['points_table'] = False if self.checkBox_4.isChecked() != True: self.settings['metrics_table'] = False if self.checkBox.isChecked() != True: self.settings['spec_and_sens'] = False if self.checkBox_3.isChecked() != True: self.settings['spec_and_sens_table'] = False if self.checkBox_9.isChecked() != True: self.settings['classificators_comparison'] = False with open('settings.py', 'rb') as f: data = pickle.load(f) data['MODULE_SETTINGS'].update(self.settings) data['MODULE_SETTINGS'].pop('columns') with open('settings.py', 'wb') as f: pickle.dump(data, f) module_starter = ModuleManipulator(data) threading.Thread(target=module_starter.start, daemon=True).start() self.spinner = QtWaitingSpinner(self) self.layout().addWidget(self.spinner) self.spinner.start() #QTimer.singleShot(10000, self.spinner.stop) loop = QEventLoop() QTimer.singleShot(10000, loop.quit) loop.exec_() self.spinner.stop() self.close() self.child.show() print(data) remove_if_exists()
class AppDecorator(QObject): documentWasCreatedSignal = pyqtSignal(object) # doc documentWindowWasCreatedSignal = pyqtSignal(object, object) # doc, window def __init__(self,argv): self.qApp = QApplication(argv) super(AppDecorator, self).__init__() from cadnano.gui.views.preferences import Preferences self.prefs = Preferences() #icon = QIcon(ICON_PATH) #self.qApp.setWindowIcon(icon) self.document_controllers = set() # Open documents self.active_document = None self.vh = {} # Newly created VirtualHelix register here by idnum. self.vhi = {} self.partItem = None global decode global Document global DocumentController from cadnano.gui.views.pathview import pathstyles as styles styles.setFontMetrics() # def prefsClicked(self): # self.prefs.showDialog() def exec_(self): if hasattr(self, 'qApp'): mainWindow = MainWindow() mainWindow.show() self.mainEventLoop = QEventLoop() self.mainEventLoop.exec_()
def test02_check_scene1_webpage(self): """render exported web page and check page capture""" html_path = outputPath("scene1.html") url = QUrl.fromLocalFile(html_path) url = QUrl(url.toString() + "#cx=-20&cy=16&cz=-34&tx=-2&ty=0&tz=8") loop = QEventLoop() page = QWebPage() page.setViewportSize(QSize(OUT_WIDTH, OUT_HEIGHT)) page.loadFinished.connect(loop.quit) page.mainFrame().setUrl(url) loop.exec_() page.mainFrame().evaluateJavaScript('document.getElementById("progress").style.display = "none";') timer = QTimer() timer.timeout.connect(loop.quit) timer.start(100) while page.mainFrame().evaluateJavaScript("app.loadingManager.isLoading"): loop.exec_() timer.stop() image = QImage(OUT_WIDTH, OUT_HEIGHT, QImage.Format_ARGB32_Premultiplied) painter = QPainter(image) page.mainFrame().render(painter) painter.end() filename = "scene1_qwebpage.png" image.save(outputPath(filename)) assert QImage(outputPath(filename)) == QImage(expectedDataPath(filename)), "captured image is different from expected."
class SyncRequestDecorator: """키움 API 비동기 함수 데코레이터 """ @staticmethod def kiwoom_sync_multi_request(func): def func_wrapper(self, *args, **kwargs): # self.request_thread_worker.request_queue.append((func, args, kwargs)) arg_names = inspect.getfullargspec(func).args if 'pre_next' in arg_names: index = arg_names.index('pre_next') - 1 else: index = -1 log.instance().logger().debug("요청 실행: %s %s %s" % (func.__name__, args, kwargs)) res = func(self, *args, **kwargs) if index < 0 or len(args) <= index or args[index] != '2': log.instance().logger().debug("EVENT INIT") self.event = QEventLoop() self.params = {} self.result = {} self.event.exec_() self.result['res'] = res return self.result # 콜백 결과 반환 return func_wrapper """키움 API 비동기 함수 데코레이터 """ @staticmethod def kiwoom_sync_request(func): def func_wrapper(self, *args, **kwargs): # self.request_thread_worker.request_queue.append((func, args, kwargs)) arg_names = inspect.getfullargspec(func).args if 'pre_next' in arg_names: index = arg_names.index('pre_next') - 1 else: index = -1 log.instance().logger().debug("요청 실행: %s %s %s" % (func.__name__, args, kwargs)) res = func(self, *args, **kwargs) if index < 0 or len(args) <= index or args[index] != '2': log.instance().logger().debug("EVENT INIT") self.event = QEventLoop() self.params = {} self.result = {} self.event.exec_() self.result['res'] = res return self.result # 콜백 결과 반환 return func_wrapper @staticmethod def kiwoom_sync_callback(func): def func_wrapper(self, *args, **kwargs): log.instance().logger().debug("요청 콜백: %s %s %s" % (func.__name__, args, kwargs)) func(self, *args, **kwargs) # 콜백 함수 호출 return func_wrapper
class Render(QWebPage): def __init__(self, url): self.url = url self.app = QEventLoop() QWebPage.__init__(self) self.urls = [] self.frame = self.mainFrame() self.viewport = self.setViewportSize(QSize(1600, 9000)) self.network = NetworkAccessManager() self.setNetworkAccessManager(self.network) self.loadFinished.connect(self._loadFinished) self.linkClicked.connect(self.linkClick) self.settings().setAttribute(QWebSettings.AutoLoadImages, False) self.settings().setAttribute(QWebSettings.JavascriptCanOpenWindows, False) self.setLinkDelegationPolicy(2) #self.action(QWebPage.OpenLinkInNewWindow).setEnabled(True) #self.settings().clearMemoryCaches() self.mainFrame().load(QUrl(self.url)) self.network.requestSignal.connect(self.networkRequest) self.app.exec_() allFinished = pyqtSignal() def _loadFinished(self, res): iList = self.frame.findAllElements('input') for i in iList: i.evaluateJavaScript('this.click()') aList = self.frame.findAllElements('a') for a in aList: a.evaluateJavaScript('this.click()') if self.frame.blockSignals(True) == True: self.isFinished = True self.allFinished.emit() self.app.quit() self.deleteLater() def javaScriptAlert(browser, frame, message): """Notifies session for alert, then pass.""" print "signal for javaScriptAlert (class) fired" def javaScriptConfirm(browser, frame, message): #"""Notifies session for alert, then pass.""" print "signal for javaScriptConfirm (class) fired" def javaScriptPrompt(browser, frame, message): #"""Notifies session for alert, then pass.""" print "signal for javaScriptPromt (class) fired" def networkRequest(self): self.urls = self.network.request_urls print self.network.request_urls print 'allfinished' def linkClick(self, url): self.urls.append(url.url())
def terminate(self): # Only run this once. if self._isAlive: self._isAlive = False self._terminate() # Waiting for the thread or thread pool to shut down may have # placed completed jobs in this thread's queue. Process them now to # avoid any surprises (terminated `_AsyncAbstractController`_ # instances still invoke callbacks, making it seem that the # terminate didn't fully terminate. It did, but still leaves g_ # callbacks to be run in the event queue.) el = QEventLoop(self.parent()) QTimer.singleShot(0, el.exit) el.exec_() # Delete the `QObject <http://doc.qt.io/qt-5/qobject.html>`_ # underlying this class, which disconnects all signals. sip.delete(self)
class CallbackFuture: def __init__(self, # A CallbackManager instance to register with. callbackManager, # _`callbackToWrap`: The callback function to (optionally) invoke, which this future will wrap. callbackToWrap): self.callbackManager = callbackManager self._callbackToWrap = callbackToWrap self._state = CallbackFutureState.READY self._shouldInvokeCallback = True self._qEventLoop = None # Return the callback wrapper for this future and mark it as waiting. def callback(self): assert self._state == CallbackFutureState.READY, 'A callback may only be obtained once.' self._state = CallbackFutureState.WAITING self.callbackManager.add(self) return self._callbackWrapper # The callback wrapper. Update state, then invoke the wrapped callback. def _callbackWrapper(self, *args, **kwargs): assert self._state == CallbackFutureState.WAITING self._state = CallbackFutureState.COMPLETE self.callbackManager.remove(self) # If waiting for the callback, stop! if self._qEventLoop: self._qEventLoop.quit() if self._shouldInvokeCallback: self._callbackToWrap(*args, **kwargs) def skipCallback(self): assert self._state == CallbackFutureState.WAITING, 'Only callbacks being waited for may be skipped.' self._shouldInvokeCallback = False # Wait until the wrapped callback is completed (invoked or skipped). def waitForCallback(self): assert self._state == CallbackFutureState.WAITING, 'Only callbacks being waited for may be skipped.' # Run events until the callback is invoked. self._qEventLoop = QEventLoop() self._qEventLoop.exec_() self._qEventLoop = None assert self._state == CallbackFutureState.COMPLETE
def __execJavaScript(self, script): """ Private function to execute a JavaScript function Synchroneously. @param script JavaScript script source to be executed @type str @return result of the script @rtype depending upon script result """ from PyQt5.QtCore import QEventLoop loop = QEventLoop() resultDict = {"res": None} def resultCallback(res, resDict=resultDict): if loop and loop.isRunning(): resDict["res"] = res loop.quit() self.previewView.page().runJavaScript( script, resultCallback) loop.exec_() return resultDict["res"]
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()
class CadnanoQt(QObject): dontAskAndJustDiscardUnsavedChanges = False documentWasCreatedSignal = pyqtSignal(object) # doc documentWindowWasCreatedSignal = pyqtSignal(object, object) # doc, window def __init__(self, argv): """Create the application object """ self.argns, unused = util.parse_args(argv, use_gui=True) # util.init_logging(self.argns.__dict__) # logger.info("CadnanoQt initializing...") if argv is None: argv = sys.argv self.argv = argv # print("initializing new CadnanoQt", type(QCoreApplication.instance())) if QCoreApplication.instance() is None: self.qApp = QApplication(argv) assert(QCoreApplication.instance() is not None) self.qApp.setOrganizationDomain("cadnano.org") else: self.qApp = qApp super(CadnanoQt, self).__init__() # print("initialized new CadnanoQt") from cadnano.views.preferences import Preferences self.prefs = Preferences() self.icon = icon = QIcon(ICON_PATH1) icon.addFile(ICON_PATH2, QSize(256, 256)) icon.addFile(ICON_PATH3, QSize(48, 48)) self.qApp.setWindowIcon(icon) self.main_event_loop = None self.cnmain_windows: set = set() # Open documents self.active_document = None self._document = None self.documentWasCreatedSignal.connect(self.wirePrefsSlot) # end def def document(self) -> DocT: return self._document # end def def finishInit(self): global decodeFile global Document global CNMainWindow from cadnano.document import Document from cadnano.fileio.decode import decodeFile from cadnano.views.cnmainwindow import CNMainWindow from cadnano.views.pathview import pathstyles as styles styles.setFontMetrics() doc = Document() self._document = self.createDocument(base_doc=doc) if os.environ.get('CADNANO_DISCARD_UNSAVED', False) and not self.ignoreEnv(): self.dontAskAndJustDiscardUnsavedChanges = True self.dontAskAndJustDiscardUnsavedChanges = True # end def def exec_(self): if hasattr(self, 'qApp'): self.main_event_loop = QEventLoop() self.main_event_loop.exec_() def destroyApp(self): """Destroy the QApplication. Do not set `self.qApp = None` in this method. Do it external to the CadnanoQt class """ global decodeFile global Document global CNMainWindow # print("documentWasCreatedSignal", self.documentWasCreatedSignal) if len(self.cnmain_windows) > 0: self.documentWasCreatedSignal.disconnect(self.wirePrefsSlot) decodeFile = None Document = None CNMainWindow = None self.cnmain_windows.clear() self.qApp.quit() # end def def ignoreEnv(self): return os.environ.get('CADNANO_IGNORE_ENV_VARS_EXCEPT_FOR_ME', False) def createDocument(self, base_doc: DocT = None): global CNMainWindow # print("CadnanoQt createDocument begin") default_file = self.argns.file or os.environ.get('CADNANO_DEFAULT_DOCUMENT', None) if default_file is not None and base_doc is not None: default_file = os.path.expanduser(default_file) default_file = os.path.expandvars(default_file) dw = CNMainWindow(base_doc) self.cnmain_windows.add(dw) # logger.info("Loading cadnano file %s to base document %s", default_file, base_doc) decodeFile(default_file, document=base_doc) dw.setFileName(default_file) print("Loaded default document: %s" % (default_file)) else: doc_window_count = len(self.cnmain_windows) # logger.info("Creating new empty document...") if doc_window_count == 0: # first dw # dw adds itself to app.cnmain_windows dw = CNMainWindow(base_doc) self.cnmain_windows.add(dw) elif doc_window_count == 1: # dw already exists dw = list(self.cnmain_windows)[0] dw.newDocument() # tell it to make a new doucment # print("CadnanoQt createDocument done") return dw.document() def prefsClicked(self): self.prefs.showDialog() def wirePrefsSlot(self, document: DocT): """MUST CALL THIS TO SET PREFERENCES :class:`Document` """ self.prefs.document = document
class Kiwoom(QAxWidget): def __init__(self): super().__init__() self.setControl("KHOPENAPI.KHOpenAPICtrl.1") # Loop 변수 # 비동기 방식으로 동작되는 이벤트를 동기화(순서대로 동작) 시킬 때 self.login_loop = None self.request_loop = None self.order_loop = None self.condition_loop = None # 서버구분 self.server_gubun = None # 조건식 self.condition = None # 에러 self.error = None # 주문번호 self.order_no = "" # 조회 self.inquiry = 0 # 서버에서 받은 메시지 self.msg = "" # 예수금 d+2 self.data_opw00001 = 0 # 보유종목 정보 self.data_opw00018 = {'account_evaluation': [], 'stocks': []} # 주가상세정보 self.data_opt10081 = [] * 15 self.data_opt10086 = [] * 23 # signal & slot self.OnEventConnect.connect(self.event_connect) self.OnReceiveTrData.connect(self.on_receive_tr_data) self.OnReceiveChejanData.connect(self.on_receive_chejan_data) self.OnReceiveRealData.connect(self.receive_real_data) self.OnReceiveMsg.connect(self.receive_msg) self.OnReceiveConditionVer.connect(self.receive_condition_ver) self.OnReceiveTrCondition.connect(self.receive_tr_condition) self.OnReceiveRealCondition.connect(self.receive_real_condition) # 로깅용 설정파일 logging.config.fileConfig('logging.conf') self.log = logging.getLogger('Kiwoom') ############################################################### # 로깅용 메서드 정의 # ############################################################### def logger(origin): def wrapper(*args, **kwargs): args[0].log.debug('{} args - {}, kwargs - {}'.format(origin.__name__, args, kwargs)) return origin(*args, **kwargs) return wrapper ############################################################### # 이벤트 정의 # ############################################################### def event_connect(self, return_code): """ 통신 연결 상태 변경시 이벤트 return_code 0이면 로그인 성공 그 외에는 ReturnCode 클래스 참조. :param return_code: int """ try: if return_code == ReturnCode.OP_ERR_NONE: if self.get_login_info("GetServerGubun", True): self.msg += "실서버 연결 성공" + "\r\n\r\n" else: self.msg += "모의투자서버 연결 성공" + "\r\n\r\n" else: self.msg += "연결 끊김: 원인 - " + ReturnCode.CAUSE[return_code] + "\r\n\r\n" except Exception as error: self.log.error('eventConnect {}'.format(error)) finally: # commConnect() 메서드에 의해 생성된 루프를 종료시킨다. # 로그인 후, 통신이 끊길 경우를 대비해서 예외처리함. try: self.login_loop.exit() except AttributeError: pass def receive_msg(self, screen_no, request_name, tr_code, msg): """ 수신 메시지 이벤트 서버로 어떤 요청을 했을 때(로그인, 주문, 조회 등), 그 요청에 대한 처리내용을 전달해준다. :param screen_no: string - 화면번호(4자리, 사용자 정의, 서버에 조회나 주문을 요청할 때 이 요청을 구별하기 위한 키값) :param request_name: string - TR 요청명(사용자 정의) :param tr_code: string :param msg: string - 서버로 부터의 메시지 """ if request_name == "서버구분": if msg.find('모의투자') < 0: self.server_gubun = 1 else: self.server_gubun = 0 try: self.order_loop.exit() except AttributeError: pass finally: return self.msg += request_name + ": " + msg + "\r\n\r\n" def on_receive_tr_data(self, screen_no, request_name, tr_code, record_name, inquiry, unused0, unused1, unused2, unused3): """ TR 수신 이벤트 조회요청 응답을 받거나 조회데이터를 수신했을 때 호출됩니다. request_name tr_code comm_rq_data()메소드의 매개변수와 매핑되는 값 입니다. 조회데이터는 이 이벤트 메서드 내부에서 comm_get_data() 메서드를 이용해서 얻을 수 있습니다. :param screen_no: string - 화면번호(4자리) :param request_name: string - TR 요청명(comm_rq_data() 메소드 호출시 사용된 requestName) :param tr_code: string :param record_name: string :param inquiry: string - 조회('0': 남은 데이터 없음, '2': 남은 데이터 있음) """ print("on_receive_tr_data 실행: screen_no: %s, request_name: %s, tr_code: %s, record_name: %s, inquiry: %s" % ( screen_no, request_name, tr_code, record_name, inquiry)) # 주문번호와 주문루프 self.order_no = self.comm_get_data(tr_code, "", request_name, 0, "주문번호") try: self.order_loop.exit() except AttributeError: pass self.inquiry = inquiry if request_name == "관심종목정보요청": data = self.get_comm_data_ex(tr_code, "관심종목정보") """ commGetData cnt = self.getRepeatCnt(trCode, requestName) for i in range(cnt): data = self.commGetData(trCode, "", requestName, i, "종목명") print(data) """ if request_name == "주식일봉차트조회요청": data = self.get_comm_data_ex(tr_code, "주식일봉차트조회") if data is not None: data = list(map(lambda x: list(map(lambda y: y.replace('+','').replace('--','-'), x)), np.array(data)[:,1:8].tolist())) data = list(map(lambda x: list(map(lambda y: int(y) if y != '' else 0, x)), data)) self.data_opt10081.extend(data) date = str(data[0][3]) dt = datetime.strptime(date, "%Y%m%d") if dt <= self.start_date: self.inquiry = 0 if inquiry == "0" or self.inquiry == 0: col_name = ['현재가', '거래량', '거래대금', '일자', '시가', '고가', '저가'] self.data_opt10081 = DataFrame(self.data_opt10081, columns=col_name) if request_name == "일별주가요청": data = self.get_comm_data_ex(tr_code, "일별주가요청") if data is not None: data = list(map(lambda x: list(map(lambda y: y.replace('+','').replace('--','-'), x)), data)) data = list(map(lambda x: list(map(lambda y: float(y) if y != '' else 0, x)), data)) self.data_opt10086.extend(data) date = str(int(data[0][0])) dt = datetime.strptime(date, "%Y%m%d") if dt <= self.start_date: self.inquiry = 0 if inquiry == "0" or self.inquiry == 0: col_name = ['일자', '시가', '고가', '저가', '종가', '전일비', '등락률', '거래량', '금액(백만)', '신용비', '개인', '기관', '외인수량', '외국계', '프로그램', '외인비', '체결강도', '외인보유', '외인비중', '외인순매수', '기관순매수', '개인순매수', '신용잔고율'] self.data_opt10086 = DataFrame(self.data_opt10086, columns=col_name) if request_name == "예수금상세현황요청": estimate_day2_deposit = self.comm_get_data(tr_code, "", request_name, 0, "d+2추정예수금") estimate_day2_deposit = self.change_format(estimate_day2_deposit) self.data_opw00001 = estimate_day2_deposit if request_name == '계좌평가잔고내역요청': # 계좌 평가 정보 account_evaluation = [] key_list = ["총매입금액", "총평가금액", "총평가손익금액", "총수익률(%)", "추정예탁자산"] for key in key_list: value = self.comm_get_data(tr_code, "", request_name, 0, key) if key.startswith("총수익률"): value = self.change_format(value, 1) else: value = self.change_format(value) account_evaluation.append(value) self.data_opw00018['account_evaluation'] = account_evaluation # 보유 종목 정보 cnt = self.get_repeat_cnt(tr_code, request_name) key_list = ["종목명", "보유수량", "매입가", "현재가", "평가손익", "수익률(%)", "종목번호"] for i in range(cnt): stock = [] for key in key_list: value = self.comm_get_data(tr_code, "", request_name, i, key) if key.startswith("수익률"): value = self.change_format(value, 2) elif key != "종목명" and key != "종목번호": value = self.change_format(value) stock.append(value) self.data_opw00018['stocks'].append(stock) try: self.request_loop.exit() except AttributeError: pass def receive_real_data(self, code, real_type, real_data): """ 실시간 데이터 수신 이벤트 실시간 데이터를 수신할 때 마다 호출되며, set_real_reg() 메서드로 등록한 실시간 데이터도 이 이벤트 메서드에 전달됩니다. get_comm_real_data() 메서드를 이용해서 실시간 데이터를 얻을 수 있습니다. :param code: string - 종목코드 :param real_type: string - 실시간 타입(KOA의 실시간 목록 참조) :param real_data: string - 실시간 데이터 전문 """ try: self.log.debug("[receiveRealData]") self.log.debug("({})".format(real_type)) if real_type not in RealType.REALTYPE: return data = [] if code != "": data.append(code) codeOrNot = code else: codeOrNot = real_type for fid in sorted(RealType.REALTYPE[real_type].keys()): value = self.get_comm_real_data(codeOrNot, fid) data.append(value) # TODO: DB에 저장 self.log.debug(data) except Exception as e: self.log.error('{}'.format(e)) def on_receive_chejan_data(self, gubun, item_cnt, fid_list): print("gubun: ", gubun) print(self.GetChejanData(9203)) print(self.GetChejanData(302)) print(self.GetChejanData(900)) print(self.GetChejanData(901)) def get_codelist_by_market(self, market): func = 'GetCodeListByMarket("%s")' % market codes = self.dynamicCall(func) return codes.split(';') ############################################################### # 메서드 정의: 로그인 관련 메서드 # ############################################################### def comm_connect(self): """ 로그인을 시도합니다. 수동 로그인일 경우, 로그인창을 출력해서 로그인을 시도. 자동 로그인일 경우, 로그인창 출력없이 로그인 시도. """ self.dynamicCall("CommConnect()") self.login_loop = QEventLoop() self.login_loop.exec_() def get_connect_state(self): """ 현재 접속상태를 반환합니다. 반환되는 접속상태는 아래와 같습니다. 0: 미연결, 1: 연결 :return: int """ ret = self.dynamicCall("GetConnectState()") return ret def get_login_info(self, tag, is_connect_state=False): """ 사용자의 tag에 해당하는 정보를 반환한다. tag에 올 수 있는 값은 아래와 같다. ACCOUNT_CNT: 전체 계좌의 개수를 반환한다. ACCNO: 전체 계좌 목록을 반환한다. 계좌별 구분은 ;(세미콜론) 이다. USER_ID: 사용자 ID를 반환한다. USER_NAME: 사용자명을 반환한다. GetServerGubun: 접속서버 구분을 반환합니다.(0: 모의투자, 그외: 실서버) :param tag: string :param is_connect_state: bool - 접속상태을 확인할 필요가 없는 경우 True로 설정. :return: string """ if not is_connect_state: if not self.get_connect_state(): raise KiwoomConnectError() if not isinstance(tag, str): raise ParameterTypeError() if tag not in ['ACCOUNT_CNT', 'ACCNO', 'USER_ID', 'USER_NAME', 'GetServerGubun']: raise ParameterValueError() cmd = 'GetLoginInfo("%s")' % tag info = self.dynamicCall(cmd) if tag == 'GetServerGubun' and info == "": if self.server_gubun == None: account_list = self.get_login_info("ACCNO").split(';') self.send_order("서버구분", "0102", account_list[0], 1, "066570", 0, 0, "05", "") info = self.server_gubun return info ################################################################# # 메서드 정의: 조회 관련 메서드 # # 시세조회, 관심종목 조회, 조건검색 등 이들의 합산 조회 횟수가 1초에 5회까지 허용 # ################################################################# def set_input_value(self, id, value): self.dynamicCall("SetInputValue(QString, QString)", id, value) def comm_rq_data(self, request_name, tr_code, inquiry, screen_no): """ 키움서버에 TR 요청을 한다. 조회요청메서드이며 빈번하게 조회요청시, 시세과부하 에러값 -200이 리턴된다. :param request_name: string - TR 요청명(사용자 정의) :param tr_code: string :param inquiry: int - 조회(0: 조회, 2: 남은 데이터 이어서 요청) :param screen_no: string - 화면번호(4자리) """ if not self.get_connect_state(): raise KiwoomConnectError() if not (isinstance(request_name, str) and isinstance(tr_code, str) and isinstance(inquiry, int) and isinstance(screen_no, str)): raise ParameterTypeError() return_code = self.dynamicCall("CommRqData(QString, QString, int, QString)", request_name, tr_code, inquiry, screen_no) if return_code != ReturnCode.OP_ERR_NONE: raise KiwoomProcessingError("comm_rq_data(): " + ReturnCode.CAUSE[return_code]) # 루프 생성: receive_tr_data() 메서드에서 루프를 종료시킨다. self.request_loop = QEventLoop() self.request_loop.exec_() def comm_get_data(self, code, real_type, field_name, index, item_name): """ 데이터 획득 메서드 receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 조회데이터를 얻어오는 메서드입니다. :param code: string :param real_type: string - TR 요청시 ""(빈문자)로 처리 :param field_name: string - TR 요청명(comm_rq_data() 메소드 호출시 사용된 field_name) :param index: int :param item_name: string - 수신 데이터에서 얻고자 하는 값의 키(출력항목이름) :return: string """ ret = self.dynamicCall("CommGetData(QString, QString, QString, int, QString)", code, real_type, field_name, index, item_name) return ret.strip() def get_repeat_cnt(self, tr_code, request_name): """ 서버로 부터 전달받은 데이터의 갯수를 리턴합니다.(멀티데이터의 갯수) receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다. 키움 OpenApi+에서는 데이터를 싱글데이터와 멀티데이터로 구분합니다. 싱글데이터란, 서버로 부터 전달받은 데이터 내에서, 중복되는 키(항목이름)가 하나도 없을 경우. 예를들면, 데이터가 '종목코드', '종목명', '상장일', '상장주식수' 처럼 키(항목이름)가 중복되지 않는 경우를 말합니다. 반면 멀티데이터란, 서버로 부터 전달받은 데이터 내에서, 일정 간격으로 키(항목이름)가 반복될 경우를 말합니다. 예를들면, 10일간의 일봉데이터를 요청할 경우 '종목코드', '일자', '시가', '고가', '저가' 이러한 항목이 10번 반복되는 경우입니다. 이러한 멀티데이터의 경우 반복 횟수(=데이터의 갯수)만큼, 루프를 돌면서 처리하기 위해 이 메서드를 이용하여 멀티데이터의 갯수를 얻을 수 있습니다. :param tr_code: string :param request_name: string - TR 요청명(comm_rq_data() 메소드 호출시 사용된 request_name) :return: int """ ret = self.dynamicCall("GetRepeatCnt(QString, QString)", tr_code, request_name) return ret def get_comm_data_ex(self, tr_code, multi_data_name): """ 멀티데이터 획득 메서드 receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다. :param tr_code: string :param multi_data_name: string - KOA에 명시된 멀티데이터명 :return: list - 중첩리스트 """ if not (isinstance(tr_code, str) and isinstance(multi_data_name, str)): raise ParameterTypeError() data = self.dynamicCall("GetCommDataEx(QString, QString)", tr_code, multi_data_name) return data def commKwRqData(self, codes, inquiry, codeCount, requestName, screenNo, typeFlag=0): """ 복수종목조회 메서드(관심종목조회 메서드라고도 함). 이 메서드는 setInputValue() 메서드를 이용하여, 사전에 필요한 값을 지정하지 않는다. 단지, 메서드의 매개변수에서 직접 종목코드를 지정하여 호출하며, 데이터 수신은 receiveTrData() 이벤트에서 아래 명시한 항목들을 1회 수신하며, 이후 receiveRealData() 이벤트를 통해 실시간 데이터를 얻을 수 있다. 복수종목조회 TR 코드는 OPTKWFID 이며, 요청 성공시 아래 항목들의 정보를 얻을 수 있다. 종목코드, 종목명, 현재가, 기준가, 전일대비, 전일대비기호, 등락율, 거래량, 거래대금, 체결량, 체결강도, 전일거래량대비, 매도호가, 매수호가, 매도1~5차호가, 매수1~5차호가, 상한가, 하한가, 시가, 고가, 저가, 종가, 체결시간, 예상체결가, 예상체결량, 자본금, 액면가, 시가총액, 주식수, 호가시간, 일자, 우선매도잔량, 우선매수잔량,우선매도건수, 우선매수건수, 총매도잔량, 총매수잔량, 총매도건수, 총매수건수, 패리티, 기어링, 손익분기, 잔본지지, ELW행사가, 전환비율, ELW만기일, 미결제약정, 미결제전일대비, 이론가, 내재변동성, 델타, 감마, 쎄타, 베가, 로 :param codes: string - 한번에 100종목까지 조회가능하며 종목코드사이에 세미콜론(;)으로 구분. :param inquiry: int - api 문서는 bool 타입이지만, int로 처리(0: 조회, 1: 남은 데이터 이어서 조회) :param codeCount: int - codes에 지정한 종목의 갯수. :param requestName: string :param screenNo: string :param typeFlag: int - 주식과 선물옵션 구분(0: 주식, 3: 선물옵션), 주의: 매개변수의 위치를 맨 뒤로 이동함. :return: list - 중첩 리스트 [[종목코드, 종목명 ... 종목 정보], [종목코드, 종목명 ... 종목 정보]] """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(codes, str) and isinstance(inquiry, int) and isinstance(codeCount, int) and isinstance(requestName, str) and isinstance(screenNo, str) and isinstance(typeFlag, int)): raise ParameterTypeError() returnCode = self.dynamicCall("CommKwRqData(QString, QBoolean, int, int, QString, QString)", codes, inquiry, codeCount, typeFlag, requestName, screenNo) if returnCode != ReturnCode.OP_ERR_NONE: raise KiwoomProcessingError("commKwRqData(): " + ReturnCode.CAUSE[returnCode]) # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다. self.requestLoop = QEventLoop() self.requestLoop.exec_() ############################################################### # 메서드 정의: 실시간 데이터 처리 관련 메서드 # ############################################################### def disconnect_real_data(self, screen_no): """ 해당 화면번호로 설정한 모든 실시간 데이터 요청을 제거합니다. 화면을 종료할 때 반드시 이 메서드를 호출해야 합니다. :param screen_no: string """ if not self.getConnectState(): raise KiwoomConnectError() if not isinstance(screen_no, str): raise ParameterTypeError() self.dynamicCall("DisconnectRealData(QString)", screen_no) def get_comm_real_data(self, code, fid): """ 실시간 데이터 획득 메서드 이 메서드는 반드시 receiveRealData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다. :param code: string - 종목코드 :param fid: - 실시간 타입에 포함된 fid :return: string - fid에 해당하는 데이터 """ if not (isinstance(code, str) and isinstance(fid, int)): raise ParameterTypeError() value = self.dynamicCall("GetCommRealData(QString, int)", code, fid) return value def set_real_reg(self, screen_no, codes, fids, real_reg_type): """ 실시간 데이터 요청 메서드 종목코드와 fid 리스트를 이용해서 실시간 데이터를 요청하는 메서드입니다. 한번에 등록 가능한 종목과 fid 갯수는 100종목, 100개의 fid 입니다. 실시간등록타입을 0으로 설정하면, 첫 실시간 데이터 요청을 의미하며 실시간등록타입을 1로 설정하면, 추가등록을 의미합니다. 실시간 데이터는 실시간 타입 단위로 receiveRealData() 이벤트로 전달되기 때문에, 이 메서드에서 지정하지 않은 fid 일지라도, 실시간 타입에 포함되어 있다면, 데이터 수신이 가능하다. :param screen_no: string :param codes: string - 종목코드 리스트(종목코드;종목코드;...) :param fids: string - fid 리스트(fid;fid;...) :param real_reg_type: string - 실시간등록타입(0: 첫 등록, 1: 추가 등록) """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(screen_no, str) and isinstance(codes, str) and isinstance(fids, str) and isinstance(real_reg_type, str)): raise ParameterTypeError() self.dynamicCall("SetRealReg(QString, QString, QString, QString)", screen_no, codes, fids, real_reg_type) def set_real_remove(self, screen_no, code): """ 실시간 데이터 중지 메서드 set_real_reg() 메서드로 등록한 종목만, 이 메서드를 통해 실시간 데이터 받기를 중지 시킬 수 있습니다. :param screen_no: string - 화면번호 또는 ALL 키워드 사용가능 :param code: string - 종목코드 또는 ALL 키워드 사용가능 """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(screen_no, str) and isinstance(code, str)): raise ParameterTypeError() self.dynamicCall("SetRealRemove(QString, QString)", screen_no, code) ############################################################### # 메서드 정의: 조건검색 관련 메서드와 이벤트 # ############################################################### def receive_condition_ver(self, receive, msg): """ getConditionLoad() 메서드의 조건식 목록 요청에 대한 응답 이벤트 :param receive: int - 응답결과(1: 성공, 나머지 실패) :param msg: string - 메세지 """ try: if not receive: return self.condition = self.get_condition_name_list() print("조건식 개수: ", len(self.condition)) for key in self.condition.keys(): print("조건식: ", key, ": ", self.condition[key]) print("key type: ", type(key)) except Exception as e: print(e) finally: self.condition_loop.exit() def receive_tr_condition(self, screen_no, codes, condition_name, condition_index, inquiry): """ (1회성, 실시간) 종목 조건검색 요청시 발생되는 이벤트 :param screen_no: string :param codes: string - 종목코드 목록(각 종목은 세미콜론으로 구분됨) :param condition_name: string - 조건식 이름 :param condition_index: int - 조건식 인덱스 :param inquiry: int - 조회구분(0: 남은데이터 없음, 2: 남은데이터 있음) """ print("[receive_tr_condition]") try: if codes == "": return code_list = codes.split(';') del code_list[-1] print(code_list) print("종목개수: ", len(code_list)) finally: self.condition_loop.exit() def receive_real_condition(self, code, event, condition_name, condition_index): """ 실시간 종목 조건검색 요청시 발생되는 이벤트 :param code: string - 종목코드 :param event: string - 이벤트종류("I": 종목편입, "D": 종목이탈) :param condition_name: string - 조건식 이름 :param condition_index: string - 조건식 인덱스(여기서만 인덱스가 string 타입으로 전달됨) """ print("[receive_real_condition]") print("종목코드: ", code) print("이벤트: ", "종목편입" if event == "I" else "종목이탈") def get_condition_load(self): """ 조건식 목록 요청 메서드 """ if not self.get_connect_state(): raise KiwoomConnectError() is_load = self.dynamicCall("GetConditionLoad()") # 요청 실패시 if not is_load: raise KiwoomProcessingError("getConditionLoad(): 조건식 요청 실패") # receiveConditionVer() 이벤트 메서드에서 루프 종료 self.condition_loop = QEventLoop() self.condition_loop.exec_() def get_condition_name_list(self): """ 조건식 획득 메서드 조건식을 딕셔너리 형태로 반환합니다. 이 메서드는 반드시 receiveConditionVer() 이벤트 메서드안에서 사용해야 합니다. :return: dict - {인덱스:조건명, 인덱스:조건명, ...} """ data = self.dynamicCall("get_condition_name_list()") if data == "": raise KiwoomProcessingError("get_condition_name_list(): 사용자 조건식이 없습니다.") conditionList = data.split(';') del conditionList[-1] condition_dictionary = {} for condition in conditionList: key, value = condition.split('^') condition_dictionary[int(key)] = value return condition_dictionary def send_condition(self, screen_no, condition_name, condition_index, is_real_time): """ 종목 조건검색 요청 메서드 이 메서드로 얻고자 하는 것은 해당 조건에 맞는 종목코드이다. 해당 종목에 대한 상세정보는 set_real_reg() 메서드로 요청할 수 있다. 요청이 실패하는 경우는, 해당 조건식이 없거나, 조건명과 인덱스가 맞지 않거나, 조회 횟수를 초과하는 경우 발생한다. 조건검색에 대한 결과는 1회성 조회의 경우, receiveTrCondition() 이벤트로 결과값이 전달되며 실시간 조회의 경우, receiveTrCondition()과 receiveRealCondition() 이벤트로 결과값이 전달된다. :param screen_no: string :param condition_name: string - 조건식 이름 :param condition_index: int - 조건식 인덱스 :param is_real_time: int - 조건검색 조회구분(0: 1회성 조회, 1: 실시간 조회) """ if not self.get_connect_state(): raise KiwoomConnectError() if not (isinstance(screen_no, str) and isinstance(condition_name, str) and isinstance(condition_index, int) and isinstance(is_real_time, int)): raise ParameterTypeError() is_request = self.dynamicCall("SendCondition(QString, QString, int, int", screen_no, condition_name, condition_index, is_real_time) if not is_request: raise KiwoomProcessingError("sendCondition(): 조건검색 요청 실패") # receiveTrCondition() 이벤트 메서드에서 루프 종료 self.condition_loop = QEventLoop() self.condition_loop.exec_() def send_condition_stop(self, screen_no, condition_name, condition_index): """ 종목 조건검색 중지 메서드 """ if not self.get_connect_state(): raise KiwoomConnectError() if not (isinstance(screen_no, str) and isinstance(condition_name, str) and isinstance(condition_index, int)): raise ParameterTypeError() self.dynamicCall("SendConditionStop(QString, QString, int)", screen_no, condition_name, condition_index) ############################################################### # 메서드 정의: 주문과 잔고처리 관련 메서드 # # 1초에 5회까지 주문 허용 # ############################################################### def send_order(self, request_name, screen_no, account_no, order_type, code, qty, price, hoga_type, origin_order_no): """ 주식 주문 메서드 send_order() 메소드 실행시, OnReceiveMsg, OnReceiveTrData, OnReceiveChejanData 이벤트가 발생한다. 이 중, 주문에 대한 결과 데이터를 얻기 위해서는 OnReceiveChejanData 이벤트를 통해서 처리한다. OnReceiveTrData 이벤트를 통해서는 주문번호를 얻을 수 있는데, 주문후 이 이벤트에서 주문번호가 ''공백으로 전달되면, 주문접수 실패를 의미한다. :param request_name: string - 주문 요청명(사용자 정의) :param screen_no: string - 화면번호(4자리) :param account_no: string - 계좌번호(10자리) :param order_type: int - 주문유형(1: 신규매수, 2: 신규매도, 3: 매수취소, 4: 매도취소, 5: 매수정정, 6: 매도정정) :param code: string - 종목코드 :param qty: int - 주문수량 :param price: int - 주문단가 :param hoga_type: string - 거래구분(00: 지정가, 03: 시장가, 05: 조건부지정가, 06: 최유리지정가, 그외에는 api 문서참조) :param origin_order_no: string - 원주문번호(신규주문에는 공백, 정정및 취소주문시 원주문번호르 입력합니다.) """ if not self.get_connect_state(): raise KiwoomConnectError() if not (isinstance(request_name, str) and isinstance(screen_no, str) and isinstance(account_no, str) and isinstance(order_type, int) and isinstance(code, str) and isinstance(qty, int) and isinstance(price, int) and isinstance(hoga_type, str) and isinstance(origin_order_no, str)): raise ParameterTypeError() return_code = self.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)", [request_name, screen_no, account_no, order_type, code, qty, price, hoga_type, origin_order_no]) if return_code != ReturnCode.OP_ERR_NONE: raise KiwoomProcessingError("send_order(): " + ReturnCode.CAUSE[return_code]) # receiveTrData() 에서 루프종료 self.order_loop = QEventLoop() self.order_loop.exec_() def GetChejanData(self, nFid): cmd = 'GetChejanData("%s")' % nFid ret = self.dynamicCall(cmd) return ret ############################################################### # 기타 메서드 정의 # ############################################################### def get_code_list(self, *market): """ 여러 시장의 종목코드를 List 형태로 반환하는 헬퍼 메서드. :param market: Tuple - 여러 개의 문자열을 매개변수로 받아 Tuple로 처리한다. :return: List """ code_list = [] for m in market: tmp_list = self.get_codelist_by_market(m) code_list += tmp_list return code_list def get_master_code_name(self, code): """ 종목코드의 한글명을 반환한다. :param code: string - 종목코드 :return: string - 종목코드의 한글명 """ if not self.get_connect_state(): raise KiwoomConnectError() if not isinstance(code, str): raise ParameterTypeError() cmd = 'GetMasterCodeName("%s")' % code name = self.dynamicCall(cmd) return name def change_format(self, data, percent=0): if percent == 0: d = int(data) format_data = '{:-,d}'.format(d) elif percent == 1: f = float(data) f -= 100 format_data = '{:-,.2f}'.format(f) elif percent == 2: f = float(data) format_data = '{:-,.2f}'.format(f) return format_data def opw_data_reset(self): """ 잔고 및 보유종목 데이터 초기화 """ self.data_opw00001 = 0 self.data_opw00018 = {'account_evaluation': [], 'stocks': []}
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
class CadnanoQt(QObject): dontAskAndJustDiscardUnsavedChanges = False shouldPerformBoilerplateStartupScript = False documentWasCreatedSignal = pyqtSignal(object) # doc documentWindowWasCreatedSignal = pyqtSignal(object, object) # doc, window def __init__(self, argv): """ Create the application object """ if argv is None: argv = [] self.argv = argv if QCoreApplication.instance() == None: self.qApp = QApplication(argv) self.qApp.setWindowIcon(QIcon(ICON_PATH)) assert(QCoreApplication.instance() != None) self.qApp.setOrganizationDomain("cadnano.org") else: self.qApp = qApp super(CadnanoQt, self).__init__() from cadnano.gui.views.preferences import Preferences self.prefs = Preferences() icon = QIcon(ICON_PATH) self.qApp.setWindowIcon(icon) self.document_controllers = set() # Open documents self.active_document = None self.vh = {} # Newly created VirtualHelix register here by idnum. self.vhi = {} self.partItem = None def finishInit(self): global decode global Document global DocumentController from cadnano.document import Document from cadnano.fileio.nnodecode import decode from cadnano.gui.controllers.documentcontroller import DocumentController from cadnano.gui.views.pathview import pathstyles as styles doc = Document() self.d = self.newDocument(base_doc=doc) styles.setFontMetrics() os.environ['CADNANO_DISCARD_UNSAVED'] = 'True' ## added by Nick if os.environ.get('CADNANO_DISCARD_UNSAVED', False) and not self.ignoreEnv(): self.dontAskAndJustDiscardUnsavedChanges = True if os.environ.get('CADNANO_DEFAULT_DOCUMENT', False) and not self.ignoreEnv(): self.shouldPerformBoilerplateStartupScript = True util.loadAllPlugins() if "-i" in self.argv: print("Welcome to cadnano's debug mode!") print("Some handy locals:") print("\ta\tcadnano.app() (the shared cadnano application object)") print("\td()\tthe last created Document") def d(): return self.d print("\tw()\tshortcut for d().controller().window()") def w(): return self.d.controller().window() print("\tp()\tshortcut for d().selectedPart()") def p(): return self.d.selectedPart() print("\tpi()\tthe PartItem displaying p()") def pi(): return w().pathroot.partItemForPart(p()) print( "\tvh(i)\tshortcut for p().virtualHelix(i)") def vh(vhref): return p().virtualHelix(vhref) print( "\tvhi(i)\tvirtualHelixItem displaying vh(i)") def vhi(vhref): partitem = pi() vHelix = vh(vhref) return partitem.vhItemForVH(vHelix) print("\tquit()\tquit (for when the menu fails)") print("\tgraphicsItm.findChild() see help(pi().findChild)") interact('', local={'a':self, 'd':d, 'w':w,\ 'p':p, 'pi':pi, 'vh':vh, 'vhi':vhi,\ }) # else: # self.exec_() def exec_(self): if hasattr(self, 'qApp'): self.mainEventLoop = QEventLoop() self.mainEventLoop.exec_() #self.qApp.exec_() def ignoreEnv(self): return os.environ.get('CADNANO_IGNORE_ENV_VARS_EXCEPT_FOR_ME', False) def newDocument(self, base_doc=None): global DocumentController default_file = os.environ.get('CADNANO_DEFAULT_DOCUMENT', None) if default_file is not None and base_doc is not None: default_file = path.expanduser(default_file) default_file = path.expandvars(default_file) dc = DocumentController(doc) with open(default_file) as fd: file_contents = fd.read() decode(doc, file_contents) print("Loaded default document: %s" % (doc)) else: doc_ctrlr_count = len(self.document_controllers) if doc_ctrlr_count == 0: # first dc # dc adds itself to app.document_controllers dc = DocumentController(base_doc) elif doc_ctrlr_count == 1: # dc already exists dc = list(self.document_controllers)[0] dc.newDocument() # tell it to make a new doucment return dc.document() def prefsClicked(self): self.prefs.showDialog()
class Kiwoom(QAxWidget): def __init__(self): super().__init__() self.setControl("KHOPENAPI.KHOpenAPICtrl.1") # Loop 변수 # 비동기 방식으로 동작되는 이벤트를 동기화(순서대로 동작) 시킬 때 self.loginLoop = None self.requestLoop = None self.orderLoop = None self.conditionLoop = None # 서버구분 self.server = None # 조건식 self.condition = None # 에러 self.error = None # 주문번호 self.orderNo = "" # 조회 self.inquiry = 0 # 서버에서 받은 메시지 self.msg = "" # 예수금 d+2 self.opw00001Data = 0 # 보유종목 정보 self.opw00018Data = {'accountEvaluation': [], 'stocks': []} # signal & slot self.OnEventConnect.connect(self.eventConnect) self.OnReceiveTrData.connect(self.receiveTrData) self.OnReceiveChejanData.connect(self.receiveChejanData) self.OnReceiveRealData.connect(self.receiveRealData) self.OnReceiveMsg.connect(self.receiveMsg) self.OnReceiveConditionVer.connect(self.receiveConditionVer) self.OnReceiveTrCondition.connect(self.receiveTrCondition) self.OnReceiveRealCondition.connect(self.receiveRealCondition) # 로깅용 설정파일 logging.config.fileConfig('logging.conf') self.log = logging.getLogger('Kiwoom') ############################################################### # 로깅용 메서드 정의 # ############################################################### def logger(origin): def wrapper(*args, **kwargs): args[0].log.debug('{} args - {}, kwargs - {}'.format(origin.__name__, args, kwargs)) return origin(*args, **kwargs) return wrapper ############################################################### # 이벤트 정의 # ############################################################### def eventConnect(self, returnCode): """ 통신 연결 상태 변경시 이벤트 returnCode가 0이면 로그인 성공 그 외에는 ReturnCode 클래스 참조. :param returnCode: int """ try: if returnCode == ReturnCode.OP_ERR_NONE: self.server = self.getLoginInfo("GetServerGubun", True) if len(self.server) == 0 or self.server != "1": self.msg += "실서버 연결 성공" + "\r\n\r\n" else: self.msg += "모의투자서버 연결 성공" + "\r\n\r\n" else: self.msg += "연결 끊김: 원인 - " + ReturnCode.CAUSE[returnCode] + "\r\n\r\n" except Exception as error: self.log.error('eventConnect {}'.format(error)) finally: # commConnect() 메서드에 의해 생성된 루프를 종료시킨다. # 로그인 후, 통신이 끊길 경우를 대비해서 예외처리함. try: self.loginLoop.exit() except AttributeError: pass def receiveMsg(self, screenNo, requestName, trCode, msg): """ 수신 메시지 이벤트 서버로 어떤 요청을 했을 때(로그인, 주문, 조회 등), 그 요청에 대한 처리내용을 전달해준다. :param screenNo: string - 화면번호(4자리, 사용자 정의, 서버에 조회나 주문을 요청할 때 이 요청을 구별하기 위한 키값) :param requestName: string - TR 요청명(사용자 정의) :param trCode: string :param msg: string - 서버로 부터의 메시지 """ self.msg += requestName + ": " + msg + "\r\n\r\n" def receiveTrData(self, screenNo, requestName, trCode, recordName, inquiry, deprecated1, deprecated2, deprecated3, deprecated4): """ TR 수신 이벤트 조회요청 응답을 받거나 조회데이터를 수신했을 때 호출됩니다. requestName과 trCode는 commRqData()메소드의 매개변수와 매핑되는 값 입니다. 조회데이터는 이 이벤트 메서드 내부에서 getCommData() 메서드를 이용해서 얻을 수 있습니다. :param screenNo: string - 화면번호(4자리) :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName) :param trCode: string :param recordName: string :param inquiry: string - 조회('0': 남은 데이터 없음, '2': 남은 데이터 있음) """ print("receiveTrData 실행: ", screenNo, requestName, trCode, recordName, inquiry) # 주문번호와 주문루프 self.orderNo = self.commGetData(trCode, "", requestName, 0, "주문번호") try: self.orderLoop.exit() except AttributeError: pass self.inquiry = inquiry if requestName == "관심종목정보요청": data = self.getCommDataEx(trCode, "관심종목정보") print(type(data)) print(data) """ commGetData cnt = self.getRepeatCnt(trCode, requestName) for i in range(cnt): data = self.commGetData(trCode, "", requestName, i, "종목명") print(data) """ elif requestName == "주식일봉차트조회요청": data = self.getCommDataEx(trCode, "주식일봉차트조회") colName = ['종목코드', '현재가', '거래량', '거래대금', '일자', '시가', '고가', '저가', '수정주가구분', '수정비율', '대업종구분', '소업종구분', '종목정보', '수정주가이벤트', '전일종가'] data = DataFrame(data, columns=colName) print(type(data)) print(data.head(5)) """ commGetData cnt = self.getRepeatCnt(trCode, requestName) for i in range(cnt): date = self.commGetData(trCode, "", requestName, i, "일자") open = self.commGetData(trCode, "", requestName, i, "시가") high = self.commGetData(trCode, "", requestName, i, "고가") low = self.commGetData(trCode, "", requestName, i, "저가") close = self.commGetData(trCode, "", requestName, i, "현재가") print(date, ": ", open, ' ', high, ' ', low, ' ', close) """ elif requestName == "예수금상세현황요청": deposit = self.commGetData(trCode, "", requestName, 0, "d+2추정예수금") deposit = self.changeFormat(deposit) self.opw00001Data = deposit elif requestName == "계좌평가잔고내역요청": # 계좌 평가 정보 accountEvaluation = [] keyList = ["총매입금액", "총평가금액", "총평가손익금액", "총수익률(%)", "추정예탁자산"] for key in keyList: value = self.commGetData(trCode, "", requestName, 0, key) if key.startswith("총수익률"): value = self.changeFormat(value, 1) else: value = self.changeFormat(value) accountEvaluation.append(value) self.opw00018Data['accountEvaluation'] = accountEvaluation # 보유 종목 정보 cnt = self.getRepeatCnt(trCode, requestName) keyList = ["종목명", "보유수량", "매입가", "현재가", "평가손익", "수익률(%)"] for i in range(cnt): stock = [] for key in keyList: value = self.commGetData(trCode, "", requestName, i, key) if key.startswith("수익률"): value = self.changeFormat(value, 2) elif key != "종목명": value = self.changeFormat(value) stock.append(value) self.opw00018Data['stocks'].append(stock) try: self.requestLoop.exit() except AttributeError: pass def receiveRealData(self, code, realType, realData): """ 실시간 데이터 수신 이벤트 실시간 데이터를 수신할 때 마다 호출되며, setRealReg() 메서드로 등록한 실시간 데이터도 이 이벤트 메서드에 전달됩니다. getCommRealData() 메서드를 이용해서 실시간 데이터를 얻을 수 있습니다. :param code: string - 종목코드 :param realType: string - 실시간 타입(KOA의 실시간 목록 참조) :param realData: string - 실시간 데이터 전문 """ try: self.log.debug("[receiveRealData]") self.log.debug("({})".format(realType)) if realType not in RealType.REALTYPE: return data = [] if code != "": data.append(code) codeOrNot = code else: codeOrNot = realType for fid in sorted(RealType.REALTYPE[realType].keys()): value = self.getCommRealData(codeOrNot, fid) data.append(value) # TODO: DB에 저장 self.log.debug(data) except Exception as e: self.log.error('{}'.format(e)) def receiveChejanData(self, gubun, itemCnt, fidList): """ 주문 접수/확인 수신시 이벤트 주문요청후 주문접수, 체결통보, 잔고통보를 수신할 때 마다 호출됩니다. :param gubun: string - 체결구분('0': 주문접수/주문체결, '1': 잔고통보, '3': 특이신호) :param itemCnt: int - fid의 갯수 :param fidList: string - fidList 구분은 ;(세미콜론) 이다. """ fids = fidList.split(';') print("[receiveChejanData]") print("gubun: ", gubun, "itemCnt: ", itemCnt, "fidList: ", fidList) print("========================================") print("[ 구분: ", self.getChejanData(913) if '913' in fids else '잔고통보', "]") for fid in fids: print(FidList.CHEJAN[int(fid)] if int(fid) in FidList.CHEJAN else fid, ": ", self.getChejanData(int(fid))) print("========================================") ############################################################### # 메서드 정의: 로그인 관련 메서드 # ############################################################### def commConnect(self): """ 로그인을 시도합니다. 수동 로그인일 경우, 로그인창을 출력해서 로그인을 시도. 자동 로그인일 경우, 로그인창 출력없이 로그인 시도. """ self.dynamicCall("CommConnect()") self.loginLoop = QEventLoop() self.loginLoop.exec_() def getConnectState(self): """ 현재 접속상태를 반환합니다. 반환되는 접속상태는 아래와 같습니다. 0: 미연결, 1: 연결 :return: int """ state = self.dynamicCall("GetConnectState()") return state def getLoginInfo(self, tag, isConnectState=False): """ 사용자의 tag에 해당하는 정보를 반환한다. tag에 올 수 있는 값은 아래와 같다. ACCOUNT_CNT: 전체 계좌의 개수를 반환한다. ACCNO: 전체 계좌 목록을 반환한다. 계좌별 구분은 ;(세미콜론) 이다. USER_ID: 사용자 ID를 반환한다. USER_NAME: 사용자명을 반환한다. GetServerGubun: 접속서버 구분을 반환합니다.("1": 모의투자, 그외(빈 문자열포함): 실서버) :param tag: string :param isConnectState: bool - 접속상태을 확인할 필요가 없는 경우 True로 설정. :return: string """ if not isConnectState: if not self.getConnectState(): raise KiwoomConnectError() if not isinstance(tag, str): raise ParameterTypeError() if tag not in ['ACCOUNT_CNT', 'ACCNO', 'USER_ID', 'USER_NAME', 'GetServerGubun']: raise ParameterValueError() if tag == "GetServerGubun": info = self.getServerGubun() else: cmd = 'GetLoginInfo("%s")' % tag info = self.dynamicCall(cmd) return info def getServerGubun(self): """ 서버구분 정보를 반환한다. 리턴값이 "1"이면 모의투자 서버이고, 그 외에는 실서버(빈 문자열포함). :return: string """ ret = self.dynamicCall("KOA_Functions(QString, QString)", "GetServerGubun", "") return ret ################################################################# # 메서드 정의: 조회 관련 메서드 # # 시세조회, 관심종목 조회, 조건검색 등 이들의 합산 조회 횟수가 1초에 5회까지 허용 # ################################################################# def setInputValue(self, key, value): """ TR 전송에 필요한 값을 설정한다. :param key: string - TR에 명시된 input 이름 :param value: string - key에 해당하는 값 """ if not (isinstance(key, str) and isinstance(value, str)): raise ParameterTypeError() self.dynamicCall("SetInputValue(QString, QString)", key, value) def commRqData(self, requestName, trCode, inquiry, screenNo): """ 키움서버에 TR 요청을 한다. 조회요청메서드이며 빈번하게 조회요청시, 시세과부하 에러값 -200이 리턴된다. :param requestName: string - TR 요청명(사용자 정의) :param trCode: string :param inquiry: int - 조회(0: 조회, 2: 남은 데이터 이어서 요청) :param screenNo: string - 화면번호(4자리) """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(requestName, str) and isinstance(trCode, str) and isinstance(inquiry, int) and isinstance(screenNo, str)): raise ParameterTypeError() returnCode = self.dynamicCall("CommRqData(QString, QString, int, QString)", requestName, trCode, inquiry, screenNo) if returnCode != ReturnCode.OP_ERR_NONE: raise KiwoomProcessingError("commRqData(): " + ReturnCode.CAUSE[returnCode]) # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다. self.requestLoop = QEventLoop() self.requestLoop.exec_() def commGetData(self, trCode, realType, requestName, index, key): """ 데이터 획득 메서드 receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 조회데이터를 얻어오는 메서드입니다. getCommData() 메서드로 위임. :param trCode: string :param realType: string - TR 요청시 ""(빈문자)로 처리 :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName) :param index: int :param key: string :return: string """ return self.getCommData(trCode, requestName, index, key) def getCommData(self, trCode, requestName, index, key): """ 데이터 획득 메서드 receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 조회데이터를 얻어오는 메서드입니다. :param trCode: string :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName) :param index: int :param key: string - 수신 데이터에서 얻고자 하는 값의 키(출력항목이름) :return: string """ if not (isinstance(trCode, str) and isinstance(requestName, str) and isinstance(index, int) and isinstance(key, str)): raise ParameterTypeError() data = self.dynamicCall("GetCommData(QString, QString, int, QString)", trCode, requestName, index, key) return data.strip() def getRepeatCnt(self, trCode, requestName): """ 서버로 부터 전달받은 데이터의 갯수를 리턴합니다.(멀티데이터의 갯수) receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다. 키움 OpenApi+에서는 데이터를 싱글데이터와 멀티데이터로 구분합니다. 싱글데이터란, 서버로 부터 전달받은 데이터 내에서, 중복되는 키(항목이름)가 하나도 없을 경우. 예를들면, 데이터가 '종목코드', '종목명', '상장일', '상장주식수' 처럼 키(항목이름)가 중복되지 않는 경우를 말합니다. 반면 멀티데이터란, 서버로 부터 전달받은 데이터 내에서, 일정 간격으로 키(항목이름)가 반복될 경우를 말합니다. 예를들면, 10일간의 일봉데이터를 요청할 경우 '종목코드', '일자', '시가', '고가', '저가' 이러한 항목이 10번 반복되는 경우입니다. 이러한 멀티데이터의 경우 반복 횟수(=데이터의 갯수)만큼, 루프를 돌면서 처리하기 위해 이 메서드를 이용하여 멀티데이터의 갯수를 얻을 수 있습니다. :param trCode: string :param requestName: string - TR 요청명(commRqData() 메소드 호출시 사용된 requestName) :return: int """ if not (isinstance(trCode, str) and isinstance(requestName, str)): raise ParameterTypeError() count = self.dynamicCall("GetRepeatCnt(QString, QString)", trCode, requestName) return count def getCommDataEx(self, trCode, multiDataName): """ 멀티데이터 획득 메서드 receiveTrData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다. :param trCode: string :param multiDataName: string - KOA에 명시된 멀티데이터명 :return: list - 중첩리스트 """ if not (isinstance(trCode, str) and isinstance(multiDataName, str)): raise ParameterTypeError() data = self.dynamicCall("GetCommDataEx(QString, QString)", trCode, multiDataName) return data def commKwRqData(self, codes, inquiry, codeCount, requestName, screenNo, typeFlag=0): """ 복수종목조회 메서드(관심종목조회 메서드라고도 함). 이 메서드는 setInputValue() 메서드를 이용하여, 사전에 필요한 값을 지정하지 않는다. 단지, 메서드의 매개변수에서 직접 종목코드를 지정하여 호출하며, 데이터 수신은 receiveTrData() 이벤트에서 아래 명시한 항목들을 1회 수신하며, 이후 receiveRealData() 이벤트를 통해 실시간 데이터를 얻을 수 있다. 복수종목조회 TR 코드는 OPTKWFID 이며, 요청 성공시 아래 항목들의 정보를 얻을 수 있다. 종목코드, 종목명, 현재가, 기준가, 전일대비, 전일대비기호, 등락율, 거래량, 거래대금, 체결량, 체결강도, 전일거래량대비, 매도호가, 매수호가, 매도1~5차호가, 매수1~5차호가, 상한가, 하한가, 시가, 고가, 저가, 종가, 체결시간, 예상체결가, 예상체결량, 자본금, 액면가, 시가총액, 주식수, 호가시간, 일자, 우선매도잔량, 우선매수잔량,우선매도건수, 우선매수건수, 총매도잔량, 총매수잔량, 총매도건수, 총매수건수, 패리티, 기어링, 손익분기, 잔본지지, ELW행사가, 전환비율, ELW만기일, 미결제약정, 미결제전일대비, 이론가, 내재변동성, 델타, 감마, 쎄타, 베가, 로 :param codes: string - 한번에 100종목까지 조회가능하며 종목코드사이에 세미콜론(;)으로 구분. :param inquiry: int - api 문서는 bool 타입이지만, int로 처리(0: 조회, 1: 남은 데이터 이어서 조회) :param codeCount: int - codes에 지정한 종목의 갯수. :param requestName: string :param screenNo: string :param typeFlag: int - 주식과 선물옵션 구분(0: 주식, 3: 선물옵션), 주의: 매개변수의 위치를 맨 뒤로 이동함. :return: list - 중첩 리스트 [[종목코드, 종목명 ... 종목 정보], [종목코드, 종목명 ... 종목 정보]] """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(codes, str) and isinstance(inquiry, int) and isinstance(codeCount, int) and isinstance(requestName, str) and isinstance(screenNo, str) and isinstance(typeFlag, int)): raise ParameterTypeError() returnCode = self.dynamicCall("CommKwRqData(QString, QBoolean, int, int, QString, QString)", codes, inquiry, codeCount, typeFlag, requestName, screenNo) if returnCode != ReturnCode.OP_ERR_NONE: raise KiwoomProcessingError("commKwRqData(): " + ReturnCode.CAUSE[returnCode]) # 루프 생성: receiveTrData() 메서드에서 루프를 종료시킨다. self.requestLoop = QEventLoop() self.requestLoop.exec_() ############################################################### # 메서드 정의: 실시간 데이터 처리 관련 메서드 # ############################################################### def disconnectRealData(self, screenNo): """ 해당 화면번호로 설정한 모든 실시간 데이터 요청을 제거합니다. 화면을 종료할 때 반드시 이 메서드를 호출해야 합니다. :param screenNo: string """ if not self.getConnectState(): raise KiwoomConnectError() if not isinstance(screenNo, str): raise ParameterTypeError() self.dynamicCall("DisconnectRealData(QString)", screenNo) def getCommRealData(self, code, fid): """ 실시간 데이터 획득 메서드 이 메서드는 반드시 receiveRealData() 이벤트 메서드가 호출될 때, 그 안에서 사용해야 합니다. :param code: string - 종목코드 :param fid: - 실시간 타입에 포함된 fid :return: string - fid에 해당하는 데이터 """ if not (isinstance(code, str) and isinstance(fid, int)): raise ParameterTypeError() value = self.dynamicCall("GetCommRealData(QString, int)", code, fid) return value def setRealReg(self, screenNo, codes, fids, realRegType): """ 실시간 데이터 요청 메서드 종목코드와 fid 리스트를 이용해서 실시간 데이터를 요청하는 메서드입니다. 한번에 등록 가능한 종목과 fid 갯수는 100종목, 100개의 fid 입니다. 실시간등록타입을 0으로 설정하면, 첫 실시간 데이터 요청을 의미하며 실시간등록타입을 1로 설정하면, 추가등록을 의미합니다. 실시간 데이터는 실시간 타입 단위로 receiveRealData() 이벤트로 전달되기 때문에, 이 메서드에서 지정하지 않은 fid 일지라도, 실시간 타입에 포함되어 있다면, 데이터 수신이 가능하다. :param screenNo: string :param codes: string - 종목코드 리스트(종목코드;종목코드;...) :param fids: string - fid 리스트(fid;fid;...) :param realRegType: string - 실시간등록타입(0: 첫 등록, 1: 추가 등록) """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(screenNo, str) and isinstance(codes, str) and isinstance(fids, str) and isinstance(realRegType, str)): raise ParameterTypeError() self.dynamicCall("SetRealReg(QString, QString, QString, QString)", screenNo, codes, fids, realRegType) def setRealRemove(self, screenNo, code): """ 실시간 데이터 중지 메서드 setRealReg() 메서드로 등록한 종목만, 이 메서드를 통해 실시간 데이터 받기를 중지 시킬 수 있습니다. :param screenNo: string - 화면번호 또는 ALL 키워드 사용가능 :param code: string - 종목코드 또는 ALL 키워드 사용가능 """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(screenNo, str) and isinstance(code, str)): raise ParameterTypeError() self.dynamicCall("SetRealRemove(QString, QString)", screenNo, code) ############################################################### # 메서드 정의: 조건검색 관련 메서드와 이벤트 # ############################################################### def receiveConditionVer(self, receive, msg): """ getConditionLoad() 메서드의 조건식 목록 요청에 대한 응답 이벤트 :param receive: int - 응답결과(1: 성공, 나머지 실패) :param msg: string - 메세지 """ try: if not receive: return self.condition = self.getConditionNameList() print("조건식 개수: ", len(self.condition)) for key in self.condition.keys(): print("조건식: ", key, ": ", self.condition[key]) print("key type: ", type(key)) except Exception as e: print(e) finally: self.conditionLoop.exit() def receiveTrCondition(self, screenNo, codes, conditionName, conditionIndex, inquiry): """ (1회성, 실시간) 종목 조건검색 요청시 발생되는 이벤트 :param screenNo: string :param codes: string - 종목코드 목록(각 종목은 세미콜론으로 구분됨) :param conditionName: string - 조건식 이름 :param conditionIndex: int - 조건식 인덱스 :param inquiry: int - 조회구분(0: 남은데이터 없음, 2: 남은데이터 있음) """ print("[receiveTrCondition]") try: if codes == "": return codeList = codes.split(';') del codeList[-1] print(codeList) print("종목개수: ", len(codeList)) finally: self.conditionLoop.exit() def receiveRealCondition(self, code, event, conditionName, conditionIndex): """ 실시간 종목 조건검색 요청시 발생되는 이벤트 :param code: string - 종목코드 :param event: string - 이벤트종류("I": 종목편입, "D": 종목이탈) :param conditionName: string - 조건식 이름 :param conditionIndex: string - 조건식 인덱스(여기서만 인덱스가 string 타입으로 전달됨) """ print("[receiveRealCondition]") print("종목코드: ", code) print("이벤트: ", "종목편입" if event == "I" else "종목이탈") def getConditionLoad(self): """ 조건식 목록 요청 메서드 """ if not self.getConnectState(): raise KiwoomConnectError() isLoad = self.dynamicCall("GetConditionLoad()") # 요청 실패시 if not isLoad: raise KiwoomProcessingError("getConditionLoad(): 조건식 요청 실패") # receiveConditionVer() 이벤트 메서드에서 루프 종료 self.conditionLoop = QEventLoop() self.conditionLoop.exec_() def getConditionNameList(self): """ 조건식 획득 메서드 조건식을 딕셔너리 형태로 반환합니다. 이 메서드는 반드시 receiveConditionVer() 이벤트 메서드안에서 사용해야 합니다. :return: dict - {인덱스:조건명, 인덱스:조건명, ...} """ data = self.dynamicCall("GetConditionNameList()") if data == "": raise KiwoomProcessingError("getConditionNameList(): 사용자 조건식이 없습니다.") conditionList = data.split(';') del conditionList[-1] conditionDictionary = {} for condition in conditionList: key, value = condition.split('^') conditionDictionary[int(key)] = value return conditionDictionary def sendCondition(self, screenNo, conditionName, conditionIndex, isRealTime): """ 종목 조건검색 요청 메서드 이 메서드로 얻고자 하는 것은 해당 조건에 맞는 종목코드이다. 해당 종목에 대한 상세정보는 setRealReg() 메서드로 요청할 수 있다. 요청이 실패하는 경우는, 해당 조건식이 없거나, 조건명과 인덱스가 맞지 않거나, 조회 횟수를 초과하는 경우 발생한다. 조건검색에 대한 결과는 1회성 조회의 경우, receiveTrCondition() 이벤트로 결과값이 전달되며 실시간 조회의 경우, receiveTrCondition()과 receiveRealCondition() 이벤트로 결과값이 전달된다. :param screenNo: string :param conditionName: string - 조건식 이름 :param conditionIndex: int - 조건식 인덱스 :param isRealTime: int - 조건검색 조회구분(0: 1회성 조회, 1: 실시간 조회) """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(screenNo, str) and isinstance(conditionName, str) and isinstance(conditionIndex, int) and isinstance(isRealTime, int)): raise ParameterTypeError() isRequest = self.dynamicCall("SendCondition(QString, QString, int, int", screenNo, conditionName, conditionIndex, isRealTime) if not isRequest: raise KiwoomProcessingError("sendCondition(): 조건검색 요청 실패") # receiveTrCondition() 이벤트 메서드에서 루프 종료 self.conditionLoop = QEventLoop() self.conditionLoop.exec_() def sendConditionStop(self, screenNo, conditionName, conditionIndex): """ 종목 조건검색 중지 메서드 """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(screenNo, str) and isinstance(conditionName, str) and isinstance(conditionIndex, int)): raise ParameterTypeError() self.dynamicCall("SendConditionStop(QString, QString, int)", screenNo, conditionName, conditionIndex) ############################################################### # 메서드 정의: 주문과 잔고처리 관련 메서드 # # 1초에 5회까지 주문 허용 # ############################################################### def sendOrder(self, requestName, screenNo, accountNo, orderType, code, qty, price, hogaType, originOrderNo): """ 주식 주문 메서드 sendOrder() 메소드 실행시, OnReceiveMsg, OnReceiveTrData, OnReceiveChejanData 이벤트가 발생한다. 이 중, 주문에 대한 결과 데이터를 얻기 위해서는 OnReceiveChejanData 이벤트를 통해서 처리한다. OnReceiveTrData 이벤트를 통해서는 주문번호를 얻을 수 있는데, 주문후 이 이벤트에서 주문번호가 ''공백으로 전달되면, 주문접수 실패를 의미한다. :param requestName: string - 주문 요청명(사용자 정의) :param screenNo: string - 화면번호(4자리) :param accountNo: string - 계좌번호(10자리) :param orderType: int - 주문유형(1: 신규매수, 2: 신규매도, 3: 매수취소, 4: 매도취소, 5: 매수정정, 6: 매도정정) :param code: string - 종목코드 :param qty: int - 주문수량 :param price: int - 주문단가 :param hogaType: string - 거래구분(00: 지정가, 03: 시장가, 05: 조건부지정가, 06: 최유리지정가, 그외에는 api 문서참조) :param originOrderNo: string - 원주문번호(신규주문에는 공백, 정정및 취소주문시 원주문번호르 입력합니다.) """ if not self.getConnectState(): raise KiwoomConnectError() if not (isinstance(requestName, str) and isinstance(screenNo, str) and isinstance(accountNo, str) and isinstance(orderType, int) and isinstance(code, str) and isinstance(qty, int) and isinstance(price, int) and isinstance(hogaType, str) and isinstance(originOrderNo, str)): raise ParameterTypeError() returnCode = self.dynamicCall("SendOrder(QString, QString, QString, int, QString, int, int, QString, QString)", [requestName, screenNo, accountNo, orderType, code, qty, price, hogaType, originOrderNo]) if returnCode != ReturnCode.OP_ERR_NONE: raise KiwoomProcessingError("sendOrder(): " + ReturnCode.CAUSE[returnCode]) # receiveTrData() 에서 루프종료 self.orderLoop = QEventLoop() self.orderLoop.exec_() def getChejanData(self, fid): """ 주문접수, 주문체결, 잔고정보를 얻어오는 메서드 이 메서드는 receiveChejanData() 이벤트 메서드가 호출될 때 그 안에서 사용해야 합니다. :param fid: int :return: string """ if not isinstance(fid, int): raise ParameterTypeError() cmd = 'GetChejanData("%s")' % fid data = self.dynamicCall(cmd) return data ############################################################### # 기타 메서드 정의 # ############################################################### def getCodeListByMarket(self, market): """ 시장 구분에 따른 종목코드의 목록을 List로 반환한다. market에 올 수 있는 값은 아래와 같다. '0': 장내, '3': ELW, '4': 뮤추얼펀드, '5': 신주인수권, '6': 리츠, '8': ETF, '9': 하이일드펀드, '10': 코스닥, '30': 제3시장 :param market: string :return: List """ if not self.getConnectState(): raise KiwoomConnectError() if not isinstance(market, str): raise ParameterTypeError() if market not in ['0', '3', '4', '5', '6', '8', '9', '10', '30']: raise ParameterValueError() cmd = 'GetCodeListByMarket("%s")' % market codeList = self.dynamicCall(cmd) return codeList.split(';') def getCodeList(self, *market): """ 여러 시장의 종목코드를 List 형태로 반환하는 헬퍼 메서드. :param market: Tuple - 여러 개의 문자열을 매개변수로 받아 Tuple로 처리한다. :return: List """ codeList = [] for m in market: tmpList = self.getCodeListByMarket(m) codeList += tmpList return codeList def getMasterCodeName(self, code): """ 종목코드의 한글명을 반환한다. :param code: string - 종목코드 :return: string - 종목코드의 한글명 """ if not self.getConnectState(): raise KiwoomConnectError() if not isinstance(code, str): raise ParameterTypeError() cmd = 'GetMasterCodeName("%s")' % code name = self.dynamicCall(cmd) return name def changeFormat(self, data, percent=0): if percent == 0: d = int(data) formatData = '{:-,d}'.format(d) elif percent == 1: f = int(data) / 100 formatData = '{:-,.2f}'.format(f) elif percent == 2: f = float(data) formatData = '{:-,.2f}'.format(f) return formatData def opwDataReset(self): """ 잔고 및 보유종목 데이터 초기화 """ self.opw00001Data = 0 self.opw00018Data = {'accountEvaluation': [], 'stocks': []}
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()
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)
class CharacterDialog(WindowModalDialog): def __init__(self, parent): super(CharacterDialog, self).__init__(parent) self.setWindowTitle(_("KeepKey Seed Recovery")) self.character_pos = 0 self.word_pos = 0 self.loop = QEventLoop() self.word_help = QLabel() self.char_buttons = [] vbox = QVBoxLayout(self) vbox.addWidget(WWLabel(CHARACTER_RECOVERY)) hbox = QHBoxLayout() hbox.addWidget(self.word_help) for i in range(4): char_button = CharacterButton('*') char_button.setMaximumWidth(36) self.char_buttons.append(char_button) hbox.addWidget(char_button) self.accept_button = CharacterButton(_("Accept Word")) self.accept_button.clicked.connect(partial(self.process_key, 32)) self.rejected.connect(partial(self.loop.exit, 1)) hbox.addWidget(self.accept_button) hbox.addStretch(1) vbox.addLayout(hbox) self.finished_button = QPushButton(_("Seed Entered")) self.cancel_button = QPushButton(_("Cancel")) self.finished_button.clicked.connect(partial(self.process_key, Qt.Key_Return)) self.cancel_button.clicked.connect(self.rejected) buttons = Buttons(self.finished_button, self.cancel_button) vbox.addSpacing(40) vbox.addLayout(buttons) self.refresh() self.show() def refresh(self): self.word_help.setText("Enter seed word %2d:" % (self.word_pos + 1)) self.accept_button.setEnabled(self.character_pos >= 3) self.finished_button.setEnabled((self.word_pos in (11, 17, 23) and self.character_pos >= 3)) for n, button in enumerate(self.char_buttons): button.setEnabled(n == self.character_pos) if n == self.character_pos: button.setFocus() def is_valid_alpha_space(self, key): # Auto-completion requires at least 3 characters if key == ord(' ') and self.character_pos >= 3: return True # Firmware aborts protocol if the 5th character is non-space if self.character_pos >= 4: return False return (key >= ord('a') and key <= ord('z') or (key >= ord('A') and key <= ord('Z'))) def process_key(self, key): self.data = None if key == Qt.Key_Return and self.finished_button.isEnabled(): self.data = {'done': True} elif key == Qt.Key_Backspace and (self.word_pos or self.character_pos): self.data = {'delete': True} elif self.is_valid_alpha_space(key): self.data = {'character': chr(key).lower()} if self.data: self.loop.exit(0) def keyPressEvent(self, event): self.process_key(event.key()) if not self.data: QDialog.keyPressEvent(self, event) def get_char(self, word_pos, character_pos): self.word_pos = word_pos self.character_pos = character_pos self.refresh() if self.loop.exec_(): self.data = None # User cancelled
class CadnanoQt(QObject): dontAskAndJustDiscardUnsavedChanges = False documentWasCreatedSignal = pyqtSignal(object) # doc documentWindowWasCreatedSignal = pyqtSignal(object, object) # doc, window def __init__(self, argv): """ Create the application object """ self.argns, unused = util.parse_args(argv, gui=True) # util.init_logging(self.argns.__dict__) # logger.info("CadnanoQt initializing...") if argv is None: argv = sys.argv self.argv = argv # print("initializing new CadnanoQt", type(QCoreApplication.instance())) if QCoreApplication.instance() is None: self.qApp = QApplication(argv) assert(QCoreApplication.instance() is not None) self.qApp.setOrganizationDomain("cadnano.org") else: self.qApp = qApp super(CadnanoQt, self).__init__() # print("initialized new CadnanoQt") from cadnano.gui.views.preferences import Preferences self.prefs = Preferences() self.icon = icon = QIcon(ICON_PATH1) icon.addFile(ICON_PATH2, QSize(256, 256)) icon.addFile(ICON_PATH3, QSize(48, 48)) self.qApp.setWindowIcon(icon) self.main_event_loop = None self.document_controllers = set() # Open documents self.active_document = None self._document = None self.documentWasCreatedSignal.connect(self.wirePrefsSlot) # end def def document(self): return self._document # end def def finishInit(self): global decodeFile global Document global DocumentController from cadnano.document import Document from cadnano.fileio.nnodecode import decodeFile from cadnano.gui.controllers.documentcontroller import DocumentController from cadnano.gui.views.pathview import pathstyles as styles doc = Document() self._document = self.createDocument(base_doc=doc) styles.setFontMetrics() os.environ['CADNANO_DISCARD_UNSAVED'] = 'True' # added by Nick if os.environ.get('CADNANO_DISCARD_UNSAVED', False) and not self.ignoreEnv(): self.dontAskAndJustDiscardUnsavedChanges = True util.loadAllPlugins() if self.argns.interactive: print("Welcome to cadnano's debug mode!") print("Some handy locals:") print("\ta\tcadnano.app() (the shared cadnano application object)") print("\td()\tthe last created Document") def d(): return self._document print("\tw()\tshortcut for d().controller().window()") def w(): return self._document.controller().window() print("\tp()\tshortcut for d().selectedInstance().reference()") def p(): return self._document.selectedInstance().reference() print("\tpi()\tthe PartItem displaying p()") def pi(): part_instance = self._document.selectedInstance() return w().pathroot.partItemForPart(part_instance) print("\tvh(i)\tshortcut for p().reference().getStrandSets(i)") def strandsets(id_num): return p().reference().getStrandSets(id_num) print("\tvhi(i)\tvirtualHelixItem displaying vh(i)") def vhi(id_num): partitem = pi() return partitem.vhItemForIdNum(id_num) print("\tquit()\tquit (for when the menu fails)") print("\tgraphicsItm.findChild() see help(pi().findChild)") interact('', local={'a': self, 'd': d, 'w': w, 'p': p, 'pi': pi, 'vhi': vhi, }) # end def def exec_(self): if hasattr(self, 'qApp'): self.main_event_loop = QEventLoop() self.main_event_loop.exec_() def destroyApp(self): """ Destroy the QApplication. Do not set `self.qApp = None` in this method. Do it external to the CadnanoQt class """ global decodeFile global Document global DocumentController # print("documentWasCreatedSignal", self.documentWasCreatedSignal) if self.document_controllers: self.documentWasCreatedSignal.disconnect(self.wirePrefsSlot) decodeFile = None Document = None DocumentController = None self.document_controllers.clear() self.qApp.quit() # end def def ignoreEnv(self): return os.environ.get('CADNANO_IGNORE_ENV_VARS_EXCEPT_FOR_ME', False) def createDocument(self, base_doc=None): global DocumentController # print("CadnanoQt createDocument begin") default_file = self.argns.file or os.environ.get('CADNANO_DEFAULT_DOCUMENT', None) if default_file is not None and base_doc is not None: default_file = os.path.expanduser(default_file) default_file = os.path.expandvars(default_file) dc = DocumentController(base_doc) # logger.info("Loading cadnano file %s to base document %s", default_file, base_doc) decodeFile(default_file, document=base_doc) dc.setFileName(default_file) print("Loaded default document: %s" % (default_file)) else: doc_ctrlr_count = len(self.document_controllers) # logger.info("Creating new empty document...") if doc_ctrlr_count == 0: # first dc # dc adds itself to app.document_controllers dc = DocumentController(base_doc) elif doc_ctrlr_count == 1: # dc already exists dc = list(self.document_controllers)[0] dc.newDocument() # tell it to make a new doucment # print("CadnanoQt createDocument done") return dc.document() def prefsClicked(self): self.prefs.showDialog() def wirePrefsSlot(self, document): self.prefs.document = document
class QtReactor(posixbase.PosixReactorBase): implements(IReactorFDSet) def __init__(self): self._reads = {} self._writes = {} self._notifiers = {} self._timer = QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self.iterate) if QCoreApplication.instance() is None: # Application Object has not been started yet self.qApp = QCoreApplication([]) self._ownApp = True else: self.qApp = QCoreApplication.instance() self._ownApp = False self._blockApp = None posixbase.PosixReactorBase.__init__(self) def _add(self, xer, primary, type): """ Private method for adding a descriptor from the event loop. It takes care of adding it if new or modifying it if already added for another state (read -> read/write for example). """ if xer not in primary: primary[xer] = TwistedSocketNotifier(None, self, xer, type) def addReader(self, reader): """ Add a FileDescriptor for notification of data available to read. """ self._add(reader, self._reads, QSocketNotifier.Read) def addWriter(self, writer): """ Add a FileDescriptor for notification of data available to write. """ self._add(writer, self._writes, QSocketNotifier.Write) def _remove(self, xer, primary): """ Private method for removing a descriptor from the event loop. It does the inverse job of _add, and also add a check in case of the fd has gone away. """ if xer in primary: notifier = primary.pop(xer) notifier.shutdown() def removeReader(self, reader): """ Remove a Selectable for notification of data available to read. """ self._remove(reader, self._reads) def removeWriter(self, writer): """ Remove a Selectable for notification of data available to write. """ self._remove(writer, self._writes) def removeAll(self): """ Remove all selectables, and return a list of them. """ rv = self._removeAll(self._reads, self._writes) return rv def getReaders(self): return self._reads.keys() def getWriters(self): return self._writes.keys() def callLater(self, howlong, *args, **kargs): rval = super(QtReactor, self).callLater(howlong, *args, **kargs) self.reactorInvocation() return rval def reactorInvocation(self): self._timer.stop() self._timer.setInterval(0) self._timer.start() def _iterate(self, delay=None, fromqt=False): """ See twisted.internet.interfaces.IReactorCore.iterate. """ self.runUntilCurrent() self.doIteration(delay, fromqt) iterate = _iterate def doIteration(self, delay=None, fromqt=False): """ This method is called by a Qt timer or by network activity on a file descriptor """ if not self.running and self._blockApp: self._blockApp.quit() self._timer.stop() delay = max(delay, 1) if not fromqt: self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000) if self.timeout() is None: timeout = 0.1 elif self.timeout() == 0: timeout = 0 else: timeout = self.timeout() self._timer.setInterval(timeout * 1000) self._timer.start() def runReturn(self, installSignalHandlers=True): self.startRunning(installSignalHandlers=installSignalHandlers) self.reactorInvocation() def run(self, installSignalHandlers=True): if self._ownApp: self._blockApp = self.qApp else: self._blockApp = QEventLoop() self.runReturn() self._blockApp.exec_()
def waitForSignal(sender, senderSignal, timeoutMs, expectedSignalParams=None): """ Wait up to timeoutMs after calling sender() for senderSignal to be emitted. It returns True if the senderSignal was emitted; otherwise, it returns False. If expectedSignalParams is not None, it is compared against the parameters emitted by the senderSignal. This function was inspired by http://stackoverflow.com/questions/2629055/qtestlib-qnetworkrequest-not-executed/2630114#2630114. """ # Create a single-shot timer. Could use QTimer.singleShot(), # but can't cancel this / disconnect it. timer = QTimer() timer.setSingleShot(True) # Create an event loop to run in. Otherwise, we need to use the # QApplication main loop, which may already be running and therefore # unusable. qe = QEventLoop() # Create a 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). senderSignalArgsWrong = [] def senderSignalSlot(*args): # If the senderSignal args should be checked and they # don't match, then they're wrong. In all other cases, # they're right. senderSignalArgsWrong.append( (expectedSignalParams is not None) and (expectedSignalParams != args)) # We received the requested signal, so exit the event loop. qe.exit() # Connect both signals to a slot which quits the event loop. senderSignal.connect(senderSignalSlot) timer.timeout.connect(qe.quit) # Start the sender and the timer and at the beginning of the event loop. # Just calling sender() may cause signals emitted in sender # not to reach their connected slots. QTimer.singleShot(0, sender) timer.start(timeoutMs) # Catch any exceptions which the EventLoop would otherwise catch # and not re-raise. exceptions = [] def excepthook(type_, value, tracebackObj): exceptions.append((value, tracebackObj)) oldExcHook = sys.excepthook sys.excepthook = excepthook # Wait for an emitted signal. qe.exec_() # If an exception occurred in the event loop, re-raise it. if exceptions: value, tracebackObj = exceptions[0] raise value.with_traceback(tracebackObj) # Clean up: don't allow the timer to call app.quit after this # function exits, which would produce "interesting" behavior. ret = timer.isActive() 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. senderSignal.disconnect(senderSignalSlot) timer.timeout.disconnect(qe.quit) # Restore the old exception hook sys.excepthook = oldExcHook return ret and senderSignalArgsWrong and (not senderSignalArgsWrong[0])
def send(params): url = self.formatUrl(endpoint, params) request = QNetworkRequest(url) headers['User-Agent'] = 'Divi QGIS Plugin/%s' % PLUGIN_VERSION QgsMessageLog.logMessage(str(headers), 'DIVI') for key, value in headers.items(): request.setRawHeader(key.encode('utf-8'), value.encode('utf-8')) if method == 'delete': reply = manager.sendCustomRequest(request, 'DELETE'.encode('utf-8'), data) else: if not data: reply = getattr(manager, method)(request) elif isinstance(data, QHttpMultiPart) == True: reply = getattr(manager, method)(request, data) elif isinstance(data, str) == True: reply = getattr(manager, method)(request, data.encode('utf-8')) loop = QEventLoop() reply.uploadProgress.connect(self.uploadProgress) reply.downloadProgress.connect(self.downloadProgress) reply.metaDataChanged.connect(self.metaDataChanged) #reply.error.connect(self._error) reply.finished.connect(loop.exit) self.abort_sig.connect( reply.abort ) loop.exec_() self.abort_sig.disconnect( reply.abort ) return reply