본문 바로가기
SWE/Qt

[Qt 윈도우 프로그램] 타이머 만들기 - 마우스 호버 동작

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

Done

(1) 디자인 다시 구성

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

=> (1)(2) : 위젯의 투명도를 조절했다. setWindoOpacity()를 이용해 투명도를 바꿨더니 마우스 이벤트를 잘 받을 수 있게 됐다!

그리고 버튼의 위치도 변경하고 pause랑 reset버튼을 다시 나눴다. 대신 disabled 처리를 시켜서 무슨 버튼을 눌러야하는지 직관적으로 알 수 있도록 하였다.

 

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

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

 

TBD

(1) 디버깅 방법 추가

(2) os 환경에 따른 수정사항 체크

 

 

timer.h timer.cpp timer.ui

 

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_mouseMove;
	int m_mouseX; 
	int m_mouseY;
	int m_absX;
	int m_absY;

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

protected:
	virtual void mouseReleaseEvent(QMouseEvent*);
	virtual void mouseMoveEvent(QMouseEvent*);
	virtual void enterEvent(QEvent* event);
	virtual void leaveEvent(QEvent* event);
	
signals:
	void resume();
	void pause();
	void reset();

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

#endif

 

timer.cpp

#include "timer.h"
#include <qdebug.h>

#define Width 520
#define Height 150

/**
 */
timer::timer(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	setWindowFlags(Qt::Widget | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);  
	
	initUi();

	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()) );
}

/**
*/
void timer::initUi()
{
	setFixedSize(Width, Height);

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

	showButtons(false);

	this->setWindowOpacity(0.8);

	ui.pauseButton->setDisabled(true);
	ui.resetButton->setDisabled(true);

	QString sheet =
		"QWidget#timer {"
		" background-color: rgb(47,47,47);"
		"}"

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

	setStyleSheet(sheet);
}

/**
 */
void timer::on_closeButton_clicked()
{
	m_clockThread.quit();
	m_clockThread.wait();
	QCoreApplication::quit();
}

/**
 */
void timer::on_startButton_clicked()
{
	ui.startButton->setDisabled(true);
	ui.pauseButton->setDisabled(false);
	ui.resetButton->setDisabled(false);

	if (m_clockThread.isRunning())
	{
		emit resume();
	}
	else
	{
		m_clockThread.start();
	}
}

/**
 */
void timer::on_pauseButton_clicked()
{
	ui.startButton->setDisabled(false);
	ui.pauseButton->setDisabled(true);
	ui.resetButton->setDisabled(false);
	emit pause();
}

/**
*/
void timer::on_resetButton_clicked()
{
	ui.startButton->setDisabled(false);
	ui.pauseButton->setDisabled(true);
	ui.resetButton->setDisabled(true);
	emit reset();
}

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

/**
*/
void timer::mouseReleaseEvent(QMouseEvent*)
{
	qDebug() << "in mouseReleaseEvent";
	m_mouseMove = true;
}

/**
*/
void timer::enterEvent(QEvent* event)
{
	qDebug() << "in enterEvent";
	showButtons(true);
}

/**
*/
void timer::leaveEvent(QEvent* event)
{
	qDebug() << "in leaveEvent";
	showButtons(false);
}


