Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
mahuifa
QMDemo
提交
1195d65c
Q
QMDemo
项目概览
mahuifa
/
QMDemo
通知
1
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Q
QMDemo
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
1195d65c
编写于
11月 19, 2022
作者:
mahuifa
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat:完成截图工具功能开发
1、完成窗口截图功能开发; 2、完成linux系统适配。
上级
50963ac8
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
305 addition
and
9 deletion
+305
-9
FunctionalModule/SnippingTool/SnippingTool.pro
FunctionalModule/SnippingTool/SnippingTool.pro
+8
-2
FunctionalModule/SnippingTool/mainwindow.cpp
FunctionalModule/SnippingTool/mainwindow.cpp
+21
-7
FunctionalModule/SnippingTool/mainwindow.h
FunctionalModule/SnippingTool/mainwindow.h
+2
-0
FunctionalModule/SnippingTool/windowrect.cpp
FunctionalModule/SnippingTool/windowrect.cpp
+216
-0
FunctionalModule/SnippingTool/windowrect.h
FunctionalModule/SnippingTool/windowrect.h
+58
-0
未找到文件。
FunctionalModule/SnippingTool/SnippingTool.pro
浏览文件 @
1195d65c
#---------------------------------------------------------------------------------------
#
@
功能:
Qt
实现截图工具
#
1
、实现
Windows
、
linux系统下截图功能
;
#
2
、实现全屏截图、矩形截图、窗口截图功能;
#
3
、实现保存截图、取消截图功能;
#
4
、实时显示截取的图片。
#
@
编译器:
Desktop
Qt
5.12
.
5
MSVC2017
64
bit
(也支持其它编译器)
#
@
Qt
IDE
:
D
:/
Qt
/
Qt5
.
12.5
/
Tools
/
QtCreator
/
share
/
qtcreator
#
...
...
@@ -20,13 +24,15 @@ FORMS += \
HEADERS
+=
\
mainwindow
.
h
\
playimage
.
h
\
screenrect
.
h
screenrect
.
h
\
windowrect
.
h
SOURCES
+=
\
main
.
cpp
\
mainwindow
.
cpp
\
playimage
.
cpp
\
screenrect
.
cpp
screenrect
.
cpp
\
windowrect
.
cpp
#
Default
rules
for
deployment
.
...
...
FunctionalModule/SnippingTool/mainwindow.cpp
浏览文件 @
1195d65c
...
...
@@ -9,12 +9,15 @@
#include <QToolBar>
#include <QDebug>
#include <QFileDialog>
#include <QDateTime>
#include <QMessageBox>
MainWindow
::
MainWindow
(
QWidget
*
parent
)
:
QMainWindow
(
parent
)
,
ui
(
new
Ui
::
MainWindow
)
{
ui
->
setupUi
(
this
);
this
->
setWindowTitle
(
QString
(
"Qt-截图工具 - V%1"
).
arg
(
APP_VERSION
));
// 设置工具栏
QAction
*
acNew
=
new
QAction
(
"新建截图"
);
...
...
@@ -39,7 +42,7 @@ MainWindow::MainWindow(QWidget *parent)
connect
(
acSave
,
&
QAction
::
triggered
,
this
,
&
MainWindow
::
on_saveImage
);
connect
(
acClear
,
&
QAction
::
triggered
,
this
,
&
MainWindow
::
on_clearImage
);
connect
(
&
m_screenRect
,
&
ScreenRect
::
selectRect
,
this
,
&
MainWindow
::
on_selectRect
);
connect
(
&
m_windowRect
,
&
WindowRect
::
selectRect
,
this
,
&
MainWindow
::
on_selectRect
);
}
...
...
@@ -72,7 +75,8 @@ void MainWindow::on_newGrab(bool checked)
}
else
if
(
strModel
==
"窗口"
)
{
this
->
hide
();
m_windowRect
.
show
();
}
else
{
...
...
@@ -85,15 +89,23 @@ void MainWindow::on_newGrab(bool checked)
*/
void
MainWindow
::
grabPixmap
(
QRect
rect
)
{
// this->hide(); // 截图之前将当前窗口隐藏,避免截取的图像中包含当前窗口,这种方法很慢,需要延时等待几百毫秒,否则还是会有当前窗口
#if defined(Q_OS_WIN)
setWindowOpacity
(
0
);
// 最好的方法是将当前窗口设置成完全透明
QDesktopWidget
*
desk
=
QApplication
::
desktop
();
// 获取桌面根窗口
QScreen
*
screen
=
QGuiApplication
::
primaryScreen
();
// 获取默认屏幕
m_pixmap
=
screen
->
grabWindow
(
desk
->
winId
(),
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
// 抓取屏幕图像
ui
->
centralwidget
->
updatePixmap
(
m_pixmap
);
// 显示捕获的图像
// this->show(); // 截图完成后显示窗口
setWindowOpacity
(
1
);
#elif defined(Q_OS_LINUX)
// linux下setWindowOpacity设置透明后截图还可以看到一个透明的边框,效果不是很好,所以使用hide
this
->
hide
();
// 截图之前将当前窗口隐藏,避免截取的图像中包含当前窗口,这种方法很慢,需要延时等待几百毫秒,否则还是会有当前窗口
QThread
::
msleep
(
300
);
QDesktopWidget
*
desk
=
QApplication
::
desktop
();
// 获取桌面根窗口
QScreen
*
screen
=
QGuiApplication
::
primaryScreen
();
// 获取默认屏幕
m_pixmap
=
screen
->
grabWindow
(
desk
->
winId
(),
rect
.
x
(),
rect
.
y
(),
rect
.
width
(),
rect
.
height
());
// 抓取屏幕图像
ui
->
centralwidget
->
updatePixmap
(
m_pixmap
);
// 显示捕获的图像
this
->
show
();
// 截图完成后显示窗口
#endif
}
/**
...
...
@@ -105,7 +117,9 @@ void MainWindow::on_saveImage(bool checked)
Q_UNUSED
(
checked
)
if
(
m_pixmap
.
isNull
())
return
;
QString
strName
=
QFileDialog
::
getSaveFileName
(
this
,
"保存到"
,
"./"
,
"便携式网络图形(*.png);;JPEG文件(*.jpg)"
);
// linux下getSaveFileName不会返回默认文件后缀,所以需要在文件名中添加后缀,否则QImage::save无法通过后缀推测出文件类型,就会保存失败
QString
name
=
QString
(
"%1.png"
).
arg
(
QDateTime
::
currentDateTime
().
toString
(
"yyyy-MM-dd hh-mm-ss"
));
QString
strName
=
QFileDialog
::
getSaveFileName
(
this
,
"保存到"
,
name
,
"便携式网络图形(*.png);;JPEG文件(*.jpg)"
);
if
(
strName
.
isEmpty
())
return
;
QImage
image
=
m_pixmap
.
toImage
();
...
...
@@ -115,7 +129,7 @@ void MainWindow::on_saveImage(bool checked)
}
else
{
qDebug
()
<<
"保存失败!"
;
QMessageBox
::
warning
(
this
,
"注意!"
,
"文件保存失败,请检查有没有输入文件后缀名。"
)
;
}
}
...
...
FunctionalModule/SnippingTool/mainwindow.h
浏览文件 @
1195d65c
...
...
@@ -4,6 +4,7 @@
#include <QMainWindow>
#include <qtoolbutton.h>
#include "screenrect.h"
#include "windowrect.h"
QT_BEGIN_NAMESPACE
namespace
Ui
{
class
MainWindow
;
}
...
...
@@ -30,5 +31,6 @@ private:
QToolButton
*
m_butModel
=
nullptr
;
QPixmap
m_pixmap
;
// 保存截取的图像
ScreenRect
m_screenRect
;
WindowRect
m_windowRect
;
};
#endif // MAINWINDOW_H
FunctionalModule/SnippingTool/windowrect.cpp
0 → 100644
浏览文件 @
1195d65c
#include "windowrect.h"
#include <QDebug>
#include <QGridLayout>
#include <QEvent>
#include <QMouseEvent>
#if defined(Q_OS_WIN)
#include <Windows.h>
#include <windef.h>
#elif defined(Q_OS_LINUX)
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#elif defined(Q_OS_MAC)
#endif
#if defined(Q_OS_WIN)
static
HHOOK
g_hook
=
nullptr
;
/**
* @brief 处理鼠标事件的回调函数
* @param nCode
* @param wParam
* @param lParam
* @return
*/
LRESULT
CALLBACK
CallBackProc
(
int
nCode
,
WPARAM
wParam
,
LPARAM
lParam
)
{
switch
(
wParam
)
{
case
WM_LBUTTONDOWN
:
// 鼠标左键按下
{
emit
MouseEvent
::
getInstance
()
->
mouseSignal
(
new
QMouseEvent
(
QEvent
::
MouseButtonPress
,
QCursor
::
pos
(),
Qt
::
LeftButton
,
Qt
::
LeftButton
,
Qt
::
NoModifier
));
break
;
}
default:
break
;
}
return
CallNextHookEx
(
nullptr
,
nCode
,
wParam
,
lParam
);
// 注意这一行一定不能少,否则会出大问题
}
#endif
WindowRect
::
WindowRect
(
QWidget
*
parent
)
:
QWidget
(
parent
)
{
#if defined(Q_OS_WIN)
// linux下鼠标穿透要放在后面两行代码的全前面,否则无效(但是鼠标穿透了会导致一些奇怪的问题,如窗口显示不全,所以这里不使用)
// windows下如果不设置鼠标穿透则只能捕获到当前窗口
this
->
setAttribute
(
Qt
::
WA_TransparentForMouseEvents
,
true
);
#endif
this
->
setWindowFlags
(
Qt
::
FramelessWindowHint
);
// 去掉边框、标题栏
this
->
setAttribute
(
Qt
::
WA_TranslucentBackground
);
// 背景透明
this
->
setWindowFlags
(
this
->
windowFlags
()
|
Qt
::
WindowStaysOnTopHint
);
// 设置顶级窗口,防止遮挡
// 在当前窗口上增加一层QWidget,否则不会显示边框
QGridLayout
*
gridLayout
=
new
QGridLayout
(
this
);
gridLayout
->
setSpacing
(
0
);
gridLayout
->
setContentsMargins
(
0
,
0
,
0
,
0
);
gridLayout
->
addWidget
(
new
QWidget
(),
0
,
0
,
1
,
1
);
this
->
setStyleSheet
(
" background-color: rgba(58, 196, 255, 40); border: 2px solid rgba(58, 196, 255, 200);"
);
// 设置窗口边框样式 dashed虚线,solid 实线
connect
(
MouseEvent
::
getInstance
(),
&
MouseEvent
::
mouseSignal
,
this
,
&
WindowRect
::
on_mouseSignal
);
// 使用定时器定时获取当前鼠标位置的窗口位置信息
connect
(
&
m_timer
,
&
QTimer
::
timeout
,
this
,
&
WindowRect
::
on_timeout
);
m_timer
.
start
(
200
);
}
WindowRect
::~
WindowRect
()
{
#if defined(Q_OS_WIN)
if
(
g_hook
)
{
bool
ret
=
UnhookWindowsHookEx
(
g_hook
);
if
(
ret
)
{
qDebug
()
<<
"卸载鼠标钩子。"
;
}
}
#endif
}
/**
* @brief 通过截图全局鼠标事件将当前窗口大小发生出去
* @param event
*/
void
WindowRect
::
on_mouseSignal
(
QEvent
*
event
)
{
delete
event
;
this
->
hide
();
emit
this
->
selectRect
(
QRect
(
this
->
pos
(),
this
->
size
()));
}
/**
* @brief Windows使用全局鼠标钩子,显示窗口时挂载鼠标钩子
* @param event
*/
void
WindowRect
::
showEvent
(
QShowEvent
*
event
)
{
#if defined(Q_OS_WIN)
// 由于windows不透明的窗体如果不设置设置鼠标穿透WindowFromPoint只能捕捉到当前窗体,而设置鼠标穿透后想要获取鼠标事件只能通过鼠标钩子
g_hook
=
SetWindowsHookExW
(
WH_MOUSE_LL
,
CallBackProc
,
GetModuleHandleW
(
nullptr
),
0
);
// 挂载全局鼠标钩子
if
(
g_hook
)
{
qDebug
()
<<
"鼠标钩子挂接成功,线程ID:"
<<
GetCurrentThreadId
();
}
else
{
qDebug
()
<<
"鼠标钩子挂接失败:"
<<
GetLastError
();
}
#endif
QWidget
::
showEvent
(
event
);
}
/**
* @brief 隐藏窗口时卸载鼠标钩子
* @param event
*/
void
WindowRect
::
hideEvent
(
QHideEvent
*
event
)
{
#if defined(Q_OS_WIN)
if
(
g_hook
)
{
bool
ret
=
UnhookWindowsHookEx
(
g_hook
);
if
(
ret
)
{
qDebug
()
<<
"卸载鼠标钩子。"
;
g_hook
=
nullptr
;
}
}
#endif
QWidget
::
hideEvent
(
event
);
}
/**
* @brief linux下使用自带的鼠标点击事件就可以
* @param event
*/
void
WindowRect
::
mousePressEvent
(
QMouseEvent
*
event
)
{
#if defined(Q_OS_LINUX)
this
->
hide
();
emit
this
->
selectRect
(
QRect
(
this
->
pos
(),
this
->
size
()));
#endif
QWidget
::
mousePressEvent
(
event
);
}
void
WindowRect
::
on_timeout
()
{
QPoint
point
=
QCursor
::
pos
();
// 获取鼠标当前位置
#if defined(Q_OS_WIN)
POINT
pos
;
pos
.
x
=
point
.
x
();
pos
.
y
=
point
.
y
();
HWND
hwnd
=
nullptr
;
hwnd
=
WindowFromPoint
(
pos
);
// 通过鼠标位置获取窗口句柄
if
(
!
hwnd
)
return
;
RECT
lrect
;
bool
ret
=
GetWindowRect
(
hwnd
,
&
lrect
);
//获取窗口位置
if
(
!
ret
)
return
;
QRect
rect
;
rect
.
setX
(
lrect
.
left
);
rect
.
setY
(
lrect
.
top
);
rect
.
setWidth
(
lrect
.
right
-
lrect
.
left
);
rect
.
setHeight
(
lrect
.
bottom
-
lrect
.
top
);
this
->
setGeometry
(
rect
);
// 设置窗口边框
#elif defined(Q_OS_LINUX) // linux下使用x11获取的窗口大小有可能不太准确,例如浏览器的大小会偏小
// 获取根窗口
Display
*
display
=
XOpenDisplay
(
nullptr
);
Window
rootWindow
=
DefaultRootWindow
(
display
);
Window
root_return
,
parent_return
;
Window
*
children
=
nullptr
;
unsigned
int
nchildren
=
0
;
// 函数详细说明见xlib文档:https://tronche.com/gui/x/xlib/window-information/XQueryTree.html
// 该函数会返回父窗口的子窗口列表children,因为这里用的是当前桌面的根窗口作为父窗口,所以会返回所有子窗口
// 注意:窗口顺序(z-order)为自底向上
XQueryTree
(
display
,
rootWindow
,
&
root_return
,
&
parent_return
,
&
children
,
&
nchildren
);
QRect
recte
;
// 保存鼠标当前所在窗口的范围
for
(
unsigned
int
i
=
0
;
i
<
nchildren
;
++
i
)
{
if
(
children
[
i
]
==
this
->
winId
())
continue
;
// 由于当前窗口一直在最顶层,所以这里要过滤掉当前窗口,否则一直获取到的就是当前窗口大小
XWindowAttributes
attrs
;
XGetWindowAttributes
(
display
,
children
[
i
],
&
attrs
);
// 获取窗口参数
if
(
attrs
.
map_state
==
IsViewable
)
// 只处理可见的窗口, 三个状态:IsUnmapped, IsUnviewable, IsViewable
{
#if 0
QRect rect(attrs.x + 1, attrs.y, attrs.width, attrs.height); // 这里x+1防止全屏显示,如果不+1,设置的大小等于屏幕大小是会自动切换成全屏显示状态,后面就无法缩小了
#else
QRect
rect
(
attrs
.
x
,
attrs
.
y
,
attrs
.
width
,
attrs
.
height
);
#endif
if
(
rect
.
contains
(
point
))
// 判断鼠标坐标是否在窗口范围内
{
recte
=
rect
;
// 记录最后一个窗口的范围
}
}
}
#if 0 // 在linux下使用setGeometry设置窗口会有一些问题
this->showNormal(); // 第一次显示是如果是屏幕大小,则后面无法缩小,这里需要设置还原
this->setGeometry(recte); // 设置窗口边框
#else
// 使用setFixedSize+move可以避免这些问题
this
->
move
(
recte
.
x
(),
recte
.
y
());
this
->
setFixedSize
(
recte
.
width
(),
recte
.
height
());
#endif
// qDebug() << this->rect() <<recte<< this->windowState();
// 注意释放资源
XFree
(
children
);
XCloseDisplay
(
display
);
#elif defined(Q_OS_MAC)
#endif
}
FunctionalModule/SnippingTool/windowrect.h
0 → 100644
浏览文件 @
1195d65c
/******************************************************************************
* @文件名 windowrect.h
* @功能 选中鼠标当前位置窗口的类
*
* @开发者 mhf
* @邮箱 1603291350@qq.com
* @时间 2022/11/19
* @备注
*****************************************************************************/
#ifndef WINDOWRECT_H
#define WINDOWRECT_H
#include <QTimer>
#include <QWidget>
class
WindowRect
:
public
QWidget
{
Q_OBJECT
public:
explicit
WindowRect
(
QWidget
*
parent
=
nullptr
);
~
WindowRect
()
override
;
signals:
void
selectRect
(
QRect
rect
);
private
slots
:
void
on_mouseSignal
(
QEvent
*
event
);
void
showEvent
(
QShowEvent
*
event
)
override
;
void
hideEvent
(
QHideEvent
*
event
)
override
;
void
mousePressEvent
(
QMouseEvent
*
event
)
override
;
protected:
void
on_timeout
();
private:
QTimer
m_timer
;
};
/**
* 全局鼠标事件单例信号类
*/
class
MouseEvent
:
public
QObject
{
Q_OBJECT
public:
static
MouseEvent
*
getInstance
()
{
static
MouseEvent
mouseEvent
;
return
&
mouseEvent
;
}
signals:
void
mouseSignal
(
QEvent
*
event
);
private:
MouseEvent
(){}
};
#endif // WINDOWRECT_H
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录