• 카테고리

    질문 & 답변
  • 세부 분야

    프로그래밍 언어

  • 해결 여부

    미해결

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

20.02.13 14:50 작성 조회수 179

1

수업내용이아니라 제가 스스로만드는 것에서 질문을 해도 될지 모르겠습니다 ^^;

배운것을 토대로 공부한 시간을 날짜별로 저장해서 그래프로 보여주는 프로그램을 만들고있는데,

맨 첫 창을 "공부시작", "공부량보기"(그래프) 로 설정하고

공부시작을 누르면 타이머가 작동하도록 하고싶은데

타이머창이 열리지않고 아무창도 안떠서 질문드립니다..

제 계획은 맨 첫창이 뜬 뒤 "공부시작"을 누르면 

맨첫창은 그대로 있고 타이머만 새로운 창으로 열려서 동작하게 할 생각인데,

그러면 def __init__(self)를 아예 수정하고 다른 함수에서

원래있던 def __init__(self)의 설정들을 저장해줘야할것 같은데

어떻게 코드를 짜야할지 감이 안잡힙니다 ㅠ

그리고 https://doc.qt.io/qt-5/qwidget.html

에서 버튼 클릭시 새로운 창이 뜨게하는 함수를 찾으려해도

양이 너무 방대해서 어떻게 원하는 함수를 딱 찾을수 있는지도 모르겠습니다

조금만 알려주시면 정말 감사하겠습니다

from PyQt5 import QtWidgets
from PyQt5 import QtCore
import datetime,time
import numpy as np
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
class MyClock(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        # 스톱와치 용 변수
        self.watch_start_time = 0
        self.mouseClick = False
        self.setWindowTitle("시계")
        self.setFixedSize(250100)#사이즈고정
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint)#타이틀바 없앰
        self.startMenu()
        self.show()
        self.days = datetime.date.today().day
        self.months=datetime.date.today().month

    def keyPressEvent(selfe):#esc누르면 종료
        if e.key() == QtCore.Qt.Key_Escape:
            day = str(self.months)+"/"+str(self.days)
            try:#이미 파일을 만들었으면
                data = pd.read_csv('C:\\python\\graph.csv')
                
                if data.loc[len(data)-1"day"]==day:#같은 날짜면 시간 누적
                    data.loc[data["day"]==day, "time"]+=self.watch_start_time
                    data.to_csv('graph.csv',header=Trueindex=False)
                else:#새로운 날짜,시간 append
                    data2=pd.DataFrame({"day":[day],"time":[self.watch_start_time]})
                    pd.merge(data,data2)
                    data.to_csv('graph.csv',header=Trueindex=False)
            except:#파일이없으면
                df=pd.DataFrame({"day":[day],"time":[self.watch_start_time]})
                df.to_csv('graph.csv',header=Trueindex=False)#header:column이름 정보
            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 watch(self):
        self.initWidgets()

    def graph(self):
        pass

    def startMenu(self):
        self.layout_s = QtWidgets.QVBoxLayout(self#전체틀

        self.start_layout = QtWidgets.QHBoxLayout() #버튼을 담을 틀
        self.btn_watch = QtWidgets.QPushButton("공부 시작",self)#버튼watch
        self.btn_graph = QtWidgets.QPushButton("공부량 보기",self)#버튼graph
        self.btn_watch.resize(self.btn_watch.sizeHint())#sizeHint=holds the recommended size for the widget
        self.btn_graph.resize(self.btn_graph.sizeHint())
        self.start_layout.addWidget(self.btn_watch)#틀에 버튼 담음
        self.start_layout.addWidget(self.btn_graph)

        self.layout_s.addLayout(self.start_layout)#전체틀에 버튼을 담은 틀을 담음
        self.setLayout(self.layout_s)

        self.btn_watch.clicked.connect(self.initWidgets)#버튼 클릭시 함수와 연결
        self.btn_graph.clicked.connect(self.graph)



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

        # 시작, 초기화 버튼 2개를 HBoxLayout 에 추가합니다.
        self.button_layout = QtWidgets.QHBoxLayout() # 버튼을 담기위한 레이아웃
        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.showWatch)  # 타임아웃 이벤트를 showWatch와 연결
        # 정한 시간이 지날때마다 show_time 실행
        self.timer.start(1000)#1초에 한번씩

        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 startWatch(self):
        '''스탑와치를 시작하는 함수 입니다.
        버튼 클릭시 시작과 중지를 한 버튼으로 처리하기 위해
        버튼의 글자를 가져와서 각 상황에 맞게 동작합니다.'''
        text = self.btn_start.text()
        if text == "시작":
            self.btn_start.setText("중지")
            self.timer.start(1000)
        elif text == "중지":
            self.btn_start.setText("시작")
            self.timer.stop()
     
    def resetWatch(self):
        '''스탑와치를 초기화 합니다.'''
        text = "00:00:00"
        self.watch_start_time = 0
        self.lcd.display(text)
    
    def showWatch(self):
        '''스탑와치의 현재시간 - 시작시간을 계산해서 화면에 출력하는 함수'''
        # 현재시간 - 스탑와치 시작시간을 total_seconds() 로 변환해서 초만 받습니다.
        self.watch_start_time+=1
        # 진행된 초를 시:분:초로 출력하기 위해서 계산합니다.
        hour = self.watch_start_time // 3600
        minute = self.watch_start_time % 3600 // 60
        second = self.watch_start_time % 60
        # 시:분:초 형태로 문자열 포맷팅을 합니다.
        text = '{:02d}:{:02d}:{:02d}'.format(hour, minute, second)
        # 출력
        self.lcd.display(text)


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

