본문 바로가기
SWE/Qt

[Qt 윈도우 프로그램] 타이머 만들기 - 디자이너가 필요한 이유

by S나라라2 2021. 3. 2.
반응형

 

Done

(1) 평소 버튼 hide, 마우스 호버 시에 버튼 show

  -> QGridLayout을 삭제하였다. 레이아웃 없이 좌표로 올려놓았다.

  -> 배경이 투명으로 되어 있어서 hover event가 오지 않는다. 

      시간이 업데이트 되는 라벨 위에 마우스가 올라 갈 때만 hover event가 온다. (수정 해야할 사항)

(2) 버튼 스타일 작업

  -> 버튼 모서리 둥근 것, normal, hover, pressed일 때 각각 색상을 입혀줬다.  

 

TBD

(1) 디자인 다시 구성

(2) 타이머 라벨 외에 빈 공간(위젯)에 마우스 호버 이벤트 받을 수 있도록 수정

(3) 다른 프로그램 실행 중에도 무조건 항상 상단 유지하도록 변경

(4) 디버깅 방법 추가

(5) 쓰레드 실행 중에 프로그램 종료 버튼 누를 경우, 프로그램 에러 팝업 뜸

     -> 쓰레드 종료 전에  main loop가 종료되기 때문에 에러코드를 리턴하며 프로그램이 종료되는 것

 

 

main.cpp

timer.cpp timer.h timer.ui

clockthread.cpp clockthread.h

 

main.cpp

#include "timer.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	timer w;
	w.show();
	return a.exec();
}

 

timer.cpp

#include "timer.h"

/**
 */
