From 4ad963d9bd0c7634ef52545bf7c91435c5e45be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?= <101191390+HIllya51@users.noreply.github.com> Date: Mon, 27 May 2024 00:17:36 +0800 Subject: [PATCH] screens --- .../LunaTranslator/gui/rangeselect.py | 25 ++- LunaTranslator/LunaTranslator/myutils/hwnd.py | 7 + .../LunaTranslator/myutils/ocrutil.py | 17 +- .../LunaTranslator/winsharedutils.py | 27 ++- plugins/CMakeLists.txt | 4 +- plugins/winsharedutils/CMakeLists.txt | 2 +- plugins/winsharedutils/screenshot.cpp | 176 ++++++++++++++++++ 7 files changed, 226 insertions(+), 32 deletions(-) create mode 100644 plugins/winsharedutils/screenshot.cpp diff --git a/LunaTranslator/LunaTranslator/gui/rangeselect.py b/LunaTranslator/LunaTranslator/gui/rangeselect.py index 8b356e35..1ae52781 100644 --- a/LunaTranslator/LunaTranslator/gui/rangeselect.py +++ b/LunaTranslator/LunaTranslator/gui/rangeselect.py @@ -7,13 +7,13 @@ from PyQt5.QtWidgets import ( QDialog, QDesktopWidget, ) -from PyQt5.QtGui import QPainter, QPen, QColor +from PyQt5.QtGui import QPainter, QPen, QColor, QResizeEvent from PyQt5.QtCore import Qt, QPoint, QRect, QEvent from myutils.config import _TR import gobject from myutils.config import globalconfig from gui.resizeablemainwindow import Mainw -import windows +import windows, winsharedutils class rangeadjust(Mainw): @@ -131,25 +131,24 @@ class rangeselct(QMainWindow): Qt.FramelessWindowHint | Qt.Tool ) # |Qt.WindowStaysOnTopHint ) self.rectlabel = QLabel(self) - self.setAttribute(Qt.WA_TranslucentBackground) + # self.setAttribute(Qt.WA_TranslucentBackground) + self.setWindowOpacity(0.5) + self.setMouseTracking(True) + self.setCursor(Qt.CrossCursor) + self.reset() def reset(self): - # screens = QDesktopWidget().screenCount() - # desktop = QDesktopWidget().screenGeometry(0) - # for i in range(1, screens): - # desktop = desktop.united(QDesktopWidget().screenGeometry(i)) - desktop = QApplication.primaryScreen().virtualGeometry() - self.setGeometry(desktop) - self.rectlabel.setGeometry(desktop) - self.setCursor(Qt.CrossCursor) + winsharedutils.maximum_window(int(self.winId())) + # desktop = QApplication.primaryScreen().virtualGeometry() + # self.setGeometry(desktop) self.is_drawing = False - self.setMouseTracking(True) self.start_point = QPoint() self.end_point = QPoint() self.__start = None self.__end = None self.startauto = False self.clickrelease = False + self.rectlabel.resize(0, 0) self.rectlabel.setStyleSheet( " border:%spx solid %s; background-color: rgba(0,0,0, 0.01)" % (globalconfig["ocrrangewidth"], globalconfig["ocrrangecolor"]) @@ -246,8 +245,8 @@ def rangeselct_function(parent, callback, clickrelease, startauto): global screen_shot_ui if screen_shot_ui is None: screen_shot_ui = rangeselct(parent) - screen_shot_ui.reset() screen_shot_ui.show() + screen_shot_ui.reset() screen_shot_ui.callback = callback windows.SetFocus(int(screen_shot_ui.winId())) diff --git a/LunaTranslator/LunaTranslator/myutils/hwnd.py b/LunaTranslator/LunaTranslator/myutils/hwnd.py index acb5a3a4..826d8e93 100644 --- a/LunaTranslator/LunaTranslator/myutils/hwnd.py +++ b/LunaTranslator/LunaTranslator/myutils/hwnd.py @@ -252,3 +252,10 @@ def mouseselectwindow(callback): pass threading.Thread(target=_loop).start() + + +def screenshot(x1, y1, x2, y2): + bs = winsharedutils.gdi_screenshot(x1, y1, x2, y2) + pixmap = QPixmap() + pixmap.loadFromData(bs) + return pixmap diff --git a/LunaTranslator/LunaTranslator/myutils/ocrutil.py b/LunaTranslator/LunaTranslator/myutils/ocrutil.py index 1355b7c2..8bbd2924 100644 --- a/LunaTranslator/LunaTranslator/myutils/ocrutil.py +++ b/LunaTranslator/LunaTranslator/myutils/ocrutil.py @@ -5,7 +5,7 @@ from PyQt5.QtWidgets import QApplication, QDesktopWidget from PyQt5.QtGui import QImage from PyQt5.QtCore import QByteArray, QBuffer from myutils.commonbase import ArgsEmptyExc -from myutils.hwnd import dynamic_rate +from myutils.hwnd import dynamic_rate, screenshot from myutils.utils import stringfyerror from traceback import print_exc import gobject, winsharedutils @@ -83,20 +83,9 @@ def imageCut(hwnd, x1, y1, x2, y2, viscompare=True, rawimage=False) -> QImage: except: print_exc() else: + if QDesktopWidget().screenCount() > 1: - desktop = QApplication.primaryScreen().virtualGeometry() - pix = screen.grabWindow( - QApplication.desktop().winId(), - desktop.x(), - desktop.y(), - desktop.width(), - desktop.height(), - ) - x1 = x1 - desktop.x() - y1 = y1 - desktop.y() - x2 = x2 - desktop.x() - y2 = y2 - desktop.y() - pix = pix.copy(x1, y1, x2 - x1, y2 - y1) + pix = screenshot(x1, y1, x2, y2) else: pix = screen.grabWindow( QApplication.desktop().winId(), x1, y1, x2 - x1, y2 - y1 diff --git a/LunaTranslator/LunaTranslator/winsharedutils.py b/LunaTranslator/LunaTranslator/winsharedutils.py index 90bb0b56..8adeb838 100644 --- a/LunaTranslator/LunaTranslator/winsharedutils.py +++ b/LunaTranslator/LunaTranslator/winsharedutils.py @@ -20,7 +20,7 @@ from ctypes import ( c_double, c_char, ) -from ctypes.wintypes import WORD, HANDLE, HWND, LONG, DWORD +from ctypes.wintypes import WORD, HANDLE, HWND, LONG, DWORD, RECT, BYTE from windows import WINDOWPLACEMENT import gobject, csv @@ -126,7 +126,9 @@ def SAPI_Speak(content, v, voiceid, rate, volume): return data -def distance(s1, s2): # 词典更适合用编辑距离,因为就一两个字符,相似度会很小,预翻译适合用相似度 +def distance( + s1, s2 +): # 词典更适合用编辑距离,因为就一两个字符,相似度会很小,预翻译适合用相似度 return _levenshtein_distance(len(s1), s1, len(s2), s2) @@ -359,3 +361,24 @@ PlayAudioInMem.restype = c_int PlayAudioInMem_Stop = utilsdll.PlayAudioInMem_Stop PlayAudioInMem_Stop.argtypes = c_void_p, c_void_p + +_gdi_screenshot = utilsdll.gdi_screenshot +_gdi_screenshot.argtypes = RECT, POINTER(c_size_t) +_gdi_screenshot.restype = POINTER(BYTE) + + +def gdi_screenshot(x1, y1, x2, y2): + sz = c_size_t() + rect = RECT() + rect.left = x1 + rect.top = y1 + rect.right = x2 + rect.bottom = y2 + bf = _gdi_screenshot(rect, pointer(sz)) + data = cast(bf, POINTER(c_char))[: sz.value] + c_free(bf) + return data + + +maximum_window = utilsdll.maximum_window +maximum_window.argtypes = (HWND,) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 66f3d1f9..e37ce83d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -28,8 +28,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/version) include(generate_product_version) set(VERSION_MAJOR 2) -set(VERSION_MINOR 52) -set(VERSION_PATCH 8) +set(VERSION_MINOR 53) +set(VERSION_PATCH 0) add_library(pch pch.cpp) target_precompile_headers(pch PUBLIC pch.h) diff --git a/plugins/winsharedutils/CMakeLists.txt b/plugins/winsharedutils/CMakeLists.txt index e158075a..c0f40c1b 100644 --- a/plugins/winsharedutils/CMakeLists.txt +++ b/plugins/winsharedutils/CMakeLists.txt @@ -11,7 +11,7 @@ generate_product_version( VERSION_PATCH ${VERSION_PATCH} ) -add_library(winsharedutils MODULE audio.cpp ../implsapi.cpp hwnd.cpp darklistener.cpp theme.cpp version.cpp otsu.cpp cinterface.cpp clipboard.cpp lnk.cpp dllmain.cpp levenshtein.cpp muteprocess.cpp sapi_dll.cpp simplemecab.cpp SimpleBrowser.cpp MWebBrowser.cpp icon.cpp maglistener.cpp ${versioninfo}) +add_library(winsharedutils MODULE screenshot.cpp audio.cpp ../implsapi.cpp hwnd.cpp darklistener.cpp theme.cpp version.cpp otsu.cpp cinterface.cpp clipboard.cpp lnk.cpp dllmain.cpp levenshtein.cpp muteprocess.cpp sapi_dll.cpp simplemecab.cpp SimpleBrowser.cpp MWebBrowser.cpp icon.cpp maglistener.cpp ${versioninfo}) target_precompile_headers(winsharedutils REUSE_FROM pch) if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) set_target_properties(winsharedutils PROPERTIES OUTPUT_NAME "winsharedutils64") diff --git a/plugins/winsharedutils/screenshot.cpp b/plugins/winsharedutils/screenshot.cpp new file mode 100644 index 00000000..a3f118ba --- /dev/null +++ b/plugins/winsharedutils/screenshot.cpp @@ -0,0 +1,176 @@ + +#include "define.h" + +void GetVirtualDesktopRect(RECT &rect) +{ + // 获取虚拟桌面的尺寸和位置 + rect.left = GetSystemMetrics(SM_XVIRTUALSCREEN); + rect.top = GetSystemMetrics(SM_YVIRTUALSCREEN); + rect.right = rect.left + GetSystemMetrics(SM_CXVIRTUALSCREEN); + rect.bottom = rect.top + GetSystemMetrics(SM_CYVIRTUALSCREEN); +} + +HBITMAP GetBitmap(RECT &rect, HDC hDC) +{ + HDC hMemDC; + int x, y; + int nWidth, nHeight; + HBITMAP hBitmap, hOldBitmap; + + hMemDC = CreateCompatibleDC(hDC); + // RECT rect; + // GetVirtualDesktopRect(rect); + hBitmap = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top); + hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); + + BitBlt(hMemDC, 0, 0, rect.right - rect.left, rect.bottom - rect.top, hDC, rect.left, rect.top, SRCCOPY); + + hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap); + + DeleteDC(hMemDC); + + return hBitmap; +} +int SaveBitmapToFile(HBITMAP hBitmap, LPCWSTR lpFileName) +{ + WORD wBitCount; // 位图中每个像素所占字节数 + // 定义调色板大小,位图中像素字节大小,位图文件大小,写入文件字节数 + DWORD dwPaletteSize = 0, dwBmBitsSize, dwDIBSize, dwWritten; + BITMAP Bitmap; // 位图属性结构 + BITMAPFILEHEADER bmfHdr; // 位图文件头结构 + BITMAPINFOHEADER bi; // 位图信息头结构 + HANDLE fh; // 定义文件,分配内存句柄,调色板句柄 + LPSTR lpbk, lpmem; + + wBitCount = 32; + // 设置位图信息头结构 + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; // 为负,正向的位图;为正,倒向的位图 + bi.biPlanes = 1; + bi.biBitCount = wBitCount; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight; + + // 创建位图文件 + fh = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (fh == INVALID_HANDLE_VALUE) + return FALSE; + // 设置位图文件头 + bmfHdr.bfType = 0x4D42; // "BM" + dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmBitsSize; + bmfHdr.bfSize = dwDIBSize; + bmfHdr.bfReserved1 = 0; + bmfHdr.bfReserved2 = 0; + bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); + + // 写入位图文件头 + WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); + // 写入位图信息头 + WriteFile(fh, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwWritten, NULL); + + // 获取位图阵列 + lpmem = new char[dwBmBitsSize]; + lpbk = (LPSTR) new char[dwBmBitsSize]; + GetBitmapBits(hBitmap, dwBmBitsSize, lpmem); // 正向的内存图象数据 + + // 转化为倒向数据(仅在bmHeight为正时需要) + for (int i = 0; i < Bitmap.bmHeight; i++) + { + memcpy(lpbk + Bitmap.bmWidth * i * 4, lpmem + Bitmap.bmWidth * (Bitmap.bmHeight - i - 1) * 4, Bitmap.bmWidth * 4); + } + // 写位图数据 + WriteFile(fh, lpbk, dwBmBitsSize, &dwWritten, NULL); + + // 清除 + delete[] lpbk; + delete[] lpmem; + + CloseHandle(fh); + return TRUE; +} + +BYTE *SaveBitmapToBuffer(HBITMAP hBitmap, size_t *size) +{ + WORD wBitCount; // 位图中每个像素所占字节数 + // 定义调色板大小,位图中像素字节大小,位图文件大小,写入文件字节数 + DWORD dwPaletteSize = 0, dwBmBitsSize, dwDIBSize, dwWritten; + BITMAP Bitmap; // 位图属性结构 + BITMAPFILEHEADER bmfHdr; // 位图文件头结构 + BITMAPINFOHEADER bi; // 位图信息头结构 + + LPSTR lpbk, lpmem; + + wBitCount = 32; + // 设置位图信息头结构 + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; // 为负,正向的位图;为正,倒向的位图 + bi.biPlanes = 1; + bi.biBitCount = wBitCount; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight; + + // 设置位图文件头 + bmfHdr.bfType = 0x4D42; // "BM" + dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmBitsSize; + bmfHdr.bfSize = dwDIBSize; + bmfHdr.bfReserved1 = 0; + bmfHdr.bfReserved2 = 0; + bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); + + // 写入位图文件头 + // WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); + // 写入位图信息头 + // WriteFile(fh, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwWritten, NULL); + + // 获取位图阵列 + lpmem = new char[dwBmBitsSize]; + lpbk = (LPSTR) new char[dwBmBitsSize]; + GetBitmapBits(hBitmap, dwBmBitsSize, lpmem); // 正向的内存图象数据 + + for (int i = 0; i < Bitmap.bmHeight; i++) + { + memcpy(lpbk + Bitmap.bmWidth * i * 4, lpmem + Bitmap.bmWidth * (Bitmap.bmHeight - i - 1) * 4, Bitmap.bmWidth * 4); + } + // 写位图数据 + // WriteFile(fh, lpbk, dwBmBitsSize, &dwWritten, NULL); + auto buffer = new BYTE[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmBitsSize]; + memcpy(buffer, &bmfHdr, sizeof(BITMAPFILEHEADER)); + memcpy(buffer + sizeof(BITMAPFILEHEADER), &bi, sizeof(BITMAPINFOHEADER)); + memcpy(buffer + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), lpbk, dwBmBitsSize); + *size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwBmBitsSize; + // 清除 + delete[] lpbk; + delete[] lpmem; + + return buffer; +} +DECLARE BYTE *gdi_screenshot(RECT rect, size_t *size) +{ + auto bm = GetBitmap(rect, GetDC(GetDesktopWindow())); + // SaveBitmapToFile(bm, LR"(.\2.bmp)"); + auto bf = SaveBitmapToBuffer(bm, size); + DeleteObject(bf); + return bf; +} + +DECLARE void maximum_window(HWND hwnd) +{ + RECT rect; + GetVirtualDesktopRect(rect); + MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); +} \ No newline at end of file