我希望屏幕截图中有鼠标光标,但是 QT 中的抓取 API -QPixmap::grabWindow()
只能制作没有光标的屏幕截图。
我可以自己在图片中绘制光标,但我必须获取光标图片,并且屏幕截图中的光标将与不可更改的光标图片保持一致。太丑了 有更好的解决方案吗?
根据 QPixmap::grabWindow 文档,它不会捕获鼠标光标,
我认为您需要自己获取鼠标位置并绘制光标。您可以使用 API 从 QWidget 获取 QCursor QCursor cursor() const
。
并且 QCursor 有QPixmap pixmap ()
API,你可以使用这个 Pixmap 来绘制鼠标光标。
抱歉,如果我无法正确理解您的问题。
这是跨平台解决方案:
#include <assert.h>
#include <QCursor>
#include <QPainter>
#ifdef Q_OS_LINUX
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xfixes.h>
#else // Q_OS_WINDOWs
#include <Windows.h>
#include <wingdi.h>
#pragma comment (lib, "User32.lib")
#pragma comment (lib, "Gdi32.lib")
#include <QtWinExtras>
#endif
namespace imageutil {
#ifdef Q_OS_LINUX
/* WebCore/plugins/qt/QtX11ImageConversion.cpp */
QImage qimageFromXImage(XImage* xi) {
QImage::Format format = QImage::Format_ARGB32_Premultiplied;
if (xi->depth == 24)
format = QImage::Format_RGB32;
else if (xi->depth == 16)
format = QImage::Format_RGB16;
QImage image = QImage(reinterpret_cast<uchar*>(xi->data), xi->width, xi->height, xi->bytes_per_line, format).copy();
// we may have to swap the byte order
if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && xi->byte_order == MSBFirst)
|| (QSysInfo::ByteOrder == QSysInfo::BigEndian && xi->byte_order == LSBFirst)) {
for (int i = 0; i < image.height(); i++) {
if (xi->depth == 16) {
ushort* p = reinterpret_cast<ushort*>(image.scanLine(i));
ushort* end = p + image.width();
while (p < end) {
*p = ((*p << 8) & 0xff00) | ((*p >> 8) & 0x00ff);
p++;
}
} else {
uint* p = reinterpret_cast<uint*>(image.scanLine(i));
uint* end = p + image.width();
while (p < end) {
*p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000)
| ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff);
p++;
}
}
}
}
// fix-up alpha channel
if (format == QImage::Format_RGB32) {
QRgb* p = reinterpret_cast<QRgb*>(image.bits());
for (int y = 0; y < xi->height; ++y) {
for (int x = 0; x < xi->width; ++x)
p[x] |= 0xff000000;
p += xi->bytes_per_line / 4;
}
}
return image;
}
#endif // Q_OS_LINUX
QPixmap takeScreenShot(const QRect& area) {
QRect screen; /* interested display area */
QImage qimage; /* result image */
#ifdef Q_OS_LINUX
QPoint cursorPos;
Display* display = XOpenDisplay(nullptr);
Window root = DefaultRootWindow(display);
XWindowAttributes gwa;
XGetWindowAttributes(display, root, &gwa);
const auto goodArea = QRect(0, 0, gwa.width, gwa.height).contains(area);
if (!goodArea) {
screen = QRect(0, 0, gwa.width, gwa.height);
cursorPos = QCursor::pos();
} else {
screen = area;
cursorPos = QCursor::pos() - screen.topLeft();
}
XImage* image = XGetImage(display, root, screen.x(), screen.y(), screen.width(), screen.height(), AllPlanes, ZPixmap);
assert(nullptr != image);
qimage = qimageFromXImage(image);
/* draw mouse cursor into QImage
* https://msnkambule.wordpress.com/2010/04/09/capturing-a-screenshot-showing-mouse-cursor-in-kde/
* https://github.com/rprichard/x11-canvas-screencast/blob/master/CursorX11.cpp#L31
* */
{
XFixesCursorImage* cursor = XFixesGetCursorImage(display);
cursorPos -= QPoint(cursor->xhot, cursor->yhot);
std::vector<uint32_t> pixels(cursor->width * cursor->height);
for (size_t i = 0; i < pixels.size(); ++i)
pixels[i] = cursor->pixels[i];
QImage cursorImage((uchar*)(pixels.data()), cursor->width, cursor->height, QImage::Format_ARGB32_Premultiplied);
QPainter painter(&qimage);
painter.drawImage(cursorPos, cursorImage);
XFree(cursor);
}
XDestroyImage(image);
XDestroyWindow(display, root);
XCloseDisplay(display);
#elif defined(Q_OS_WINDOWS)
HWND hwnd = GetDesktopWindow();
HDC hdc = GetWindowDC(hwnd);
HDC hdcMem = CreateCompatibleDC(hdc);
RECT rect = { 0, 0, GetDeviceCaps(hdc, HORZRES), GetDeviceCaps(hdc, VERTRES) };
const auto goodArea = QRect(rect.left, rect.top, rect.right, rect.bottom).contains(area);
if (!goodArea) {
screen = QRect(rect.left, rect.top, rect.right, rect.bottom);
} else {
screen = area;
}
HBITMAP hbitmap(nullptr);
hbitmap = CreateCompatibleBitmap(hdc, screen.width(), screen.height());
SelectObject(hdcMem, hbitmap);
BitBlt(hdcMem, 0, 0, screen.width(), screen.height(), hdc, screen.x(), screen.y(), SRCCOPY);
/* draw mouse cursor into DC
* https://stackoverflow.com/a/48925443/5446734
* */
CURSORINFO cursor = { sizeof(cursor) };
if (GetCursorInfo(&cursor) && cursor.flags == CURSOR_SHOWING) {
RECT rect;
GetWindowRect(hwnd, &rect);
ICONINFO info = { sizeof(info) };
GetIconInfo(cursor.hCursor, &info);
const int x = (cursor.ptScreenPos.x - rect.left - rect.left - info.xHotspot) - screen.left();
const int y = (cursor.ptScreenPos.y - rect.left - rect.left - info.yHotspot) - screen.top();
BITMAP bmpCursor = { 0 };
GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
DrawIconEx(hdcMem, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, nullptr, DI_NORMAL);
}
qimage = QtWin::imageFromHBITMAP(hdc, hbitmap, screen.width(), screen.height());
#endif // Q_OS_LINUX
return QPixmap::fromImage(qimage);
}
} // namespace imageutil
不要忘记添加到 qmake:
win32: QT += winextras
unix:!macx: LIBS += -lX11 -lXext -lXfixes
QPixmap::pixmap 仅在光标为像素图时有效。当前游标是标准游标时不起作用。一些想法总是可以得到一个包含实际光标的像素图?