/**
*/ 
void timer::mouseMoveEvent(QMouseEvent *mouse)
{
	qDebug() << "in mouseMoveEvent";
	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.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>520</width>
    <height>150</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>10</y>
     <width>520</width>
     <height>110</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="palette">
    <palette>
     <active>
      <colorrole role="WindowText">
       <brush brushstyle="SolidPattern">
        <color alpha="255">
         <red>255</red>
         <green>255</green>
         <blue>255</blue>
        </color>
       </brush>
      </colorrole>
     </active>
     <inactive>
      <colorrole role="WindowText">
       <brush brushstyle="SolidPattern">
        <color alpha="255">
         <red>255</red>
         <green>255</green>
         <blue>255</blue>
        </color>
       </brush>
      </colorrole>
     </inactive>
     <disabled>
      <colorrole role="WindowText">
       <brush brushstyle="SolidPattern">
        <color alpha="255">
         <red>120</red>
         <green>120</green>
         <blue>120</blue>
        </color>
       </brush>
      </colorrole>
     </disabled>
    </palette>
   </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>485</x>
     <y>4</y>
     <width>30</width>
     <height>28</height>
    </rect>
   </property>
   <property name="text">
    <string>x</string>
   </property>
  </widget>
  <widget class="QPushButton" name="startButton">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>110</y>
     <width>160</width>
     <height>35</height>
    </rect>
   </property>
   <property name="text">
    <string>Start</string>
   </property>
  </widget>
  <widget class="QPushButton" name="pauseButton">
   <property name="geometry">
    <rect>
     <x>180</x>
     <y>110</y>
     <width>160</width>
     <height>35</height>
    </rect>
   </property>
   <property name="text">
    <string>Pause</string>
   </property>
  </widget>
  <widget class="QPushButton" name="resetButton">
   <property name="geometry">
    <rect>
     <x>350</x>
     <y>110</y>
     <width>160</width>
     <height>35</height>
    </rect>
   </property>
   <property name="text">
    <string>Reset</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

 

 

수정 중 겪었던 어려움

  • 투명 위젯과 마우스 이벤트의 딜레마

원하는 동작 : 위젯의 배경은 투명, 텍스트는 불투명. 평소에는 버튼들이 hide되어 있다가 마우스가 위젯 위로 호버시에만 버튼들을 show 

 

먼저, 위젯의 배경을 투명하게 만들기 위해 다음과 같은 속성을 추가했다.

setParent(0);
setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_TranslucentBackground, true);
setAttribute(Qt::WA_PaintOnScreen);

위의 4개가 모두 설정되었을 때만 '배경이 투명에 라벨의 텍스트가 repaint'되었다.

(WA_TranslucentBackground 속성의 경우 Qt::FramelessWindowHint 플래그도 함께 설정해야한다.)

근데 문제는 위의 속성을 설정하면 배경에서 이벤트를 받지 못한다. 나는 마우스가 배경에 위치할 경우 이벤트를 전달받고 싶은데 배경은 말그대로 뚫린게 되버림!

그래서 위젯 뒤에 있는 다른 웹이나 프로그램의 클릭이 먹힌다.

마우스 좌표가 타이머 라벨(시간초) 위에 있을 때만 이벤트를 전달 받을 수 있다. 

 

이게 왜 문제냐면 사용자가 느끼기엔 0 : 0 : 0를 감싸는 네모난 큰 테두리가 모두 프로그램이라고 생각할텐데 마우스를 올려도 버튼이 보이지 않으니까 어색할 것이다.

그리고 그 숫자를 조금만 벗어나도 버튼들이 사라지니까 마우스의 미세한 움직임으로 버튼이 보였다 안보였다 깜빡임이 심할 수 있다.

 

그래서 코드를 수정하려고 2가지 시도를 해봤다.

 

(1) this->installEventFilter() 

QWidget::event() 가상함수에서 HoverEnter, HoverLeave 이벤트 

=> 투명 배경에서 이벤트 전달 받지 못함

 

(2) setMouseTracking(true) mouseMoveEvent()

=> 마우스 왼쪽 클릭을 하지 않으면 해당 이벤트 함수를 타지 않음

 

 

 

m.blog.naver.com/yehyang0512/221949964871#

 

[Qt프로그래밍] Qt응용프로그램 윈도우PC로 배포(release) 하는 방법(Visual Studio2019, Qt Creator 5.14.2)

제목의 의미: Qt Creator로 열심히(?) 만든 .exe(운영체제가 윈도우인 PC에서 실행되는 프로그램)를 Q...

blog.naver.com

 

반응형