At 실시간 현재가 차트 » 이력 » 버전 1
이태훈, 2025/02/12 04:40
| 1 | 1 | 이태훈 | h1. At 차트UI생성 |
|---|---|---|---|
| 2 | |||
| 3 | h3. 소스코드 |
||
| 4 | |||
| 5 | <pre> |
||
| 6 | import sys |
||
| 7 | import time |
||
| 8 | import pyupbit |
||
| 9 | import pybithumb |
||
| 10 | from PyQt5 import uic |
||
| 11 | from PyQt5.QtWidgets import QApplication, QWidget |
||
| 12 | from PyQt5.QtChart import QLineSeries, QChart, QValueAxis, QDateTimeAxis |
||
| 13 | from PyQt5.QtGui import QPainter |
||
| 14 | from PyQt5.QtCore import Qt, QDateTime, QThread, pyqtSignal |
||
| 15 | |||
| 16 | class PriceWorker(QThread): |
||
| 17 | dataSent = pyqtSignal(float) |
||
| 18 | |||
| 19 | def __init__(self, ticker): |
||
| 20 | super().__init__() |
||
| 21 | self.ticker = ticker |
||
| 22 | self.alive = True |
||
| 23 | |||
| 24 | def run(self): |
||
| 25 | while self.alive: |
||
| 26 | data = pyupbit.get_current_price(self.ticker) # 업비트 금액 조회 |
||
| 27 | # data = pybithumb.get_current_price(self.ticker) # 빗썸 금액 조회 |
||
| 28 | time.sleep(1) # 1초에 한번씩 갱신 |
||
| 29 | self.dataSent.emit(data) |
||
| 30 | |||
| 31 | def close(self): |
||
| 32 | self.alive = False |
||
| 33 | class ChartWidget(QWidget): |
||
| 34 | def __init__(self, parent=None, ticker="KRW-BTC"): # 업비트 : KRW-BTC | 빗썸 : BTC |
||
| 35 | super().__init__(parent) |
||
| 36 | uic.loadUi("resource/chart.ui", self) |
||
| 37 | self.ticker = ticker |
||
| 38 | |||
| 39 | self.viewLimit = 128 # 가로 폭 갯수 |
||
| 40 | |||
| 41 | self.priceData = QLineSeries() |
||
| 42 | |||
| 43 | self.priceChart = QChart() |
||
| 44 | self.priceChart.addSeries(self.priceData) |
||
| 45 | self.priceChart.legend().hide() |
||
| 46 | |||
| 47 | axisX = QDateTimeAxis() |
||
| 48 | axisX.setFormat("hh:mm:ss") |
||
| 49 | axisX.setTickCount(4) |
||
| 50 | dt = QDateTime.currentDateTime() |
||
| 51 | axisX.setRange(dt, dt.addSecs(self.viewLimit)) |
||
| 52 | |||
| 53 | axisY = QValueAxis() |
||
| 54 | axisY.setVisible(True) |
||
| 55 | |||
| 56 | self.priceChart.addAxis(axisX, Qt.AlignBottom) |
||
| 57 | self.priceChart.addAxis(axisY, Qt.AlignRight) |
||
| 58 | self.priceData.attachAxis(axisX) |
||
| 59 | self.priceData.attachAxis(axisY) |
||
| 60 | self.priceChart.layout().setContentsMargins(0, 0, 0, 0) |
||
| 61 | |||
| 62 | self.priceView.setChart(self.priceChart) |
||
| 63 | self.priceView.setRenderHints(QPainter.Antialiasing) |
||
| 64 | |||
| 65 | self.pw = PriceWorker(ticker) |
||
| 66 | self.pw.dataSent.connect(self.appendData) |
||
| 67 | self.pw.start() |
||
| 68 | |||
| 69 | # -------------------------------------------- |
||
| 70 | |||
| 71 | def appendData(self, currentPrice): |
||
| 72 | if len(self.priceData) == self.viewLimit: |
||
| 73 | self.priceData.remove(0) |
||
| 74 | dt = QDateTime.currentDateTime() |
||
| 75 | self.priceData.append(dt.toMSecsSinceEpoch(), currentPrice) |
||
| 76 | self.__updateAxis() |
||
| 77 | |||
| 78 | def __updateAxis(self): |
||
| 79 | pvs = self.priceData.pointsVector() |
||
| 80 | dtStart = QDateTime.fromMSecsSinceEpoch(int(pvs[0].x())) |
||
| 81 | if len(self.priceData) == self.viewLimit: |
||
| 82 | dtLast = QDateTime.fromMSecsSinceEpoch(int(pvs[-1].x())) |
||
| 83 | else: |
||
| 84 | dtLast = dtStart.addSecs(self.viewLimit) |
||
| 85 | |||
| 86 | ax = self.priceChart.axisX() |
||
| 87 | ax.setRange(dtStart, dtLast) |
||
| 88 | |||
| 89 | ay = self.priceChart.axisY() |
||
| 90 | dataY = [v.y() for v in pvs] |
||
| 91 | ay.setRange(min(dataY) - 10000, max(dataY) + 10000) # 여유가 없는 경우 최대/최소로만 그려짐 |
||
| 92 | |||
| 93 | def closeEvent(self, event): |
||
| 94 | self.pw.close() |
||
| 95 | |||
| 96 | if __name__ == "__main__": |
||
| 97 | app = QApplication(sys.argv) |
||
| 98 | cw = ChartWidget() |
||
| 99 | cw.show() |
||
| 100 | exit(app.exec_()) |
||
| 101 | </pre> |
||
| 102 | |||
| 103 | h3. 결과 |
||
| 104 | |||
| 105 | !clipboard-202502121340-xsm1q.png! |