timer::timer(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	setWindowFlags(Qt::Widget | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
	
	setParent(0); // Create toplevel widget -> ?

	setAttribute(Qt::WA_NoSystemBackground, true);
	// 위젯이 배경이 없는 것을 나타낸다.
	// 위젯이 paint Event를 받을 때, 배경이 자동적으로 다시 그려지지 않는다.
	// WA_OpaquePaintEvent와는 다르게, 새로 노출된 부분이 절대 배경으로 채워지지 않는다.
	// WA_OpaquePaintEvent : 윈도우가 처음에 보여진 이후에, 프로그램이 paint event를 동작시킬 때까지 이것을 통해 user는 볼 수 있다.

	setAttribute(Qt::WA_TranslucentBackground, true);
	// 위젯이 투명한 배경을 가져야만 한다.
	// 위젯의 불투명하지 않은 부분이 투명해진다. 왜냐하면 위젯이 alpha 채널을 가지고 잇기 때문이다.
	// WA_TranslucentBackground 플래그를 셋팅하는 것은 WA_NoSystemBackground를 셋팅하도록 만든다.
	// 또한 윈도우에서 실행되는 프로그램이라면 Qt::FramelessWindowHint 플래그도 설정해야한다.

	setAttribute(Qt::WA_PaintOnScreen); 

	setAttribute(Qt::WA_Hover, true);

	this->installEventFilter(this);

	m_pause = true;

	m_mouseMove = true;
	m_mouseX = this->geometry().x(); // 현재 마우스 좌표로 초기화
	m_mouseY = this->geometry().y();
	m_absX = this->geometry().x();
	m_absY = this->geometry().y();

	connect(&m_clockThread, SIGNAL(sendTime(QString)), ui.label, SLOT(setText(QString)), Qt::QueuedConnection);
	connect(this, SIGNAL(resume()), &m_clockThread, SLOT(onResume()));
	connect(this, SIGNAL(reset()), &m_clockThread, SLOT(onReset()) );
	connect(this, SIGNAL(pause()), &m_clockThread, SLOT(onPause()) );

	initUi();
}

/**
*/
void timer::initUi()
{
	setFixedSize(480, 150);

	ui.label->setText("0 : 0 : 0");

	showButtons(false);

	QString sheet =
		"QPushButton#startButton {"
		" color : white;"
		" background-color: rgb(58,134,255);"
		" border-radius : 6px;"
		"}"
		"QPushButton#startButton::hover {"
		" color : white;"
		" background-color: rgb(33,90,181);"
		" border-radius : 6px;"
		"}"
		"QPushButton#startButton::pressed {"
		" color : white;"
		" background-color: rgb(27,48,82);"
		" border-radius : 6px;"
		"}"

		"QPushButton#pauseButton {"
		" color : white;"
		" background-color: rgb(58,134,255);"
		" border-radius : 6px;"
		"}"
		"QPushButton#pauseButton::hover {"
		" color : white;"
		" background-color: rgb(33,90,181);"
		" border-radius : 6px;"
		"}"
		"QPushButton#pauseButton::pressed {"
		" color : white;"
		" background-color: rgb(27,48,82);"
		" border-radius : 6px;"
		"}"

		"QPushButton#closeButton {"
		" color : white;"
		" background-color: rgb(58,134,255);"
		" border-radius : 6px;"
		"}"
		"QPushButton#closeButton::hover {"
		" color : white;"
		" background-color: rgb(33,90,181);"
		" border-radius : 6px;"
		"}"
		"QPushButton#closeButton::pressed {"
		" color : white;"
		" background-color: rgb(27,48,82);"
		" border-radius : 6px;"
		"}"
		;

	setStyleSheet(sheet);
}

/**
 */
void timer::on_closeButton_clicked()
{
	// TBD) thead running 중에 quit하면 에러 팝업 나옴
	m_clockThread.quit();
	QCoreApplication::quit();
}

/**
 */
void timer::on_startButton_clicked()
{
	if (m_clockThread.isRunning())
	{
		m_pause = true;
		ui.pauseButton->setText("Pause");
		emit resume();
	}
	else
	{
		m_clockThread.start();
	}
}

/**
 */
void timer::on_pauseButton_clicked()
{
	if (m_pause)
	{
		m_pause = false;
		ui.pauseButton->setText("Reset");
		emit pause();
	}
	else
	{
		m_pause = true;
		ui.pauseButton->setText("Pause");
		emit reset();
	}
}

void timer::showButtons(bool show)
{
	if (show)
	{
		ui.startButton->show();
		ui.pauseButton->show();
		ui.closeButton->show();
	}
	else
	{
		ui.startButton->hide();
		ui.pauseButton->hide();
		ui.closeButton->hide();
	}
}

/**
*/
bool timer::event(QEvent* e)
{
	switch (e->type())
	{
	case QEvent::HoverEnter:
		showButtons(true);
		return true;
	case QEvent::HoverLeave:
		showButtons(false);
		return true;
	default:
		break;
	}
	return QWidget::event(e);
}

/**
*/
void timer::mouseReleaseEvent(QMouseEvent*)
{
	m_mouseMove = true;
}

/**
*/ 
void timer::mouseMoveEvent(QMouseEvent *mouse)
{
	if (mouse->button() == Qt::RightButton)
	{
		return;
	}

	m_mouseX = QCursor::pos().x();
	m_mouseY = QCursor::pos().y();

	if (m_mouseMove)
	{
		m_absX = mouse->pos().x() + 7; 
		m_absY = mouse->pos().y() + 7;
		m_mouseMove = false;
	}

	this->move(m_mouseX-m_absX, m_mouseY-m_absY);
}

 

timer.h

#pragma once

#ifndef TIMER_H
#define TIMER_H

#include "clockthread.h"
#include <QWidget>
#include <QMouseEvent>
#include "ui_timer.h"

class ClockThread;

class timer : public QWidget
{
	Q_OBJECT

public:
	timer(QWidget *parent = Q_NULLPTR);

private:
	Ui::timer ui;
	ClockThread m_clockThread;

	bool m_pause;
	
	bool m_mouseMove;
	int m_mouseX; 
	int m_mouseY;
	int m_absX;
	int m_absY;

private:
	void initUi();
	void showButtons(bool show);

protected:
	virtual bool event(QEvent*);
	void mouseReleaseEvent(QMouseEvent*);
	void mouseMoveEvent(QMouseEvent*);

signals:
	void resume();
	void pause();
	void reset();

private slots:
	void on_closeButton_clicked();
	void on_startButton_clicked();
	void on_pauseButton_clicked();
};

#endif

 

timer.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>timer</class>
 <widget class="QWidget" name="timer">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>460</width>
    <height>130</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>-10</x>
     <y>0</y>
     <width>480</width>
     <height>120</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="font">
    <font>
     <family>Arial Rounded MT Bold</family>
     <pointsize>50</pointsize>
     <weight>75</weight>
     <bold>true</bold>
    </font>
   </property>
   <property name="text">
    <string notr="true">0 : 0 : 0</string>
   </property>
   <property name="alignment">
    <set>Qt::AlignCenter</set>
   </property>
  </widget>
  <widget class="QPushButton" name="closeButton">
   <property name="geometry">
    <rect>
     <x>380</x>
     <y>20</y>
     <width>31</width>
     <height>28</height>
    </rect>
   </property>
   <property name="text">
    <string>x</string>
   </property>
  </widget>
  <widget class="QPushButton" name="startButton">
   <property name="geometry">
    <rect>
     <x>140</x>
     <y>60</y>
     <width>90</width>
     <height>40</height>
    </rect>
   </property>
   <property name="text">
    <string>Start</string>
   </property>
  </widget>
  <widget class="QPushButton" name="pauseButton">
   <property name="geometry">
    <rect>
     <x>230</x>
     <y>60</y>
     <width>90</width>
     <height>40</height>
    </rect>
   </property>
   <property name="text">
    <string>Pause</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

 

clockthread.h

#ifndef CLOCKTHREAD_H
#define CLOCKTHREAD_H

#include <QThread>
#include <QObject>

class ClockThread : public QThread
{
	Q_OBJECT;

public:
	ClockThread(QObject* parent=Q_NULLPTR);
	virtual ~ClockThread();

private:
	bool m_firstTime;
	bool m_running;
	int m_hours;
	int m_minutes;
	int m_seconds;

private:
	void init();
	void run();

signals:
	void sendTime(QString time);

private slots:
	void onResume();
	void onPause();
	void onReset();
	void timerHit();
};

#endif

 

clockthread.cpp

#include "clockthread.h"

#include <QTimer>

ClockThread::ClockThread(QObject* parent)
: QThread(parent)
{
	init();
}

ClockThread::~ClockThread()
{
}

void ClockThread::init()
{
	m_running = false;
	m_hours = 0;
	m_minutes = 0;
	m_seconds = 0;

	QString time = QString("%1 : %2 : %3").arg(m_hours).arg(m_minutes).arg(m_seconds);
	emit sendTime(time);
}

void ClockThread::onResume()
{
	m_running = true;
}

void ClockThread::onPause()
{
	m_running = false;
}

void ClockThread::onReset()
{
	init();
	quit();
}

void ClockThread::run()
{
	m_running = true;

	QTimer timer;
	connect(&timer, SIGNAL(timeout()), this, SLOT(timerHit()), Qt::DirectConnection);
	timer.setInterval(1000); // 1000msc = 1 sec
	timer.start();
	exec();
}

void ClockThread::timerHit()
{
	if(m_running)
	{
		m_seconds++;
		if(m_seconds>60)
		{
			m_minutes++;
			m_seconds = 0;
		}
		if(m_minutes>60)
		{
			m_hours++;
			m_minutes = 0;
		}

		QString time = QString("%1 : %2 : %3").arg(m_hours).arg(m_minutes).arg(m_seconds);
		emit sendTime(time);
	}
}
반응형