diff --git a/widget/0snap/colorwidget.jpg b/widget/0snap/colorwidget.jpg new file mode 100644 index 0000000..4a821c9 Binary files /dev/null and b/widget/0snap/colorwidget.jpg differ diff --git a/widget/0snap/framelesswidget.jpg b/widget/0snap/framelesswidget.jpg new file mode 100644 index 0000000..effa921 Binary files /dev/null and b/widget/0snap/framelesswidget.jpg differ diff --git a/widget/0snap/gifwidget.jpg b/widget/0snap/gifwidget.jpg new file mode 100644 index 0000000..0539b4c Binary files /dev/null and b/widget/0snap/gifwidget.jpg differ diff --git a/widget/0snap/lunarcalendarwidget.jpg b/widget/0snap/lunarcalendarwidget.jpg new file mode 100644 index 0000000..6a3fedb Binary files /dev/null and b/widget/0snap/lunarcalendarwidget.jpg differ diff --git a/widget/0snap/maskwidget.jpg b/widget/0snap/maskwidget.jpg new file mode 100644 index 0000000..30d8746 Binary files /dev/null and b/widget/0snap/maskwidget.jpg differ diff --git a/widget/0snap/movewidget.jpg b/widget/0snap/movewidget.jpg new file mode 100644 index 0000000..72678ae Binary files /dev/null and b/widget/0snap/movewidget.jpg differ diff --git a/widget/0snap/screenwidget.jpg b/widget/0snap/screenwidget.jpg new file mode 100644 index 0000000..c611532 Binary files /dev/null and b/widget/0snap/screenwidget.jpg differ diff --git a/widget/bin/colorwidget.exe b/widget/bin/colorwidget.exe new file mode 100644 index 0000000..7876d3f Binary files /dev/null and b/widget/bin/colorwidget.exe differ diff --git a/widget/bin/colorwidget.ilk b/widget/bin/colorwidget.ilk new file mode 100644 index 0000000..03a59c5 Binary files /dev/null and b/widget/bin/colorwidget.ilk differ diff --git a/widget/bin/colorwidget.pdb b/widget/bin/colorwidget.pdb new file mode 100644 index 0000000..9d37a2a Binary files /dev/null and b/widget/bin/colorwidget.pdb differ diff --git a/widget/bin/framelesswidget.exe b/widget/bin/framelesswidget.exe new file mode 100644 index 0000000..1878fd1 Binary files /dev/null and b/widget/bin/framelesswidget.exe differ diff --git a/widget/bin/framelesswidget.ilk b/widget/bin/framelesswidget.ilk new file mode 100644 index 0000000..1fc9e36 Binary files /dev/null and b/widget/bin/framelesswidget.ilk differ diff --git a/widget/bin/framelesswidget.pdb b/widget/bin/framelesswidget.pdb new file mode 100644 index 0000000..ff4a007 Binary files /dev/null and b/widget/bin/framelesswidget.pdb differ diff --git a/widget/bin/gifwidget.exe b/widget/bin/gifwidget.exe new file mode 100644 index 0000000..72eb209 Binary files /dev/null and b/widget/bin/gifwidget.exe differ diff --git a/widget/bin/gifwidget.ilk b/widget/bin/gifwidget.ilk new file mode 100644 index 0000000..ca19738 Binary files /dev/null and b/widget/bin/gifwidget.ilk differ diff --git a/widget/bin/gifwidget.pdb b/widget/bin/gifwidget.pdb new file mode 100644 index 0000000..2632aa8 Binary files /dev/null and b/widget/bin/gifwidget.pdb differ diff --git a/widget/bin/lunarcalendarwidget.exe b/widget/bin/lunarcalendarwidget.exe new file mode 100644 index 0000000..796f2dc Binary files /dev/null and b/widget/bin/lunarcalendarwidget.exe differ diff --git a/widget/bin/lunarcalendarwidget.ilk b/widget/bin/lunarcalendarwidget.ilk new file mode 100644 index 0000000..8a4793a Binary files /dev/null and b/widget/bin/lunarcalendarwidget.ilk differ diff --git a/widget/bin/lunarcalendarwidget.pdb b/widget/bin/lunarcalendarwidget.pdb new file mode 100644 index 0000000..62921cf Binary files /dev/null and b/widget/bin/lunarcalendarwidget.pdb differ diff --git a/widget/bin/maskwidget.exe b/widget/bin/maskwidget.exe new file mode 100644 index 0000000..8cdf7c9 Binary files /dev/null and b/widget/bin/maskwidget.exe differ diff --git a/widget/bin/maskwidget.ilk b/widget/bin/maskwidget.ilk new file mode 100644 index 0000000..cbb6eea Binary files /dev/null and b/widget/bin/maskwidget.ilk differ diff --git a/widget/bin/maskwidget.pdb b/widget/bin/maskwidget.pdb new file mode 100644 index 0000000..666420e Binary files /dev/null and b/widget/bin/maskwidget.pdb differ diff --git a/widget/bin/movewidget.exe b/widget/bin/movewidget.exe new file mode 100644 index 0000000..7b62615 Binary files /dev/null and b/widget/bin/movewidget.exe differ diff --git a/widget/bin/movewidget.ilk b/widget/bin/movewidget.ilk new file mode 100644 index 0000000..f61fa40 Binary files /dev/null and b/widget/bin/movewidget.ilk differ diff --git a/widget/bin/movewidget.pdb b/widget/bin/movewidget.pdb new file mode 100644 index 0000000..1010d06 Binary files /dev/null and b/widget/bin/movewidget.pdb differ diff --git a/widget/bin/screenwidget.exe b/widget/bin/screenwidget.exe new file mode 100644 index 0000000..c92a1bb Binary files /dev/null and b/widget/bin/screenwidget.exe differ diff --git a/widget/bin/screenwidget.ilk b/widget/bin/screenwidget.ilk new file mode 100644 index 0000000..7853444 Binary files /dev/null and b/widget/bin/screenwidget.ilk differ diff --git a/widget/bin/screenwidget.pdb b/widget/bin/screenwidget.pdb new file mode 100644 index 0000000..04285ee Binary files /dev/null and b/widget/bin/screenwidget.pdb differ diff --git a/widget/colorwidget/colorwidget.cpp b/widget/colorwidget/colorwidget.cpp new file mode 100644 index 0000000..2f50abb --- /dev/null +++ b/widget/colorwidget/colorwidget.cpp @@ -0,0 +1,174 @@ +#pragma execution_character_set("utf-8") + +#include "colorwidget.h" +#include "qmutex.h" +#include "qgridlayout.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qapplication.h" +#include "qtimer.h" +#include "qevent.h" +#include "qdebug.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) +#include "qscreen.h" +#define deskGeometry qApp->primaryScreen()->geometry() +#define deskGeometry2 qApp->primaryScreen()->availableGeometry() +#else +#include "qdesktopwidget.h" +#define deskGeometry qApp->desktop()->geometry() +#define deskGeometry2 qApp->desktop()->availableGeometry() +#endif + +ColorWidget *ColorWidget::instance = 0; +ColorWidget *ColorWidget::Instance() +{ + if (!instance) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (!instance) { + instance = new ColorWidget; + } + } + + return instance; +} + +ColorWidget::ColorWidget(QWidget *parent) : QWidget(parent) +{ + gridLayout = new QGridLayout(this); + gridLayout->setSpacing(6); + gridLayout->setContentsMargins(11, 11, 11, 11); + + verticalLayout = new QVBoxLayout(); + verticalLayout->setSpacing(0); + + labColor = new QLabel(this); + labColor->setText("+"); + labColor->setStyleSheet("background-color: rgb(255, 107, 107);color: rgb(250, 250, 250);"); + labColor->setAlignment(Qt::AlignCenter); + QFont font; + font.setPixelSize(35); + font.setBold(true); + labColor->setFont(font); + + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(labColor->sizePolicy().hasHeightForWidth()); + labColor->setSizePolicy(sizePolicy); + labColor->setMinimumSize(QSize(80, 70)); + labColor->setMaximumSize(QSize(80, 70)); + labColor->setCursor(QCursor(Qt::CrossCursor)); + labColor->setFrameShape(QFrame::StyledPanel); + labColor->setFrameShadow(QFrame::Sunken); + + verticalLayout->addWidget(labColor); + + label = new QLabel(this); + label->setMinimumSize(QSize(0, 18)); + label->setStyleSheet("background-color: rgb(0, 0, 0);color: rgb(200, 200, 200);"); + label->setAlignment(Qt::AlignCenter); + + verticalLayout->addWidget(label); + gridLayout->addLayout(verticalLayout, 0, 0, 3, 1); + + labWeb = new QLabel(this); + gridLayout->addWidget(labWeb, 0, 1, 1, 1); + + txtWeb = new QLineEdit(this); + gridLayout->addWidget(txtWeb, 0, 2, 1, 1); + + labRgb = new QLabel(this); + gridLayout->addWidget(labRgb, 1, 1, 1, 1); + + txtRgb = new QLineEdit(this); + gridLayout->addWidget(txtRgb, 1, 2, 1, 1); + + labPoint = new QLabel(this); + gridLayout->addWidget(labPoint, 2, 1, 1, 1); + + txtPoint = new QLineEdit(this); + gridLayout->addWidget(txtPoint, 2, 2, 1, 1); + + label->setText("当前颜色"); + labWeb->setText("web值:"); + labRgb->setText("rgb值:"); + labPoint->setText("坐标值:"); + + this->setLayout(gridLayout); + this->setWindowTitle("屏幕拾色器"); + this->setFixedSize(300, 108); + + cp = QApplication::clipboard(); + pressed = false; + + timer = new QTimer(this); + timer->setInterval(100); + connect(timer, SIGNAL(timeout()), this, SLOT(showColorValue())); + timer->start(); +} + +ColorWidget::~ColorWidget() +{ +} + +void ColorWidget::mousePressEvent(QMouseEvent *e) +{ + if (labColor->rect().contains(e->pos())) { + pressed = true; + } +} + +void ColorWidget::mouseReleaseEvent(QMouseEvent *) +{ + pressed = false; +} + +void ColorWidget::showColorValue() +{ + if (!pressed) { + return; + } + + int x = QCursor::pos().x(); + int y = QCursor::pos().y(); + txtPoint->setText(tr("x:%1 y:%2").arg(x).arg(y)); + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QScreen *screen = qApp->primaryScreen(); + QPixmap pixmap = screen->grabWindow(0, x, y, 2, 2); +#else + QPixmap pixmap = QPixmap::grabWindow(qApp->desktop()->winId(), x, y, 2, 2); +#endif + + int red, green, blue; + QString strDecimalValue, strHex; + if (pixmap.isNull()) { + return; + } + + QImage image = pixmap.toImage(); + if (image.valid(0, 0)) { + QColor color = image.pixel(0, 0); + red = color.red(); + green = color.green(); + blue = color.blue(); + QString strRed = tr("%1").arg(red & 0xFF, 2, 16, QChar('0')); + QString strGreen = tr("%1").arg(green & 0xFF, 2, 16, QChar('0')); + QString strBlue = tr("%1").arg(blue & 0xFF, 2, 16, QChar('0')); + + strDecimalValue = tr("%1, %2, %3").arg(red).arg(green).arg(blue); + strHex = tr("#%1%2%3").arg(strRed.toUpper()).arg(strGreen.toUpper()).arg(strBlue.toUpper()); + } + + //根据背景色自动计算合适的前景色 + QColor color(red, green, blue); + double gray = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255; + QColor textColor = gray > 0.5 ? Qt::black : Qt::white; + + QString str = tr("background:rgb(%1);color:%2").arg(strDecimalValue).arg(textColor.name()); + labColor->setStyleSheet(str); + txtRgb->setText(strDecimalValue); + txtWeb->setText(strHex); +} diff --git a/widget/colorwidget/colorwidget.h b/widget/colorwidget/colorwidget.h new file mode 100644 index 0000000..374dbc2 --- /dev/null +++ b/widget/colorwidget/colorwidget.h @@ -0,0 +1,58 @@ +#ifndef COLORWIDGET_H +#define COLORWIDGET_H + +/** + * 屏幕拾色器 作者:feiyangqingyun(QQ:517216493) 2016-11-11 + * 1. 鼠标按下实时采集鼠标处的颜色。 + * 2. 实时显示颜色值。 + * 3. 支持16进制格式和rgb格式。 + * 4. 实时显示预览颜色。 + * 5. 根据背景色自动计算合适的前景色。 + */ + +#include + +class QGridLayout; +class QVBoxLayout; +class QLabel; +class QLineEdit; + +#ifdef quc +class Q_DECL_EXPORT ColorWidget : public QWidget +#else +class ColorWidget : public QWidget +#endif + +{ + Q_OBJECT +public: + static ColorWidget *Instance(); + explicit ColorWidget(QWidget *parent = 0); + ~ColorWidget(); + +protected: + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + +private: + static ColorWidget *instance; + QClipboard *cp; + bool pressed; + QTimer *timer; + + QGridLayout *gridLayout; + QVBoxLayout *verticalLayout; + QLabel *labColor; + QLabel *label; + QLabel *labWeb; + QLineEdit *txtWeb; + QLabel *labRgb; + QLineEdit *txtRgb; + QLabel *labPoint; + QLineEdit *txtPoint; + +private Q_SLOTS: + void showColorValue(); +}; + +#endif // COLORWIDGET_H diff --git a/widget/colorwidget/colorwidget.pro b/widget/colorwidget/colorwidget.pro new file mode 100644 index 0000000..1b3b62d --- /dev/null +++ b/widget/colorwidget/colorwidget.pro @@ -0,0 +1,17 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = colorwidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off + +SOURCES += main.cpp +SOURCES += frmcolorwidget.cpp +SOURCES += colorwidget.cpp + +HEADERS += colorwidget.h +HEADERS += frmcolorwidget.h + +FORMS += frmcolorwidget.ui diff --git a/widget/colorwidget/frmcolorwidget.cpp b/widget/colorwidget/frmcolorwidget.cpp new file mode 100644 index 0000000..20d56fa --- /dev/null +++ b/widget/colorwidget/frmcolorwidget.cpp @@ -0,0 +1,18 @@ +#include "frmcolorwidget.h" +#include "ui_frmcolorwidget.h" +#include "colorwidget.h" + +frmColorWidget::frmColorWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmColorWidget) +{ + ui->setupUi(this); +} + +frmColorWidget::~frmColorWidget() +{ + delete ui; +} + +void frmColorWidget::on_pushButton_clicked() +{ + ColorWidget::Instance()->show(); +} diff --git a/widget/colorwidget/frmcolorwidget.h b/widget/colorwidget/frmcolorwidget.h new file mode 100644 index 0000000..3412bab --- /dev/null +++ b/widget/colorwidget/frmcolorwidget.h @@ -0,0 +1,25 @@ +#ifndef FRMCOLORWIDGET_H +#define FRMCOLORWIDGET_H + +#include + +namespace Ui { +class frmColorWidget; +} + +class frmColorWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmColorWidget(QWidget *parent = 0); + ~frmColorWidget(); + +private slots: + void on_pushButton_clicked(); + +private: + Ui::frmColorWidget *ui; +}; + +#endif // FRMCOLORWIDGET_H diff --git a/widget/colorwidget/frmcolorwidget.ui b/widget/colorwidget/frmcolorwidget.ui new file mode 100644 index 0000000..cc237ab --- /dev/null +++ b/widget/colorwidget/frmcolorwidget.ui @@ -0,0 +1,32 @@ + + + frmColorWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + 10 + 10 + 92 + 28 + + + + 弹出 + + + + + + diff --git a/widget/colorwidget/main.cpp b/widget/colorwidget/main.cpp new file mode 100644 index 0000000..da06ead --- /dev/null +++ b/widget/colorwidget/main.cpp @@ -0,0 +1,34 @@ +#pragma execution_character_set("utf-8") + +#include "frmcolorwidget.h" +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmColorWidget w; + w.setWindowTitle("屏幕拾色器 (QQ: 517216493 WX: feiyangqingyun)"); + w.show(); + + return a.exec(); +} diff --git a/widget/framelesswidget/framelesscore/framelesscore.pri b/widget/framelesswidget/framelesscore/framelesscore.pri new file mode 100644 index 0000000..3662227 --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelesscore.pri @@ -0,0 +1,9 @@ +HEADERS += \ + $$PWD/framelessdialog.h \ + $$PWD/framelessmainwindow.h \ + $$PWD/framelesswidget.h + +SOURCES += \ + $$PWD/framelessdialog.cpp \ + $$PWD/framelessmainwindow.cpp \ + $$PWD/framelesswidget.cpp diff --git a/widget/framelesswidget/framelesscore/framelessdialog.cpp b/widget/framelesswidget/framelesscore/framelessdialog.cpp new file mode 100644 index 0000000..0e7d5b2 --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelessdialog.cpp @@ -0,0 +1,377 @@ +#include "framelessdialog.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qdebug.h" + +#ifdef Q_OS_WIN +#include "windows.h" +#include "windowsx.h" +#pragma comment (lib,"user32.lib") +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) + +FramelessDialog::FramelessDialog(QWidget *parent) : QDialog(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + isMin = false; + flags = this->windowFlags(); + titleBar = 0; + + //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的bug +#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0)) + this->setAttribute(Qt::WA_TranslucentBackground); +#endif + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + //安装事件过滤器识别拖动 + this->installEventFilter(this); + + //设置属性产生win窗体效果,移动到边缘半屏或者最大化等 + //设置以后会产生标题栏需要在下面拦截消息重新去掉 +#ifdef Q_OS_WIN + HWND hwnd = (HWND)this->winId(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif +} + +void FramelessDialog::showEvent(QShowEvent *event) +{ + //解决有时候窗体重新显示的时候假死不刷新的bug + setAttribute(Qt::WA_Mapped); + QDialog::showEvent(event); +} + +void FramelessDialog::doWindowStateChange(QEvent *event) +{ + //非最大化才能移动和拖动大小 + if (windowState() == Qt::WindowNoState) { + moveEnable = true; + resizeEnable = true; + } else { + moveEnable = false; + resizeEnable = false; + } + + //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字 + Q_EMIT windowStateChange(!moveEnable); + + //解决mac系统上无边框最小化失效的bug +#ifdef Q_OS_MACOS + if (windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + this->setVisible(true); + isMin = false; + } + } +#endif +} + +void FramelessDialog::doResizeEvent(QEvent *event) +{ + //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸 + //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的 +#ifndef Q_OS_WIN + if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = this->width(); + int height = this->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + this->move(this->x() + offsetX, this->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = this->width() - offsetX; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + this->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(rectX, this->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + this->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = this->width() - offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = this->width() - offsetX; + int resizeH = rectH + offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = this->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + this->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } +#endif +} + +bool FramelessDialog::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if (event->type() == QEvent::WindowStateChange) { + doWindowStateChange(event); + } else { + doResizeEvent(event); + } + } else if (watched == titleBar) { + //双击标题栏发出双击信号给主界面 + //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏 +#ifndef Q_OS_WIN + if (event->type() == QEvent::MouseButtonDblClick) { + Q_EMIT titleDblClick(); + } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + Q_EMIT titleDblClick(); + } +#endif + } + + return QDialog::eventFilter(watched, event); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +bool FramelessDialog::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool FramelessDialog::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + if (eventType == "windows_generic_MSG") { +#ifdef Q_OS_WIN + MSG *msg = static_cast(message); + //qDebug() << TIMEMS << "nativeEvent" << msg->wParam << msg->message; + + //不同的消息类型和参数进行不同的处理 + if (msg->message == WM_NCCALCSIZE) { + *result = 0; + return true; + } else if (msg->message == WM_SYSKEYDOWN) { + //屏蔽alt键按下 + } else if (msg->message == WM_SYSKEYUP) { + //屏蔽alt键松开 + } else if (msg->message == WM_NCHITTEST) { + //计算鼠标对应的屏幕坐标 + //这里最开始用的 LOWORD HIWORD 在多屏幕的时候会有问题 + //官方说明在这里 https://docs.microsoft.com/zh-cn/windows/win32/inputdev/wm-nchittest + long x = GET_X_LPARAM(msg->lParam); + long y = GET_Y_LPARAM(msg->lParam); + QPoint pos = mapFromGlobal(QPoint(x, y)); + + //判断当前鼠标位置在哪个区域 + bool left = pos.x() < padding; + bool right = pos.x() > width() - padding; + bool top = pos.y() < padding; + bool bottom = pos.y() > height() - padding; + + //鼠标移动到四个角,这个消息是当鼠标移动或者有鼠标键按下时候发出的 + *result = 0; + if (resizeEnable) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + + //先处理掉拉伸 + if (0 != *result) { + return true; + } + + //识别标题栏拖动产生半屏全屏效果 + if (titleBar && titleBar->rect().contains(pos)) { + QWidget *child = titleBar->childAt(pos); + if (!child) { + *result = HTCAPTION; + return true; + } + } + } else if (msg->wParam == PBT_APMSUSPEND && msg->message == WM_POWERBROADCAST) { + //系统休眠的时候自动最小化可以规避程序可能出现的问题 + this->showMinimized(); + } else if (msg->wParam == PBT_APMRESUMEAUTOMATIC) { + //休眠唤醒后自动打开 + this->showNormal(); + } +#endif + } else if (eventType == "NSEvent") { +#ifdef Q_OS_MACOS +#endif + } else if (eventType == "xcb_generic_event_t") { +#ifdef Q_OS_LINUX +#endif + } + return false; +} + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN +bool FramelessDialog::winEvent(MSG *message, long *result) +{ + return nativeEvent("windows_generic_MSG", message, result); +} +#endif +#endif + +void FramelessDialog::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessDialog::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessDialog::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessDialog::setTitleBar(QWidget *titleBar) +{ + this->titleBar = titleBar; + this->titleBar->installEventFilter(this); +} + + diff --git a/widget/framelesswidget/framelesscore/framelessdialog.h b/widget/framelesswidget/framelesscore/framelessdialog.h new file mode 100644 index 0000000..fd6d5fb --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelessdialog.h @@ -0,0 +1,96 @@ +#ifndef FRAMELESSDIALOG_H +#define FRAMELESSDIALOG_H + +/** + * 无边框窗体类 作者:feiyangqingyun(QQ:517216493) 2021-07-27 + * 1. 同时支持Qt4-Qt6,亲测Qt4.7到Qt6.2。 + * 2. 同时支持mingw、msvc、gcc等。 + * 3. 同时支持windows、linux、mac。 + * 4. 同时支持QMainWindow、QWidget、QDialog。 + * 5. 使用方法极其简单,只需要将继承类修改即可。 + * 6. 自动识别双击标题栏响应。 + * 7. 无边框拉伸在windows下不抖动。 + * 8. 在windows下具有移动到边缘半屏、移动到顶部全屏特性。 + * 9. 解决mac系统上无边框最小化最大化失效的bug。 + * 10. 解决系统休眠后再次启动程序懵逼的bug。 + * 11. 解决有时候窗体重新显示的时候假死不刷新的bug。 + * 12. 轻量级,1个代码文件,核心代码行数不到300行。 + * 13. 注释详细,示例完美,非常适合阅读和学习。 + * 14. 开源开箱即用,保证任意Qt版本可正常编译运行,无需任何调整。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT FramelessDialog : public QDialog +#else +class FramelessDialog : public QDialog +#endif + +{ + Q_OBJECT +public: + explicit FramelessDialog(QWidget *parent = 0); + +protected: + //窗体显示的时候触发 + void showEvent(QShowEvent *event); + + //事件过滤器识别拖动拉伸等 + void doWindowStateChange(QEvent *event); + void doResizeEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + + //拦截系统事件用于修复系统休眠后唤醒程序的bug +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#else + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif + + //Qt4的写法 +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN + bool winEvent(MSG *message, long *result); +#endif +#endif + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //标题栏控件 + QWidget *titleBar; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public Q_SLOTS: + //设置边距+可拖动+可拉伸 + void setPadding(int padding); + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + + //设置标题栏控件 + void setTitleBar(QWidget *titleBar); + +Q_SIGNALS: + void titleDblClick(); + void windowStateChange(bool max); +}; + +#endif // FRAMELESSDIALOG_H diff --git a/widget/framelesswidget/framelesscore/framelessmainwindow.cpp b/widget/framelesswidget/framelesscore/framelessmainwindow.cpp new file mode 100644 index 0000000..967922f --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelessmainwindow.cpp @@ -0,0 +1,375 @@ +#include "framelessmainwindow.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qdebug.h" + +#ifdef Q_OS_WIN +#include "windows.h" +#include "windowsx.h" +#pragma comment (lib,"user32.lib") +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) + +FramelessMainWindow::FramelessMainWindow(QWidget *parent) : QMainWindow(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + isMin = false; + flags = this->windowFlags(); + titleBar = 0; + + //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的bug +#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0)) + this->setAttribute(Qt::WA_TranslucentBackground); +#endif + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + //安装事件过滤器识别拖动 + this->installEventFilter(this); + + //设置属性产生win窗体效果,移动到边缘半屏或者最大化等 + //设置以后会产生标题栏,需要在下面拦截消息WM_NCCALCSIZE重新去掉 +#ifdef Q_OS_WIN + HWND hwnd = (HWND)this->winId(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif +} + +void FramelessMainWindow::showEvent(QShowEvent *event) +{ + //解决有时候窗体重新显示的时候假死不刷新的bug + setAttribute(Qt::WA_Mapped); + QMainWindow::showEvent(event); +} + +void FramelessMainWindow::doWindowStateChange(QEvent *event) +{ + //非最大化才能移动和拖动大小 + if (windowState() == Qt::WindowNoState) { + moveEnable = true; + resizeEnable = true; + } else { + moveEnable = false; + resizeEnable = false; + } + + //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字 + Q_EMIT windowStateChange(!moveEnable); + + //解决mac系统上无边框最小化失效的bug +#ifdef Q_OS_MACOS + if (windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + this->setVisible(true); + isMin = false; + } + } +#endif +} + +void FramelessMainWindow::doResizeEvent(QEvent *event) +{ + //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸 + //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的 +#ifndef Q_OS_WIN + if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = this->width(); + int height = this->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + this->move(this->x() + offsetX, this->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = this->width() - offsetX; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + this->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(rectX, this->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + this->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = this->width() - offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = this->width() - offsetX; + int resizeH = rectH + offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = this->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + this->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } +#endif +} + +bool FramelessMainWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if (event->type() == QEvent::WindowStateChange) { + doWindowStateChange(event); + } else { + doResizeEvent(event); + } + } else if (watched == titleBar) { + //双击标题栏发出双击信号给主界面 + //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏 +#ifndef Q_OS_WIN + if (event->type() == QEvent::MouseButtonDblClick) { + Q_EMIT titleDblClick(); + } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + Q_EMIT titleDblClick(); + } +#endif + } + + return QMainWindow::eventFilter(watched, event); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +bool FramelessMainWindow::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool FramelessMainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + if (eventType == "windows_generic_MSG") { +#ifdef Q_OS_WIN + MSG *msg = static_cast(message); + //qDebug() << TIMEMS << "nativeEvent" << msg->wParam << msg->message; + + //不同的消息类型和参数进行不同的处理 + if (msg->message == WM_NCCALCSIZE) { + *result = 0; + return true; + } else if (msg->message == WM_SYSKEYDOWN) { + //屏蔽alt键按下 + } else if (msg->message == WM_SYSKEYUP) { + //屏蔽alt键松开 + } else if (msg->message == WM_NCHITTEST) { + //计算鼠标对应的屏幕坐标 + //这里最开始用的 LOWORD HIWORD 在多屏幕的时候会有问题 + //官方说明在这里 https://docs.microsoft.com/zh-cn/windows/win32/inputdev/wm-nchittest + long x = GET_X_LPARAM(msg->lParam); + long y = GET_Y_LPARAM(msg->lParam); + QPoint pos = mapFromGlobal(QPoint(x, y)); + + //判断当前鼠标位置在哪个区域 + bool left = pos.x() < padding; + bool right = pos.x() > width() - padding; + bool top = pos.y() < padding; + bool bottom = pos.y() > height() - padding; + + //鼠标移动到四个角,这个消息是当鼠标移动或者有鼠标键按下时候发出的 + *result = 0; + if (resizeEnable) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + + //先处理掉拉伸 + if (0 != *result) { + return true; + } + + //识别标题栏拖动产生半屏全屏效果 + if (titleBar && titleBar->rect().contains(pos)) { + QWidget *child = titleBar->childAt(pos); + if (!child) { + *result = HTCAPTION; + return true; + } + } + } else if (msg->wParam == PBT_APMSUSPEND && msg->message == WM_POWERBROADCAST) { + //系统休眠的时候自动最小化可以规避程序可能出现的问题 + this->showMinimized(); + } else if (msg->wParam == PBT_APMRESUMEAUTOMATIC) { + //休眠唤醒后自动打开 + this->showNormal(); + } +#endif + } else if (eventType == "NSEvent") { +#ifdef Q_OS_MACOS +#endif + } else if (eventType == "xcb_generic_event_t") { +#ifdef Q_OS_LINUX +#endif + } + return false; +} + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN +bool FramelessMainWindow::winEvent(MSG *message, long *result) +{ + return nativeEvent("windows_generic_MSG", message, result); +} +#endif +#endif + +void FramelessMainWindow::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessMainWindow::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessMainWindow::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessMainWindow::setTitleBar(QWidget *titleBar) +{ + this->titleBar = titleBar; + this->titleBar->installEventFilter(this); +} diff --git a/widget/framelesswidget/framelesscore/framelessmainwindow.h b/widget/framelesswidget/framelesscore/framelessmainwindow.h new file mode 100644 index 0000000..a069705 --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelessmainwindow.h @@ -0,0 +1,96 @@ +#ifndef FRAMELESSMAINWINDOW_H +#define FRAMELESSMAINWINDOW_H + +/** + * 无边框窗体类 作者:feiyangqingyun(QQ:517216493) 2021-07-27 + * 1. 同时支持Qt4-Qt6,亲测Qt4.7到Qt6.2。 + * 2. 同时支持mingw、msvc、gcc等。 + * 3. 同时支持windows、linux、mac。 + * 4. 同时支持QMainWindow、QWidget、QDialog。 + * 5. 使用方法极其简单,只需要将继承类修改即可。 + * 6. 自动识别双击标题栏响应。 + * 7. 无边框拉伸在windows下不抖动。 + * 8. 在windows下具有移动到边缘半屏、移动到顶部全屏特性。 + * 9. 解决mac系统上无边框最小化最大化失效的bug。 + * 10. 解决系统休眠后再次启动程序懵逼的bug。 + * 11. 解决有时候窗体重新显示的时候假死不刷新的bug。 + * 12. 轻量级,1个代码文件,核心代码行数不到300行。 + * 13. 注释详细,示例完美,非常适合阅读和学习。 + * 14. 开源开箱即用,保证任意Qt版本可正常编译运行,无需任何调整。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT FramelessMainWindow : public QMainWindow +#else +class FramelessMainWindow : public QMainWindow +#endif + +{ + Q_OBJECT +public: + explicit FramelessMainWindow(QWidget *parent = 0); + +protected: + //窗体显示的时候触发 + void showEvent(QShowEvent *event); + + //事件过滤器识别拖动拉伸等 + void doWindowStateChange(QEvent *event); + void doResizeEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + + //拦截系统事件用于修复系统休眠后唤醒程序的bug +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#else + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif + + //Qt4的写法 +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN + bool winEvent(MSG *message, long *result); +#endif +#endif + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //标题栏控件 + QWidget *titleBar; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public Q_SLOTS: + //设置边距+可拖动+可拉伸 + void setPadding(int padding); + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + + //设置标题栏控件 + void setTitleBar(QWidget *titleBar); + +Q_SIGNALS: + void titleDblClick(); + void windowStateChange(bool max); +}; + +#endif // FRAMELESSMAINWINDOW_H diff --git a/widget/framelesswidget/framelesscore/framelesswidget.cpp b/widget/framelesswidget/framelesscore/framelesswidget.cpp new file mode 100644 index 0000000..a27a832 --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelesswidget.cpp @@ -0,0 +1,377 @@ +#include "framelesswidget.h" +#include "qdatetime.h" +#include "qevent.h" +#include "qdebug.h" + +#ifdef Q_OS_WIN +#include "windows.h" +#include "windowsx.h" +#pragma comment (lib,"user32.lib") +#endif + +#define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz")) + +FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + isMin = false; + flags = this->windowFlags(); + titleBar = 0; + + //设置背景透明 官方在5.3以后才彻底修复 WA_TranslucentBackground+FramelessWindowHint 并存不绘制的bug +#if (QT_VERSION >= QT_VERSION_CHECK(5,3,0)) + this->setAttribute(Qt::WA_TranslucentBackground); +#endif + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + //安装事件过滤器识别拖动 + this->installEventFilter(this); + + //设置属性产生win窗体效果,移动到边缘半屏或者最大化等 + //设置以后会产生标题栏需要在下面拦截消息重新去掉 +#ifdef Q_OS_WIN + HWND hwnd = (HWND)this->winId(); + DWORD style = ::GetWindowLong(hwnd, GWL_STYLE); + ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION); +#endif +} + +void FramelessWidget::showEvent(QShowEvent *event) +{ + //解决有时候窗体重新显示的时候假死不刷新的bug + setAttribute(Qt::WA_Mapped); + QWidget::showEvent(event); +} + +void FramelessWidget::doWindowStateChange(QEvent *event) +{ + //非最大化才能移动和拖动大小 + if (windowState() == Qt::WindowNoState) { + moveEnable = true; + resizeEnable = true; + } else { + moveEnable = false; + resizeEnable = false; + } + + //发出最大化最小化等改变事件,以便界面上更改对应的信息比如右上角图标和文字 + Q_EMIT windowStateChange(!moveEnable); + + //解决mac系统上无边框最小化失效的bug +#ifdef Q_OS_MACOS + if (windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + this->setWindowFlags(flags | Qt::FramelessWindowHint); + this->setVisible(true); + isMin = false; + } + } +#endif +} + +void FramelessWidget::doResizeEvent(QEvent *event) +{ + //非win系统的无边框拉伸,win系统上已经采用了nativeEvent来处理拉伸 + //为何不统一用计算的方式因为在win上用这个方式往左拉伸会发抖妹的 +#ifndef Q_OS_WIN + if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = this->width(); + int height = this->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + this->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + this->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + this->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + this->setCursor(Qt::SizeFDiagCursor); + } else { + this->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + this->move(this->x() + offsetX, this->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = this->width() - offsetX; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + this->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(rectX, this->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + this->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = this->width() - offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = this->height() - offsetY; + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = this->width() - offsetX; + int resizeH = rectH + offsetY; + if (this->minimumWidth() <= resizeW) { + this->setGeometry(this->x() + offsetX, this->y(), resizeW, resizeH); + } + if (this->minimumHeight() <= resizeH) { + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + this->setGeometry(this->x(), this->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = this->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + this->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } +#endif +} + +bool FramelessWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == this) { + if (event->type() == QEvent::WindowStateChange) { + doWindowStateChange(event); + } else { + doResizeEvent(event); + } + } else if (watched == titleBar) { + //双击标题栏发出双击信号给主界面 + //下面的 *result = HTCAPTION; 标志位也会自动识别双击标题栏 +#ifndef Q_OS_WIN + if (event->type() == QEvent::MouseButtonDblClick) { + Q_EMIT titleDblClick(); + } else if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) { + Q_EMIT titleDblClick(); + } +#endif + } + + return QWidget::eventFilter(watched, event); +} + +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) +bool FramelessWidget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result) +#else +bool FramelessWidget::nativeEvent(const QByteArray &eventType, void *message, long *result) +#endif +{ + if (eventType == "windows_generic_MSG") { +#ifdef Q_OS_WIN + MSG *msg = static_cast(message); + //qDebug() << TIMEMS << "nativeEvent" << msg->wParam << msg->message; + + //不同的消息类型和参数进行不同的处理 + if (msg->message == WM_NCCALCSIZE) { + *result = 0; + return true; + } else if (msg->message == WM_SYSKEYDOWN) { + //屏蔽alt键按下 + } else if (msg->message == WM_SYSKEYUP) { + //屏蔽alt键松开 + } else if (msg->message == WM_NCHITTEST) { + //计算鼠标对应的屏幕坐标 + //这里最开始用的 LOWORD HIWORD 在多屏幕的时候会有问题 + //官方说明在这里 https://docs.microsoft.com/zh-cn/windows/win32/inputdev/wm-nchittest + long x = GET_X_LPARAM(msg->lParam); + long y = GET_Y_LPARAM(msg->lParam); + QPoint pos = mapFromGlobal(QPoint(x, y)); + + //判断当前鼠标位置在哪个区域 + bool left = pos.x() < padding; + bool right = pos.x() > width() - padding; + bool top = pos.y() < padding; + bool bottom = pos.y() > height() - padding; + + //鼠标移动到四个角,这个消息是当鼠标移动或者有鼠标键按下时候发出的 + *result = 0; + if (resizeEnable) { + if (left && top) { + *result = HTTOPLEFT; + } else if (left && bottom) { + *result = HTBOTTOMLEFT; + } else if (right && top) { + *result = HTTOPRIGHT; + } else if (right && bottom) { + *result = HTBOTTOMRIGHT; + } else if (left) { + *result = HTLEFT; + } else if (right) { + *result = HTRIGHT; + } else if (top) { + *result = HTTOP; + } else if (bottom) { + *result = HTBOTTOM; + } + } + + //先处理掉拉伸 + if (0 != *result) { + return true; + } + + //识别标题栏拖动产生半屏全屏效果 + if (titleBar && titleBar->rect().contains(pos)) { + QWidget *child = titleBar->childAt(pos); + if (!child) { + *result = HTCAPTION; + return true; + } + } + } else if (msg->wParam == PBT_APMSUSPEND && msg->message == WM_POWERBROADCAST) { + //系统休眠的时候自动最小化可以规避程序可能出现的问题 + this->showMinimized(); + } else if (msg->wParam == PBT_APMRESUMEAUTOMATIC) { + //休眠唤醒后自动打开 + this->showNormal(); + } +#endif + } else if (eventType == "NSEvent") { +#ifdef Q_OS_MACOS +#endif + } else if (eventType == "xcb_generic_event_t") { +#ifdef Q_OS_LINUX +#endif + } + return false; +} + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN +bool FramelessWidget::winEvent(MSG *message, long *result) +{ + return nativeEvent("windows_generic_MSG", message, result); +} +#endif +#endif + +void FramelessWidget::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessWidget::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessWidget::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessWidget::setTitleBar(QWidget *titleBar) +{ + this->titleBar = titleBar; + this->titleBar->installEventFilter(this); +} + + diff --git a/widget/framelesswidget/framelesscore/framelesswidget.h b/widget/framelesswidget/framelesscore/framelesswidget.h new file mode 100644 index 0000000..6494fc9 --- /dev/null +++ b/widget/framelesswidget/framelesscore/framelesswidget.h @@ -0,0 +1,96 @@ +#ifndef FRAMELESSWIDGET_H +#define FRAMELESSWIDGET_H + +/** + * 无边框窗体类 作者:feiyangqingyun(QQ:517216493) 2021-07-27 + * 1. 同时支持Qt4-Qt6,亲测Qt4.7到Qt6.2。 + * 2. 同时支持mingw、msvc、gcc等。 + * 3. 同时支持windows、linux、mac。 + * 4. 同时支持QMainWindow、QWidget、QDialog。 + * 5. 使用方法极其简单,只需要将继承类修改即可。 + * 6. 自动识别双击标题栏响应。 + * 7. 无边框拉伸在windows下不抖动。 + * 8. 在windows下具有移动到边缘半屏、移动到顶部全屏特性。 + * 9. 解决mac系统上无边框最小化最大化失效的bug。 + * 10. 解决系统休眠后再次启动程序懵逼的bug。 + * 11. 解决有时候窗体重新显示的时候假死不刷新的bug。 + * 12. 轻量级,1个代码文件,核心代码行数不到300行。 + * 13. 注释详细,示例完美,非常适合阅读和学习。 + * 14. 开源开箱即用,保证任意Qt版本可正常编译运行,无需任何调整。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT FramelessWidget : public QWidget +#else +class FramelessWidget : public QWidget +#endif + +{ + Q_OBJECT +public: + explicit FramelessWidget(QWidget *parent = 0); + +protected: + //窗体显示的时候触发 + void showEvent(QShowEvent *event); + + //事件过滤器识别拖动拉伸等 + void doWindowStateChange(QEvent *event); + void doResizeEvent(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + + //拦截系统事件用于修复系统休眠后唤醒程序的bug +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result); +#else + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#endif + + //Qt4的写法 +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#ifdef Q_OS_WIN + bool winEvent(MSG *message, long *result); +#endif +#endif + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //标题栏控件 + QWidget *titleBar; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public Q_SLOTS: + //设置边距+可拖动+可拉伸 + void setPadding(int padding); + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + + //设置标题栏控件 + void setTitleBar(QWidget *titleBar); + +Q_SIGNALS: + void titleDblClick(); + void windowStateChange(bool max); +}; + +#endif // FRAMELESSWIDGET_H diff --git a/widget/framelesswidget/framelessform/dialog.cpp b/widget/framelesswidget/framelessform/dialog.cpp new file mode 100644 index 0000000..037cb9f --- /dev/null +++ b/widget/framelesswidget/framelessform/dialog.cpp @@ -0,0 +1,69 @@ +#include "dialog.h" +#include "ui_dialog.h" + +#pragma execution_character_set("utf-8") +Dialog::Dialog(QWidget *parent) : FramelessDialog(parent), ui(new Ui::Dialog) +{ + ui->setupUi(this); + this->initForm(); +} + +Dialog::~Dialog() +{ + delete ui; +} + +void Dialog::initForm() +{ + //设置标题栏控件 + ui->labTitle->setText("无边框窗体示例-支持win、linux、mac等系统 (QQ: 517216493 WX: feiyangqingyun)"); + this->setWindowTitle(ui->labTitle->text()); + this->setTitleBar(ui->labTitle); + + //关联信号 + connect(this, SIGNAL(titleDblClick()), this, SLOT(titleDblClick())); + connect(this, SIGNAL(windowStateChange(bool)), this, SLOT(windowStateChange(bool))); + + //设置样式表 + QStringList list; + list << "#titleBar{background:#BBBBBB;}"; + list << "#titleBar{border-top-left-radius:8px;border-top-right-radius:8px;}"; + list << "#widgetMain{border:2px solid #BBBBBB;background:#FFFFFF;}"; + //list << "#widgetMain{border-bottom-left-radius:8px;border-bottom-right-radius:8px;}"; + this->setStyleSheet(list.join("")); +} + +void Dialog::titleDblClick() +{ + on_btnMenu_Max_clicked(); +} + +void Dialog::windowStateChange(bool max) +{ + ui->btnMenu_Max->setText(max ? "还原" : "最大"); +} + +void Dialog::on_btnMenu_Min_clicked() +{ +#ifdef Q_OS_MACOS + this->setWindowFlags(this->windowFlags() & ~Qt::FramelessWindowHint); +#endif + this->showMinimized(); +} + +void Dialog::on_btnMenu_Max_clicked() +{ + if (this->isMaximized()) { + this->showNormal(); + ui->btnMenu_Max->setText("最大"); + } else { + this->showMaximized(); + ui->btnMenu_Max->setText("还原"); + } +} + +void Dialog::on_btnMenu_Close_clicked() +{ + this->close(); +} + diff --git a/widget/framelesswidget/framelessform/dialog.h b/widget/framelesswidget/framelessform/dialog.h new file mode 100644 index 0000000..d87d2af --- /dev/null +++ b/widget/framelesswidget/framelessform/dialog.h @@ -0,0 +1,32 @@ +#ifndef DIALOG_H +#define DIALOG_H + +#include "framelessdialog.h" + +namespace Ui { +class Dialog; +} + +class Dialog : public FramelessDialog +{ + Q_OBJECT + +public: + explicit Dialog(QWidget *parent = 0); + ~Dialog(); + +private: + Ui::Dialog *ui; + +private slots: + void initForm(); + void titleDblClick(); + void windowStateChange(bool max); + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); +}; + +#endif // DIALOG_H diff --git a/widget/framelesswidget/framelessform/dialog.ui b/widget/framelesswidget/framelessform/dialog.ui new file mode 100644 index 0000000..574cc27 --- /dev/null +++ b/widget/framelesswidget/framelessform/dialog.ui @@ -0,0 +1,223 @@ + + + Dialog + + + + 0 + 0 + 800 + 600 + + + + Dialog + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16777215 + 28 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 最小化 + + + 最小 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Qt::NoFocus + + + 最大 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 关闭 + + + 关闭 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + FramelessDialog + QWidget +
framelessdialog.h
+ 1 +
+
+ + +
diff --git a/widget/framelesswidget/framelessform/framelessform.pri b/widget/framelesswidget/framelessform/framelessform.pri new file mode 100644 index 0000000..2307635 --- /dev/null +++ b/widget/framelesswidget/framelessform/framelessform.pri @@ -0,0 +1,14 @@ +FORMS += \ + $$PWD/dialog.ui \ + $$PWD/mainwindow.ui \ + $$PWD/widget.ui + +HEADERS += \ + $$PWD/dialog.h \ + $$PWD/mainwindow.h \ + $$PWD/widget.h + +SOURCES += \ + $$PWD/dialog.cpp \ + $$PWD/mainwindow.cpp \ + $$PWD/widget.cpp diff --git a/widget/framelesswidget/framelessform/mainwindow.cpp b/widget/framelesswidget/framelessform/mainwindow.cpp new file mode 100644 index 0000000..2c52138 --- /dev/null +++ b/widget/framelesswidget/framelessform/mainwindow.cpp @@ -0,0 +1,68 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#pragma execution_character_set("utf-8") +MainWindow::MainWindow(QWidget *parent) : FramelessMainWindow(parent), ui(new Ui::MainWindow) +{ + ui->setupUi(this); + this->initForm(); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::initForm() +{ + //设置标题栏控件 + ui->labTitle->setText("无边框窗体示例-支持win、linux、mac等系统 (QQ: 517216493 WX: feiyangqingyun)"); + this->setWindowTitle(ui->labTitle->text()); + this->setTitleBar(ui->labTitle); + + //关联信号 + connect(this, SIGNAL(titleDblClick()), this, SLOT(titleDblClick())); + connect(this, SIGNAL(windowStateChange(bool)), this, SLOT(windowStateChange(bool))); + + //设置样式表 + QStringList list; + list << "#titleBar{background:#BBBBBB;}"; + list << "#titleBar{border-top-left-radius:8px;border-top-right-radius:8px;}"; + list << "#widgetMain{border:2px solid #BBBBBB;background:#FFFFFF;}"; + //list << "#widgetMain{border-bottom-left-radius:8px;border-bottom-right-radius:8px;}"; + this->setStyleSheet(list.join("")); +} + +void MainWindow::titleDblClick() +{ + on_btnMenu_Max_clicked(); +} + +void MainWindow::windowStateChange(bool max) +{ + ui->btnMenu_Max->setText(max ? "还原" : "最大"); +} + +void MainWindow::on_btnMenu_Min_clicked() +{ +#ifdef Q_OS_MACOS + this->setWindowFlags(this->windowFlags() & ~Qt::FramelessWindowHint); +#endif + this->showMinimized(); +} + +void MainWindow::on_btnMenu_Max_clicked() +{ + if (this->isMaximized()) { + this->showNormal(); + ui->btnMenu_Max->setText("最大"); + } else { + this->showMaximized(); + ui->btnMenu_Max->setText("还原"); + } +} + +void MainWindow::on_btnMenu_Close_clicked() +{ + this->close(); +} diff --git a/widget/framelesswidget/framelessform/mainwindow.h b/widget/framelesswidget/framelessform/mainwindow.h new file mode 100644 index 0000000..676af4f --- /dev/null +++ b/widget/framelesswidget/framelessform/mainwindow.h @@ -0,0 +1,32 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "framelessmainwindow.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public FramelessMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + Ui::MainWindow *ui; + +private slots: + void initForm(); + void titleDblClick(); + void windowStateChange(bool max); + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); +}; + +#endif // MAINWINDOW_H diff --git a/widget/framelesswidget/framelessform/mainwindow.ui b/widget/framelesswidget/framelessform/mainwindow.ui new file mode 100644 index 0000000..29516ab --- /dev/null +++ b/widget/framelesswidget/framelessform/mainwindow.ui @@ -0,0 +1,233 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + 0 + 0 + 207 + 59 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16777215 + 28 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 最小化 + + + 最小 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Qt::NoFocus + + + 最大 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 关闭 + + + 关闭 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + FramelessMainWindow + QWidget +
framelessmainwindow.h
+ 1 +
+
+ + +
diff --git a/widget/framelesswidget/framelessform/widget.cpp b/widget/framelesswidget/framelessform/widget.cpp new file mode 100644 index 0000000..724aab3 --- /dev/null +++ b/widget/framelesswidget/framelessform/widget.cpp @@ -0,0 +1,68 @@ +#include "widget.h" +#include "ui_widget.h" + +#pragma execution_character_set("utf-8") +Widget::Widget(QWidget *parent) : FramelessWidget(parent), ui(new Ui::Widget) +{ + ui->setupUi(this); + this->initForm(); +} + +Widget::~Widget() +{ + delete ui; +} + +void Widget::initForm() +{ + //设置标题栏控件 + ui->labTitle->setText("无边框窗体示例-支持win、linux、mac等系统 (QQ: 517216493 WX: feiyangqingyun)"); + this->setWindowTitle(ui->labTitle->text()); + this->setTitleBar(ui->labTitle); + + //关联信号 + connect(this, SIGNAL(titleDblClick()), this, SLOT(titleDblClick())); + connect(this, SIGNAL(windowStateChange(bool)), this, SLOT(windowStateChange(bool))); + + //设置样式表 + QStringList list; + list << "#titleBar{background:#BBBBBB;}"; + list << "#titleBar{border-top-left-radius:8px;border-top-right-radius:8px;}"; + list << "#widgetMain{border:2px solid #BBBBBB;background:#FFFFFF;}"; + //list << "#widgetMain{border-bottom-left-radius:8px;border-bottom-right-radius:8px;}"; + this->setStyleSheet(list.join("")); +} + +void Widget::titleDblClick() +{ + on_btnMenu_Max_clicked(); +} + +void Widget::windowStateChange(bool max) +{ + ui->btnMenu_Max->setText(max ? "还原" : "最大"); +} + +void Widget::on_btnMenu_Min_clicked() +{ +#ifdef Q_OS_MACOS + this->setWindowFlags(this->windowFlags() & ~Qt::FramelessWindowHint); +#endif + this->showMinimized(); +} + +void Widget::on_btnMenu_Max_clicked() +{ + if (this->isMaximized()) { + this->showNormal(); + ui->btnMenu_Max->setText("最大"); + } else { + this->showMaximized(); + ui->btnMenu_Max->setText("还原"); + } +} + +void Widget::on_btnMenu_Close_clicked() +{ + this->close(); +} diff --git a/widget/framelesswidget/framelessform/widget.h b/widget/framelesswidget/framelessform/widget.h new file mode 100644 index 0000000..39e3a64 --- /dev/null +++ b/widget/framelesswidget/framelessform/widget.h @@ -0,0 +1,32 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include "framelesswidget.h" + +namespace Ui { +class Widget; +} + +class Widget : public FramelessWidget +{ + Q_OBJECT + +public: + explicit Widget(QWidget *parent = 0); + ~Widget(); + +private: + Ui::Widget *ui; + +private slots: + void initForm(); + void titleDblClick(); + void windowStateChange(bool max); + +private slots: + void on_btnMenu_Min_clicked(); + void on_btnMenu_Max_clicked(); + void on_btnMenu_Close_clicked(); +}; + +#endif // WIDGET_H diff --git a/widget/framelesswidget/framelessform/widget.ui b/widget/framelesswidget/framelessform/widget.ui new file mode 100644 index 0000000..52428dc --- /dev/null +++ b/widget/framelesswidget/framelessform/widget.ui @@ -0,0 +1,223 @@ + + + Widget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16777215 + 28 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 最小化 + + + 最小 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Qt::NoFocus + + + 最大 + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + ArrowCursor + + + Qt::NoFocus + + + 关闭 + + + 关闭 + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + FramelessWidget + QWidget +
framelesswidget.h
+ 1 +
+
+ + +
diff --git a/widget/framelesswidget/framelesswidget.pro b/widget/framelesswidget/framelesswidget.pro new file mode 100644 index 0000000..afce1dc --- /dev/null +++ b/widget/framelesswidget/framelesswidget.pro @@ -0,0 +1,23 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = framelesswidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off + +SOURCES += main.cpp +SOURCES += frmframelesswidget.cpp +SOURCES += framelesswidget2.cpp + +HEADERS += frmframelesswidget.h +HEADERS += framelesswidget2.h + +FORMS += frmframelesswidget.ui + +INCLUDEPATH += $$PWD/framelesscore +INCLUDEPATH += $$PWD/framelessform + +include ($$PWD/framelesscore/framelesscore.pri) +include ($$PWD/framelessform/framelessform.pri) diff --git a/widget/framelesswidget/framelesswidget2.cpp b/widget/framelesswidget/framelesswidget2.cpp new file mode 100644 index 0000000..9939c5d --- /dev/null +++ b/widget/framelesswidget/framelesswidget2.cpp @@ -0,0 +1,226 @@ +#include "framelesswidget2.h" +#include "qevent.h" +#include "qdebug.h" + +FramelessWidget2::FramelessWidget2(QObject *parent) : QObject(parent) +{ + padding = 8; + moveEnable = true; + resizeEnable = true; + widget = 0; + + mousePressed = false; + mousePoint = QPoint(0, 0); + mouseRect = QRect(0, 0, 0, 0); + + for (int i = 0; i < 8; ++i) { + pressedArea << false; + pressedRect << QRect(0, 0, 0, 0); + } + + //如果父类是窗体则直接设置 + if (parent->isWidgetType()) { + setWidget((QWidget *)parent); + } +} + +bool FramelessWidget2::eventFilter(QObject *watched, QEvent *event) +{ + if (widget && watched == widget) { + if (event->type() == QEvent::WindowStateChange) { + //解决mac系统上无边框最小化失效的bug +#ifdef Q_OS_MACOS + if (widget->windowState() & Qt::WindowMinimized) { + isMin = true; + } else { + if (isMin) { + //设置无边框属性 + widget->setWindowFlags(flags | Qt::FramelessWindowHint); + widget->setVisible(true); + isMin = false; + } + } +#endif + } else if (event->type() == QEvent::Resize) { + //重新计算八个描点的区域,描点区域的作用还有就是计算鼠标坐标是否在某一个区域内 + int width = widget->width(); + int height = widget->height(); + + //左侧描点区域 + pressedRect[0] = QRect(0, padding, padding, height - padding * 2); + //右侧描点区域 + pressedRect[1] = QRect(width - padding, padding, padding, height - padding * 2); + //上侧描点区域 + pressedRect[2] = QRect(padding, 0, width - padding * 2, padding); + //下侧描点区域 + pressedRect[3] = QRect(padding, height - padding, width - padding * 2, padding); + + //左上角描点区域 + pressedRect[4] = QRect(0, 0, padding, padding); + //右上角描点区域 + pressedRect[5] = QRect(width - padding, 0, padding, padding); + //左下角描点区域 + pressedRect[6] = QRect(0, height - padding, padding, padding); + //右下角描点区域 + pressedRect[7] = QRect(width - padding, height - padding, padding, padding); + } else if (event->type() == QEvent::HoverMove) { + //设置对应鼠标形状,这个必须放在这里而不是下面,因为可以在鼠标没有按下的时候识别 + QHoverEvent *hoverEvent = (QHoverEvent *)event; + QPoint point = hoverEvent->pos(); + if (resizeEnable) { + if (pressedRect.at(0).contains(point)) { + widget->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(1).contains(point)) { + widget->setCursor(Qt::SizeHorCursor); + } else if (pressedRect.at(2).contains(point)) { + widget->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(3).contains(point)) { + widget->setCursor(Qt::SizeVerCursor); + } else if (pressedRect.at(4).contains(point)) { + widget->setCursor(Qt::SizeFDiagCursor); + } else if (pressedRect.at(5).contains(point)) { + widget->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(6).contains(point)) { + widget->setCursor(Qt::SizeBDiagCursor); + } else if (pressedRect.at(7).contains(point)) { + widget->setCursor(Qt::SizeFDiagCursor); + } else { + widget->setCursor(Qt::ArrowCursor); + } + } + + //根据当前鼠标位置,计算XY轴移动了多少 + int offsetX = point.x() - mousePoint.x(); + int offsetY = point.y() - mousePoint.y(); + + //根据按下处的位置判断是否是移动控件还是拉伸控件 + if (moveEnable && mousePressed) { + widget->move(widget->x() + offsetX, widget->y() + offsetY); + } + + if (resizeEnable) { + int rectX = mouseRect.x(); + int rectY = mouseRect.y(); + int rectW = mouseRect.width(); + int rectH = mouseRect.height(); + + if (pressedArea.at(0)) { + int resizeW = widget->width() - offsetX; + if (widget->minimumWidth() <= resizeW) { + widget->setGeometry(widget->x() + offsetX, rectY, resizeW, rectH); + } + } else if (pressedArea.at(1)) { + widget->setGeometry(rectX, rectY, rectW + offsetX, rectH); + } else if (pressedArea.at(2)) { + int resizeH = widget->height() - offsetY; + if (widget->minimumHeight() <= resizeH) { + widget->setGeometry(rectX, widget->y() + offsetY, rectW, resizeH); + } + } else if (pressedArea.at(3)) { + widget->setGeometry(rectX, rectY, rectW, rectH + offsetY); + } else if (pressedArea.at(4)) { + int resizeW = widget->width() - offsetX; + int resizeH = widget->height() - offsetY; + if (widget->minimumWidth() <= resizeW) { + widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH); + } + if (widget->minimumHeight() <= resizeH) { + widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(5)) { + int resizeW = rectW + offsetX; + int resizeH = widget->height() - offsetY; + if (widget->minimumHeight() <= resizeH) { + widget->setGeometry(widget->x(), widget->y() + offsetY, resizeW, resizeH); + } + } else if (pressedArea.at(6)) { + int resizeW = widget->width() - offsetX; + int resizeH = rectH + offsetY; + if (widget->minimumWidth() <= resizeW) { + widget->setGeometry(widget->x() + offsetX, widget->y(), resizeW, resizeH); + } + if (widget->minimumHeight() <= resizeH) { + widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH); + } + } else if (pressedArea.at(7)) { + int resizeW = rectW + offsetX; + int resizeH = rectH + offsetY; + widget->setGeometry(widget->x(), widget->y(), resizeW, resizeH); + } + } + } else if (event->type() == QEvent::MouseButtonPress) { + //记住鼠标按下的坐标+窗体区域 + QMouseEvent *mouseEvent = (QMouseEvent *)event; + mousePoint = mouseEvent->pos(); + mouseRect = widget->geometry(); + + //判断按下的手柄的区域位置 + if (pressedRect.at(0).contains(mousePoint)) { + pressedArea[0] = true; + } else if (pressedRect.at(1).contains(mousePoint)) { + pressedArea[1] = true; + } else if (pressedRect.at(2).contains(mousePoint)) { + pressedArea[2] = true; + } else if (pressedRect.at(3).contains(mousePoint)) { + pressedArea[3] = true; + } else if (pressedRect.at(4).contains(mousePoint)) { + pressedArea[4] = true; + } else if (pressedRect.at(5).contains(mousePoint)) { + pressedArea[5] = true; + } else if (pressedRect.at(6).contains(mousePoint)) { + pressedArea[6] = true; + } else if (pressedRect.at(7).contains(mousePoint)) { + pressedArea[7] = true; + } else { + mousePressed = true; + } + } else if (event->type() == QEvent::MouseMove) { + //改成用HoverMove识别 + } else if (event->type() == QEvent::MouseButtonRelease) { + //恢复所有 + widget->setCursor(Qt::ArrowCursor); + mousePressed = false; + for (int i = 0; i < 8; ++i) { + pressedArea[i] = false; + } + } + } + + return QObject::eventFilter(watched, event); +} + +void FramelessWidget2::setPadding(int padding) +{ + this->padding = padding; +} + +void FramelessWidget2::setMoveEnable(bool moveEnable) +{ + this->moveEnable = moveEnable; +} + +void FramelessWidget2::setResizeEnable(bool resizeEnable) +{ + this->resizeEnable = resizeEnable; +} + +void FramelessWidget2::setMousePressed(bool mousePressed) +{ + this->mousePressed = mousePressed; +} + +void FramelessWidget2::setWidget(QWidget *widget) +{ + if (this->widget == 0) { + this->widget = widget; + //设置鼠标追踪为真 + this->widget->setMouseTracking(true); + //绑定事件过滤器 + this->widget->installEventFilter(this); + //设置悬停为真,必须设置这个,不然当父窗体里边还有子窗体全部遮挡了识别不到MouseMove,需要识别HoverMove + this->widget->setAttribute(Qt::WA_Hover, true); + + isMin = false; + flags = widget->windowFlags(); + } +} diff --git a/widget/framelesswidget/framelesswidget2.h b/widget/framelesswidget/framelesswidget2.h new file mode 100644 index 0000000..453ed11 --- /dev/null +++ b/widget/framelesswidget/framelesswidget2.h @@ -0,0 +1,66 @@ +#ifndef FRAMELESSWIDGET2_H +#define FRAMELESSWIDGET2_H + +/** + * 无边框窗体类 作者:feiyangqingyun(QQ:517216493) 2019-10-03 + * 1. 可以指定需要无边框的widget。 + * 2. 边框四周八个方位都可以自由拉伸。 + * 3. 可设置对应位置的边距,以便识别更大区域。 + * 4. 可设置是否允许拖动。 + * 5. 可设置是否允许拉伸。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT FramelessWidget2 : public QObject +#else +class FramelessWidget2 : public QObject +#endif + +{ + Q_OBJECT +public: + explicit FramelessWidget2(QObject *parent = 0); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + //边距+可移动+可拉伸 + int padding; + bool moveEnable; + bool resizeEnable; + + //无边框窗体 + QWidget *widget; + + //鼠标是否按下+按下坐标+按下时窗体区域 + bool mousePressed; + QPoint mousePoint; + QRect mouseRect; + + //鼠标是否按下某个区域+按下区域的大小 + //依次为 左侧+右侧+上侧+下侧+左上侧+右上侧+左下侧+右下侧 + QList pressedArea; + QList pressedRect; + + //记录是否最小化 + bool isMin; + //存储窗体默认的属性 + Qt::WindowFlags flags; + +public Q_SLOTS: + //设置边距 + void setPadding(int padding); + //设置是否可拖动+拉伸 + void setMoveEnable(bool moveEnable); + void setResizeEnable(bool resizeEnable); + //修复部分控件不能自动识别 MouseButtonRelease 的bug + void setMousePressed(bool mousePressed); + + //设置要无边框的窗体 + void setWidget(QWidget *widget); +}; + +#endif // FRAMELESSWIDGET2_H diff --git a/widget/framelesswidget/frmframelesswidget.cpp b/widget/framelesswidget/frmframelesswidget.cpp new file mode 100644 index 0000000..7848adc --- /dev/null +++ b/widget/framelesswidget/frmframelesswidget.cpp @@ -0,0 +1,110 @@ +#pragma execution_character_set("utf-8") + +#include "frmframelesswidget.h" +#include "ui_frmframelesswidget.h" +#include "qpushbutton.h" +#include "qcheckbox.h" +#include "qdebug.h" +#include "framelesswidget2.h" + +#ifndef Q_CC_MSVC +#include "framelessform/dialog.h" +#include "framelessform/widget.h" +#include "framelessform/mainwindow.h" +#endif + +frmFramelessWidget::frmFramelessWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmFramelessWidget) +{ + ui->setupUi(this); + this->initForm(); +} + +frmFramelessWidget::~frmFramelessWidget() +{ + delete ui; +} + +void frmFramelessWidget::initForm() +{ + widget = 0; + frameless = 0; + + connect(ui->btnDialog, SIGNAL(clicked(bool)), this, SLOT(buttonClicked())); + connect(ui->btnWidget, SIGNAL(clicked(bool)), this, SLOT(buttonClicked())); + connect(ui->btnMainWindow, SIGNAL(clicked(bool)), this, SLOT(buttonClicked())); +} + +void frmFramelessWidget::initWidget(QWidget *w) +{ + //设置无边框属性 + w->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint); + //w->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); + w->setWindowTitle("自由拉伸无边框窗体"); + w->setMinimumSize(200, 120); + w->resize(480, 320); + + //设置下背景颜色区别看 + QPalette palette = w->palette(); + palette.setBrush(QPalette::Window, QColor(162, 121, 197)); + w->setPalette(palette); + + QPushButton *btn = new QPushButton(w); + btn->setText("关闭"); + btn->setGeometry(10, 10, 130, 25); + connect(btn, SIGNAL(clicked(bool)), w, SLOT(close())); + + QCheckBox *cboxMove = new QCheckBox(w); + cboxMove->setText("可移动"); + cboxMove->setChecked(true); + cboxMove->setGeometry(10, 40, 70, 25); + connect(cboxMove, SIGNAL(stateChanged(int)), this, SLOT(stateChanged1(int))); + + QCheckBox *cboxResize = new QCheckBox(w); + cboxResize->setText("可拉伸"); + cboxResize->setChecked(true); + cboxResize->setGeometry(80, 40, 70, 25); + connect(cboxResize, SIGNAL(stateChanged(int)), this, SLOT(stateChanged2(int))); +} + +void frmFramelessWidget::on_pushButton_clicked() +{ + if (widget == 0) { + widget = new QWidget; + this->initWidget(widget); + frameless = new FramelessWidget2(widget); + frameless->setWidget(widget); + } + + widget->show(); +} + +void frmFramelessWidget::stateChanged1(int arg1) +{ + if (frameless != 0) { + frameless->setMoveEnable(arg1 != 0); + } +} + +void frmFramelessWidget::stateChanged2(int arg1) +{ + if (frameless != 0) { + frameless->setResizeEnable(arg1 != 0); + } +} + +void frmFramelessWidget::buttonClicked() +{ +#ifndef Q_CC_MSVC + QString objName = sender()->objectName(); + if (objName == "btnDialog") { + Dialog dialog; + dialog.exec(); + } else if (objName == "btnWidget") { + Widget *widget = new Widget; + widget->show(); + } else if (objName == "btnMainWindow") { + MainWindow *window = new MainWindow; + window->show(); + } +#endif +} diff --git a/widget/framelesswidget/frmframelesswidget.h b/widget/framelesswidget/frmframelesswidget.h new file mode 100644 index 0000000..ffd9660 --- /dev/null +++ b/widget/framelesswidget/frmframelesswidget.h @@ -0,0 +1,33 @@ +#ifndef FRMFRAMELESSWIDGET_H +#define FRMFRAMELESSWIDGET_H + +#include +class FramelessWidget2; + +namespace Ui { +class frmFramelessWidget; +} + +class frmFramelessWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmFramelessWidget(QWidget *parent = 0); + ~frmFramelessWidget(); + +private: + Ui::frmFramelessWidget *ui; + QWidget *widget; + FramelessWidget2 *frameless; + +private slots: + void initForm(); + void initWidget(QWidget *w); + void on_pushButton_clicked(); + void stateChanged1(int arg1); + void stateChanged2(int arg1); + void buttonClicked(); +}; + +#endif // FRMFRAMELESSWIDGET_H diff --git a/widget/framelesswidget/frmframelesswidget.ui b/widget/framelesswidget/frmframelesswidget.ui new file mode 100644 index 0000000..d2af8a5 --- /dev/null +++ b/widget/framelesswidget/frmframelesswidget.ui @@ -0,0 +1,59 @@ + + + frmFramelessWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + 10 + 10 + 491 + 30 + + + + + + + 弹出 + + + + + + + Dialog + + + + + + + Widget + + + + + + + MainWindow + + + + + + + + + diff --git a/widget/framelesswidget/main.cpp b/widget/framelesswidget/main.cpp new file mode 100644 index 0000000..f75be6a --- /dev/null +++ b/widget/framelesswidget/main.cpp @@ -0,0 +1,42 @@ +#pragma execution_character_set("utf-8") + +#include "frmframelesswidget.h" +#include +#include + +int main(int argc, char *argv[]) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); +#endif + + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmFramelessWidget w; + w.setWindowTitle("无边框窗体 (QQ: 517216493 WX: feiyangqingyun)"); + w.resize(800, 600); + w.show(); + + return a.exec(); +} diff --git a/widget/gifwidget/frmgifwidget.cpp b/widget/gifwidget/frmgifwidget.cpp new file mode 100644 index 0000000..aded8af --- /dev/null +++ b/widget/gifwidget/frmgifwidget.cpp @@ -0,0 +1,20 @@ +#include "frmgifwidget.h" +#include "ui_frmgifwidget.h" +#include "gifwidget.h" + +frmGifWidget::frmGifWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmGifWidget) +{ + ui->setupUi(this); +} + +frmGifWidget::~frmGifWidget() +{ + delete ui; +} + +void frmGifWidget::on_pushButton_clicked() +{ + //设置截图窗口置顶显示 + GifWidget::Instance()->setWindowFlags(GifWidget::Instance()->windowFlags() | Qt::WindowStaysOnTopHint); + GifWidget::Instance()->show(); +} diff --git a/widget/gifwidget/frmgifwidget.h b/widget/gifwidget/frmgifwidget.h new file mode 100644 index 0000000..05991d7 --- /dev/null +++ b/widget/gifwidget/frmgifwidget.h @@ -0,0 +1,25 @@ +#ifndef FRMGIFWIDGET_H +#define FRMGIFWIDGET_H + +#include + +namespace Ui { +class frmGifWidget; +} + +class frmGifWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmGifWidget(QWidget *parent = 0); + ~frmGifWidget(); + +private slots: + void on_pushButton_clicked(); + +private: + Ui::frmGifWidget *ui; +}; + +#endif // FRMGIFWIDGET_H diff --git a/widget/gifwidget/frmgifwidget.ui b/widget/gifwidget/frmgifwidget.ui new file mode 100644 index 0000000..a43fbae --- /dev/null +++ b/widget/gifwidget/frmgifwidget.ui @@ -0,0 +1,32 @@ + + + frmGifWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + 10 + 10 + 92 + 28 + + + + 弹出 + + + + + + diff --git a/widget/gifwidget/gif.h b/widget/gifwidget/gif.h new file mode 100644 index 0000000..0881914 --- /dev/null +++ b/widget/gifwidget/gif.h @@ -0,0 +1,795 @@ +// +// gif.h +// by Charlie Tangora +// Public domain. +// Email me : ctangora -at- gmail -dot- com +// +// This file offers a simple, very limited way to create animated GIFs directly in code. +// +// Those looking for particular cleverness are likely to be disappointed; it's pretty +// much a straight-ahead implementation of the GIF format with optional Floyd-Steinberg +// dithering. (It does at least use delta encoding - only the changed portions of each +// frame are saved.) +// +// So resulting files are often quite large. The hope is that it will be handy nonetheless +// as a quick and easily-integrated way for programs to spit out animations. +// +// Only RGBA8 is currently supported as an input format. (The alpha is ignored.) +// +// USAGE: +// Create a GifWriter struct. Pass it to GifBegin() to initialize and write the header. +// Pass subsequent frames to GifWriteFrame(). +// Finally, call GifEnd() to close the file handle and free memory. +// + +#ifndef __gif_h__ +#define __gif_h__ + +#include // for FILE* +#include // for memcpy and bzero + +#ifdef _MSC_VER +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include // for integer typedefs +#endif + +// Define these macros to hook into a custom memory allocator. +// TEMP_MALLOC and TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs +// and any temp memory allocated by a function will be freed before it exits. +// MALLOC and FREE are used only by GifBegin and GifEnd respectively (to allocate a buffer the size of the image, which +// is used to find changed pixels for delta-encoding.) + +#ifndef GIF_TEMP_MALLOC +#include +#define GIF_TEMP_MALLOC malloc +#endif + +#ifndef GIF_TEMP_FREE +#include +#define GIF_TEMP_FREE free +#endif + +#ifndef GIF_MALLOC +#include +#define GIF_MALLOC malloc +#endif + +#ifndef GIF_FREE +#include +#define GIF_FREE free +#endif + +class Gif +{ +public: + int kGifTransIndex; + struct GifPalette { + int bitDepth; + uint8_t r[256]; + uint8_t g[256]; + uint8_t b[256]; + // k-d tree over RGB space, organized in heap fashion + // i.e. left child of node i is node i*2, right child is node i*2+1 + // nodes 256-511 are implicitly the leaves, containing a color + uint8_t treeSplitElt[255]; + uint8_t treeSplit[255]; + }; + + // max, min, and abs functions + int GifIMax(int l, int r) { + return l > r ? l : r; + } + int GifIMin(int l, int r) { + return l < r ? l : r; + } + int GifIAbs(int i) { + return i < 0 ? -i : i; + } + + // walks the k-d tree to pick the palette entry for a desired color. + // Takes as in/out parameters the current best color and its error - + // only changes them if it finds a better color in its subtree. + // this is the major hotspot in the code at the moment. + void GifGetClosestPaletteColor(GifPalette *pPal, int r, int g, int b, int &bestInd, int &bestDiff, int treeRoot = 1) { + // base case, reached the bottom of the tree + if (treeRoot > (1 << pPal->bitDepth) - 1) { + int ind = treeRoot - (1 << pPal->bitDepth); + if (ind == kGifTransIndex) { + return; + } + // check whether this color is better than the current winner + int r_err = r - ((int32_t)pPal->r[ind]); + int g_err = g - ((int32_t)pPal->g[ind]); + int b_err = b - ((int32_t)pPal->b[ind]); + int diff = GifIAbs(r_err) + GifIAbs(g_err) + GifIAbs(b_err); + if (diff < bestDiff) { + bestInd = ind; + bestDiff = diff; + } + return; + } + // take the appropriate color (r, g, or b) for this node of the k-d tree + int comps[3]; + comps[0] = r; + comps[1] = g; + comps[2] = b; + int splitComp = comps[pPal->treeSplitElt[treeRoot]]; + + int splitPos = pPal->treeSplit[treeRoot]; + if (splitPos > splitComp) { + // check the left subtree + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot * 2); + if (bestDiff > splitPos - splitComp) { + // cannot prove there's not a better value in the right subtree, check that too + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot * 2 + 1); + } + } else { + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot * 2 + 1); + if (bestDiff > splitComp - splitPos) { + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot * 2); + } + } + } + + void GifSwapPixels(uint8_t *image, int pixA, int pixB) { + uint8_t rA = image[pixA * 4]; + uint8_t gA = image[pixA * 4 + 1]; + uint8_t bA = image[pixA * 4 + 2]; + uint8_t aA = image[pixA * 4 + 3]; + + uint8_t rB = image[pixB * 4]; + uint8_t gB = image[pixB * 4 + 1]; + uint8_t bB = image[pixB * 4 + 2]; + uint8_t aB = image[pixA * 4 + 3]; + + image[pixA * 4] = rB; + image[pixA * 4 + 1] = gB; + image[pixA * 4 + 2] = bB; + image[pixA * 4 + 3] = aB; + + image[pixB * 4] = rA; + image[pixB * 4 + 1] = gA; + image[pixB * 4 + 2] = bA; + image[pixB * 4 + 3] = aA; + } + + // just the partition operation from quicksort + int GifPartition(uint8_t *image, const int left, const int right, const int elt, int pivotIndex) { + const int pivotValue = image[(pivotIndex) * 4 + elt]; + GifSwapPixels(image, pivotIndex, right - 1); + int storeIndex = left; + bool split = 0; + for (int ii = left; ii < right - 1; ++ii) { + int arrayVal = image[ii * 4 + elt]; + if (arrayVal < pivotValue) { + GifSwapPixels(image, ii, storeIndex); + ++storeIndex; + } else if (arrayVal == pivotValue) { + if (split) { + GifSwapPixels(image, ii, storeIndex); + ++storeIndex; + } + split = !split; + } + } + GifSwapPixels(image, storeIndex, right - 1); + return storeIndex; + } + + // Perform an incomplete sort, finding all elements above and below the desired median + void GifPartitionByMedian(uint8_t *image, int left, int right, int com, int neededCenter) { + if (left < right - 1) { + int pivotIndex = left + (right - left) / 2; + pivotIndex = GifPartition(image, left, right, com, pivotIndex); + // Only "sort" the section of the array that contains the median + if (pivotIndex > neededCenter) { + GifPartitionByMedian(image, left, pivotIndex, com, neededCenter); + } + if (pivotIndex < neededCenter) { + GifPartitionByMedian(image, pivotIndex + 1, right, com, neededCenter); + } + } + } + + // Builds a palette by creating a balanced k-d tree of all pixels in the image + void GifSplitPalette(uint8_t *image, + int numPixels, int firstElt, + int lastElt, int splitElt, + int splitDist, int treeNode, + bool buildForDither, GifPalette *pal) { + if (lastElt <= firstElt || numPixels == 0) { + return; + } + // base case, bottom of the tree + if (lastElt == firstElt + 1) { + if (buildForDither) { + // Dithering needs at least one color as dark as anything + // in the image and at least one brightest color - + // otherwise it builds up error and produces strange artifacts + if (firstElt == 1) { + // special case: the darkest color in the image + uint32_t r = 255, g = 255, b = 255; + for (int ii = 0; ii < numPixels; ++ii) { + r = (uint32_t)GifIMin((int32_t)r, image[ii * 4 + 0]); + g = (uint32_t)GifIMin((int32_t)g, image[ii * 4 + 1]); + b = (uint32_t)GifIMin((int32_t)b, image[ii * 4 + 2]); + } + pal->r[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + return; + } + + if (firstElt == (1 << pal->bitDepth) - 1) { + // special case: the lightest color in the image + uint32_t r = 0, g = 0, b = 0; + for (int ii = 0; ii < numPixels; ++ii) { + r = (uint32_t)GifIMax((int32_t)r, image[ii * 4 + 0]); + g = (uint32_t)GifIMax((int32_t)g, image[ii * 4 + 1]); + b = (uint32_t)GifIMax((int32_t)b, image[ii * 4 + 2]); + } + pal->r[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + return; + } + } + // otherwise, take the average of all colors in this subcube + uint64_t r = 0, g = 0, b = 0; + for (int ii = 0; ii < numPixels; ++ii) { + r += image[ii * 4 + 0]; + g += image[ii * 4 + 1]; + b += image[ii * 4 + 2]; + } + + r += (uint64_t)numPixels / 2; // round to nearest + g += (uint64_t)numPixels / 2; + b += (uint64_t)numPixels / 2; + + r /= (uint64_t)numPixels; + g /= (uint64_t)numPixels; + b /= (uint64_t)numPixels; + + pal->r[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + return; + } + // Find the axis with the largest range + int minR = 255, maxR = 0; + int minG = 255, maxG = 0; + int minB = 255, maxB = 0; + for (int ii = 0; ii < numPixels; ++ii) { + int r = image[ii * 4 + 0]; + int g = image[ii * 4 + 1]; + int b = image[ii * 4 + 2]; + + if (r > maxR) { + maxR = r; + } + if (r < minR) { + minR = r; + } + + if (g > maxG) { + maxG = g; + } + if (g < minG) { + minG = g; + } + + if (b > maxB) { + maxB = b; + } + if (b < minB) { + minB = b; + } + } + + int rRange = maxR - minR; + int gRange = maxG - minG; + int bRange = maxB - minB; + // and split along that axis. (incidentally, this means this isn't a "proper" k-d tree but I don't know what else to call it) + int splitCom = 1; + if (bRange > gRange) { + splitCom = 2; + } + if (rRange > bRange && rRange > gRange) { + splitCom = 0; + } + + int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); + int subPixelsB = numPixels - subPixelsA; + + GifPartitionByMedian(image, 0, numPixels, splitCom, subPixelsA); + + pal->treeSplitElt[treeNode] = (uint8_t)splitCom; + pal->treeSplit[treeNode] = image[subPixelsA * 4 + splitCom]; + + GifSplitPalette(image, subPixelsA, firstElt, splitElt, splitElt - splitDist, splitDist / 2, treeNode * 2, buildForDither, pal); + GifSplitPalette(image + subPixelsA * 4, subPixelsB, splitElt, lastElt, splitElt + splitDist, splitDist / 2, treeNode * 2 + 1, buildForDither, pal); + } + + // Finds all pixels that have changed from the previous image and + // moves them to the fromt of th buffer. + // This allows us to build a palette optimized for the colors of the + // changed pixels only. + int GifPickChangedPixels(const uint8_t *lastFrame, uint8_t *frame, int numPixels) { + int numChanged = 0; + uint8_t *writeIter = frame; + for (int ii = 0; ii < numPixels; ++ii) { + if (lastFrame[0] != frame[0] || + lastFrame[1] != frame[1] || + lastFrame[2] != frame[2]) { + writeIter[0] = frame[0]; + writeIter[1] = frame[1]; + writeIter[2] = frame[2]; + ++numChanged; + writeIter += 4; + } + lastFrame += 4; + frame += 4; + } + return numChanged; + } + + // Creates a palette by placing all the image pixels in a k-d tree and then averaging the blocks at the bottom. + // This is known as the "modified median split" technique + void GifMakePalette(const uint8_t *lastFrame, + const uint8_t *nextFrame, + uint32_t width, uint32_t height, + int bitDepth, bool buildForDither, + GifPalette *pPal) { + pPal->bitDepth = bitDepth; + + // SplitPalette is destructive (it sorts the pixels by color) so + // we must create a copy of the image for it to destroy + size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t)); + uint8_t *destroyableImage = (uint8_t *)GIF_TEMP_MALLOC(imageSize); + memcpy(destroyableImage, nextFrame, imageSize); + + int numPixels = (int)(width * height); + if (lastFrame) { + numPixels = GifPickChangedPixels(lastFrame, destroyableImage, numPixels); + } + + const int lastElt = 1 << bitDepth; + const int splitElt = lastElt / 2; + const int splitDist = splitElt / 2; + + GifSplitPalette(destroyableImage, numPixels, 1, lastElt, splitElt, splitDist, 1, buildForDither, pPal); + + GIF_TEMP_FREE(destroyableImage); + + // add the bottom node for the transparency index + pPal->treeSplit[1 << (bitDepth - 1)] = 0; + pPal->treeSplitElt[1 << (bitDepth - 1)] = 0; + + pPal->r[0] = pPal->g[0] = pPal->b[0] = 0; + } + + // Implements Floyd-Steinberg dithering, writes palette value to alpha + void GifDitherImage(const uint8_t *lastFrame, const uint8_t *nextFrame, + uint8_t *outFrame, uint32_t width, + uint32_t height, GifPalette *pPal) { + int numPixels = (int)(width * height); + + // quantPixels initially holds color*256 for all pixels + // The extra 8 bits of precision allow for sub-single-color error values + // to be propagated + int32_t *quantPixels = (int32_t *)GIF_TEMP_MALLOC(sizeof(int32_t) * (size_t)numPixels * 4); + for (int ii = 0; ii < numPixels * 4; ++ii) { + uint8_t pix = nextFrame[ii]; + int32_t pix16 = int32_t(pix) * 256; + quantPixels[ii] = pix16; + } + + for (uint32_t yy = 0; yy < height; ++yy) { + for (uint32_t xx = 0; xx < width; ++xx) { + int32_t *nextPix = quantPixels + 4 * (yy * width + xx); + const uint8_t *lastPix = lastFrame ? lastFrame + 4 * (yy * width + xx) : NULL; + // Compute the colors we want (rounding to nearest) + int32_t rr = (nextPix[0] + 127) / 256; + int32_t gg = (nextPix[1] + 127) / 256; + int32_t bb = (nextPix[2] + 127) / 256; + // if it happens that we want the color from last frame, then just write out + // a transparent pixel + if (lastFrame && + lastPix[0] == rr && + lastPix[1] == gg && + lastPix[2] == bb) { + nextPix[0] = rr; + nextPix[1] = gg; + nextPix[2] = bb; + nextPix[3] = kGifTransIndex; + continue; + } + + int32_t bestDiff = 1000000; + int32_t bestInd = kGifTransIndex; + // Search the palete + GifGetClosestPaletteColor(pPal, rr, gg, bb, bestInd, bestDiff); + // Write the result to the temp buffer + int32_t r_err = nextPix[0] - int32_t(pPal->r[bestInd]) * 256; + int32_t g_err = nextPix[1] - int32_t(pPal->g[bestInd]) * 256; + int32_t b_err = nextPix[2] - int32_t(pPal->b[bestInd]) * 256; + + nextPix[0] = pPal->r[bestInd]; + nextPix[1] = pPal->g[bestInd]; + nextPix[2] = pPal->b[bestInd]; + nextPix[3] = bestInd; + + // Propagate the error to the four adjacent locations + // that we haven't touched yet + int quantloc_7 = (int)(yy * width + xx + 1); + int quantloc_3 = (int)(yy * width + width + xx - 1); + int quantloc_5 = (int)(yy * width + width + xx); + int quantloc_1 = (int)(yy * width + width + xx + 1); + + if (quantloc_7 < numPixels) { + int32_t *pix7 = quantPixels + 4 * quantloc_7; + pix7[0] += GifIMax(-pix7[0], r_err * 7 / 16); + pix7[1] += GifIMax(-pix7[1], g_err * 7 / 16); + pix7[2] += GifIMax(-pix7[2], b_err * 7 / 16); + } + + if (quantloc_3 < numPixels) { + int32_t *pix3 = quantPixels + 4 * quantloc_3; + pix3[0] += GifIMax(-pix3[0], r_err * 3 / 16); + pix3[1] += GifIMax(-pix3[1], g_err * 3 / 16); + pix3[2] += GifIMax(-pix3[2], b_err * 3 / 16); + } + + if (quantloc_5 < numPixels) { + int32_t *pix5 = quantPixels + 4 * quantloc_5; + pix5[0] += GifIMax(-pix5[0], r_err * 5 / 16); + pix5[1] += GifIMax(-pix5[1], g_err * 5 / 16); + pix5[2] += GifIMax(-pix5[2], b_err * 5 / 16); + } + + if (quantloc_1 < numPixels) { + int32_t *pix1 = quantPixels + 4 * quantloc_1; + pix1[0] += GifIMax(-pix1[0], r_err / 16); + pix1[1] += GifIMax(-pix1[1], g_err / 16); + pix1[2] += GifIMax(-pix1[2], b_err / 16); + } + } + } + // Copy the palettized result to the output buffer + for (int ii = 0; ii < numPixels * 4; ++ii) { + outFrame[ii] = (uint8_t)quantPixels[ii]; + } + + GIF_TEMP_FREE(quantPixels); + } + + // Picks palette colors for the image using simple thresholding, no dithering + void GifThresholdImage(const uint8_t *lastFrame, const uint8_t *nextFrame, + uint8_t *outFrame, uint32_t width, uint32_t height, + GifPalette *pPal) { + uint32_t numPixels = width * height; + for (uint32_t ii = 0; ii < numPixels; ++ii) { + // if a previous color is available, and it matches the current color, + // set the pixel to transparent + if (lastFrame && + lastFrame[0] == nextFrame[0] && + lastFrame[1] == nextFrame[1] && + lastFrame[2] == nextFrame[2]) { + outFrame[0] = lastFrame[0]; + outFrame[1] = lastFrame[1]; + outFrame[2] = lastFrame[2]; + outFrame[3] = kGifTransIndex; + } else { + // palettize the pixel + int32_t bestDiff = 1000000; + int32_t bestInd = 1; + GifGetClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], bestInd, bestDiff); + + // Write the resulting color to the output buffer + outFrame[0] = pPal->r[bestInd]; + outFrame[1] = pPal->g[bestInd]; + outFrame[2] = pPal->b[bestInd]; + outFrame[3] = (uint8_t)bestInd; + } + if (lastFrame) { + lastFrame += 4; + } + outFrame += 4; + nextFrame += 4; + } + } + + // Simple structure to write out the LZW-compressed portion of the image + // one bit at a time + struct GifBitStatus { + uint8_t bitIndex; // how many bits in the partial byte written so far + uint8_t byte; // current partial byte + + uint32_t chunkIndex; + uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file + }; + + // insert a single bit + void GifWriteBit(GifBitStatus &stat, uint32_t bit) { + bit = bit & 1; + bit = bit << stat.bitIndex; + stat.byte |= bit; + + ++stat.bitIndex; + if (stat.bitIndex > 7) { + // move the newly-finished byte to the chunk buffer + stat.chunk[stat.chunkIndex++] = stat.byte; + // and start a new byte + stat.bitIndex = 0; + stat.byte = 0; + } + } + + // write all bytes so far to the file + void GifWriteChunk(FILE *f, GifBitStatus &stat) { + fputc((int)stat.chunkIndex, f); + fwrite(stat.chunk, 1, stat.chunkIndex, f); + + stat.bitIndex = 0; + stat.byte = 0; + stat.chunkIndex = 0; + } + + void GifWriteCode(FILE *f, GifBitStatus &stat, uint32_t code, uint32_t length) { + for (uint32_t ii = 0; ii < length; ++ii) { + GifWriteBit(stat, code); + code = code >> 1; + if (stat.chunkIndex == 255) { + GifWriteChunk(f, stat); + } + } + } + + // The LZW dictionary is a 256-ary tree constructed as the file is encoded, + // this is one node + struct GifLzwNode { + uint16_t m_next[256]; + }; + + // write a 256-color (8-bit) image palette to the file + void GifWritePalette(const GifPalette *pPal, FILE *f) { + fputc(0, f); // first color: transparency + fputc(0, f); + fputc(0, f); + for (int ii = 1; ii < (1 << pPal->bitDepth); ++ii) { + uint32_t r = pPal->r[ii]; + uint32_t g = pPal->g[ii]; + uint32_t b = pPal->b[ii]; + fputc((int)r, f); + fputc((int)g, f); + fputc((int)b, f); + } + } + + // write the image header, LZW-compress and write out the image + void GifWriteLzwImage(FILE *f, uint8_t *image, uint32_t left, + uint32_t top, uint32_t width, + uint32_t height, uint32_t delay, + GifPalette *pPal) { + // graphics control extension + fputc(0x21, f); + fputc(0xf9, f); + fputc(0x04, f); + fputc(0x05, f); // leave prev frame in place, this frame has transparency + fputc(delay & 0xff, f); + fputc((delay >> 8) & 0xff, f); + fputc(kGifTransIndex, f); // transparent color index + fputc(0, f); + + fputc(0x2c, f); // image descriptor block + + fputc(left & 0xff, f); // corner of image in canvas space + fputc((left >> 8) & 0xff, f); + fputc(top & 0xff, f); + fputc((top >> 8) & 0xff, f); + + fputc(width & 0xff, f); // width and height of image + fputc((width >> 8) & 0xff, f); + fputc(height & 0xff, f); + fputc((height >> 8) & 0xff, f); + + //fputc(0, f); // no local color table, no transparency + //fputc(0x80, f); // no local color table, but transparency + + fputc(0x80 + pPal->bitDepth - 1, f); // local color table present, 2 ^ bitDepth entries + GifWritePalette(pPal, f); + + const int minCodeSize = pPal->bitDepth; + const uint32_t clearCode = 1 << pPal->bitDepth; + + fputc(minCodeSize, f); // min code size 8 bits + + GifLzwNode *codetree = (GifLzwNode *)GIF_TEMP_MALLOC(sizeof(GifLzwNode) * 4096); + + memset(codetree, 0, sizeof(GifLzwNode) * 4096); + int32_t curCode = -1; + uint32_t codeSize = (uint32_t)minCodeSize + 1; + uint32_t maxCode = clearCode + 1; + + GifBitStatus stat; + stat.byte = 0; + stat.bitIndex = 0; + stat.chunkIndex = 0; + + GifWriteCode(f, stat, clearCode, codeSize); // start with a fresh LZW dictionary + + for (uint32_t yy = 0; yy < height; ++yy) { + for (uint32_t xx = 0; xx < width; ++xx) { + uint8_t nextValue = image[(yy * width + xx) * 4 + 3]; + // "loser mode" - no compression, every single code is followed immediately by a clear + //WriteCode( f, stat, nextValue, codeSize ); + //WriteCode( f, stat, 256, codeSize ); + if (curCode < 0) { + // first value in a new run + curCode = nextValue; + } else if (codetree[curCode].m_next[nextValue]) { + // current run already in the dictionary + curCode = codetree[curCode].m_next[nextValue]; + } else { + // finish the current run, write a code + GifWriteCode(f, stat, (uint32_t)curCode, codeSize); + // insert the new run into the dictionary + codetree[curCode].m_next[nextValue] = (uint16_t)++maxCode; + if (maxCode >= (1ul << codeSize)) { + // dictionary entry count has broken a size barrier, + // we need more bits for codes + codeSize++; + } + if (maxCode == 4095) { + // the dictionary is full, clear it out and begin anew + GifWriteCode(f, stat, clearCode, codeSize); // clear tree + + memset(codetree, 0, sizeof(GifLzwNode) * 4096); + codeSize = (uint32_t)(minCodeSize + 1); + maxCode = clearCode + 1; + } + curCode = nextValue; + } + } + } + // compression footer + GifWriteCode(f, stat, (uint32_t)curCode, codeSize); + GifWriteCode(f, stat, clearCode, codeSize); + GifWriteCode(f, stat, clearCode + 1, (uint32_t)minCodeSize + 1); + // write out the last partial chunk + while (stat.bitIndex) { + GifWriteBit(stat, 0); + } + if (stat.chunkIndex) { + GifWriteChunk(f, stat); + } + + fputc(0, f); // image block terminator + + GIF_TEMP_FREE(codetree); + } + + struct GifWriter { + FILE *f; + uint8_t *oldImage; + bool firstFrame; + }; + + // Creates a gif file. + // The input GIFWriter is assumed to be uninitialized. + // The delay value is the time between frames in hundredths of a second - note that not all viewers pay much attention to this value. + bool GifBegin(GifWriter *writer, const char *filename, + uint32_t width, uint32_t height, + uint32_t delay, int32_t bitDepth = 8, + bool dither = false) { + (void)bitDepth; + (void)dither; // Mute "Unused argument" warnings +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + writer->f = 0; + fopen_s(&writer->f, filename, "wb"); +#else + writer->f = fopen(filename, "wb"); +#endif + if (!writer->f) { + return false; + } + + writer->firstFrame = true; + + // allocate + writer->oldImage = (uint8_t *)GIF_MALLOC(width * height * 4); + fputs("GIF89a", writer->f); + + // screen descriptor + fputc(width & 0xff, writer->f); + fputc((width >> 8) & 0xff, writer->f); + fputc(height & 0xff, writer->f); + fputc((height >> 8) & 0xff, writer->f); + + fputc(0xf0, writer->f); // there is an unsorted global color table of 2 entries + fputc(0, writer->f); // background color + fputc(0, writer->f); // pixels are square (we need to specify this because it's 1989) + + // now the "global" palette (really just a dummy palette) + // color 0: black + fputc(0, writer->f); + fputc(0, writer->f); + fputc(0, writer->f); + // color 1: also black + fputc(0, writer->f); + fputc(0, writer->f); + fputc(0, writer->f); + + if (delay != 0) { + // animation header + fputc(0x21, writer->f); // extension + fputc(0xff, writer->f); // application specific + fputc(11, writer->f); // length 11 + fputs("NETSCAPE2.0", writer->f); // yes, really + fputc(3, writer->f); // 3 bytes of NETSCAPE2.0 data + + fputc(1, writer->f); // JUST BECAUSE + fputc(0, writer->f); // loop infinitely (byte 0) + fputc(0, writer->f); // loop infinitely (byte 1) + + fputc(0, writer->f); // block terminator + } + return true; + } + + // Writes out a new frame to a GIF in progress. + // The GIFWriter should have been created by GIFBegin. + // AFAIK, it is legal to use different bit depths for different frames of an image - + // this may be handy to save bits in animations that don't change much. + bool GifWriteFrame(GifWriter *writer, const uint8_t *image, + uint32_t width, uint32_t height, + uint32_t delay, int bitDepth = 8, bool dither = false) { + if (!writer->f) { + return false; + } + + const uint8_t *oldImage = writer->firstFrame ? NULL : writer->oldImage; + writer->firstFrame = false; + + GifPalette pal; + GifMakePalette((dither ? NULL : oldImage), image, width, height, bitDepth, dither, &pal); + + if (dither) { + GifDitherImage(oldImage, image, writer->oldImage, width, height, &pal); + } else { + GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal); + } + + GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal); + + return true; + } + + // Writes the EOF code, closes the file handle, and frees temp memory used by a GIF. + // Many if not most viewers will still display a GIF properly if the EOF code is missing, + // but it's still a good idea to write it out. + bool GifEnd(GifWriter *writer) { + if (!writer->f) { + return false; + } + + fputc(0x3b, writer->f); // end of file + fclose(writer->f); + GIF_FREE(writer->oldImage); + + writer->f = NULL; + writer->oldImage = NULL; + + return true; + } +}; + +#endif diff --git a/widget/gifwidget/gifwidget.cpp b/widget/gifwidget/gifwidget.cpp new file mode 100644 index 0000000..36bfd39 --- /dev/null +++ b/widget/gifwidget/gifwidget.cpp @@ -0,0 +1,382 @@ +#pragma execution_character_set("utf-8") + +#include "gifwidget.h" +#include "qmutex.h" +#include "qlabel.h" +#include "qlineedit.h" +#include "qpushbutton.h" +#include "qlayout.h" +#include "qpainter.h" +#include "qevent.h" +#include "qstyle.h" +#include "qpixmap.h" +#include "qtimer.h" +#include "qdatetime.h" +#include "qapplication.h" +#include "qdesktopservices.h" +#include "qfiledialog.h" +#include "qurl.h" +#include "qtextcodec.h" +#include "qdebug.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) +#include "qscreen.h" +#define deskGeometry qApp->primaryScreen()->geometry() +#define deskGeometry2 qApp->primaryScreen()->availableGeometry() +#else +#include "qdesktopwidget.h" +#define deskGeometry qApp->desktop()->geometry() +#define deskGeometry2 qApp->desktop()->availableGeometry() +#endif + +QScopedPointer GifWidget::self; +GifWidget *GifWidget::Instance() +{ + if (self.isNull()) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (self.isNull()) { + self.reset(new GifWidget); + } + } + + return self.data(); +} + +GifWidget::GifWidget(QWidget *parent) : QDialog(parent) +{ + this->initControl(); + this->initForm(); +} + +bool GifWidget::eventFilter(QObject *watched, QEvent *event) +{ + static QPoint mousePoint; + static bool mousePressed = false; + + QMouseEvent *mouseEvent = static_cast(event); + if (mouseEvent->type() == QEvent::MouseButtonPress) { + if (mouseEvent->button() == Qt::LeftButton) { + mousePressed = true; + mousePoint = mouseEvent->globalPos() - this->pos(); + return true; + } + } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { + mousePressed = false; + return true; + } else if (mouseEvent->type() == QEvent::MouseMove) { + if (mousePressed) { + this->move(mouseEvent->globalPos() - mousePoint); + return true; + } + } + + return QWidget::eventFilter(watched, event); +} + +void GifWidget::resizeEvent(QResizeEvent *e) +{ + //拉动右下角改变大小自动赋值 + txtWidth->setText(QString::number(widgetMain->width())); + txtHeight->setText(QString::number(widgetMain->height())); + QDialog::resizeEvent(e); +} + +void GifWidget::paintEvent(QPaintEvent *) +{ + int width = txtWidth->text().toInt(); + int height = txtHeight->text().toInt(); + rectGif = QRect(borderWidth, widgetTop->height(), width - (borderWidth * 2), height); + + QPainter painter(this); + painter.setPen(Qt::NoPen); + painter.setBrush(bgColor); + painter.drawRoundedRect(this->rect(), 5, 5); + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(rectGif, Qt::SolidPattern); +} + +int GifWidget::getBorderWidth() const +{ + return this->borderWidth; +} + +QColor GifWidget::getBgColor() const +{ + return this->bgColor; +} + +void GifWidget::initControl() +{ + this->setObjectName("GifWidget"); + this->resize(800, 600); + this->setSizeGripEnabled(true); + QVBoxLayout *verticalLayout = new QVBoxLayout(this); + verticalLayout->setSpacing(0); + verticalLayout->setContentsMargins(11, 11, 11, 11); + verticalLayout->setObjectName("verticalLayout"); + verticalLayout->setContentsMargins(0, 0, 0, 0); + + widgetTop = new QWidget(this); + widgetTop->setObjectName("widgetTop"); + widgetTop->setMinimumSize(QSize(0, 35)); + widgetTop->setMaximumSize(QSize(16777215, 35)); + + QHBoxLayout *layoutTop = new QHBoxLayout(widgetTop); + layoutTop->setSpacing(0); + layoutTop->setContentsMargins(11, 11, 11, 11); + layoutTop->setObjectName("layoutTop"); + layoutTop->setContentsMargins(0, 0, 0, 0); + + QPushButton *btnIcon = new QPushButton(widgetTop); + btnIcon->setObjectName("btnIcon"); + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(btnIcon->sizePolicy().hasHeightForWidth()); + btnIcon->setSizePolicy(sizePolicy); + btnIcon->setMinimumSize(QSize(35, 0)); + btnIcon->setFlat(true); + layoutTop->addWidget(btnIcon); + + QLabel *labTitle = new QLabel(widgetTop); + labTitle->setObjectName("labTitle"); + layoutTop->addWidget(labTitle); + + QSpacerItem *horizontalSpacer = new QSpacerItem(87, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + layoutTop->addItem(horizontalSpacer); + + QPushButton *btnClose = new QPushButton(widgetTop); + btnClose->setObjectName("btnClose"); + sizePolicy.setHeightForWidth(btnClose->sizePolicy().hasHeightForWidth()); + btnClose->setSizePolicy(sizePolicy); + btnClose->setMinimumSize(QSize(35, 0)); + btnClose->setFocusPolicy(Qt::NoFocus); + btnClose->setFlat(true); + layoutTop->addWidget(btnClose); + verticalLayout->addWidget(widgetTop); + + widgetMain = new QWidget(this); + widgetMain->setObjectName("widgetMain"); + QSizePolicy sizePolicy1(QSizePolicy::Preferred, QSizePolicy::Expanding); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(widgetMain->sizePolicy().hasHeightForWidth()); + widgetMain->setSizePolicy(sizePolicy1); + verticalLayout->addWidget(widgetMain); + + widgetBottom = new QWidget(this); + widgetBottom->setObjectName("widgetBottom"); + widgetBottom->setMinimumSize(QSize(0, 45)); + widgetBottom->setMaximumSize(QSize(16777215, 45)); + + QHBoxLayout *layoutBottom = new QHBoxLayout(widgetBottom); + layoutBottom->setSpacing(6); + layoutBottom->setContentsMargins(11, 11, 11, 11); + layoutBottom->setObjectName("layoutBottom"); + layoutBottom->setContentsMargins(9, 9, -1, -1); + + QLabel *labFps = new QLabel(widgetBottom); + labFps->setObjectName("labFps"); + layoutBottom->addWidget(labFps); + + txtFps = new QLineEdit(widgetBottom); + txtFps->setObjectName("txtFps"); + txtFps->setMaximumSize(QSize(50, 16777215)); + txtFps->setAlignment(Qt::AlignCenter); + layoutBottom->addWidget(txtFps); + + QLabel *labWidth = new QLabel(widgetBottom); + labWidth->setObjectName("labWidth"); + layoutBottom->addWidget(labWidth); + + txtWidth = new QLineEdit(widgetBottom); + txtWidth->setObjectName("txtWidth"); + txtWidth->setEnabled(true); + txtWidth->setMaximumSize(QSize(50, 16777215)); + txtWidth->setAlignment(Qt::AlignCenter); + layoutBottom->addWidget(txtWidth); + + QLabel *labHeight = new QLabel(widgetBottom); + labHeight->setObjectName("labHeight"); + layoutBottom->addWidget(labHeight); + + txtHeight = new QLineEdit(widgetBottom); + txtHeight->setObjectName("txtHeight"); + txtHeight->setEnabled(true); + txtHeight->setMaximumSize(QSize(50, 16777215)); + txtHeight->setAlignment(Qt::AlignCenter); + layoutBottom->addWidget(txtHeight); + + labStatus = new QLabel(widgetBottom); + labStatus->setObjectName("labStatus"); + QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy2.setHorizontalStretch(0); + sizePolicy2.setVerticalStretch(0); + sizePolicy2.setHeightForWidth(labStatus->sizePolicy().hasHeightForWidth()); + labStatus->setSizePolicy(sizePolicy2); + labStatus->setAlignment(Qt::AlignCenter); + layoutBottom->addWidget(labStatus); + + btnStart = new QPushButton(widgetBottom); + btnStart->setObjectName("btnStart"); + sizePolicy.setHeightForWidth(btnStart->sizePolicy().hasHeightForWidth()); + btnStart->setSizePolicy(sizePolicy); + layoutBottom->addWidget(btnStart); + verticalLayout->addWidget(widgetBottom); + + labTitle->setText("GIF录屏工具(QQ:517216493)"); + labFps->setText("帧率"); + labWidth->setText("宽度"); + labHeight->setText("高度"); + btnStart->setText("开始"); + this->setWindowTitle(labTitle->text()); + + btnIcon->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon)); + btnClose->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton)); + + connect(btnClose, SIGNAL(clicked(bool)), this, SLOT(closeAll())); + connect(btnStart, SIGNAL(clicked(bool)), this, SLOT(record())); + connect(txtWidth, SIGNAL(editingFinished()), this, SLOT(resizeForm())); + connect(txtHeight, SIGNAL(editingFinished()), this, SLOT(resizeForm())); +} + +void GifWidget::initForm() +{ + borderWidth = 3; + bgColor = QColor(34, 163, 169); + + fps = 10; + txtFps->setText(QString::number(fps)); + gifWriter = 0; + + timer = new QTimer(this); + timer->setInterval(100); + connect(timer, SIGNAL(timeout()), this, SLOT(saveImage())); + + this->setAttribute(Qt::WA_TranslucentBackground); + this->setWindowFlags(Qt::FramelessWindowHint); + this->installEventFilter(this); + + QStringList qss; + qss.append("QLabel{color:#ffffff;}"); + qss.append("#btnClose,#btnIcon{border:none;border-radius:0px;}"); + qss.append("#btnClose:hover{background-color:#ff0000;}"); + qss.append("#btnClose{border-top-right-radius:5px;}"); + qss.append("#labTitle{font:bold 16px;}"); + qss.append("#labStatus{font:15px;}"); + this->setStyleSheet(qss.join("")); +} + +void GifWidget::saveImage() +{ + if (!gifWriter) { + return; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QScreen *screen = QApplication::primaryScreen(); + QPixmap pix = screen->grabWindow(0, x() + rectGif.x(), y() + rectGif.y(), rectGif.width(), rectGif.height()); + QImage image = pix.toImage().convertToFormat(QImage::Format_RGBA8888); +#else + //由于qt4没有RGBA8888,采用最接近RGBA8888的是ARGB32,颜色会有点偏差 + QPixmap pix = QPixmap::grabWindow(0, x() + rectGif.x(), y() + rectGif.y(), rectGif.width(), rectGif.height()); + QImage image = pix.toImage().convertToFormat(QImage::Format_ARGB32); +#endif + + gif.GifWriteFrame(gifWriter, image.bits(), rectGif.width(), rectGif.height(), fps); + count++; + labStatus->setText(QString("正在录制 第 %1 帧").arg(count)); +} + +void GifWidget::record() +{ + if (btnStart->text() == "开始") { + if (0 != gifWriter) { + delete gifWriter; + gifWriter = 0; + } + + //先弹出文件保存对话框 + //fileName = qApp->applicationDirPath() + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss.gif"); + fileName = QFileDialog::getSaveFileName(this, "选择保存位置", qApp->applicationDirPath() + "/", "gif图片(*.gif)"); + if (fileName.isEmpty()) { + return; + } + + int width = txtWidth->text().toInt(); + int height = txtHeight->text().toInt(); + fps = txtFps->text().toInt(); + +#ifdef Q_OS_WIN + //windows上需要先转码 +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + QTextCodec *code = QTextCodec::codecForName("utf-8"); +#else + QTextCodec *code = QTextCodec::codecForName("gbk"); +#endif + const char *name = code->fromUnicode(fileName).constData(); +#else + const char *name = fileName.toUtf8().constData(); +#endif + + gifWriter = new Gif::GifWriter; + bool ok = gif.GifBegin(gifWriter, name, width, height, fps); + if (!ok) { + delete gifWriter; + gifWriter = 0; + return; + } + + count = 0; + labStatus->setText("开始录制..."); + btnStart->setText("停止"); + //延时启动 + timer->setInterval(1000 / fps); + QTimer::singleShot(1000, timer, SLOT(start())); + //saveImage(); + } else { + timer->stop(); + gif.GifEnd(gifWriter); + + delete gifWriter; + gifWriter = 0; + + labStatus->setText(QString("录制完成 共 %1 帧").arg(count)); + btnStart->setText("开始"); + QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)); + } +} + +void GifWidget::closeAll() +{ + if (0 != gifWriter) { + delete gifWriter; + gifWriter = 0; + } + + this->close(); +} + +void GifWidget::resizeForm() +{ + int width = txtWidth->text().toInt(); + int height = txtHeight->text().toInt(); + this->resize(width, height + widgetTop->height() + widgetBottom->height()); +} + +void GifWidget::setBorderWidth(int borderWidth) +{ + if (this->borderWidth != borderWidth) { + this->borderWidth = borderWidth; + this->update(); + } +} + +void GifWidget::setBgColor(const QColor &bgColor) +{ + if (this->bgColor != bgColor) { + this->bgColor = bgColor; + this->update(); + } +} diff --git a/widget/gifwidget/gifwidget.h b/widget/gifwidget/gifwidget.h new file mode 100644 index 0000000..b057d17 --- /dev/null +++ b/widget/gifwidget/gifwidget.h @@ -0,0 +1,80 @@ +#ifndef GIFWIDGET_H +#define GIFWIDGET_H + +/** + * GIF录屏控件 作者:feiyangqingyun(QQ:517216493) 2019-04-03 + * 1. 可设置要录制屏幕的宽高,支持右下角直接拉动改变。 + * 2. 可设置变宽的宽度。 + * 3. 可设置录屏控件的背景颜色。 + * 4. 可设置录制的帧数。 + * 5. 录制区域可自由拖动选择。 + */ + +#include +#include "gif.h" + +class QLineEdit; +class QLabel; + +#ifdef quc +class Q_DECL_EXPORT GifWidget : public QDialog +#else +class GifWidget : public QDialog +#endif + +{ + Q_OBJECT + + Q_PROPERTY(int borderWidth READ getBorderWidth WRITE setBorderWidth) + Q_PROPERTY(QColor bgColor READ getBgColor WRITE setBgColor) + +public: + static GifWidget *Instance(); + explicit GifWidget(QWidget *parent = 0); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + void resizeEvent(QResizeEvent *); + void paintEvent(QPaintEvent *); + +private: + static QScopedPointer self; + QWidget *widgetTop; //标题栏 + QWidget *widgetMain; //中间部分 + QWidget *widgetBottom; //底部栏 + QLineEdit *txtFps; //帧率输入框 + QLineEdit *txtWidth; //宽度输入框 + QLineEdit *txtHeight; //高度输入框 + QPushButton *btnStart; //开始按钮 + QLabel *labStatus; //显示状态信息 + + int fps; //帧数 100为1s + int borderWidth; //边框宽度 + QColor bgColor; //背景颜色 + + int count; //帧数计数 + QString fileName; //保存文件名称 + QRect rectGif; //截屏区域 + QTimer *timer; //截屏定时器 + + Gif gif; //gif类对象 + Gif::GifWriter *gifWriter; //gif写入对象 + +public: + int getBorderWidth() const; + QColor getBgColor() const; + +private slots: + void initControl(); + void initForm(); + void saveImage(); + void record(); + void closeAll(); + void resizeForm(); + +public Q_SLOTS: + void setBorderWidth(int borderWidth); + void setBgColor(const QColor &bgColor); +}; + +#endif // GIFWIDGET_H diff --git a/widget/gifwidget/gifwidget.pro b/widget/gifwidget/gifwidget.pro new file mode 100644 index 0000000..fa8a9ee --- /dev/null +++ b/widget/gifwidget/gifwidget.pro @@ -0,0 +1,19 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = gifwidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off + +SOURCES += main.cpp +SOURCES += frmgifwidget.cpp +SOURCES += gifwidget.cpp + +HEADERS += gifwidget.h +HEADERS += frmgifwidget.h +HEADERS += gif.h + +FORMS += frmgifwidget.ui +RESOURCES += main.qrc diff --git a/widget/gifwidget/image/gifwidget.ico b/widget/gifwidget/image/gifwidget.ico new file mode 100644 index 0000000..c23ad4a Binary files /dev/null and b/widget/gifwidget/image/gifwidget.ico differ diff --git a/widget/gifwidget/image/gifwidget.png b/widget/gifwidget/image/gifwidget.png new file mode 100644 index 0000000..c6bc6d1 Binary files /dev/null and b/widget/gifwidget/image/gifwidget.png differ diff --git a/widget/gifwidget/main.cpp b/widget/gifwidget/main.cpp new file mode 100644 index 0000000..a8a27e8 --- /dev/null +++ b/widget/gifwidget/main.cpp @@ -0,0 +1,44 @@ +#pragma execution_character_set("utf-8") + +#include "frmgifwidget.h" +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif +#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0)) + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); +#endif + + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + a.setWindowIcon(QIcon(":/image/gifwidget.ico")); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmGifWidget w; + w.setWindowTitle("GIF录屏 (QQ: 517216493 WX: feiyangqingyun)"); + w.show(); + + return a.exec(); +} diff --git a/widget/gifwidget/main.qrc b/widget/gifwidget/main.qrc new file mode 100644 index 0000000..a71d67b --- /dev/null +++ b/widget/gifwidget/main.qrc @@ -0,0 +1,5 @@ + + + image/gifwidget.ico + + diff --git a/widget/lunarcalendarwidget/font/fontawesome-webfont.ttf b/widget/lunarcalendarwidget/font/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/widget/lunarcalendarwidget/font/fontawesome-webfont.ttf differ diff --git a/widget/lunarcalendarwidget/frmlunarcalendarwidget.cpp b/widget/lunarcalendarwidget/frmlunarcalendarwidget.cpp new file mode 100644 index 0000000..aa9cf61 --- /dev/null +++ b/widget/lunarcalendarwidget/frmlunarcalendarwidget.cpp @@ -0,0 +1,38 @@ +#include "frmlunarcalendarwidget.h" +#include "ui_frmlunarcalendarwidget.h" + +frmLunarCalendarWidget::frmLunarCalendarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmLunarCalendarWidget) +{ + ui->setupUi(this); + this->initForm(); +} + +frmLunarCalendarWidget::~frmLunarCalendarWidget() +{ + delete ui; +} + +void frmLunarCalendarWidget::initForm() +{ + ui->cboxWeekNameFormat->setCurrentIndex(2); +} + +void frmLunarCalendarWidget::on_cboxCalendarStyle_currentIndexChanged(int index) +{ + ui->lunarCalendarWidget->setCalendarStyle((LunarCalendarWidget::CalendarStyle)index); +} + +void frmLunarCalendarWidget::on_cboxSelectType_currentIndexChanged(int index) +{ + ui->lunarCalendarWidget->setSelectType((LunarCalendarWidget::SelectType)index); +} + +void frmLunarCalendarWidget::on_cboxWeekNameFormat_currentIndexChanged(int index) +{ + ui->lunarCalendarWidget->setWeekNameFormat((LunarCalendarWidget::WeekNameFormat)index); +} + +void frmLunarCalendarWidget::on_ckShowLunar_stateChanged(int arg1) +{ + ui->lunarCalendarWidget->setShowLunar(arg1 != 0); +} diff --git a/widget/lunarcalendarwidget/frmlunarcalendarwidget.h b/widget/lunarcalendarwidget/frmlunarcalendarwidget.h new file mode 100644 index 0000000..82fbca3 --- /dev/null +++ b/widget/lunarcalendarwidget/frmlunarcalendarwidget.h @@ -0,0 +1,29 @@ +#ifndef FRMLUNARCALENDARWIDGET_H +#define FRMLUNARCALENDARWIDGET_H + +#include + +namespace Ui { +class frmLunarCalendarWidget; +} + +class frmLunarCalendarWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmLunarCalendarWidget(QWidget *parent = 0); + ~frmLunarCalendarWidget(); + +private: + Ui::frmLunarCalendarWidget *ui; + +private slots: + void initForm(); + void on_cboxCalendarStyle_currentIndexChanged(int index); + void on_cboxSelectType_currentIndexChanged(int index); + void on_cboxWeekNameFormat_currentIndexChanged(int index); + void on_ckShowLunar_stateChanged(int arg1); +}; + +#endif // FRMLUNARCALENDARWIDGET_H diff --git a/widget/lunarcalendarwidget/frmlunarcalendarwidget.ui b/widget/lunarcalendarwidget/frmlunarcalendarwidget.ui new file mode 100644 index 0000000..c184d9d --- /dev/null +++ b/widget/lunarcalendarwidget/frmlunarcalendarwidget.ui @@ -0,0 +1,176 @@ + + + frmLunarCalendarWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + + + 0 + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 整体样式 + + + + + + + + 90 + 0 + + + + + 红色风格 + + + + + + + + 选中样式 + + + + + + + + 90 + 0 + + + + + 矩形背景 + + + + + 圆形背景 + + + + + 角标背景 + + + + + 图片背景 + + + + + + + + 星期格式 + + + + + + + + 90 + 0 + + + + + 短名称 + + + + + 普通名称 + + + + + 长名称 + + + + + 英文名称 + + + + + + + + 显示农历 + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + LunarCalendarWidget + QWidget +
lunarcalendarwidget.h
+ 1 +
+
+ + +
diff --git a/widget/lunarcalendarwidget/image/bg_calendar.png b/widget/lunarcalendarwidget/image/bg_calendar.png new file mode 100644 index 0000000..d541811 Binary files /dev/null and b/widget/lunarcalendarwidget/image/bg_calendar.png differ diff --git a/widget/lunarcalendarwidget/lunarcalendarinfo.cpp b/widget/lunarcalendarwidget/lunarcalendarinfo.cpp new file mode 100644 index 0000000..a4073c6 --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendarinfo.cpp @@ -0,0 +1,796 @@ +#pragma execution_character_set("utf-8") + +#include "lunarcalendarinfo.h" +#include "qmutex.h" +#include "qdebug.h" + +#define year_2099 + +QScopedPointer LunarCalendarInfo::self; +LunarCalendarInfo *LunarCalendarInfo::Instance() +{ + if (self.isNull()) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (self.isNull()) { + self.reset(new LunarCalendarInfo); + } + } + + return self.data(); +} + +LunarCalendarInfo::LunarCalendarInfo(QObject *parent) : QObject(parent) +{ + //农历查表 + lunarCalendarTable << 0x04AE53 << 0x0A5748 << 0x5526BD << 0x0D2650 << 0x0D9544 << 0x46AAB9 << 0x056A4D << 0x09AD42 << 0x24AEB6 << 0x04AE4A; //1901-1910 + lunarCalendarTable << 0x6A4DBE << 0x0A4D52 << 0x0D2546 << 0x5D52BA << 0x0B544E << 0x0D6A43 << 0x296D37 << 0x095B4B << 0x749BC1 << 0x049754; //1911-1920 + lunarCalendarTable << 0x0A4B48 << 0x5B25BC << 0x06A550 << 0x06D445 << 0x4ADAB8 << 0x02B64D << 0x095742 << 0x2497B7 << 0x04974A << 0x664B3E; //1921-1930 + lunarCalendarTable << 0x0D4A51 << 0x0EA546 << 0x56D4BA << 0x05AD4E << 0x02B644 << 0x393738 << 0x092E4B << 0x7C96BF << 0x0C9553 << 0x0D4A48; //1931-1940 + lunarCalendarTable << 0x6DA53B << 0x0B554F << 0x056A45 << 0x4AADB9 << 0x025D4D << 0x092D42 << 0x2C95B6 << 0x0A954A << 0x7B4ABD << 0x06CA51; //1941-1950 + lunarCalendarTable << 0x0B5546 << 0x555ABB << 0x04DA4E << 0x0A5B43 << 0x352BB8 << 0x052B4C << 0x8A953F << 0x0E9552 << 0x06AA48 << 0x6AD53C; //1951-1960 + lunarCalendarTable << 0x0AB54F << 0x04B645 << 0x4A5739 << 0x0A574D << 0x052642 << 0x3E9335 << 0x0D9549 << 0x75AABE << 0x056A51 << 0x096D46; //1961-1970 + lunarCalendarTable << 0x54AEBB << 0x04AD4F << 0x0A4D43 << 0x4D26B7 << 0x0D254B << 0x8D52BF << 0x0B5452 << 0x0B6A47 << 0x696D3C << 0x095B50; //1971-1980 + lunarCalendarTable << 0x049B45 << 0x4A4BB9 << 0x0A4B4D << 0xAB25C2 << 0x06A554 << 0x06D449 << 0x6ADA3D << 0x0AB651 << 0x093746 << 0x5497BB; //1981-1990 + lunarCalendarTable << 0x04974F << 0x064B44 << 0x36A537 << 0x0EA54A << 0x86B2BF << 0x05AC53 << 0x0AB647 << 0x5936BC << 0x092E50 << 0x0C9645; //1991-2000 + lunarCalendarTable << 0x4D4AB8 << 0x0D4A4C << 0x0DA541 << 0x25AAB6 << 0x056A49 << 0x7AADBD << 0x025D52 << 0x092D47 << 0x5C95BA << 0x0A954E; //2001-2010 + lunarCalendarTable << 0x0B4A43 << 0x4B5537 << 0x0AD54A << 0x955ABF << 0x04BA53 << 0x0A5B48 << 0x652BBC << 0x052B50 << 0x0A9345 << 0x474AB9; //2011-2020 + lunarCalendarTable << 0x06AA4C << 0x0AD541 << 0x24DAB6 << 0x04B64A << 0x69573D << 0x0A4E51 << 0x0D2646 << 0x5E933A << 0x0D534D << 0x05AA43; //2021-2030 + lunarCalendarTable << 0x36B537 << 0x096D4B << 0xB4AEBF << 0x04AD53 << 0x0A4D48 << 0x6D25BC << 0x0D254F << 0x0D5244 << 0x5DAA38 << 0x0B5A4C; //2031-2040 + lunarCalendarTable << 0x056D41 << 0x24ADB6 << 0x049B4A << 0x7A4BBE << 0x0A4B51 << 0x0AA546 << 0x5B52BA << 0x06D24E << 0x0ADA42 << 0x355B37; //2041-2050 + lunarCalendarTable << 0x09374B << 0x8497C1 << 0x049753 << 0x064B48 << 0x66A53C << 0x0EA54F << 0x06B244 << 0x4AB638 << 0x0AAE4C << 0x092E42; //2051-2060 + lunarCalendarTable << 0x3C9735 << 0x0C9649 << 0x7D4ABD << 0x0D4A51 << 0x0DA545 << 0x55AABA << 0x056A4E << 0x0A6D43 << 0x452EB7 << 0x052D4B; //2061-2070 + lunarCalendarTable << 0x8A95BF << 0x0A9553 << 0x0B4A47 << 0x6B553B << 0x0AD54F << 0x055A45 << 0x4A5D38 << 0x0A5B4C << 0x052B42 << 0x3A93B6; //2071-2080 + lunarCalendarTable << 0x069349 << 0x7729BD << 0x06AA51 << 0x0AD546 << 0x54DABA << 0x04B64E << 0x0A5743 << 0x452738 << 0x0D264A << 0x8E933E; //2081-2090 + lunarCalendarTable << 0x0D5252 << 0x0DAA47 << 0x66B53B << 0x056D4F << 0x04AE45 << 0x4A4EB9 << 0x0A4D4C << 0x0D1541 << 0x2D92B5; //2091-2099 + + //每年春节对应的公历日期 + springFestival << 130 << 217 << 206; // 1968 1969 1970 + springFestival << 127 << 215 << 203 << 123 << 211 << 131 << 218 << 207 << 128 << 216; // 1971--1980 + springFestival << 205 << 125 << 213 << 202 << 220 << 209 << 219 << 217 << 206 << 127; // 1981--1990 + springFestival << 215 << 204 << 123 << 210 << 131 << 219 << 207 << 128 << 216 << 205; // 1991--2000 + springFestival << 124 << 212 << 201 << 122 << 209 << 129 << 218 << 207 << 126 << 214; // 2001--2010 + springFestival << 203 << 123 << 210 << 131 << 219 << 208 << 128 << 216 << 205 << 125; // 2011--2020 + springFestival << 212 << 201 << 122 << 210 << 129 << 217 << 206 << 126 << 213 << 203; // 2021--2030 + springFestival << 123 << 211 << 131 << 219 << 208 << 128 << 215 << 204 << 124 << 212; // 2031--2040 + + //16--18位表示闰几月 0--12位表示农历每月的数据 高位表示1月 低位表示12月(农历闰月就会多一个月) + lunarData << 461653 << 1386 << 2413; // 1968 1969 1970 + lunarData << 330077 << 1197 << 2637 << 268877 << 3365 << 531109 << 2900 << 2922 << 398042 << 2395; // 1971--1980 + lunarData << 1179 << 267415 << 2635 << 661067 << 1701 << 1748 << 398772 << 2742 << 2391 << 330031; // 1981--1990 + lunarData << 1175 << 1611 << 200010 << 3749 << 527717 << 1452 << 2742 << 332397 << 2350 << 3222; // 1991--2000 + lunarData << 268949 << 3402 << 3493 << 133973 << 1386 << 464219 << 605 << 2349 << 334123 << 2709; // 2001--2010 + lunarData << 2890 << 267946 << 2773 << 592565 << 1210 << 2651 << 395863 << 1323 << 2707 << 265877; // 2011--2020 + lunarData << 1706 << 2773 << 133557 << 1206 << 398510 << 2638 << 3366 << 335142 << 3411 << 1450; // 2021--2030 + lunarData << 200042 << 2413 << 723293 << 1197 << 2637 << 399947 << 3365 << 3410 << 334676 << 2906; // 2031--2040 + + //二十四节气表 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 1970 + chineseTwentyFourData << 0x96 << 0xB4 << 0x96 << 0xA6 << 0x97 << 0x97 << 0x78 << 0x79 << 0x79 << 0x69 << 0x78 << 0x77; // 1971 + chineseTwentyFourData << 0x96 << 0xA4 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 1972 + chineseTwentyFourData << 0xA5 << 0xB5 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 1973 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 1974 + chineseTwentyFourData << 0x96 << 0xB4 << 0x96 << 0xA6 << 0x97 << 0x97 << 0x78 << 0x79 << 0x78 << 0x69 << 0x78 << 0x77; // 1975 + chineseTwentyFourData << 0x96 << 0xA4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x88 << 0x89 << 0x88 << 0x78 << 0x87 << 0x87; // 1976 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 1977 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x79 << 0x78 << 0x87; // 1978 + chineseTwentyFourData << 0x96 << 0xB4 << 0x96 << 0xA6 << 0x96 << 0x97 << 0x78 << 0x79 << 0x78 << 0x69 << 0x78 << 0x77; // 1979 + chineseTwentyFourData << 0x96 << 0xA4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 1980 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x77 << 0x87; // 1981 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 1982 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x78 << 0x79 << 0x78 << 0x69 << 0x78 << 0x77; // 1983 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 1984 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA6 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 1985 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 1986 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x79 << 0x78 << 0x69 << 0x78 << 0x87; // 1987 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 1988 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 1989 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 1990 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x86 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 1991 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 1992 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 1993 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 1994 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x76 << 0x78 << 0x69 << 0x78 << 0x87; // 1995 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 1996 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 1997 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 1998 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 1999 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2000 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2001 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 2002 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 2003 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2004 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2005 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2006 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 2007 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2008 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2009 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2010 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x79 << 0x78 << 0x87; // 2011 + chineseTwentyFourData << 0x96 << 0xB4 << 0xA5 << 0xB5 << 0xA5 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2012 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2013 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2014 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 2015 + chineseTwentyFourData << 0x95 << 0xB4 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2016 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2017 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA6 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2018 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 2019 + chineseTwentyFourData << 0x95 << 0xB4 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x86; // 2020 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2021 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2022 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 2023 + chineseTwentyFourData << 0x95 << 0xB4 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2024 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2025 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2026 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 2027 + chineseTwentyFourData << 0x95 << 0xB4 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2028 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2029 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2030 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 2031 + chineseTwentyFourData << 0x95 << 0xB4 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2032 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2033 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x78 << 0x88 << 0x78 << 0x87 << 0x87; // 2034 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2035 + chineseTwentyFourData << 0x95 << 0xB4 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2036 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2037 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2038 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2039 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x69 << 0x78 << 0x87; // 2040 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA5 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2041 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2042 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2043 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x97 << 0x88 << 0x78 << 0x78 << 0x79 << 0x78 << 0x87; // 2044 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2045 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2046 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2047 + chineseTwentyFourData << 0x95 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 2048 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x86; // 2049 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2050 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2051 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 2052 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x86; // 2053 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2054 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2055 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x79 << 0x77 << 0x87; // 2056 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2057 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2058 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2059 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 2060 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2061 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2062 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2063 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0x96 << 0x96 << 0x88 << 0x78 << 0x78 << 0x78 << 0x87 << 0x87; // 2064 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2065 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2066 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2067 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2068 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2069 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA5 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2070 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2071 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2072 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x88 << 0x87 << 0x96; // 2073 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA5 << 0xA6 << 0x87 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2074 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2075 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2076 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x88 << 0x87 << 0x96; // 2077 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x88 << 0x87 << 0x78 << 0x87 << 0x86; // 2078 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2079 + chineseTwentyFourData << 0xA5 << 0xB4 << 0x96 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x78 << 0x78 << 0x87 << 0x87; // 2080 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0x97 << 0x87 << 0x87 << 0x88 << 0x86 << 0x96; // 2081 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x86; // 2082 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2083 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA6 << 0xA5 << 0xA6 << 0x96 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2084 + chineseTwentyFourData << 0xB4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0x97 << 0x87 << 0x87 << 0x88 << 0x86 << 0x96; // 2085 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x86; // 2086 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2087 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2088 + chineseTwentyFourData << 0xB4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0x97 << 0x87 << 0x87 << 0x88 << 0x96 << 0x96; // 2089 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2090 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2091 + chineseTwentyFourData << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2092 + chineseTwentyFourData << 0xB4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0x97 << 0x87 << 0x87 << 0x87 << 0x96 << 0x96; // 2093 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2094 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2095 + chineseTwentyFourData << 0xA5 << 0xB3 << 0xA5 << 0xA5 << 0xA6 << 0xA6 << 0x88 << 0x88 << 0x88 << 0x78 << 0x87 << 0x87; // 2096 + chineseTwentyFourData << 0xB4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA5 << 0x97 << 0x97 << 0x87 << 0x87 << 0x96 << 0x96; // 2097 + chineseTwentyFourData << 0xA4 << 0xC3 << 0xA5 << 0xB4 << 0xA5 << 0xA6 << 0x97 << 0x87 << 0x87 << 0x78 << 0x87 << 0x96; // 2098 + chineseTwentyFourData << 0xA5 << 0xC3 << 0xA5 << 0xB5 << 0xA6 << 0xA6 << 0x87 << 0x88 << 0x88 << 0x78 << 0x87 << 0x86; // 2099 + + //公历每月前面的天数 + monthAdd << 0 << 31 << 59 << 90 << 120 << 151 << 181 << 212 << 243 << 273 << 304 << 334; + + //农历日期名称 + listDayName << "*" << "初一" << "初二" << "初三" << "初四" << "初五" << "初六" << "初七" << "初八" << "初九" << "初十" + << "十一" << "十二" << "十三" << "十四" << "十五" << "十六" << "十七" << "十八" << "十九" << "二十" + << "廿一" << "廿二" << "廿三" << "廿四" << "廿五" << "廿六" << "廿七" << "廿八" << "廿九" << "三十"; + + //农历月份名称 + listMonthName << "*" << "正月" << "二月" << "三月" << "四月" << "五月" << "六月" << "七月" << "八月" << "九月" << "十月" << "冬月" << "腊月"; + + //二十四节气名称 + listSolarTerm << "小寒" << "大寒" << "立春" << "雨水" << "惊蛰" << "春分" << "清明" << "谷雨" + << "立夏" << "小满" << "芒种" << "夏至" << "小暑" << "大暑" << "立秋" << "处暑" + << "白露" << "秋分" << "寒露" << "霜降" << "立冬" << "小雪" << "大雪" << "冬至"; + + //天干 + listTianGan << "甲" << "乙" << "丙" << "丁" << "戊" << "己" << "庚" << "辛" << "壬" << "癸"; + + //地支 + listDiZhi << "子" << "丑" << "寅" << "卯" << "辰" << "巳" << "午" << "未" << "申" << "酉" << "戌" << "亥"; + + //属相 + listShuXiang << "鼠" << "牛" << "虎" << "兔" << "龙" << "蛇" << "马" << "羊" << "猴" << "鸡" << "狗" << "猪"; +} + +//计算是否闰年 +bool LunarCalendarInfo::isLoopYear(int year) +{ + return (((0 == (year % 4)) && (0 != (year % 100))) || (0 == (year % 400))); +} + +//计算指定年月该月共多少天 +int LunarCalendarInfo::getMonthDays(int year, int month) +{ + int countDay = 0; + int loopDay = isLoopYear(year) ? 1 : 0; + + switch (month) { + case 1: + countDay = 31; + break; + case 2: + countDay = 28 + loopDay; + break; + case 3: + countDay = 31; + break; + case 4: + countDay = 30; + break; + case 5: + countDay = 31; + break; + case 6: + countDay = 30; + break; + case 7: + countDay = 31; + break; + case 8: + countDay = 31; + break; + case 9: + countDay = 30; + break; + case 10: + countDay = 31; + break; + case 11: + countDay = 30; + break; + case 12: + countDay = 31; + break; + default: + countDay = 30; + break; + } + + return countDay; +} + +//计算指定年月对应到该月共多少天 +int LunarCalendarInfo::getTotalMonthDays(int year, int month) +{ + int countDay = 0; + int loopDay = isLoopYear(year) ? 1 : 0; + + switch (month) { + case 1: + countDay = 0; + break; + case 2: + countDay = 31; + break; + case 3: + countDay = 59 + loopDay; + break; + case 4: + countDay = 90 + loopDay; + break; + case 5: + countDay = 120 + loopDay; + break; + case 6: + countDay = 151 + loopDay; + break; + case 7: + countDay = 181 + loopDay; + break; + case 8: + countDay = 212 + loopDay; + break; + case 9: + countDay = 243 + loopDay; + break; + case 10: + countDay = 273 + loopDay; + break; + case 11: + countDay = 304 + loopDay; + break; + case 12: + countDay = 334 + loopDay; + break; + default: + countDay = 0; + break; + } + + return countDay; +} + +//计算指定年月对应星期几 +int LunarCalendarInfo::getFirstDayOfWeek(int year, int month) +{ + int week = 0; + week = (year + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400) % 7; + week += getTotalMonthDays(year, month); + week = week % 7; + return week; +} + +//计算国际节日 +QString LunarCalendarInfo::getHoliday(int month, int day) +{ + int temp = (month << 8) | day; + QString strHoliday; + + switch (temp) { + case 0x0101: + strHoliday = "元旦"; + break; + case 0x020E: + strHoliday = "情人节"; + break; + case 0x0303: + strHoliday = "爱耳日"; + break; + case 0x0305: + strHoliday = "志愿者服务日"; + break; + case 0x0308: + strHoliday = "妇女节"; + break; + case 0x0309: + strHoliday = "保护母亲河"; + break; + case 0x030C: + strHoliday = "植树节"; + break; + case 0x030F: + strHoliday = "消费者权益日"; + break; + case 0x0401: + strHoliday = "愚人节"; + break; + case 0x0501: + strHoliday = "劳动节"; + break; + case 0x0504: + strHoliday = "青年节"; + break; + case 0x0601: + strHoliday = "儿童节"; + break; + case 0x0606: + strHoliday = "全国爱眼日"; + break; + case 0x0701: + strHoliday = "建党节"; + break; + case 0x0707: + strHoliday = "抗战纪念日"; + break; + case 0x0801: + strHoliday = "建军节"; + break; + case 0x090A: + strHoliday = "教师节"; + break; + case 0x0910: + strHoliday = "脑健康日"; + break; + case 0x0914: + strHoliday = "爱牙日"; + break; + case 0x0A01: + strHoliday = "国庆节"; + break; + case 0x0A0A: + strHoliday = "高血压日"; + break; + case 0x0A1C: + strHoliday = "男性健康日"; + break; + case 0x0B08: + strHoliday = "记者节"; + break; + case 0x0B09: + strHoliday = "消防宣传日"; + break; + case 0x0C04: + strHoliday = "法制宣传日"; + break; + case 0x0C18: + strHoliday = "平安夜"; + break; + case 0x0C19: + strHoliday = "圣诞节"; + break; + default: + break; + } + + return strHoliday; +} + +//计算二十四节气 +QString LunarCalendarInfo::getSolarTerms(int year, int month, int day) +{ + QString strSolarTerms; + int dayTemp = 0; + //24节气对应表在1970年以后指定? + int index = (year - 1970) * 12 + month - 1; + if (index < 0) { + return ""; + } + + if (day < 15) { + dayTemp = 15 - day; + if ((chineseTwentyFourData.at(index) >> 4) == dayTemp) { + strSolarTerms = listSolarTerm.at(2 * (month - 1)); + } + } else if (day > 15) { + dayTemp = day - 15; + if ((chineseTwentyFourData.at(index) & 0x0f) == dayTemp) { + strSolarTerms = listSolarTerm.at(2 * (month - 1) + 1); + } + } + + return strSolarTerms; +} + +//计算农历节日(必须传入农历年份月份) +QString LunarCalendarInfo::getLunarFestival(int month, int day) +{ + int temp = (month << 8) | day; + QString strFestival; + + switch (temp) { + case 0x0101: + strFestival = "春节"; + break; + case 0x010F: + strFestival = "元宵节"; + break; + case 0x0202: + strFestival = "龙抬头"; + break; + case 0x0505: + strFestival = "端午节"; + break; + case 0x0707: + strFestival = "七夕节"; + break; + case 0x080F: + strFestival = "中秋节"; + break; + case 0x0909: + strFestival = "重阳节"; + break; + case 0x0C08: + strFestival = "腊八节"; + break; + case 0x0C1E: + strFestival = "除夕"; + break; + default: + break; + } + + return strFestival; +} + +//计算农历年 天干+地支+生肖 +QString LunarCalendarInfo::getLunarYear(int year) +{ + QString strYear; + if (year > 1924) { + int temp = year - 1924; + strYear.append(listTianGan.at(temp % 10)); + strYear.append(listDiZhi.at(temp % 12)); + strYear.append(listShuXiang.at(temp % 12)); + } + + return strYear; +} + +void LunarCalendarInfo::getLunarCalendarInfo(int year, int month, int day, + QString &strHoliday, + QString &strSolarTerms, + QString &strLunarFestival, + QString &strLunarYear, + QString &strLunarMonth, + QString &strLunarDay) +{ + //过滤不在范围内的年月日 + if (year < 1901 || year > 2099 || month < 1 || month > 12 || day < 1 || day > 31) { + return; + } + + strHoliday = getHoliday(month, day); + strSolarTerms = getSolarTerms(year, month, day); + +#ifndef year_2099 + //现在计算农历:获得当年春节的公历日期(比如:2015年春节日期为(2月19日)) + //以此为分界点,2.19前面的农历是2014年农历(用2014年农历数据来计算) + //2.19以及之后的日期,农历为2015年农历(用2015年农历数据来计算) + int temp, tempYear, tempMonth, isEnd, k, n; + tempMonth = year - 1968; + int springFestivalMonth = springFestival.at(tempMonth) / 100; + int springFestivalDaty = springFestival.at(tempMonth) % 100; + + if (month < springFestivalMonth) { + tempMonth--; + tempYear = 365 * 1 + day + monthAdd.at(month - 1) - 31 * ((springFestival.at(tempMonth) / 100) - 1) - springFestival.at(tempMonth) % 100 + 1; + + if ((!(year % 4)) && (month > 2)) { + tempYear = tempYear + 1; + } + + if ((!((year - 1) % 4))) { + tempYear = tempYear + 1; + } + } else if (month == springFestivalMonth) { + if (day < springFestivalDaty) { + tempMonth--; + tempYear = 365 * 1 + day + monthAdd.at(month - 1) - 31 * ((springFestival.at(tempMonth) / 100) - 1) - springFestival.at(tempMonth) % 100 + 1; + + if ((!(year % 4)) && (month > 2)) { + tempYear = tempYear + 1; + } + + if ((!((year - 1) % 4))) { + tempYear = tempYear + 1; + } + } else { + tempYear = day + monthAdd.at(month - 1) - 31 * ((springFestival.at(tempMonth) / 100) - 1) - springFestival.at(tempMonth) % 100 + 1; + + if ((!(year % 4)) && (month > 2)) { + tempYear = tempYear + 1; + } + } + } else { + tempYear = day + monthAdd.at(month - 1) - 31 * ((springFestival.at(tempMonth) / 100) - 1) - springFestival.at(tempMonth) % 100 + 1; + + if ((!(year % 4)) && (month > 2)) { + tempYear = tempYear + 1; + } + } + + //计算农历天干地支月日 + isEnd = 0; + while (isEnd != 1) { + if (lunarData.at(tempMonth) < 4095) { + k = 11; + } else { + k = 12; + } + + n = k; + + while (n >= 0) { + temp = lunarData.at(tempMonth); + temp = temp >> n; + temp = temp % 2; + + if (tempYear <= (29 + temp)) { + isEnd = 1; + break; + } + + tempYear = tempYear - 29 - temp; + n = n - 1; + } + + if (isEnd) { + break; + } + + tempMonth = tempMonth + 1; + } + + //农历的年月日 + year = 1969 + tempMonth - 1; + month = k - n + 1; + day = tempYear; + + if (k == 12) { + if (month == (lunarData.at(tempMonth) / 65536) + 1) { + month = 1 - month; + } else if (month > (lunarData.at(tempMonth) / 65536) + 1) { + month = month - 1; + } + } + + strLunarYear = getLunarYear(year); + + if (month < 1 && (1 == day)) { + month = month * -1; + strLunarMonth = "闰" + listMonthName.at(month); + } else { + strLunarMonth = listMonthName.at(month); + } + + strLunarDay = listDayName.at(day); + strLunarFestival = getLunarFestival(month, day); +#else + //记录春节离当年元旦的天数 + int springOffset = 0; + //记录阳历日离当年元旦的天数 + int newYearOffset = 0; + //记录大小月的天数 29或30 + int monthCount = 0; + + if (((lunarCalendarTable.at(year - 1901) & 0x0060) >> 5) == 1) { + springOffset = (lunarCalendarTable.at(year - 1901) & 0x001F) - 1; + } else { + springOffset = (lunarCalendarTable.at(year - 1901) & 0x001F) - 1 + 31; + } + + //如果是不闰年且不是2月份 +1 + newYearOffset = monthAdd.at(month - 1) + day - 1; + if ((!(year % 4)) && (month > 2)) { + newYearOffset++; + } + + //记录从哪个月开始来计算 + int index = 0; + //用来对闰月的特殊处理 + int flag = 0; + + //判断阳历日在春节前还是春节后,计算农历的月/日 + if (newYearOffset >= springOffset) { + newYearOffset -= springOffset; + month = 1; + index = 1; + flag = 0; + + if ((lunarCalendarTable.at(year - 1901) & (0x80000 >> (index - 1))) == 0) { + monthCount = 29; + } else { + monthCount = 30; + } + + while (newYearOffset >= monthCount) { + newYearOffset -= monthCount; + index++; + if (month == ((lunarCalendarTable.at(year - 1901) & 0xF00000) >> 20)) { + flag = ~flag; + if (flag == 0) { + month++; + } + } else { + month++; + } + + if ((lunarCalendarTable.at(year - 1901) & (0x80000 >> (index - 1))) == 0) { + monthCount = 29; + } else { + monthCount = 30; + } + } + + day = newYearOffset + 1; + } else { + springOffset -= newYearOffset; + year--; + month = 12; + flag = 0; + + if (((lunarCalendarTable.at(year - 1901) & 0xF00000) >> 20) == 0) { + index = 12; + } else { + index = 13; + } + + if ((lunarCalendarTable.at(year - 1901) & (0x80000 >> (index - 1))) == 0) { + monthCount = 29; + } else { + monthCount = 30; + } + + while (springOffset > monthCount) { + springOffset -= monthCount; + index--; + + if (flag == 0) { + month--; + } + + if (month == ((lunarCalendarTable.at(year - 1901) & 0xF00000) >> 20)) { + flag = ~flag; + } + + if ((lunarCalendarTable.at(year - 1901) & (0x80000 >> (index - 1))) == 0) { + monthCount = 29; + } else { + monthCount = 30; + } + } + + day = monthCount - springOffset + 1; + } + + //计算农历的索引配置 + int temp = 0; + temp |= day; + temp |= (month << 6); + + //转换农历的年月 + month = (temp & 0x3C0) >> 6; + day = temp & 0x3F; + + strLunarYear = getLunarYear(year); + + if ((month == ((lunarCalendarTable.at(year - 1901) & 0xF00000) >> 20)) && (1 == day)) { + strLunarMonth = "闰" + listMonthName.at(month); + } else { + strLunarMonth = listMonthName.at(month); + } + + strLunarDay = listDayName.at(day); + strLunarFestival = getLunarFestival(month, day); +#endif +} + +QString LunarCalendarInfo::getLunarInfo(int year, int month, int day, bool yearInfo, bool monthInfo, bool dayInfo) +{ + QString strHoliday, strSolarTerms, strLunarFestival, strLunarYear, strLunarMonth, strLunarDay; + + LunarCalendarInfo::Instance()->getLunarCalendarInfo(year, month, day, + strHoliday, strSolarTerms, strLunarFestival, + strLunarYear, strLunarMonth, strLunarDay); + + //农历节日优先,其次农历节气,然后公历节日,最后才是农历月份名称 + if (!strLunarFestival.isEmpty()) { + strLunarDay = strLunarFestival; + } else if (!strSolarTerms.isEmpty()) { + strLunarDay = strSolarTerms; + } else if (!strHoliday.isEmpty()) { + strLunarDay = strHoliday; + } + + QString info = QString("%1%2%3") + .arg(yearInfo ? strLunarYear + "年" : "") + .arg(monthInfo ? strLunarMonth : "") + .arg(dayInfo ? strLunarDay : ""); + + return info; +} + +QString LunarCalendarInfo::getLunarYearMonthDay(int year, int month, int day) +{ + return getLunarInfo(year, month, day, true, true, true); +} + +QString LunarCalendarInfo::getLunarMonthDay(int year, int month, int day) +{ + return getLunarInfo(year, month, day, false, true, true); +} + +QString LunarCalendarInfo::getLunarDay(int year, int month, int day) +{ + return getLunarInfo(year, month, day, false, false, true); +} diff --git a/widget/lunarcalendarwidget/lunarcalendarinfo.h b/widget/lunarcalendarwidget/lunarcalendarinfo.h new file mode 100644 index 0000000..552446d --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendarinfo.h @@ -0,0 +1,84 @@ +#ifndef LUNARCALENDARINFO_H +#define LUNARCALENDARINFO_H + +/** + * 农历信息类 作者:倪大侠 整理:feiyangqingyun(QQ:517216493) 2016-12-10 + * 1. 计算是否闰年。 + * 2. 计算国际节日。 + * 3. 计算二十四节气。 + * 4. 计算农历年 天干、地支、生肖。 + * 5. 计算指定年月日农历信息,包括公历节日和农历节日及二十四节气。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT LunarCalendarInfo : public QObject +#else +class LunarCalendarInfo : public QObject +#endif + +{ + Q_OBJECT +public: + static LunarCalendarInfo *Instance(); + explicit LunarCalendarInfo(QObject *parent = 0); + + //计算是否闰年 + bool isLoopYear(int year); + + //计算指定年月该月共多少天 + int getMonthDays(int year, int month); + + //计算指定年月对应到该月共多少天 + int getTotalMonthDays(int year, int month); + + //计算指定年月对应星期几 + int getFirstDayOfWeek(int year, int month); + + //计算国际节日 + QString getHoliday(int month, int day); + + //计算二十四节气 + QString getSolarTerms(int year, int month, int day); + + //计算农历节日(必须传入农历年份月份) + QString getLunarFestival(int month, int day); + + //计算农历年 天干+地支+生肖 + QString getLunarYear(int year); + + //计算指定年月日农历信息,包括公历节日和农历节日及二十四节气 + void getLunarCalendarInfo(int year, int month, int day, + QString &strHoliday, + QString &strSolarTerms, + QString &strLunarFestival, + QString &strLunarYear, + QString &strLunarMonth, + QString &strLunarDay); + + //获取指定年月日农历信息 + QString getLunarInfo(int year, int month, int day, bool yearInfo, bool monthInfo, bool dayInfo); + QString getLunarYearMonthDay(int year, int month, int day); + QString getLunarMonthDay(int year, int month, int day); + QString getLunarDay(int year, int month, int day); + +private: + static QScopedPointer self; + + QList lunarCalendarTable; //农历年表 + QList springFestival; //春节公历日期 + QList lunarData; //农历每月数据 + QList chineseTwentyFourData; //农历二十四节气数据 + QList monthAdd; //公历每月前面的天数 + + QList listDayName; //农历日期名称集合 + QList listMonthName; //农历月份名称集合 + QList listSolarTerm; //二十四节气名称集合 + + QList listTianGan; //天干名称集合 + QList listDiZhi; //地支名称集合 + QList listShuXiang; //属相名称集合 +}; + +#endif // LUNARCALENDARINFO_H diff --git a/widget/lunarcalendarwidget/lunarcalendaritem.cpp b/widget/lunarcalendarwidget/lunarcalendaritem.cpp new file mode 100644 index 0000000..7434029 --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendaritem.cpp @@ -0,0 +1,553 @@ +#pragma execution_character_set("utf-8") + +#include "lunarcalendaritem.h" +#include "qpainter.h" +#include "qevent.h" +#include "qdatetime.h" +#include "qdebug.h" + +LunarCalendarItem::LunarCalendarItem(QWidget *parent) : QWidget(parent) +{ + hover = false; + pressed = false; + listDayName << "*" << "初一" << "初二" << "初三" << "初四" << "初五" << "初六" << "初七" << "初八" << "初九" << "初十" + << "十一" << "十二" << "十三" << "十四" << "十五" << "十六" << "十七" << "十八" << "十九" << "二十" + << "廿一" << "廿二" << "廿三" << "廿四" << "廿五" << "廿六" << "廿七" << "廿八" << "廿九" << "三十"; + + select = false; + showLunar = true; + bgImage = ":/image/bg_calendar.png"; + selectType = SelectType_Rect; + + date = QDate::currentDate(); + lunar = "初一"; + dayType = DayType_MonthCurrent; + + borderColor = QColor(180, 180, 180); + weekColor = QColor(255, 0, 0); + superColor = QColor(255, 129, 6); + lunarColor = QColor(55, 156, 238); + + currentTextColor = QColor(0, 0, 0); + otherTextColor = QColor(200, 200, 200); + selectTextColor = QColor(255, 255, 255); + hoverTextColor = QColor(250, 250, 250); + + currentLunarColor = QColor(150, 150, 150); + otherLunarColor = QColor(200, 200, 200); + selectLunarColor = QColor(255, 255, 255); + hoverLunarColor = QColor(250, 250, 250); + + currentBgColor = QColor(255, 255, 255); + otherBgColor = QColor(240, 240, 240); + selectBgColor = QColor(208, 47, 18); + hoverBgColor = QColor(204, 183, 180); +} + +void LunarCalendarItem::enterEvent(QEvent *) +{ + hover = true; + this->update(); +} + +void LunarCalendarItem::leaveEvent(QEvent *) +{ + hover = false; + this->update(); +} + +void LunarCalendarItem::mousePressEvent(QMouseEvent *) +{ + pressed = true; + this->update(); + + Q_EMIT clicked(date, dayType); +} + +void LunarCalendarItem::mouseReleaseEvent(QMouseEvent *) +{ + pressed = false; + this->update(); +} + +void LunarCalendarItem::paintEvent(QPaintEvent *) +{ + //绘制准备工作,启用反锯齿 + QPainter painter(this); + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + + //绘制背景和边框 + drawBg(&painter); + + //优先绘制选中状态,其次绘制悬停状态 + if (select) { + drawBgCurrent(&painter, selectBgColor); + } else if (hover) { + drawBgCurrent(&painter, hoverBgColor); + } + + //绘制日期 + drawDay(&painter); + + //绘制农历信息 + drawLunar(&painter); +} + +void LunarCalendarItem::drawBg(QPainter *painter) +{ + painter->save(); + + //根据当前类型选择对应的颜色 + QColor bgColor = currentBgColor; + if (dayType == DayType_MonthPre || dayType == DayType_MonthNext) { + bgColor = otherBgColor; + } + + painter->setPen(borderColor); + painter->setBrush(bgColor); + painter->drawRect(rect()); + + painter->restore(); +} + +void LunarCalendarItem::drawBgCurrent(QPainter *painter, const QColor &color) +{ + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + + painter->save(); + + painter->setPen(Qt::NoPen); + painter->setBrush(color); + + //根据设定绘制背景样式 + if (selectType == SelectType_Rect) { + painter->drawRect(rect()); + } else if (selectType == SelectType_Circle) { + int radius = side / 2 - 3; + painter->drawEllipse(QPointF(width / 2, height / 2), radius, radius); + } else if (selectType == SelectType_Triangle) { + int radius = side / 3; + QPolygon pts; + pts.setPoints(3, 1, 1, radius, 1, 1, radius); + painter->drawRect(rect()); + painter->setBrush(superColor); + painter->drawConvexPolygon(pts); + } else if (selectType == SelectType_Image) { + //等比例缩放居中绘制 + QImage img(bgImage); + if (!img.isNull()) { + img = img.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + int x = (width - img.width()) / 2; + int y = (height - img.height()) / 2; + painter->drawImage(x, y, img); + } + } + + painter->restore(); +} + +void LunarCalendarItem::drawDay(QPainter *painter) +{ + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + + painter->save(); + + //根据当前类型选择对应的颜色 + QColor color = currentTextColor; + if (dayType == DayType_MonthPre || dayType == DayType_MonthNext) { + color = otherTextColor; + } else if (dayType == DayType_WeekEnd) { + color = weekColor; + } + + if (select) { + color = selectTextColor; + } else if (hover) { + color = hoverTextColor; + } + + painter->setPen(color); + + if (showLunar) { + QFont font; + font.setPixelSize(side / 2.7); + painter->setFont(font); + + QRect dayRect = QRect(0, 0, width, height / 1.7); + painter->drawText(dayRect, Qt::AlignHCenter | Qt::AlignBottom, QString::number(date.day())); + } else { + QFont font; + font.setPixelSize(side / 2); + painter->setFont(font); + + QRect dayRect = QRect(0, 0, width, height); + painter->drawText(dayRect, Qt::AlignCenter, QString::number(date.day())); + } + + painter->restore(); +} + +void LunarCalendarItem::drawLunar(QPainter *painter) +{ + if (!showLunar) { + return; + } + + int width = this->width(); + int height = this->height(); + int side = qMin(width, height); + + painter->save(); + + //判断当前农历文字是否节日,是节日且是当月则用农历节日颜色显示 + bool exist = (!listDayName.contains(lunar) && dayType != DayType_MonthPre && dayType != DayType_MonthNext); + + //根据当前类型选择对应的颜色 + QColor color = currentLunarColor; + if (dayType == DayType_MonthPre || dayType == DayType_MonthNext) { + color = otherLunarColor; + } + + if (select) { + color = selectTextColor; + } else if (hover) { + color = hoverTextColor; + } else if (exist) { + color = lunarColor; + } + + painter->setPen(color); + + QFont font; + font.setPixelSize(side / 5); + painter->setFont(font); + + QRect lunarRect(0, height / 2, width, height / 2); + painter->drawText(lunarRect, Qt::AlignCenter, lunar); + + painter->restore(); +} + +QSize LunarCalendarItem::sizeHint() const +{ + return QSize(100, 100); +} + +QSize LunarCalendarItem::minimumSizeHint() const +{ + return QSize(20, 20); +} + +bool LunarCalendarItem::getSelect() const +{ + return this->select; +} + +void LunarCalendarItem::setSelect(bool select) +{ + if (this->select != select) { + this->select = select; + this->update(); + } +} + +bool LunarCalendarItem::getShowLunar() const +{ + return this->showLunar; +} + +void LunarCalendarItem::setShowLunar(bool showLunar) +{ + if (this->showLunar != showLunar) { + this->showLunar = showLunar; + this->update(); + } +} + +QString LunarCalendarItem::getBgImage() const +{ + return this->bgImage; +} + +void LunarCalendarItem::setBgImage(const QString &bgImage) +{ + if (this->bgImage != bgImage) { + this->bgImage = bgImage; + this->update(); + } +} + +LunarCalendarItem::SelectType LunarCalendarItem::getSelectType() const +{ + return this->selectType; +} + +void LunarCalendarItem::setSelectType(const LunarCalendarItem::SelectType &selectType) +{ + if (this->selectType != selectType) { + this->selectType = selectType; + this->update(); + } +} + +QDate LunarCalendarItem::getDate() const +{ + return this->date; +} + +void LunarCalendarItem::setDate(const QDate &date) +{ + if (this->date != date) { + this->date = date; + this->update(); + } +} + +QString LunarCalendarItem::getLunar() const +{ + return this->lunar; +} + +void LunarCalendarItem::setLunar(const QString &lunar) +{ + if (this->lunar != lunar) { + this->lunar = lunar; + this->update(); + } +} + +LunarCalendarItem::DayType LunarCalendarItem::getDayType() const +{ + return this->dayType; +} + +void LunarCalendarItem::setDayType(const LunarCalendarItem::DayType &dayType) +{ + if (this->dayType != dayType) { + this->dayType = dayType; + this->update(); + } +} + +void LunarCalendarItem::setDate(const QDate &date, const QString &lunar, const DayType &dayType) +{ + this->date = date; + this->lunar = lunar; + this->dayType = dayType; + this->update(); +} + +QColor LunarCalendarItem::getBorderColor() const +{ + return this->borderColor; +} + +void LunarCalendarItem::setBorderColor(const QColor &borderColor) +{ + if (this->borderColor != borderColor) { + this->borderColor = borderColor; + this->update(); + } +} + + +QColor LunarCalendarItem::getWeekColor() const +{ + return this->weekColor; +} + +void LunarCalendarItem::setWeekColor(const QColor &weekColor) +{ + if (this->weekColor != weekColor) { + this->weekColor = weekColor; + this->update(); + } +} + +QColor LunarCalendarItem::getSuperColor() const +{ + return this->superColor; +} + +void LunarCalendarItem::setSuperColor(const QColor &superColor) +{ + if (this->superColor != superColor) { + this->superColor = superColor; + this->update(); + } +} + +QColor LunarCalendarItem::getLunarColor() const +{ + return this->lunarColor; +} + +void LunarCalendarItem::setLunarColor(const QColor &lunarColor) +{ + if (this->lunarColor != lunarColor) { + this->lunarColor = lunarColor; + this->update(); + } +} + +QColor LunarCalendarItem::getCurrentTextColor() const +{ + return this->currentTextColor; +} + +void LunarCalendarItem::setCurrentTextColor(const QColor ¤tTextColor) +{ + if (this->currentTextColor != currentTextColor) { + this->currentTextColor = currentTextColor; + this->update(); + } +} + +QColor LunarCalendarItem::getOtherTextColor() const +{ + return this->otherTextColor; +} + +void LunarCalendarItem::setOtherTextColor(const QColor &otherTextColor) +{ + if (this->otherTextColor != otherTextColor) { + this->otherTextColor = otherTextColor; + this->update(); + } +} + +QColor LunarCalendarItem::getSelectTextColor() const +{ + return this->selectTextColor; +} + +void LunarCalendarItem::setSelectTextColor(const QColor &selectTextColor) +{ + if (this->selectTextColor != selectTextColor) { + this->selectTextColor = selectTextColor; + this->update(); + } +} + +QColor LunarCalendarItem::getHoverTextColor() const +{ + return this->hoverTextColor; +} + +void LunarCalendarItem::setHoverTextColor(const QColor &hoverTextColor) +{ + if (this->hoverTextColor != hoverTextColor) { + this->hoverTextColor = hoverTextColor; + this->update(); + } +} + +QColor LunarCalendarItem::getCurrentLunarColor() const +{ + return this->currentLunarColor; +} + +void LunarCalendarItem::setCurrentLunarColor(const QColor ¤tLunarColor) +{ + if (this->currentLunarColor != currentLunarColor) { + this->currentLunarColor = currentLunarColor; + this->update(); + } +} + + +QColor LunarCalendarItem::getOtherLunarColor() const +{ + return this->otherLunarColor; +} + +void LunarCalendarItem::setOtherLunarColor(const QColor &otherLunarColor) +{ + if (this->otherLunarColor != otherLunarColor) { + this->otherLunarColor = otherLunarColor; + this->update(); + } +} + +QColor LunarCalendarItem::getSelectLunarColor() const +{ + return this->selectLunarColor; +} + +void LunarCalendarItem::setSelectLunarColor(const QColor &selectLunarColor) +{ + if (this->selectLunarColor != selectLunarColor) { + this->selectLunarColor = selectLunarColor; + this->update(); + } +} + +QColor LunarCalendarItem::getHoverLunarColor() const +{ + return this->hoverLunarColor; +} + +void LunarCalendarItem::setHoverLunarColor(const QColor &hoverLunarColor) +{ + if (this->hoverLunarColor != hoverLunarColor) { + this->hoverLunarColor = hoverLunarColor; + this->update(); + } +} + +QColor LunarCalendarItem::getCurrentBgColor() const +{ + return this->currentBgColor; +} + +void LunarCalendarItem::setCurrentBgColor(const QColor ¤tBgColor) +{ + if (this->currentBgColor != currentBgColor) { + this->currentBgColor = currentBgColor; + this->update(); + } +} + + +QColor LunarCalendarItem::getOtherBgColor() const +{ + return this->otherBgColor; +} + +void LunarCalendarItem::setOtherBgColor(const QColor &otherBgColor) +{ + if (this->otherBgColor != otherBgColor) { + this->otherBgColor = otherBgColor; + this->update(); + } +} + +QColor LunarCalendarItem::getSelectBgColor() const +{ + return this->selectBgColor; +} + +void LunarCalendarItem::setSelectBgColor(const QColor &selectBgColor) +{ + if (this->selectBgColor != selectBgColor) { + this->selectBgColor = selectBgColor; + this->update(); + } +} + +QColor LunarCalendarItem::getHoverBgColor() const +{ + return this->hoverBgColor; +} + +void LunarCalendarItem::setHoverBgColor(const QColor &hoverBgColor) +{ + if (this->hoverBgColor != hoverBgColor) { + this->hoverBgColor = hoverBgColor; + this->update(); + } +} diff --git a/widget/lunarcalendarwidget/lunarcalendaritem.h b/widget/lunarcalendarwidget/lunarcalendaritem.h new file mode 100644 index 0000000..d218456 --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendaritem.h @@ -0,0 +1,213 @@ +#ifndef LUNARCALENDARITEM_H +#define LUNARCALENDARITEM_H + +#include +#include + +#ifdef quc +class Q_DECL_EXPORT LunarCalendarItem : public QWidget +#else +class LunarCalendarItem : public QWidget +#endif + +{ + Q_OBJECT + Q_ENUMS(DayType) + Q_ENUMS(SelectType) + + Q_PROPERTY(bool select READ getSelect WRITE setSelect) + Q_PROPERTY(bool showLunar READ getShowLunar WRITE setShowLunar) + Q_PROPERTY(QString bgImage READ getBgImage WRITE setBgImage) + Q_PROPERTY(SelectType selectType READ getSelectType WRITE setSelectType) + + Q_PROPERTY(QDate date READ getDate WRITE setDate) + Q_PROPERTY(QString lunar READ getLunar WRITE setLunar) + Q_PROPERTY(DayType dayType READ getDayType WRITE setDayType) + + Q_PROPERTY(QColor borderColor READ getBorderColor WRITE setBorderColor) + Q_PROPERTY(QColor weekColor READ getWeekColor WRITE setWeekColor) + Q_PROPERTY(QColor superColor READ getSuperColor WRITE setSuperColor) + Q_PROPERTY(QColor lunarColor READ getLunarColor WRITE setLunarColor) + + Q_PROPERTY(QColor currentTextColor READ getCurrentTextColor WRITE setCurrentTextColor) + Q_PROPERTY(QColor otherTextColor READ getOtherTextColor WRITE setOtherTextColor) + Q_PROPERTY(QColor selectTextColor READ getSelectTextColor WRITE setSelectTextColor) + Q_PROPERTY(QColor hoverTextColor READ getHoverTextColor WRITE setHoverTextColor) + + Q_PROPERTY(QColor currentLunarColor READ getCurrentLunarColor WRITE setCurrentLunarColor) + Q_PROPERTY(QColor otherLunarColor READ getOtherLunarColor WRITE setOtherLunarColor) + Q_PROPERTY(QColor selectLunarColor READ getSelectLunarColor WRITE setSelectLunarColor) + Q_PROPERTY(QColor hoverLunarColor READ getHoverLunarColor WRITE setHoverLunarColor) + + Q_PROPERTY(QColor currentBgColor READ getCurrentBgColor WRITE setCurrentBgColor) + Q_PROPERTY(QColor otherBgColor READ getOtherBgColor WRITE setOtherBgColor) + Q_PROPERTY(QColor selectBgColor READ getSelectBgColor WRITE setSelectBgColor) + Q_PROPERTY(QColor hoverBgColor READ getHoverBgColor WRITE setHoverBgColor) + +public: + enum DayType { + DayType_MonthPre = 0, //上月剩余天数 + DayType_MonthNext = 1, //下个月的天数 + DayType_MonthCurrent = 2, //当月天数 + DayType_WeekEnd = 3 //周末 + }; + + enum SelectType { + SelectType_Rect = 0, //矩形背景 + SelectType_Circle = 1, //圆形背景 + SelectType_Triangle = 2, //带三角标 + SelectType_Image = 3 //图片背景 + }; + + explicit LunarCalendarItem(QWidget *parent = 0); + +protected: + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void paintEvent(QPaintEvent *); + void drawBg(QPainter *painter); + void drawBgCurrent(QPainter *painter, const QColor &color); + void drawDay(QPainter *painter); + void drawLunar(QPainter *painter); + +private: + bool hover; //鼠标是否悬停 + bool pressed; //鼠标是否按下 + QStringList listDayName; //农历日期 + + bool select; //是否选中 + bool showLunar; //显示农历 + QString bgImage; //背景图片 + SelectType selectType; //选中模式 + + QDate date; //当前日期 + QString lunar; //农历信息 + DayType dayType; //当前日类型 + + QColor borderColor; //边框颜色 + QColor weekColor; //周末颜色 + QColor superColor; //角标颜色 + QColor lunarColor; //农历节日颜色 + + QColor currentTextColor; //当前月文字颜色 + QColor otherTextColor; //其他月文字颜色 + QColor selectTextColor; //选中日期文字颜色 + QColor hoverTextColor; //悬停日期文字颜色 + + QColor currentLunarColor; //当前月农历文字颜色 + QColor otherLunarColor; //其他月农历文字颜色 + QColor selectLunarColor; //选中日期农历文字颜色 + QColor hoverLunarColor; //悬停日期农历文字颜色 + + QColor currentBgColor; //当前月背景颜色 + QColor otherBgColor; //其他月背景颜色 + QColor selectBgColor; //选中日期背景颜色 + QColor hoverBgColor; //悬停日期背景颜色 + +public: + //默认尺寸和最小尺寸 + QSize sizeHint() const; + QSize minimumSizeHint() const; + + //获取和设置是否选中 + bool getSelect() const; + void setSelect(bool select); + + //获取和设置是否显示农历信息 + bool getShowLunar() const; + void setShowLunar(bool showLunar); + + //获取和设置背景图片 + QString getBgImage() const; + void setBgImage(const QString &bgImage); + + //获取和设置选中背景样式 + SelectType getSelectType() const; + void setSelectType(const SelectType &selectType); + + //获取和设置日期 + QDate getDate() const; + void setDate(const QDate &date); + + //获取和设置农历 + QString getLunar() const; + void setLunar(const QString &lunar); + + //获取和设置类型 + DayType getDayType() const; + void setDayType(const DayType &dayType); + + //设置日期/农历/类型 + void setDate(const QDate &date, const QString &lunar, const DayType &dayType); + + //获取和设置边框颜色 + QColor getBorderColor() const; + void setBorderColor(const QColor &borderColor); + + //获取和设置周末颜色 + QColor getWeekColor() const; + void setWeekColor(const QColor &weekColor); + + //获取和设置角标颜色 + QColor getSuperColor() const; + void setSuperColor(const QColor &superColor); + + //获取和设置农历节日颜色 + QColor getLunarColor() const; + void setLunarColor(const QColor &lunarColor); + + //获取和设置当前月文字颜色 + QColor getCurrentTextColor() const; + void setCurrentTextColor(const QColor ¤tTextColor); + + //获取和设置其他月文字颜色 + QColor getOtherTextColor() const; + void setOtherTextColor(const QColor &otherTextColor); + + //获取和设置选中日期文字颜色 + QColor getSelectTextColor() const; + void setSelectTextColor(const QColor &selectTextColor); + + //获取和设置悬停日期文字颜色 + QColor getHoverTextColor() const; + void setHoverTextColor(const QColor &hoverTextColor); + + //获取和设置当前月农历文字颜色 + QColor getCurrentLunarColor() const; + void setCurrentLunarColor(const QColor ¤tLunarColor); + + //获取和设置其他月农历文字颜色 + QColor getOtherLunarColor() const; + void setOtherLunarColor(const QColor &otherLunarColor); + + //获取和设置选中日期农历文字颜色 + QColor getSelectLunarColor() const; + void setSelectLunarColor(const QColor &selectLunarColor); + + //获取和设置悬停日期农历文字颜色 + QColor getHoverLunarColor() const; + void setHoverLunarColor(const QColor &hoverLunarColor); + + //获取和设置当前月背景颜色 + QColor getCurrentBgColor() const; + void setCurrentBgColor(const QColor ¤tBgColor); + + //获取和设置其他月背景颜色 + QColor getOtherBgColor() const; + void setOtherBgColor(const QColor &otherBgColor); + + //获取和设置选中日期背景颜色 + QColor getSelectBgColor() const; + void setSelectBgColor(const QColor &selectBgColor); + + //获取和设置悬停日期背景颜色 + QColor getHoverBgColor() const; + void setHoverBgColor(const QColor &hoverBgColor); + +Q_SIGNALS: + void clicked(const QDate &date, const LunarCalendarItem::DayType &dayType); +}; + +#endif // LUNARCALENDARITEM_H diff --git a/widget/lunarcalendarwidget/lunarcalendarwidget.cpp b/widget/lunarcalendarwidget/lunarcalendarwidget.cpp new file mode 100644 index 0000000..9bacaa8 --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendarwidget.cpp @@ -0,0 +1,838 @@ +#pragma execution_character_set("utf-8") + +#include "lunarcalendarwidget.h" +#include "qfontdatabase.h" +#include "qdatetime.h" +#include "qlayout.h" +#include "qlabel.h" +#include "qpushbutton.h" +#include "qtoolbutton.h" +#include "qcombobox.h" +#include "qdebug.h" + +LunarCalendarWidget::LunarCalendarWidget(QWidget *parent) : QWidget(parent) +{ + //判断图形字体是否存在,不存在则加入 + QFontDatabase fontDb; + if (!fontDb.families().contains("FontAwesome")) { + int fontId = fontDb.addApplicationFont(":/font/fontawesome-webfont.ttf"); + QStringList fontName = fontDb.applicationFontFamilies(fontId); + if (fontName.count() == 0) { + qDebug() << "load fontawesome-webfont.ttf error"; + } + } + + if (fontDb.families().contains("FontAwesome")) { + iconFont = QFont("FontAwesome"); +#if (QT_VERSION >= QT_VERSION_CHECK(4,8,0)) + iconFont.setHintingPreference(QFont::PreferNoHinting); +#endif + } + + btnClick = false; + + calendarStyle = CalendarStyle_Red; + weekNameFormat = WeekNameFormat_Short; + date = QDate::currentDate(); + + weekTextColor = QColor(255, 255, 255); + weekBgColor = QColor(22, 160, 134); + + showLunar = true; + bgImage = ":/image/bg_calendar.png"; + selectType = SelectType_Rect; + + borderColor = QColor(180, 180, 180); + weekColor = QColor(255, 0, 0); + superColor = QColor(255, 129, 6); + lunarColor = QColor(55, 156, 238); + + currentTextColor = QColor(0, 0, 0); + otherTextColor = QColor(200, 200, 200); + selectTextColor = QColor(255, 255, 255); + hoverTextColor = QColor(250, 250, 250); + + currentLunarColor = QColor(150, 150, 150); + otherLunarColor = QColor(200, 200, 200); + selectLunarColor = QColor(255, 255, 255); + hoverLunarColor = QColor(250, 250, 250); + + currentBgColor = QColor(255, 255, 255); + otherBgColor = QColor(240, 240, 240); + selectBgColor = QColor(208, 47, 18); + hoverBgColor = QColor(204, 183, 180); + + initWidget(); + initStyle(); + initDate(); +} + +LunarCalendarWidget::~LunarCalendarWidget() +{ +} + +void LunarCalendarWidget::initWidget() +{ + setObjectName("lunarCalendarWidget"); + + //顶部widget + QWidget *widgetTop = new QWidget; + widgetTop->setObjectName("widgetTop"); + widgetTop->setMinimumHeight(35); + + //上一年按钮 + QToolButton *btnPrevYear = new QToolButton; + btnPrevYear->setObjectName("btnPrevYear"); + btnPrevYear->setFixedWidth(35); + btnPrevYear->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + btnPrevYear->setFont(iconFont); + btnPrevYear->setText(QChar(0xf060)); + + //下一年按钮 + QToolButton *btnNextYear = new QToolButton; + btnNextYear->setObjectName("btnNextYear"); + btnNextYear->setFixedWidth(35); + btnNextYear->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + btnNextYear->setFont(iconFont); + btnNextYear->setText(QChar(0xf061)); + + //上个月按钮 + QToolButton *btnPrevMonth = new QToolButton; + btnPrevMonth->setObjectName("btnPrevMonth"); + btnPrevMonth->setFixedWidth(35); + btnPrevMonth->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + btnPrevMonth->setFont(iconFont); + btnPrevMonth->setText(QChar(0xf060)); + + //下个月按钮 + QToolButton *btnNextMonth = new QToolButton; + btnNextMonth->setObjectName("btnNextMonth"); + btnNextMonth->setFixedWidth(35); + btnNextMonth->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + btnNextMonth->setFont(iconFont); + btnNextMonth->setText(QChar(0xf061)); + + //转到今天 + QPushButton *btnToday = new QPushButton; + btnToday->setObjectName("btnToday"); + btnToday->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + btnToday->setText("转到今天"); + + //年份下拉框 + cboxYear = new QComboBox; + cboxYear->setObjectName("cboxYear"); + for (int i = 1901; i <= 2099; ++i) { + cboxYear->addItem(QString("%1年").arg(i)); + } + + //月份下拉框 + cboxMonth = new QComboBox; + cboxMonth->setObjectName("cboxMonth"); + for (int i = 1; i <= 12; ++i) { + cboxMonth->addItem(QString("%1月").arg(i)); + } + + //中间用个空widget隔开 + QWidget *widgetBlank1 = new QWidget; + widgetBlank1->setFixedWidth(5); + QWidget *widgetBlank2 = new QWidget; + widgetBlank2->setFixedWidth(5); + + //顶部横向布局 + QHBoxLayout *layoutTop = new QHBoxLayout(widgetTop); + layoutTop->setContentsMargins(0, 0, 0, 9); + layoutTop->addWidget(btnPrevYear); + layoutTop->addWidget(cboxYear); + layoutTop->addWidget(btnNextYear); + layoutTop->addWidget(widgetBlank1); + + layoutTop->addWidget(btnPrevMonth); + layoutTop->addWidget(cboxMonth); + layoutTop->addWidget(btnNextMonth); + layoutTop->addWidget(widgetBlank2); + layoutTop->addWidget(btnToday); + + //星期widget + QWidget *widgetWeek = new QWidget; + widgetWeek->setObjectName("widgetWeek"); + widgetWeek->setMinimumHeight(30); + + //星期布局 + QHBoxLayout *layoutWeek = new QHBoxLayout(widgetWeek); + layoutWeek->setContentsMargins(0, 0, 0, 0); + layoutWeek->setSpacing(0); + + for (int i = 0; i < 7; ++i) { + QLabel *lab = new QLabel; + lab->setAlignment(Qt::AlignCenter); + layoutWeek->addWidget(lab); + labWeeks.append(lab); + } + + setWeekNameFormat(WeekNameFormat_Long); + + //日期标签widget + QWidget *widgetBody = new QWidget; + widgetBody->setObjectName("widgetBody"); + + //日期标签布局 + QGridLayout *layoutBody = new QGridLayout(widgetBody); + layoutBody->setContentsMargins(1, 1, 1, 1); + layoutBody->setHorizontalSpacing(0); + layoutBody->setVerticalSpacing(0); + + //逐个添加日标签 + for (int i = 0; i < 42; ++i) { + LunarCalendarItem *item = new LunarCalendarItem; + connect(item, SIGNAL(clicked(QDate, LunarCalendarItem::DayType)), this, SLOT(clicked(QDate, LunarCalendarItem::DayType))); + layoutBody->addWidget(item, i / 7, i % 7); + items << item; + } + + //主布局 + QVBoxLayout *verLayoutCalendar = new QVBoxLayout(this); + verLayoutCalendar->setContentsMargins(0, 0, 0, 0); + verLayoutCalendar->setSpacing(0); + verLayoutCalendar->addWidget(widgetTop); + verLayoutCalendar->addWidget(widgetWeek); + verLayoutCalendar->addWidget(widgetBody, 1); + + //绑定按钮和下拉框信号 + connect(btnPrevYear, SIGNAL(clicked(bool)), this, SLOT(showPreviousYear())); + connect(btnNextYear, SIGNAL(clicked(bool)), this, SLOT(showNextYear())); + connect(btnPrevMonth, SIGNAL(clicked(bool)), this, SLOT(showPreviousMonth())); + connect(btnNextMonth, SIGNAL(clicked(bool)), this, SLOT(showNextMonth())); + connect(btnToday, SIGNAL(clicked(bool)), this, SLOT(showToday())); + connect(cboxYear, SIGNAL(currentIndexChanged(int)), this, SLOT(yearChanged(int))); + connect(cboxMonth, SIGNAL(currentIndexChanged(int)), this, SLOT(monthChanged(int))); +} + +void LunarCalendarWidget::initStyle() +{ + //设置样式 + QStringList qss; + + //星期名称样式 + qss.append(QString("QLabel{background:%1;color:%2;}").arg(weekBgColor.name()).arg(weekTextColor.name())); + + //边框 + qss.append(QString("QWidget#widgetBody{border:1px solid %1;}").arg(borderColor.name())); + + //顶部下拉框 + qss.append(QString("QToolButton{padding:0px;background:none;border:none;border-radius:5px;}")); + qss.append(QString("QToolButton:hover{background:#16A085;color:#FFFFFF;}")); + + //转到今天 + qss.append(QString("QPushButton{background:#16A085;color:#FFFFFF;border-radius:5px;}")); + + //自定义日控件颜色 + QString strSelectType; + if (selectType == SelectType_Rect) { + strSelectType = "SelectType_Rect"; + } else if (selectType == SelectType_Circle) { + strSelectType = "SelectType_Circle"; + } else if (selectType == SelectType_Triangle) { + strSelectType = "SelectType_Triangle"; + } else if (selectType == SelectType_Image) { + strSelectType = "SelectType_Image"; + } + + qss.append(QString("LunarCalendarItem{qproperty-showLunar:%1;}").arg(showLunar)); + qss.append(QString("LunarCalendarItem{qproperty-bgImage:%1;}").arg(bgImage)); + qss.append(QString("LunarCalendarItem{qproperty-selectType:%1;}").arg(strSelectType)); + qss.append(QString("LunarCalendarItem{qproperty-borderColor:%1;}").arg(borderColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-weekColor:%1;}").arg(weekColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-superColor:%1;}").arg(superColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-lunarColor:%1;}").arg(lunarColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-currentTextColor:%1;}").arg(currentTextColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-otherTextColor:%1;}").arg(otherTextColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-selectTextColor:%1;}").arg(selectTextColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-hoverTextColor:%1;}").arg(hoverTextColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-currentLunarColor:%1;}").arg(currentLunarColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-otherLunarColor:%1;}").arg(otherLunarColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-selectLunarColor:%1;}").arg(selectLunarColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-hoverLunarColor:%1;}").arg(hoverLunarColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-currentBgColor:%1;}").arg(currentBgColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-otherBgColor:%1;}").arg(otherBgColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-selectBgColor:%1;}").arg(selectBgColor.name())); + qss.append(QString("LunarCalendarItem{qproperty-hoverBgColor:%1;}").arg(hoverBgColor.name())); + + this->setStyleSheet(qss.join("")); +} + +//初始化日期面板 +void LunarCalendarWidget::initDate() +{ + int year = date.year(); + int month = date.month(); + int day = date.day(); + + //设置为今天,设置变量防止重复触发 + btnClick = true; + cboxYear->setCurrentIndex(cboxYear->findText(QString("%1年").arg(year))); + cboxMonth->setCurrentIndex(cboxMonth->findText(QString("%1月").arg(month))); + btnClick = false; + + //首先判断当前月的第一天是星期几 + int week = LunarCalendarInfo::Instance()->getFirstDayOfWeek(year, month); + //当前月天数 + int countDay = LunarCalendarInfo::Instance()->getMonthDays(year, month); + //上月天数 + int countDayPre = LunarCalendarInfo::Instance()->getMonthDays(1 == month ? year - 1 : year, 1 == month ? 12 : month - 1); + + //如果上月天数上月刚好一周则另外处理 + int startPre, endPre, startNext, endNext, index, tempYear, tempMonth, tempDay; + if (0 == week) { + startPre = 0; + endPre = 7; + startNext = 0; + endNext = 42 - (countDay + 7); + } else { + startPre = 0; + endPre = week; + startNext = week + countDay; + endNext = 42; + } + + //纠正1月份前面部分偏差,1月份前面部分是上一年12月份 + tempYear = year; + tempMonth = month - 1; + if (tempMonth < 1) { + tempYear--; + tempMonth = 12; + } + + //显示上月天数 + for (int i = startPre; i < endPre; ++i) { + index = i; + tempDay = countDayPre - endPre + i + 1; + + QDate date(tempYear, tempMonth, tempDay); + QString lunar = LunarCalendarInfo::Instance()->getLunarDay(tempYear, tempMonth, tempDay); + items.at(index)->setDate(date, lunar, LunarCalendarItem::DayType_MonthPre); + } + + //纠正12月份后面部分偏差,12月份后面部分是下一年1月份 + tempYear = year; + tempMonth = month + 1; + if (tempMonth > 12) { + tempYear++; + tempMonth = 1; + } + + //显示下月天数 + for (int i = startNext; i < endNext; ++i) { + index = 42 - endNext + i; + tempDay = i - startNext + 1; + + QDate date(tempYear, tempMonth, tempDay); + QString lunar = LunarCalendarInfo::Instance()->getLunarDay(tempYear, tempMonth, tempDay); + items.at(index)->setDate(date, lunar, LunarCalendarItem::DayType_MonthNext); + } + + //重新置为当前年月 + tempYear = year; + tempMonth = month; + + //显示当前月 + for (int i = week; i < (countDay + week); ++i) { + index = (0 == week ? (i + 7) : i); + tempDay = i - week + 1; + + QDate date(tempYear, tempMonth, tempDay); + QString lunar = LunarCalendarInfo::Instance()->getLunarDay(tempYear, tempMonth, tempDay); + if (0 == (i % 7) || 6 == (i % 7)) { + items.at(index)->setDate(date, lunar, LunarCalendarItem::DayType_WeekEnd); + } else { + items.at(index)->setDate(date, lunar, LunarCalendarItem::DayType_MonthCurrent); + } + } + + dayChanged(this->date); +} + +void LunarCalendarWidget::yearChanged(int) +{ + //如果是单击按钮切换的日期变动则不需要触发 + if (btnClick) { + return; + } + + QString arg1 = cboxYear->currentText(); + int year = arg1.mid(0, arg1.length() - 1).toInt(); + int month = date.month(); + int day = date.day(); + dateChanged(year, month, day); +} + +void LunarCalendarWidget::monthChanged(int) +{ + //如果是单击按钮切换的日期变动则不需要触发 + if (btnClick) { + return; + } + + QString arg1 = cboxMonth->currentText(); + int year = date.year(); + int month = arg1.mid(0, arg1.length() - 1).toInt(); + int day = date.day(); + dateChanged(year, month, day); +} + +void LunarCalendarWidget::clicked(const QDate &date, const LunarCalendarItem::DayType &dayType) +{ + if (LunarCalendarItem::DayType_MonthPre == dayType) { + showPreviousMonth(); + } else if (LunarCalendarItem::DayType_MonthNext == dayType) { + showNextMonth(); + } else { + this->date = date; + dayChanged(this->date); + } +} + +void LunarCalendarWidget::dayChanged(const QDate &date) +{ + //计算星期几,当前天对应标签索引=日期+星期几-1 + int year = date.year(); + int month = date.month(); + int day = date.day(); + int week = LunarCalendarInfo::Instance()->getFirstDayOfWeek(year, month); + //qDebug() << QString("%1-%2-%3").arg(year).arg(month).arg(day); + + //选中当前日期,其他日期恢复,这里还有优化空间,比方说类似单选框机制 + for (int i = 0; i < 42; ++i) { + //当月第一天是星期天要另外计算 + int index = day + week - 1; + if (week == 0) { + index = day + 6; + } + + items.at(i)->setSelect(i == index); + } + + //发送日期单击信号 + Q_EMIT clicked(date); + //发送日期更新信号 + Q_EMIT selectionChanged(); +} + +void LunarCalendarWidget::dateChanged(int year, int month, int day) +{ + //如果原有天大于28则设置为1,防止出错 + date.setDate(year, month, day > 28 ? 1 : day); + initDate(); +} + +QSize LunarCalendarWidget::sizeHint() const +{ + return QSize(600, 500); +} + +QSize LunarCalendarWidget::minimumSizeHint() const +{ + return QSize(200, 150); +} + +LunarCalendarWidget::CalendarStyle LunarCalendarWidget::getCalendarStyle() const +{ + return this->calendarStyle; +} + +void LunarCalendarWidget::setCalendarStyle(const LunarCalendarWidget::CalendarStyle &calendarStyle) +{ + if (this->calendarStyle != calendarStyle) { + this->calendarStyle = calendarStyle; + } +} + +LunarCalendarWidget::WeekNameFormat LunarCalendarWidget::getWeekNameFormat() const +{ + return this->weekNameFormat; +} + +void LunarCalendarWidget::setWeekNameFormat(const LunarCalendarWidget::WeekNameFormat &weekNameFormat) +{ + if (this->weekNameFormat != weekNameFormat) { + this->weekNameFormat = weekNameFormat; + + QStringList listWeek; + if (weekNameFormat == WeekNameFormat_Short) { + listWeek << "日" << "一" << "二" << "三" << "四" << "五" << "六"; + } else if (weekNameFormat == WeekNameFormat_Normal) { + listWeek << "周日" << "周一" << "周二" << "周三" << "周四" << "周五" << "周六"; + } else if (weekNameFormat == WeekNameFormat_Long) { + listWeek << "星期天" << "星期一" << "星期二" << "星期三" << "星期四" << "星期五" << "星期六"; + } else if (weekNameFormat == WeekNameFormat_En) { + listWeek << "Sun" << "Mon" << "Tue" << "Wed" << "Thu" << "Fri" << "Sat"; + } + + //逐个添加日期文字 + for (int i = 0; i < 7; ++i) { + labWeeks.at(i)->setText(listWeek.at(i)); + } + } +} + +QDate LunarCalendarWidget::getDate() const +{ + return this->date; +} + +void LunarCalendarWidget::setDate(const QDate &date) +{ + if (this->date != date) { + this->date = date; + initDate(); + } +} + +QColor LunarCalendarWidget::getWeekTextColor() const +{ + return this->weekTextColor; +} + +void LunarCalendarWidget::setWeekTextColor(const QColor &weekTextColor) +{ + if (this->weekTextColor != weekTextColor) { + this->weekTextColor = weekTextColor; + this->initStyle(); + } +} + + +QColor LunarCalendarWidget::getWeekBgColor() const +{ + return this->weekBgColor; +} + +void LunarCalendarWidget::setWeekBgColor(const QColor &weekBgColor) +{ + if (this->weekBgColor != weekBgColor) { + this->weekBgColor = weekBgColor; + this->initStyle(); + } +} + +bool LunarCalendarWidget::getShowLunar() const +{ + return this->showLunar; +} + +void LunarCalendarWidget::setShowLunar(bool showLunar) +{ + if (this->showLunar != showLunar) { + this->showLunar = showLunar; + this->initStyle(); + } +} + +QString LunarCalendarWidget::getBgImage() const +{ + return this->bgImage; +} + +void LunarCalendarWidget::setBgImage(const QString &bgImage) +{ + if (this->bgImage != bgImage) { + this->bgImage = bgImage; + this->initStyle(); + } +} + +LunarCalendarWidget::SelectType LunarCalendarWidget::getSelectType() const +{ + return this->selectType; +} + +void LunarCalendarWidget::setSelectType(const LunarCalendarWidget::SelectType &selectType) +{ + if (this->selectType != selectType) { + this->selectType = selectType; + this->initStyle(); + } +} + + +QColor LunarCalendarWidget::getBorderColor() const +{ + return this->borderColor; +} + +void LunarCalendarWidget::setBorderColor(const QColor &borderColor) +{ + if (this->borderColor != borderColor) { + this->borderColor = borderColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getWeekColor() const +{ + return this->weekColor; +} + +void LunarCalendarWidget::setWeekColor(const QColor &weekColor) +{ + if (this->weekColor != weekColor) { + this->weekColor = weekColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getSuperColor() const +{ + return this->superColor; +} + +void LunarCalendarWidget::setSuperColor(const QColor &superColor) +{ + if (this->superColor != superColor) { + this->superColor = superColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getLunarColor() const +{ + return this->lunarColor; +} + +void LunarCalendarWidget::setLunarColor(const QColor &lunarColor) +{ + if (this->lunarColor != lunarColor) { + this->lunarColor = lunarColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getCurrentTextColor() const +{ + return this->currentTextColor; +} + +void LunarCalendarWidget::setCurrentTextColor(const QColor ¤tTextColor) +{ + if (this->currentTextColor != currentTextColor) { + this->currentTextColor = currentTextColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getOtherTextColor() const +{ + return this->otherTextColor; +} + +void LunarCalendarWidget::setOtherTextColor(const QColor &otherTextColor) +{ + if (this->otherTextColor != otherTextColor) { + this->otherTextColor = otherTextColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getSelectTextColor() const +{ + return this->selectTextColor; +} + +void LunarCalendarWidget::setSelectTextColor(const QColor &selectTextColor) +{ + if (this->selectTextColor != selectTextColor) { + this->selectTextColor = selectTextColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getHoverTextColor() const +{ + return this->hoverTextColor; +} + +void LunarCalendarWidget::setHoverTextColor(const QColor &hoverTextColor) +{ + if (this->hoverTextColor != hoverTextColor) { + this->hoverTextColor = hoverTextColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getCurrentLunarColor() const +{ + return this->currentLunarColor; +} + +void LunarCalendarWidget::setCurrentLunarColor(const QColor ¤tLunarColor) +{ + if (this->currentLunarColor != currentLunarColor) { + this->currentLunarColor = currentLunarColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getOtherLunarColor() const +{ + return this->otherLunarColor; +} + +void LunarCalendarWidget::setOtherLunarColor(const QColor &otherLunarColor) +{ + if (this->otherLunarColor != otherLunarColor) { + this->otherLunarColor = otherLunarColor; + this->initStyle(); + } +} + + +QColor LunarCalendarWidget::getSelectLunarColor() const +{ + return this->selectLunarColor; +} + +void LunarCalendarWidget::setSelectLunarColor(const QColor &selectLunarColor) +{ + if (this->selectLunarColor != selectLunarColor) { + this->selectLunarColor = selectLunarColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getHoverLunarColor() const +{ + return this->hoverLunarColor; +} + +void LunarCalendarWidget::setHoverLunarColor(const QColor &hoverLunarColor) +{ + if (this->hoverLunarColor != hoverLunarColor) { + this->hoverLunarColor = hoverLunarColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getCurrentBgColor() const +{ + return this->currentBgColor; +} + +void LunarCalendarWidget::setCurrentBgColor(const QColor ¤tBgColor) +{ + if (this->currentBgColor != currentBgColor) { + this->currentBgColor = currentBgColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getOtherBgColor() const +{ + return this->otherBgColor; +} + +void LunarCalendarWidget::setOtherBgColor(const QColor &otherBgColor) +{ + if (this->otherBgColor != otherBgColor) { + this->otherBgColor = otherBgColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getSelectBgColor() const +{ + return this->selectBgColor; +} + +void LunarCalendarWidget::setSelectBgColor(const QColor &selectBgColor) +{ + if (this->selectBgColor != selectBgColor) { + this->selectBgColor = selectBgColor; + this->initStyle(); + } +} + +QColor LunarCalendarWidget::getHoverBgColor() const +{ + return this->hoverBgColor; +} + +void LunarCalendarWidget::setHoverBgColor(const QColor &hoverBgColor) +{ + if (this->hoverBgColor != hoverBgColor) { + this->hoverBgColor = hoverBgColor; + this->initStyle(); + } +} + +//显示上一年 +void LunarCalendarWidget::showPreviousYear() +{ + int year = date.year(); + int month = date.month(); + int day = date.day(); + if (year <= 1901) { + return; + } + + year--; + dateChanged(year, month, day); +} + +//显示下一年 +void LunarCalendarWidget::showNextYear() +{ + int year = date.year(); + int month = date.month(); + int day = date.day(); + if (year >= 2099) { + return; + } + + year++; + dateChanged(year, month, day); +} + +//显示上月日期 +void LunarCalendarWidget::showPreviousMonth() +{ + int year = date.year(); + int month = date.month(); + int day = date.day(); + if (year <= 1901 && month == 1) { + return; + } + + month--; + if (month < 1) { + month = 12; + year--; + } + + dateChanged(year, month, day); +} + +//显示下月日期 +void LunarCalendarWidget::showNextMonth() +{ + int year = date.year(); + int month = date.month(); + int day = date.day(); + if (year >= 2099 && month == 12) { + return; + } + + month++; + if (month > 12) { + month = 1; + year++; + } + + dateChanged(year, month, day); +} + +//转到今天 +void LunarCalendarWidget::showToday() +{ + date = QDate::currentDate(); + initDate(); + dayChanged(date); +} diff --git a/widget/lunarcalendarwidget/lunarcalendarwidget.h b/widget/lunarcalendarwidget/lunarcalendarwidget.h new file mode 100644 index 0000000..f00ee03 --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendarwidget.h @@ -0,0 +1,261 @@ +#ifndef LUNARCALENDARWIDGET_H +#define LUNARCALENDARWIDGET_H + +/** + * 自定义农历控件 作者:倪大侠 整理:feiyangqingyun(QQ:517216493) 2017-11-17 + * 1. 可设置边框颜色、周末颜色、角标颜色、农历节日颜色。 + * 2. 可设置当前月文字颜色、其他月文字颜色、选中日期文字颜色、悬停日期文字颜色。 + * 3. 可设置当前月农历文字颜色、其他月农历文字颜色、选中日期农历文字颜色、悬停日期农历文字颜色。 + * 4. 可设置当前月背景颜色、其他月背景颜色、选中日期背景颜色、悬停日期背景颜色。 + * 5. 可设置三种选中背景模式,矩形背景、圆形背景、图片背景。 + * 6. 可直接切换到上一年、下一年、上一月、下一月、转到今天。 + * 7. 可设置是否显示农历信息,不显示则当做正常的日历使用。 + * 8. 支持1901年-2099年范围。 + * 9. 很方便改成多选日期。 + */ + +#include +#include + +#include "lunarcalendarinfo.h" +#include "lunarcalendaritem.h" + +class QLabel; +class QComboBox; +class LunarCalendarItem; + +#ifdef quc +class Q_DECL_EXPORT LunarCalendarWidget : public QWidget +#else +class LunarCalendarWidget : public QWidget +#endif + +{ + Q_OBJECT + Q_ENUMS(CalendarStyle) + Q_ENUMS(WeekNameFormat) + Q_ENUMS(SelectType) + + Q_PROPERTY(CalendarStyle calendarStyle READ getCalendarStyle WRITE setCalendarStyle) + Q_PROPERTY(WeekNameFormat weekNameFormat READ getWeekNameFormat WRITE setWeekNameFormat) + Q_PROPERTY(QDate date READ getDate WRITE setDate) + + Q_PROPERTY(QColor weekTextColor READ getWeekTextColor WRITE setWeekTextColor) + Q_PROPERTY(QColor weekBgColor READ getWeekBgColor WRITE setWeekBgColor) + + Q_PROPERTY(bool showLunar READ getShowLunar WRITE setShowLunar) + Q_PROPERTY(QString bgImage READ getBgImage WRITE setBgImage) + Q_PROPERTY(SelectType selectType READ getSelectType WRITE setSelectType) + + Q_PROPERTY(QColor borderColor READ getBorderColor WRITE setBorderColor) + Q_PROPERTY(QColor weekColor READ getWeekColor WRITE setWeekColor) + Q_PROPERTY(QColor superColor READ getSuperColor WRITE setSuperColor) + Q_PROPERTY(QColor lunarColor READ getLunarColor WRITE setLunarColor) + + Q_PROPERTY(QColor currentTextColor READ getCurrentTextColor WRITE setCurrentTextColor) + Q_PROPERTY(QColor otherTextColor READ getOtherTextColor WRITE setOtherTextColor) + Q_PROPERTY(QColor selectTextColor READ getSelectTextColor WRITE setSelectTextColor) + Q_PROPERTY(QColor hoverTextColor READ getHoverTextColor WRITE setHoverTextColor) + + Q_PROPERTY(QColor currentLunarColor READ getCurrentLunarColor WRITE setCurrentLunarColor) + Q_PROPERTY(QColor otherLunarColor READ getOtherLunarColor WRITE setOtherLunarColor) + Q_PROPERTY(QColor selectLunarColor READ getSelectLunarColor WRITE setSelectLunarColor) + Q_PROPERTY(QColor hoverLunarColor READ getHoverLunarColor WRITE setHoverLunarColor) + + Q_PROPERTY(QColor currentBgColor READ getCurrentBgColor WRITE setCurrentBgColor) + Q_PROPERTY(QColor otherBgColor READ getOtherBgColor WRITE setOtherBgColor) + Q_PROPERTY(QColor selectBgColor READ getSelectBgColor WRITE setSelectBgColor) + Q_PROPERTY(QColor hoverBgColor READ getHoverBgColor WRITE setHoverBgColor) + +public: + enum CalendarStyle { + CalendarStyle_Red = 0 + }; + + enum WeekNameFormat { + WeekNameFormat_Short = 0, //短名称 + WeekNameFormat_Normal = 1, //普通名称 + WeekNameFormat_Long = 2, //长名称 + WeekNameFormat_En = 3 //英文名称 + }; + + enum SelectType { + SelectType_Rect = 0, //矩形背景 + SelectType_Circle = 1, //圆形背景 + SelectType_Triangle = 2, //带三角标 + SelectType_Image = 3 //图片背景 + }; + + explicit LunarCalendarWidget(QWidget *parent = 0); + ~LunarCalendarWidget(); + +private: + QFont iconFont; //图形字体 + bool btnClick; //按钮单击,避开下拉选择重复触发 + QComboBox *cboxYear; //年份下拉框 + QComboBox *cboxMonth; //月份下拉框 + QList labWeeks; //顶部星期名称 + QList items;//日期元素 + + CalendarStyle calendarStyle; //整体样式 + WeekNameFormat weekNameFormat; //星期名称格式 + QDate date; //当前日期 + + QColor weekTextColor; //星期名称文字颜色 + QColor weekBgColor; //星期名称背景色 + + bool showLunar; //显示农历 + QString bgImage; //背景图片 + SelectType selectType; //选中模式 + + QColor borderColor; //边框颜色 + QColor weekColor; //周末颜色 + QColor superColor; //角标颜色 + QColor lunarColor; //农历节日颜色 + + QColor currentTextColor; //当前月文字颜色 + QColor otherTextColor; //其他月文字颜色 + QColor selectTextColor; //选中日期文字颜色 + QColor hoverTextColor; //悬停日期文字颜色 + + QColor currentLunarColor; //当前月农历文字颜色 + QColor otherLunarColor; //其他月农历文字颜色 + QColor selectLunarColor; //选中日期农历文字颜色 + QColor hoverLunarColor; //悬停日期农历文字颜色 + + QColor currentBgColor; //当前月背景颜色 + QColor otherBgColor; //其他月背景颜色 + QColor selectBgColor; //选中日期背景颜色 + QColor hoverBgColor; //悬停日期背景颜色 + +private slots: + void initWidget(); + void initStyle(); + void initDate(); + void yearChanged(int); + void monthChanged(int); + void clicked(const QDate &date, const LunarCalendarItem::DayType &dayType); + void dayChanged(const QDate &date); + void dateChanged(int year, int month, int day); + +public: + //默认尺寸和最小尺寸 + QSize sizeHint() const; + QSize minimumSizeHint() const; + + //获取和设置整体样式 + CalendarStyle getCalendarStyle() const; + void setCalendarStyle(const CalendarStyle &calendarStyle); + + //获取和设置星期名称格式 + WeekNameFormat getWeekNameFormat() const; + void setWeekNameFormat(const WeekNameFormat &weekNameFormat); + + //获取和设置日期 + QDate getDate() const; + void setDate(const QDate &date); + + //获取和设置顶部星期名称文字颜色 + QColor getWeekTextColor() const; + void setWeekTextColor(const QColor &weekTextColor); + + //获取和设置顶部星期名称文字背景色 + QColor getWeekBgColor() const; + void setWeekBgColor(const QColor &weekBgColor); + + //获取和设置是否显示农历信息 + bool getShowLunar() const; + void setShowLunar(bool showLunar); + + //获取和设置背景图片 + QString getBgImage() const; + void setBgImage(const QString &bgImage); + + //获取和设置选中背景样式 + SelectType getSelectType() const; + void setSelectType(const SelectType &selectType); + + //获取和设置边框颜色 + QColor getBorderColor() const; + void setBorderColor(const QColor &borderColor); + + //获取和设置周末颜色 + QColor getWeekColor() const; + void setWeekColor(const QColor &weekColor); + + //获取和设置角标颜色 + QColor getSuperColor() const; + void setSuperColor(const QColor &superColor); + + //获取和设置农历节日颜色 + QColor getLunarColor() const; + void setLunarColor(const QColor &lunarColor); + + //获取和设置当前月文字颜色 + QColor getCurrentTextColor() const; + void setCurrentTextColor(const QColor ¤tTextColor); + + //获取和设置其他月文字颜色 + QColor getOtherTextColor() const; + void setOtherTextColor(const QColor &otherTextColor); + + //获取和设置选中日期文字颜色 + QColor getSelectTextColor() const; + void setSelectTextColor(const QColor &selectTextColor); + + //获取和设置悬停日期文字颜色 + QColor getHoverTextColor() const; + void setHoverTextColor(const QColor &hoverTextColor); + + //获取和设置当前月农历文字颜色 + QColor getCurrentLunarColor() const; + void setCurrentLunarColor(const QColor ¤tLunarColor); + + //获取和设置其他月农历文字颜色 + QColor getOtherLunarColor() const; + void setOtherLunarColor(const QColor &otherLunarColor); + + //获取和设置选中日期农历文字颜色 + QColor getSelectLunarColor() const; + void setSelectLunarColor(const QColor &selectLunarColor); + + //获取和设置悬停日期农历文字颜色 + QColor getHoverLunarColor() const; + void setHoverLunarColor(const QColor &hoverLunarColor); + + //获取和设置当前月背景颜色 + QColor getCurrentBgColor() const; + void setCurrentBgColor(const QColor ¤tBgColor); + + //获取和设置其他月背景颜色 + QColor getOtherBgColor() const; + void setOtherBgColor(const QColor &otherBgColor); + + //获取和设置选中日期背景颜色 + QColor getSelectBgColor() const; + void setSelectBgColor(const QColor &selectBgColor); + + //获取和设置悬停日期背景颜色 + QColor getHoverBgColor() const; + void setHoverBgColor(const QColor &hoverBgColor); + +public Q_SLOTS: + //转到上一年 + void showPreviousYear(); + //转到下一年 + void showNextYear(); + + //转到上一月 + void showPreviousMonth(); + //转到下一月 + void showNextMonth(); + + //转到今天 + void showToday(); + +Q_SIGNALS: + void clicked(const QDate &date); + void selectionChanged(); +}; + +#endif // LUNARCALENDARWIDGET_H diff --git a/widget/lunarcalendarwidget/lunarcalendarwidget.pro b/widget/lunarcalendarwidget/lunarcalendarwidget.pro new file mode 100644 index 0000000..c952230 --- /dev/null +++ b/widget/lunarcalendarwidget/lunarcalendarwidget.pro @@ -0,0 +1,24 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = lunarcalendarwidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off +RESOURCES += main.qrc + +SOURCES += main.cpp +SOURCES += frmlunarcalendarwidget.cpp +SOURCES += lunarcalendaritem.cpp +SOURCES += lunarcalendarinfo.cpp +SOURCES += lunarcalendarwidget.cpp + +HEADERS += frmlunarcalendarwidget.h +HEADERS += lunarcalendaritem.h +HEADERS += lunarcalendarinfo.h +HEADERS += lunarcalendarwidget.h + +FORMS += frmlunarcalendarwidget.ui + +INCLUDEPATH += $$PWD diff --git a/widget/lunarcalendarwidget/main.cpp b/widget/lunarcalendarwidget/main.cpp new file mode 100644 index 0000000..c96797c --- /dev/null +++ b/widget/lunarcalendarwidget/main.cpp @@ -0,0 +1,34 @@ +#pragma execution_character_set("utf-8") + +#include "frmlunarcalendarwidget.h" +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmLunarCalendarWidget w; + w.setWindowTitle("农历控件 (QQ: 517216493 WX: feiyangqingyun)"); + w.show(); + + return a.exec(); +} diff --git a/widget/lunarcalendarwidget/main.qrc b/widget/lunarcalendarwidget/main.qrc new file mode 100644 index 0000000..afb23ea --- /dev/null +++ b/widget/lunarcalendarwidget/main.qrc @@ -0,0 +1,6 @@ + + + image/bg_calendar.png + font/fontawesome-webfont.ttf + + diff --git a/widget/maskwidget/frmmaskwidget.cpp b/widget/maskwidget/frmmaskwidget.cpp new file mode 100644 index 0000000..e2109ac --- /dev/null +++ b/widget/maskwidget/frmmaskwidget.cpp @@ -0,0 +1,34 @@ +#pragma execution_character_set("utf-8") + +#include "frmmaskwidget.h" +#include "ui_frmmaskwidget.h" +#include "maskwidget.h" +#include "qdialog.h" +#include "qtimer.h" +#include "qdebug.h" + +frmMaskWidget::frmMaskWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmMaskWidget) +{ + ui->setupUi(this); + QTimer::singleShot(1000, this, SLOT(initForm())); +} + +frmMaskWidget::~frmMaskWidget() +{ + delete ui; +} + +void frmMaskWidget::initForm() +{ + MaskWidget::Instance()->setMainWidget(this->topLevelWidget()); + MaskWidget::Instance()->setDialogNames(QStringList() << "frmTest"); +} + +void frmMaskWidget::on_pushButton_clicked() +{ + QDialog d; + d.setObjectName("frmTest"); + d.setWindowTitle("遮罩层弹出窗体"); + d.resize(400, 300); + d.exec(); +} diff --git a/widget/maskwidget/frmmaskwidget.h b/widget/maskwidget/frmmaskwidget.h new file mode 100644 index 0000000..1b0010f --- /dev/null +++ b/widget/maskwidget/frmmaskwidget.h @@ -0,0 +1,26 @@ +#ifndef FRMMASKWIDGET_H +#define FRMMASKWIDGET_H + +#include + +namespace Ui { +class frmMaskWidget; +} + +class frmMaskWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmMaskWidget(QWidget *parent = 0); + ~frmMaskWidget(); + +private: + Ui::frmMaskWidget *ui; + +private slots: + void initForm(); + void on_pushButton_clicked(); +}; + +#endif // FRMMASKWIDGET_H diff --git a/widget/maskwidget/frmmaskwidget.ui b/widget/maskwidget/frmmaskwidget.ui new file mode 100644 index 0000000..5495ae4 --- /dev/null +++ b/widget/maskwidget/frmmaskwidget.ui @@ -0,0 +1,32 @@ + + + frmMaskWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + 10 + 10 + 92 + 28 + + + + 弹出 + + + + + + diff --git a/widget/maskwidget/main.cpp b/widget/maskwidget/main.cpp new file mode 100644 index 0000000..a52e489 --- /dev/null +++ b/widget/maskwidget/main.cpp @@ -0,0 +1,34 @@ +#pragma execution_character_set("utf-8") + +#include "frmmaskwidget.h" +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmMaskWidget w; + w.setWindowTitle("遮罩层窗体 (QQ: 517216493 WX: feiyangqingyun)"); + w.show(); + + return a.exec(); +} diff --git a/widget/maskwidget/maskwidget.cpp b/widget/maskwidget/maskwidget.cpp new file mode 100644 index 0000000..cd1098a --- /dev/null +++ b/widget/maskwidget/maskwidget.cpp @@ -0,0 +1,104 @@ +#pragma execution_character_set("utf-8") + +#include "maskwidget.h" +#include "qmutex.h" +#include "qapplication.h" +#include "qdebug.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) +#include "qscreen.h" +#define deskGeometry qApp->primaryScreen()->geometry() +#define deskGeometry2 qApp->primaryScreen()->availableGeometry() +#else +#include "qdesktopwidget.h" +#define deskGeometry qApp->desktop()->geometry() +#define deskGeometry2 qApp->desktop()->availableGeometry() +#endif + +QScopedPointer MaskWidget::self; +MaskWidget *MaskWidget::Instance() +{ + if (self.isNull()) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (self.isNull()) { + self.reset(new MaskWidget); + } + } + + return self.data(); +} + +MaskWidget::MaskWidget(QWidget *parent) : QWidget(parent) +{ + mainWidget = 0; + setOpacity(0.7); + setBgColor(QColor(0, 0, 0)); + + //不设置主窗体则遮罩层大小为默认桌面大小 + this->setGeometry(deskGeometry); + this->setWindowFlags(Qt::FramelessWindowHint | Qt::Tool); + + //绑定全局事件,过滤弹窗窗体进行处理 + qApp->installEventFilter(this); +} + +void MaskWidget::setMainWidget(QWidget *mainWidget) +{ + if (this->mainWidget != mainWidget) { + this->mainWidget = mainWidget; + } +} + +void MaskWidget::setDialogNames(const QStringList &dialogNames) +{ + if (this->dialogNames != dialogNames) { + this->dialogNames = dialogNames; + } +} + +void MaskWidget::setOpacity(double opacity) +{ + this->setWindowOpacity(opacity); +} + +void MaskWidget::setBgColor(const QColor &bgColor) +{ + QPalette palette = this->palette(); + palette.setBrush(QPalette::Window, bgColor); + this->setPalette(palette); +} + +void MaskWidget::showEvent(QShowEvent *) +{ + if (mainWidget) { + this->setGeometry(mainWidget->geometry()); + } +} + +bool MaskWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Show) { + if (dialogNames.contains(obj->objectName())) { + this->show(); + this->activateWindow(); + QWidget *w = (QWidget *)obj; + w->activateWindow(); + } + } else if (event->type() == QEvent::Hide) { + if (dialogNames.contains(obj->objectName())) { + this->hide(); + } + } else if (event->type() == QEvent::WindowActivate) { + //当主窗体激活时,同时激活遮罩层 + if (mainWidget) { + if (obj->objectName() == mainWidget->objectName()) { + if (this->isVisible()) { + this->activateWindow(); + } + } + } + } + + return QObject::eventFilter(obj, event); +} diff --git a/widget/maskwidget/maskwidget.h b/widget/maskwidget/maskwidget.h new file mode 100644 index 0000000..9a608bf --- /dev/null +++ b/widget/maskwidget/maskwidget.h @@ -0,0 +1,51 @@ +#ifndef MASKWIDGET_H +#define MASKWIDGET_H + +/** + * 弹窗遮罩层控件 作者:feiyangqingyun(QQ:517216493) 2016-12-26 + * 1. 可设置需要遮罩的主窗体,自动跟随主窗体位置显示遮罩面积。 + * 2. 只需要将弹窗窗体的名称一开始传入队列即可,足够简单。 + * 3. 可设置透明度。 + * 4. 可设置遮罩层颜色。 + * 5. 不阻塞消息循坏。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT MaskWidget : public QWidget +#else +class MaskWidget : public QWidget +#endif + +{ + Q_OBJECT +public: + static MaskWidget *Instance(); + explicit MaskWidget(QWidget *parent = 0); + +protected: + void showEvent(QShowEvent *); + bool eventFilter(QObject *obj, QEvent *event); + +private: + static QScopedPointer self; + + //需要遮罩的主窗体 + QWidget *mainWidget; + //需要弹窗的窗体对象名称集合链表 + QStringList dialogNames; + +public Q_SLOTS: + //设置需要遮罩的主窗体 + void setMainWidget(QWidget *mainWidget); + //设置需要弹窗的窗体对象名称集合链表 + void setDialogNames(const QStringList &dialogNames); + + //设置遮罩颜色 + void setBgColor(const QColor &bgColor); + //设置颜色透明度 + void setOpacity(double opacity); +}; + +#endif // MASKWIDGET_H diff --git a/widget/maskwidget/maskwidget.pro b/widget/maskwidget/maskwidget.pro new file mode 100644 index 0000000..4f79156 --- /dev/null +++ b/widget/maskwidget/maskwidget.pro @@ -0,0 +1,19 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = maskwidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off + +SOURCES += main.cpp +SOURCES += maskwidget.cpp +SOURCES += frmmaskwidget.cpp + +HEADERS += maskwidget.h +HEADERS += frmmaskwidget.h + +FORMS += frmmaskwidget.ui + + diff --git a/widget/movewidget/frmmovewidget.cpp b/widget/movewidget/frmmovewidget.cpp new file mode 100644 index 0000000..6cc5cc4 --- /dev/null +++ b/widget/movewidget/frmmovewidget.cpp @@ -0,0 +1,41 @@ +#pragma execution_character_set("utf-8") + +#include "frmmovewidget.h" +#include "ui_frmmovewidget.h" +#include "qpushbutton.h" +#include "qprogressbar.h" +#include "movewidget.h" + +frmMoveWidget::frmMoveWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmMoveWidget) +{ + ui->setupUi(this); + this->initForm(); +} + +frmMoveWidget::~frmMoveWidget() +{ + delete ui; +} + +void frmMoveWidget::initForm() +{ + QPushButton *btn1 = new QPushButton(this); + btn1->setGeometry(10, 10, 250, 25); + btn1->setText("按住我拖动(仅限左键拖动)"); + MoveWidget *moveWidget1 = new MoveWidget(this); + moveWidget1->setWidget(btn1); + + QPushButton *btn2 = new QPushButton(this); + btn2->setGeometry(10, 40, 250, 25); + btn2->setText("按住我拖动(支持右键拖动)"); + MoveWidget *moveWidget2 = new MoveWidget(this); + moveWidget2->setWidget(btn2); + moveWidget2->setLeftButton(false); + + QProgressBar *bar = new QProgressBar(this); + bar->setGeometry(10, 70, 250, 25); + bar->setRange(0, 0); + bar->setTextVisible(false); + MoveWidget *moveWidget3 = new MoveWidget(this); + moveWidget3->setWidget(bar); +} \ No newline at end of file diff --git a/widget/movewidget/frmmovewidget.h b/widget/movewidget/frmmovewidget.h new file mode 100644 index 0000000..fa66160 --- /dev/null +++ b/widget/movewidget/frmmovewidget.h @@ -0,0 +1,25 @@ +#ifndef FRMMOVEWIDGET_H +#define FRMMOVEWIDGET_H + +#include + +namespace Ui { +class frmMoveWidget; +} + +class frmMoveWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmMoveWidget(QWidget *parent = 0); + ~frmMoveWidget(); + +private: + Ui::frmMoveWidget *ui; + +private slots: + void initForm(); +}; + +#endif // FRMMOVEWIDGET_H diff --git a/widget/movewidget/frmmovewidget.ui b/widget/movewidget/frmmovewidget.ui new file mode 100644 index 0000000..ef95599 --- /dev/null +++ b/widget/movewidget/frmmovewidget.ui @@ -0,0 +1,19 @@ + + + frmMoveWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + diff --git a/widget/movewidget/main.cpp b/widget/movewidget/main.cpp new file mode 100644 index 0000000..2ae74aa --- /dev/null +++ b/widget/movewidget/main.cpp @@ -0,0 +1,34 @@ +#pragma execution_character_set("utf-8") + +#include "frmmovewidget.h" +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmMoveWidget w; + w.setWindowTitle("通用移动类 (QQ: 517216493 WX: feiyangqingyun)"); + w.show(); + + return a.exec(); +} diff --git a/widget/movewidget/movewidget.cpp b/widget/movewidget/movewidget.cpp new file mode 100644 index 0000000..c22ab89 --- /dev/null +++ b/widget/movewidget/movewidget.cpp @@ -0,0 +1,74 @@ +#include "movewidget.h" +#include "qevent.h" +#include "qdebug.h" + +MoveWidget::MoveWidget(QObject *parent) : QObject(parent) +{ + lastPoint = QPoint(0, 0); + pressed = false; + leftButton = true; + inControl = true; + widget = 0; +} + +bool MoveWidget::eventFilter(QObject *watched, QEvent *event) +{ + if (widget && watched == widget) { + QMouseEvent *mouseEvent = (QMouseEvent *)event; + if (mouseEvent->type() == QEvent::MouseButtonPress) { + //如果限定了只能鼠标左键拖动则判断当前是否是鼠标左键 + if (leftButton && mouseEvent->button() != Qt::LeftButton) { + return false; + } + + //判断控件的区域是否包含了当前鼠标的坐标 + if (widget->rect().contains(mouseEvent->pos())) { + lastPoint = mouseEvent->pos(); + pressed = true; + } + } else if (mouseEvent->type() == QEvent::MouseMove && pressed) { + //计算坐标偏移值,调用move函数移动过去 + int offsetX = mouseEvent->pos().x() - lastPoint.x(); + int offsetY = mouseEvent->pos().y() - lastPoint.y(); + int x = widget->x() + offsetX; + int y = widget->y() + offsetY; + if (inControl) { + //可以自行调整限定在容器中的范围,这里默认保留20个像素在里面 + int offset = 20; + bool xyOut = (x + widget->width() < offset || y + widget->height() < offset); + bool whOut = false; + QWidget *w = (QWidget *)widget->parent(); + if (w) { + whOut = (w->width() - x < offset || w->height() - y < offset); + } + if (xyOut || whOut) { + return false; + } + } + + widget->move(x, y); + } else if (mouseEvent->type() == QEvent::MouseButtonRelease && pressed) { + pressed = false; + } + } + + return QObject::eventFilter(watched, event); +} + +void MoveWidget::setLeftButton(bool leftButton) +{ + this->leftButton = leftButton; +} + +void MoveWidget::setInControl(bool inControl) +{ + this->inControl = inControl; +} + +void MoveWidget::setWidget(QWidget *widget) +{ + if (this->widget == 0) { + this->widget = widget; + this->widget->installEventFilter(this); + } +} diff --git a/widget/movewidget/movewidget.h b/widget/movewidget/movewidget.h new file mode 100644 index 0000000..481270e --- /dev/null +++ b/widget/movewidget/movewidget.h @@ -0,0 +1,43 @@ +#ifndef MOVEWIDGET_H +#define MOVEWIDGET_H + +/** + * 通用控件移动类 作者:feiyangqingyun(QQ:517216493) 2019-09-28 + * 1. 可以指定需要移动的widget。 + * 2. 可设置是否限定鼠标左键拖动。 + * 3. 支持任意widget控件。 + */ + +#include + +#ifdef quc +class Q_DECL_EXPORT MoveWidget : public QObject +#else +class MoveWidget : public QObject +#endif + +{ + Q_OBJECT +public: + explicit MoveWidget(QObject *parent = 0); + +protected: + bool eventFilter(QObject *watched, QEvent *event); + +private: + QPoint lastPoint; //最后按下的坐标 + bool pressed; //鼠标是否按下 + bool leftButton; //限定鼠标左键 + bool inControl; //限定在容器内 + QWidget *widget; //移动的控件 + +public Q_SLOTS: + //设置是否限定鼠标左键 + void setLeftButton(bool leftButton); + //设置是否限定不能移出容器外面 + void setInControl(bool inControl); + //设置要移动的控件 + void setWidget(QWidget *widget); +}; + +#endif // MOVEWIDGET_H diff --git a/widget/movewidget/movewidget.pro b/widget/movewidget/movewidget.pro new file mode 100644 index 0000000..23fd04a --- /dev/null +++ b/widget/movewidget/movewidget.pro @@ -0,0 +1,17 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = movewidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off + +SOURCES += main.cpp +SOURCES += frmmovewidget.cpp +SOURCES += movewidget.cpp + +HEADERS += frmmovewidget.h +HEADERS += movewidget.h + +FORMS += frmmovewidget.ui diff --git a/widget/screenwidget/frmscreenwidget.cpp b/widget/screenwidget/frmscreenwidget.cpp new file mode 100644 index 0000000..900c93c --- /dev/null +++ b/widget/screenwidget/frmscreenwidget.cpp @@ -0,0 +1,19 @@ +#include "frmscreenwidget.h" +#include "ui_frmscreenwidget.h" +#include "screenwidget.h" + +frmScreenWidget::frmScreenWidget(QWidget *parent) : QWidget(parent), ui(new Ui::frmScreenWidget) +{ + ui->setupUi(this); +} + +frmScreenWidget::~frmScreenWidget() +{ + delete ui; + exit(0); +} + +void frmScreenWidget::on_pushButton_clicked() +{ + ScreenWidget::Instance()->showFullScreen(); +} diff --git a/widget/screenwidget/frmscreenwidget.h b/widget/screenwidget/frmscreenwidget.h new file mode 100644 index 0000000..439024d --- /dev/null +++ b/widget/screenwidget/frmscreenwidget.h @@ -0,0 +1,25 @@ +#ifndef FRMSCREENWIDGET_H +#define FRMSCREENWIDGET_H + +#include + +namespace Ui { +class frmScreenWidget; +} + +class frmScreenWidget : public QWidget +{ + Q_OBJECT + +public: + explicit frmScreenWidget(QWidget *parent = 0); + ~frmScreenWidget(); + +private slots: + void on_pushButton_clicked(); + +private: + Ui::frmScreenWidget *ui; +}; + +#endif // FRMSCREENWIDGET_H diff --git a/widget/screenwidget/frmscreenwidget.ui b/widget/screenwidget/frmscreenwidget.ui new file mode 100644 index 0000000..54a035a --- /dev/null +++ b/widget/screenwidget/frmscreenwidget.ui @@ -0,0 +1,32 @@ + + + frmScreenWidget + + + + 0 + 0 + 800 + 600 + + + + Form + + + + + 10 + 10 + 92 + 28 + + + + 弹出 + + + + + + diff --git a/widget/screenwidget/main.cpp b/widget/screenwidget/main.cpp new file mode 100644 index 0000000..5d88343 --- /dev/null +++ b/widget/screenwidget/main.cpp @@ -0,0 +1,41 @@ +#pragma execution_character_set("utf-8") + +#include "frmscreenwidget.h" +#include +#include + +int main(int argc, char *argv[]) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QApplication::setAttribute(Qt::AA_Use96Dpi); +#endif +#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor); +#endif + + QApplication a(argc, argv); + QFont font; + font.setFamily("Microsoft Yahei"); + font.setPixelSize(13); + a.setFont(font); + +#if (QT_VERSION < QT_VERSION_CHECK(5,0,0)) +#if _MSC_VER + QTextCodec *codec = QTextCodec::codecForName("gbk"); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); +#endif + QTextCodec::setCodecForLocale(codec); + QTextCodec::setCodecForCStrings(codec); + QTextCodec::setCodecForTr(codec); +#else + QTextCodec *codec = QTextCodec::codecForName("utf-8"); + QTextCodec::setCodecForLocale(codec); +#endif + + frmScreenWidget w; + w.setWindowTitle("屏幕截图 (QQ: 517216493 WX: feiyangqingyun)"); + w.show(); + + return a.exec(); +} diff --git a/widget/screenwidget/screenwidget.cpp b/widget/screenwidget/screenwidget.cpp new file mode 100644 index 0000000..104247a --- /dev/null +++ b/widget/screenwidget/screenwidget.cpp @@ -0,0 +1,332 @@ +#pragma execution_character_set("utf-8") + +#include "screenwidget.h" +#include "qmutex.h" +#include "qapplication.h" +#include "qpainter.h" +#include "qfiledialog.h" +#include "qevent.h" +#include "qdatetime.h" +#include "qstringlist.h" +#include "qdebug.h" + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) +#include "qscreen.h" +#define deskGeometry qApp->primaryScreen()->geometry() +#define deskGeometry2 qApp->primaryScreen()->availableGeometry() +#else +#include "qdesktopwidget.h" +#define deskGeometry qApp->desktop()->geometry() +#define deskGeometry2 qApp->desktop()->availableGeometry() +#endif + +#define STRDATETIME qPrintable (QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss")) + +Screen::Screen(QSize size) +{ + maxWidth = size.width(); + maxHeight = size.height(); + + startPos = QPoint(-1, -1); + endPos = startPos; + leftUpPos = startPos; + rightDownPos = startPos; + status = SELECT; +} + +int Screen::width() +{ + return maxWidth; +} + +int Screen::height() +{ + return maxHeight; +} + +Screen::STATUS Screen::getStatus() +{ + return status; +} + +void Screen::setStatus(STATUS status) +{ + this->status = status; +} + +void Screen::setEnd(QPoint pos) +{ + endPos = pos; + leftUpPos = startPos; + rightDownPos = endPos; + cmpPoint(leftUpPos, rightDownPos); +} + +void Screen::setStart(QPoint pos) +{ + startPos = pos; +} + +QPoint Screen::getEnd() +{ + return endPos; +} + +QPoint Screen::getStart() +{ + return startPos; +} + +QPoint Screen::getLeftUp() +{ + return leftUpPos; +} + +QPoint Screen::getRightDown() +{ + return rightDownPos; +} + +bool Screen::isInArea(QPoint pos) +{ + if (pos.x() > leftUpPos.x() && pos.x() < rightDownPos.x() && pos.y() > leftUpPos.y() && pos.y() < rightDownPos.y()) { + return true; + } + + return false; +} + +void Screen::move(QPoint p) +{ + int lx = leftUpPos.x() + p.x(); + int ly = leftUpPos.y() + p.y(); + int rx = rightDownPos.x() + p.x(); + int ry = rightDownPos.y() + p.y(); + + if (lx < 0) { + lx = 0; + rx -= p.x(); + } + + if (ly < 0) { + ly = 0; + ry -= p.y(); + } + + if (rx > maxWidth) { + rx = maxWidth; + lx -= p.x(); + } + + if (ry > maxHeight) { + ry = maxHeight; + ly -= p.y(); + } + + leftUpPos = QPoint(lx, ly); + rightDownPos = QPoint(rx, ry); + startPos = leftUpPos; + endPos = rightDownPos; +} + +void Screen::cmpPoint(QPoint &leftTop, QPoint &rightDown) +{ + QPoint l = leftTop; + QPoint r = rightDown; + + if (l.x() <= r.x()) { + if (l.y() <= r.y()) { + } else { + leftTop.setY(r.y()); + rightDown.setY(l.y()); + } + } else { + if (l.y() < r.y()) { + leftTop.setX(r.x()); + rightDown.setX(l.x()); + } else { + QPoint tmp; + tmp = leftTop; + leftTop = rightDown; + rightDown = tmp; + } + } +} + +QScopedPointer ScreenWidget::self; +ScreenWidget *ScreenWidget::Instance() +{ + if (self.isNull()) { + static QMutex mutex; + QMutexLocker locker(&mutex); + if (self.isNull()) { + self.reset(new ScreenWidget); + } + } + + return self.data(); +} + +ScreenWidget::ScreenWidget(QWidget *parent) : QWidget(parent) +{ + //this->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint); + + menu = new QMenu(this); + menu->addAction("保存当前截图", this, SLOT(saveScreen())); + menu->addAction("保存全屏截图", this, SLOT(saveFullScreen())); + menu->addAction("截图另存为", this, SLOT(saveScreenOther())); + menu->addAction("全屏另存为", this, SLOT(saveFullOther())); + menu->addAction("退出截图", this, SLOT(hide())); + + //取得屏幕大小 + screen = new Screen(deskGeometry.size()); + //保存全屏图像 + fullScreen = new QPixmap(); +} + +void ScreenWidget::paintEvent(QPaintEvent *) +{ + int x = screen->getLeftUp().x(); + int y = screen->getLeftUp().y(); + int w = screen->getRightDown().x() - x; + int h = screen->getRightDown().y() - y; + + QPainter painter(this); + + QPen pen; + pen.setColor(Qt::green); + pen.setWidth(2); + pen.setStyle(Qt::DotLine); + painter.setPen(pen); + painter.drawPixmap(0, 0, *bgScreen); + + if (w != 0 && h != 0) { + painter.drawPixmap(x, y, fullScreen->copy(x, y, w, h)); + } + + painter.drawRect(x, y, w, h); + + pen.setColor(Qt::yellow); + painter.setPen(pen); + painter.drawText(x + 2, y - 8, tr("截图范围:( %1 x %2 ) - ( %3 x %4 ) 图片大小:( %5 x %6 )") + .arg(x).arg(y).arg(x + w).arg(y + h).arg(w).arg(h)); +} + +void ScreenWidget::showEvent(QShowEvent *) +{ + QPoint point(-1, -1); + screen->setStart(point); + screen->setEnd(point); + +#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + QScreen *pscreen = QApplication::primaryScreen(); + *fullScreen = pscreen->grabWindow(0, 0, 0, screen->width(), screen->height()); +#else + *fullScreen = fullScreen->grabWindow(0, 0, 0, screen->width(), screen->height()); +#endif + + //设置透明度实现模糊背景 + QPixmap pix(screen->width(), screen->height()); + pix.fill((QColor(160, 160, 160, 200))); + bgScreen = new QPixmap(*fullScreen); + QPainter p(bgScreen); + p.drawPixmap(0, 0, pix); +} + +void ScreenWidget::saveScreen() +{ + int x = screen->getLeftUp().x(); + int y = screen->getLeftUp().y(); + int w = screen->getRightDown().x() - x; + int h = screen->getRightDown().y() - y; + + QString fileName = QString("%1/screen_%2.png").arg(qApp->applicationDirPath()).arg(STRDATETIME); + fullScreen->copy(x, y, w, h).save(fileName, "png"); + close(); +} + +void ScreenWidget::saveFullScreen() +{ + QString fileName = QString("%1/full_%2.png").arg(qApp->applicationDirPath()).arg(STRDATETIME); + fullScreen->save(fileName, "png"); + close(); +} + +void ScreenWidget::saveScreenOther() +{ + QString name = QString("%1.png").arg(STRDATETIME); + QString fileName = QFileDialog::getSaveFileName(this, "保存图片", name, "png Files (*.png)"); + if (!fileName.endsWith(".png")) { + fileName += ".png"; + } + + if (fileName.length() > 0) { + int x = screen->getLeftUp().x(); + int y = screen->getLeftUp().y(); + int w = screen->getRightDown().x() - x; + int h = screen->getRightDown().y() - y; + fullScreen->copy(x, y, w, h).save(fileName, "png"); + close(); + } +} + +void ScreenWidget::saveFullOther() +{ + QString name = QString("%1.png").arg(STRDATETIME); + QString fileName = QFileDialog::getSaveFileName(this, "保存图片", name, "png Files (*.png)"); + if (!fileName.endsWith(".png")) { + fileName += ".png"; + } + + if (fileName.length() > 0) { + fullScreen->save(fileName, "png"); + close(); + } +} + +void ScreenWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (screen->getStatus() == Screen::SELECT) { + screen->setEnd(e->pos()); + } else if (screen->getStatus() == Screen::MOV) { + QPoint p(e->x() - movPos.x(), e->y() - movPos.y()); + screen->move(p); + movPos = e->pos(); + } + + this->update(); +} + +void ScreenWidget::mousePressEvent(QMouseEvent *e) +{ + int status = screen->getStatus(); + + if (status == Screen::SELECT) { + screen->setStart(e->pos()); + } else if (status == Screen::MOV) { + if (screen->isInArea(e->pos()) == false) { + screen->setStart(e->pos()); + screen->setStatus(Screen::SELECT); + } else { + movPos = e->pos(); + this->setCursor(Qt::SizeAllCursor); + } + } + + this->update(); +} + +void ScreenWidget::mouseReleaseEvent(QMouseEvent *) +{ + if (screen->getStatus() == Screen::SELECT) { + screen->setStatus(Screen::MOV); + } else if (screen->getStatus() == Screen::MOV) { + this->setCursor(Qt::ArrowCursor); + } +} + +void ScreenWidget::contextMenuEvent(QContextMenuEvent *) +{ + this->setCursor(Qt::ArrowCursor); + menu->exec(cursor().pos()); +} diff --git a/widget/screenwidget/screenwidget.h b/widget/screenwidget/screenwidget.h new file mode 100644 index 0000000..8e9c34d --- /dev/null +++ b/widget/screenwidget/screenwidget.h @@ -0,0 +1,94 @@ +#ifndef SCREENWIDGET_H +#define SCREENWIDGET_H + +/** + * 全局截屏控件 作者:feiyangqingyun(QQ:517216493) 2016-11-11 + * 1. 鼠标右键弹出菜单。 + * 2. 支持全局截屏。 + * 3. 支持局部截屏。 + * 4. 支持截图区域拖动。 + * 5. 支持图片另存为。 + */ + +#include +#include +#include +#include + +class Screen +{ +public: + enum STATUS {SELECT, MOV, SET_W_H}; + Screen() {} + Screen(QSize size); + + void setStart(QPoint pos); + void setEnd(QPoint pos); + QPoint getStart(); + QPoint getEnd(); + + QPoint getLeftUp(); + QPoint getRightDown(); + + STATUS getStatus(); + void setStatus(STATUS status); + + int width(); + int height(); + + //检测坐标点是否在截图区域内 + bool isInArea(QPoint pos); + //按坐标移动截图区域 + void move(QPoint p); + +private: + //记录 截图区域 左上角、右下角 + QPoint leftUpPos, rightDownPos; + //记录 鼠标开始位置、结束位置 + QPoint startPos, endPos; + //记录屏幕大小 + int maxWidth, maxHeight; + //三个状态: 选择区域、移动区域、设置width height + STATUS status; + + //比较两位置,判断左上角、右下角 + void cmpPoint(QPoint &s, QPoint &e); +}; + +#ifdef quc +class Q_DECL_EXPORT ScreenWidget : public QWidget +#else +class ScreenWidget : public QWidget +#endif + +{ + Q_OBJECT +public: + static ScreenWidget *Instance(); + explicit ScreenWidget(QWidget *parent = 0); + +private: + static QScopedPointer self; + QMenu *menu; //右键菜单对象 + Screen *screen; //截屏对象 + QPixmap *fullScreen; //保存全屏图像 + QPixmap *bgScreen; //模糊背景图 + QPoint movPos; //坐标 + +protected: + void contextMenuEvent(QContextMenuEvent *); + void mousePressEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void paintEvent(QPaintEvent *); + void showEvent(QShowEvent *); + +private slots: + void saveScreen(); + void saveFullScreen(); + void saveScreenOther(); + void saveFullOther(); +}; + +#endif // SCREENWIDGET_H + diff --git a/widget/screenwidget/screenwidget.pro b/widget/screenwidget/screenwidget.pro new file mode 100644 index 0000000..3269e29 --- /dev/null +++ b/widget/screenwidget/screenwidget.pro @@ -0,0 +1,17 @@ +QT += core gui +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 5): QT += core5compat + +TARGET = screenwidget +TEMPLATE = app +DESTDIR = $$PWD/../bin +CONFIG += warn_off + +SOURCES += main.cpp +SOURCES += frmscreenwidget.cpp +SOURCES += screenwidget.cpp + +HEADERS += screenwidget.h +HEADERS += frmscreenwidget.h + +FORMS += frmscreenwidget.ui diff --git a/widget/widget.pro b/widget/widget.pro new file mode 100644 index 0000000..80eff01 --- /dev/null +++ b/widget/widget.pro @@ -0,0 +1,15 @@ +TEMPLATE = subdirs +SUBDIRS += colorwidget +SUBDIRS += framelesswidget +SUBDIRS += gifwidget +SUBDIRS += lunarcalendarwidget +SUBDIRS += maskwidget +SUBDIRS += movewidget +SUBDIRS += screenwidget + +#Qt6有些项目还不支持 +greaterThan(QT_MAJOR_VERSION, 5) { +msvc { +SUBDIRS -= lunarcalendarwidget +} +}