답변 1

답변을 작성해보세요.

1

저는 개인적으로 강좌를 보고 그걸 응용해서 새로운걸 만들어보는게 정말 최고의 공부법이라고 생각합니다. 그리고 그런 질문 주시는거에 대해 아주 환영합니다. 물론 제가 모르는 부분에 대해서는 해결해 드릴수는 없겠지만 제가 아는 한도내에선 도움을 드려야죠. ㅎㅎㅎ

일단 위에서 질문주신 내용에서 중요한 부분 한가지가 빠졌는데 바로 객체라는 관점에 대해서입니다.

말씀하신것처럼 메인창이 1개 있고 거기에 A 버튼, B 버튼 2개가 있다고 치고 A 버튼을 누를때 A-1 창이 뜨고 B 버튼을 누를때 B-2 창이 뜬다는건 이 프로그램에는 총 3개의 윈도우(Qt에서 위젯) 객체를 갖고 있어야 합니다만 위의 코드에서는 MyClock이라는 클래스 한개의 객체로 되어있습니다. (편의상 위젯을 윈도우라고 칭하겠습니다.)

물론 한개의 윈도우에서 A 버튼을 누르면 기존의 윈도우의 컨트롤들(버튼 등)을 모두 hide 시키고 새로운 컨트롤을 show 시키는 방식으로 짜여지는 프로그램도 있습니다만 쓸데없이 복잡하고 코드가 지저분한 구조를 갖게 됩니다. 그리 추천하지 않는 방식이고 위의 경우에는 3개의 윈도우를 미리 생성해서 각각 독립적으로 운영을 할수도 있고 아니면 메인윈도우에 내에 변수로 2개의 윈도우를 저장해서 사용하는 방식도 있습니다. 여기서 말한 2가지, 각각 독립적인 윈도우3개와 메인윈도우내의 변수로 사용되는 (부모자식관계) 윈도우의 구조에서는 각각의 장단점이 있습니다. 독립적으로 운영되는 경우에는 서로 동작에 영향을 주지 않는 특징이 있고 부모자식 관계인경우에는 서로 동작에 영향을 줄 수 있습니다. 동작에 영향을 준다는 의미는 예를 들어 자식창이 팝업되었을때 부모창은 동작하지 않아야 한다는 제한을 건다던가... 하는 뭐 그런 내용입니다. 간단하게만 예시를 든것 입니다. ^^;;;

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


class MainDialog(QtWidgets.QWidget):
    '''메인 윈도우가 되는 위젯. 이 창에서 나머지 기능들을 팝업시킵니다.'''
    def __init__(self):
        super().__init__()
        self.layout_s = QtWidgets.QVBoxLayout(self)
        self.start_layout = QtWidgets.QHBoxLayout()
        self.btn_watch = QtWidgets.QPushButton("공부 시작", self)
        self.btn_graph = QtWidgets.QPushButton("공부량 보기", self)
        self.btn_watch.resize(self.btn_watch.sizeHint())
        self.btn_graph.resize(self.btn_graph.sizeHint())
        self.start_layout.addWidget(self.btn_watch)
        self.start_layout.addWidget(self.btn_graph)

        self.layout_s.addLayout(self.start_layout)
        self.setLayout(self.layout_s)

        self.btn_watch.clicked.connect(self.clickWatch)
        self.btn_graph.clicked.connect(self.clickGraph)

        self.setFixedSize(250, 100)     # 사이즈고정
        self.show()

    def clickWatch(self):
        '''공부 시작이 눌리면 동작하는 함수'''
        # MyClock 객체가 생성되어 newWindow 변수에 저장
        self.newWindow = MyClock()
        # 새로운 위젯 show
        self.newWindow.show()
        pass

    def clickGraph(self):
        pass


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

일단 제가 위에서 작성하신 코드를 그대로 복사붙여넣기 해서 스탑와치 기능만 따로 새로운 창에서 동작가능하게 코드를 짜봤습니다. 기존의 코드에 불필요한 부분이 많아졌는데 그 부분은 일단 삭제 하지 않았으니 보시고 필요없는 코드는 제거하셔야 할 겁니다. 중요한 부분은 어떻게 새로운 창의 띄워졌는지 확인하시어 나머지 기능도 추가 해보심이 어떨까 생각됩니다.