인프런 커뮤니티 질문&답변

윤태영님의 프로필 이미지
윤태영

작성한 질문수

남박사의 파이썬 기초부터 실전 100% 활용

디지털시계 만들기에서 질문있습니다!

작성

·

721

1

제가 공부시간측정하는 프로그램을 만들려고 하는데,

현재 시간말고 00:00:00부터 시작하도록 하고싶습니다.

갖은방법을 써보았으나 QtCore.QTime.currentTime()를 쓰지않고

빼기연산으로 시간을 측정시 연산시간떄문에 부정확하여

구글링을 해보니 아예 다른코드뿐이라 이해가 되지않아 질문드립니다

from PyQt5 import QtWidgets
from PyQt5 import QtCore

class MyClock(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        
        self.mouseClick = False

        self.setWindowTitle("시계")
        self.initWidgets()
        self.setFixedSize(250100)#사이즈고정
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)#타이틀바 없앰
        self.show()
    
    def keyPressEvent(selfe):#esc누르면 종료
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()

    def mousePressEvent(selfe):#마우스로 창 누를때
        if e.button() == QtCore.Qt.LeftButton:#좌클릭시
            self.mouseClick = True
            self.oldPos = e.globalPos()#x,y가 튜플형태로 넘어옴
            #globalPos=윈도우상의 x,y좌표
    def mouseReleaseEvent(selfe):
        self.mouseClick = False

    def mouseMoveEvent(selfe):#마우스로 창 누른뒤 이동시킬때
        if self.mouseClick:
            delta = QtCore.QPoint(e.globalPos() - self.oldPos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.oldPos = e.globalPos()


    def initWidgets(self):
        self.layout = QtWidgets.QVBoxLayout()#QV=가로 QH=세로
        self.lcd = QtWidgets.QLCDNumber()#시계디자인 위젯
        self.lcd.setSegmentStyle(QtWidgets.QLCDNumber.Flat)#글자평평하게
        self.lcd.setDigitCount(8)#글자 총 8개까지 보여줌(hh:mm:ss)
        self.lcd.setFrameStyle(QtWidgets.QFrame.NoFrame)#박스없앰

        self.timer = QtCore.QTimer()#타이머 생성
        self.timer.timeout.connect(self.show_time)#타임아웃 이벤트를 show_time과 연결
        #정한 시간이 지날때마다 show_time 실행
        
        self.timer.start(1000)#1초에 한번씩

        self.show_time()

        self.layout.addWidget(self.lcd)
        self.setLayout(self.layout)
    
    def show_time(self):
        time = QtCore.QTime.currentTime()#현재시간
        self.currentTime = time.toString("hh:mm:ss")#모양을 만듬
        self.lcd.display(self.currentTime)

app = QtWidgets.QApplication([])
win = MyClock()
app.exec_()
app.exec_()

답변 3

1

남박사님의 프로필 이미지
남박사
지식공유자

그 부분이 문제시라면 제가 작성해서 올린 코드에서 타이머의 인터벌을 100ms 로 줄여서 해보시기 바랍니다.

self.timer.start(100)  # 이렇게 수정해서 테스트 해보시기 바랍니다.

0

윤태영님의 프로필 이미지
윤태영
질문자

import time
from PyQt5 import QtWidgets
from PyQt5 import QtCore

class MyClock(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        
        self.mouseClick = False
        self.clock=0
        self.setWindowTitle("시계")
        self.initWidgets()
        self.setFixedSize(250100)#사이즈고정
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)#타이틀바 없앰
        self.show()
        
    def keyPressEvent(selfe):#esc누르면 종료
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()

    def mousePressEvent(selfe):#마우스로 창 누를때
        if e.button() == QtCore.Qt.LeftButton:#좌클릭시
            self.mouseClick = True
            self.oldPos = e.globalPos()#x,y가 튜플형태로 넘어옴
            #globalPos=윈도우상의 x,y좌표
    def mouseReleaseEvent(selfe):
        self.mouseClick = False

    def mouseMoveEvent(selfe):#마우스로 창 누른뒤 이동시킬때
        if self.mouseClick:
            delta = QtCore.QPoint(e.globalPos() - self.oldPos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.oldPos = e.globalPos()


    def initWidgets(self):
        self.layout = QtWidgets.QVBoxLayout()#QV=가로 QH=세로
        self.lcd = QtWidgets.QLCDNumber()#시계디자인 위젯
        self.lcd.setSegmentStyle(QtWidgets.QLCDNumber.Flat)#글자평평하게
        self.lcd.setDigitCount(8)#글자 총 8개까지 보여줌(hh:mm:ss)
        self.lcd.setFrameStyle(QtWidgets.QFrame.NoFrame)#박스없앰

        self.timer = QtCore.QTimer()#타이머 생성
        self.timer.timeout.connect(self.show_time)#타임아웃 이벤트를 show_time과 연결
        #정한 시간이 지날때마다 show_time 실행
        
        self.timer.start(1000)#1초에 한번씩

        self.show_time()

        self.layout.addWidget(self.lcd)
        self.setLayout(self.layout)
    
    def show_time(self):
        self.clock+=1
        time.sleep(1)
        self.hh=self.clock//3600
        self.mm=(self.clock-self.hh*3600)//60
        self.ss=self.clock%60
        self.currentTime = str(self.hh)+":"+str(self.mm)+":"+str(self.ss)
        self.lcd.display(self.currentTime)

app = QtWidgets.QApplication([])
win = MyClock()
app.exec_()
def time_chker(func):
    def inner_function(*args, **kwargs):
        start_time=time.time()
        result=func(*args,**kwargs) #test1() 실행값
        end_time=time.time()
        print("func:{}, time:{}".format(func.__name__,end_time-start_time))
        return result
    return inner_function #result리턴
저는 이렇게 짜보았는데, 연산시간과 함수호출시간때문에 시간이 정확히 지나가지않고
2초를 한번에 건너뛰거나 1초가 좀 길게느껴지거나 했는데,
강사님께서 보내주신 코드는 이런점이 많이 개선되었지만 근본적으로 해결되진 않는것 같습니다 ㅠ
어쩔수 없는 현상인가요?

0

남박사님의 프로필 이미지
남박사
지식공유자

질문의 내용을 제가 이해하기론 스탑와치 같은걸 말씀하시는거 같습니다.

스탑와치의 로직을 즉흥적으로 생각해보면 시작한 시간을 기억해놓고 현재시간 - 시작한시간을 빼면 현재 진행된 시간을 얻을 수 있을것 같습니다. 현재 시간은 파이썬에서 제공하는 여러가지 방안들이 있을 수 있을것 같습니다만 간단하게 datetime 라이브러리를 사용해서 구하는것도 괜찮을것 같습니다. 어렵게 설명하는것 보다 코드를 보는게 빠를것 같습니다.

from PyQt5 import QtWidgets
from PyQt5 import QtCore
import datetime

class MyClock(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        # 스톱와치 용 변수
        self.watch_start_time = 0
        self.mouseClick = False
        self.setWindowTitle("시계")
        self.initWidgets()
        self.setFixedSize(250, 100)#사이즈고정
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)#타이틀바 없앰
        self.show()

    def keyPressEvent(self, e):#esc누르면 종료
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()

    def mousePressEvent(self, e):#마우스로 창 누를때
        if e.button() == QtCore.Qt.LeftButton:#좌클릭시
            self.mouseClick = True
            self.oldPos = e.globalPos()#x,y가 튜플형태로 넘어옴
            #globalPos=윈도우상의 x,y좌표

    def mouseReleaseEvent(self, e):
        self.mouseClick = False

    def mouseMoveEvent(self, e):#마우스로 창 누른뒤 이동시킬때
        if self.mouseClick:
            delta = QtCore.QPoint(e.globalPos() - self.oldPos)
            self.move(self.x() + delta.x(), self.y() + delta.y())
            self.oldPos = e.globalPos()

    def initWidgets(self):
        self.layout = QtWidgets.QVBoxLayout(self) #QV=가로 QH=세로

        # 시작, 초기화 버튼 2개를 HBoxLayout 에 추가합니다.
        self.button_layout = QtWidgets.QHBoxLayout(self) # 버튼을 담기위한 레이아웃
        self.btn_start = QtWidgets.QPushButton("시작", self)
        self.btn_reset = QtWidgets.QPushButton("초기화", self)
        self.btn_start.resize(self.btn_start.sizeHint())
        self.btn_reset.resize(self.btn_start.sizeHint())
        self.button_layout.addWidget(self.btn_start)
        self.button_layout.addWidget(self.btn_reset)

        self.lcd = QtWidgets.QLCDNumber()#시계디자인 위젯
        self.lcd.setSegmentStyle(QtWidgets.QLCDNumber.Flat)#글자평평하게
        self.lcd.setDigitCount(8)#글자 총 8개까지 보여줌(hh:mm:ss)
        self.lcd.setFrameStyle(QtWidgets.QFrame.NoFrame)#박스없앰

        self.timer = QtCore.QTimer()    # 타이머 생성
        # self.timer.timeout.connect(self.show_time)    # 타임아웃 이벤트를 show_time과 연결
        # 스탑와치용 출력 함수 연결
        self.timer.timeout.connect(self.showWatch)  # 타임아웃 이벤트를 show_time과 연결
        # 정한 시간이 지날때마다 show_time 실행
        self.timer.start(1000)#1초에 한번씩

        # self.show_time()
        self.resetWatch()
        self.layout.addWidget(self.lcd)
        # 버튼 레이아웃을 기본 레이아웃에 추가합니다.
        self.layout.addLayout(self.button_layout)
        self.setLayout(self.layout)

        self.btn_start.clicked.connect(self.startWatch)
        self.btn_reset.clicked.connect(self.resetWatch)

    def show_time(self):
        time = QtCore.QTime.currentTime()#현재시간
        self.currentTime = time.toString("hh:mm:ss")#모양을 만듬
        self.lcd.display(self.currentTime)

    def startWatch(self):
        '''스탑와치를 시작하는 함수 입니다.
        버튼 클릭시 시작과 중지를 한 버튼으로 처리하기 위해
        버튼의 글자를 가져와서 각 상황에 맞게 동작합니다.'''
        text = self.btn_start.text()
        if text == "시작":
            self.watch_start_time = datetime.datetime.now()
            self.btn_start.setText("중지")
            self.timer.start(500)
        elif text == "중지":
            self.btn_start.setText("시작")
            self.timer.stop()

    def resetWatch(self):
        '''스탑와치를 초기화 합니다.'''
        text = "00:00:00"
        self.watch_start_time = datetime.datetime.now()
        self.lcd.display(text)

    def showWatch(self):
        '''스탑와치의 현재시간 - 시작시간을 계산해서 화면에 출력하는 함수'''
        # 현재시간 - 스탑와치 시작시간을 total_seconds() 로 변환해서 초만 받습니다.
        elapsed_seconds = (datetime.datetime.now() - self.watch_start_time).total_seconds()
        # 진행된 초를 시:분:초로 출력하기 위해서 계산합니다.
        hour = int(elapsed_seconds // 3600)
        minute = int(elapsed_seconds % 3600 // 60)
        second = int(elapsed_seconds % 60)
        # 시:분:초 형태로 문자열 포맷팅을 합니다.
        text = '{:02d}:{:02d}:{:02d}'.format(hour, minute, second)
        # 출력
        self.lcd.display(text)


app = QtWidgets.QApplication([])
win = MyClock()
app.exec_()

올려주신 코드에 함수만 몇개 추가해서 작성한 코드 입니다. 0.5초마다 갱신하게 되어있으며 시:분:초로 출력됩니다. 여기서 만약 시:분:초:밀리세컨드 형태로 출력되게 하려면 0.5 초마다의 갱신을 더 짧게 잡아야 하고 datetime 라이브러리 말고 time 라이브러리 같은걸 사용하는게 더 좋을수도 있을거 같습니다. 참고가 되셨길 바랍니다. ^^

윤태영님의 프로필 이미지
윤태영

작성한 질문수

질문하기