由于工作需要,将操作录屏,但网上下载的程序,都需要收费,自己做一个吧,程序运行效果如下:

程序代码如下:
import sys
import threading
import time
import os
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QVBoxLayout,
QHBoxLayout, QWidget, QLabel, QFileDialog, QComboBox,
QSpinBox, QGroupBox, QCheckBox, QListWidget, QListWidgetItem,
QMessageBox)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtGui import QFont, QKeySequence, QIcon
from PyQt5.QtWidgets import QShortcut
import numpy as np
from PIL import ImageGrab
import mss
import imageio
import ctypes
from ctypes import wintypes
def resource_path(relative_path):
"""获取资源文件的绝对路径(支持开发和打包环境)
在开发环境中,直接从当前目录查找;
在PyInstaller打包后的环境中,从临时目录(_MEIPASS)查找。
Args:
relative_path: 相对路径的文件名
Returns:
资源的绝对路径
"""
if hasattr(sys, '_MEIPASS'):
# PyInstaller 打包后的临时目录
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
class ScreenRecorder(QMainWindow):
"""屏幕录制器主窗口类"""
def __init__(self):
"""初始化屏幕录制器"""
super().__init__()
# 录制状态标志
self.recording = False
# 录制线程
self.recording_thread = None
# 开始录制时间
self.start_time = None
# 计时器(用于更新显示录制时长)
self.timer = QTimer()
self.timer.timeout.connect(self.update_timer)
# 存储捕获的帧
self.frames = []
# 已录制的文件列表
self.recorded_files = []
# 初始化UI
self.init_ui()
# 初始化快捷键
self.init_shortcuts()
def init_ui(self):
"""初始化用户界面"""
# 设置窗口标题和大小
self.setWindowTitle('屏幕录制器 - ChipX.com')
self.setGeometry(100, 100, 500, 450)
# 设置窗口图标(如果存在)
icon_path = resource_path('icon.ico')
if os.path.exists(icon_path):
self.setWindowIcon(QIcon(icon_path))
# 创建中心部件和主布局
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
# 标题标签
title_label = QLabel('屏幕录制器')
title_label.setFont(QFont('Arial', 18, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
layout.addWidget(title_label)
# 录制设置分组
settings_group = QGroupBox('录制设置')
settings_layout = QVBoxLayout()
# 帧率设置
fps_layout = QHBoxLayout()
fps_label = QLabel('帧率 (FPS):')
self.fps_spin = QSpinBox()
self.fps_spin.setRange(5, 60) # 帧率范围5-60
self.fps_spin.setValue(30) # 默认30帧
fps_layout.addWidget(fps_label)
fps_layout.addWidget(self.fps_spin)
settings_layout.addLayout(fps_layout)
# 视频编码设置
codec_layout = QHBoxLayout()
codec_label = QLabel('视频编码:')
self.codec_combo = QComboBox()
self.codec_combo.addItems(['mp4v (MP4)', 'XVID (AVI)', 'MJPG (AVI)'])
codec_layout.addWidget(codec_label)
codec_layout.addWidget(self.codec_combo)
settings_layout.addLayout(codec_layout)
# 视频质量设置
quality_layout = QHBoxLayout()
quality_label = QLabel('质量:')
self.quality_spin = QSpinBox()
self.quality_spin.setRange(1, 100) # 质量范围1-100
self.quality_spin.setValue(80) # 默认80
quality_layout.addWidget(quality_label)
quality_layout.addWidget(self.quality_spin)
settings_layout.addLayout(quality_layout)
# 全屏录制选项
self.fullscreen_check = QCheckBox('全屏录制')
self.fullscreen_check.setChecked(False) # 默认不勾选
settings_layout.addWidget(self.fullscreen_check)
settings_group.setLayout(settings_layout)
layout.addWidget(settings_group)
# 状态显示分组
status_group = QGroupBox('状态')
status_layout = QVBoxLayout()
self.status_label = QLabel('状态: 未录制')
self.time_label = QLabel('录制时长: 00:00:00')
status_layout.addWidget(self.status_label)
status_layout.addWidget(self.time_label)
status_group.setLayout(status_layout)
layout.addWidget(status_group)
# 按钮布局
button_layout = QHBoxLayout()
# 开始录制按钮
self.start_button = QPushButton('开始录制')
self.start_button.clicked.connect(self.start_recording)
self.start_button.setStyleSheet('background-color: #4CAF50; color: white; font-weight: bold; padding: 10px;')
# 停止录制按钮
self.stop_button = QPushButton('停止录制')
self.stop_button.clicked.connect(self.stop_recording)
self.stop_button.setEnabled(False) # 初始状态禁用
self.stop_button.setStyleSheet('background-color: #f44336; color: white; font-weight: bold; padding: 10px;')
button_layout.addWidget(self.start_button)
button_layout.addWidget(self.stop_button)
layout.addLayout(button_layout)
# 录制历史分组
history_group = QGroupBox('录制历史 (双击打开视频)')
history_layout = QVBoxLayout()
self.history_list = QListWidget()
self.history_list.itemDoubleClicked.connect(self.open_video)
history_layout.addWidget(self.history_list)
history_group.setLayout(history_layout)
layout.addWidget(history_group)
# 版权信息
copyright_label = QLabel('© 2026 ChipX.com | Author: Hamilton')
copyright_label.setAlignment(Qt.AlignCenter)
copyright_label.setStyleSheet('color: #666; font-size: 10px;')
layout.addWidget(copyright_label)
central_widget.setLayout(layout)
def get_codec(self):
"""获取选择的编码器和对应文件扩展名
Returns:
tuple: (编码器名称, 文件扩展名)
"""
codec_name = self.codec_combo.currentText()
if 'mp4v' in codec_name:
return 'mp4v', '.mp4'
elif 'XVID' in codec_name:
return 'XVID', '.avi'
else:
return 'MJPG', '.avi'
def get_active_window_rect(self):
"""获取当前激活窗口的位置和大小(Windows系统)
使用Windows API获取前台窗口的矩形区域
Returns:
tuple: (left, top, width, height) 或 None(如果获取失败)
"""
try:
# 获取前台窗口句柄
hwnd = ctypes.windll.user32.GetForegroundWindow()
# 创建RECT结构体接收窗口位置
rect = wintypes.RECT()
# 获取窗口矩形
ctypes.windll.user32.GetWindowRect(hwnd, ctypes.byref(rect))
# 返回(left, top, width, height)
return (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top)
except:
return None
def start_recording(self):
"""开始录制屏幕"""
# 生成时间戳文件名
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
codec, ext = self.get_codec()
# 弹出文件保存对话框
filename, _ = QFileDialog.getSaveFileName(
self, '保存录制视频', f'screen_recording_{timestamp}{ext}',
f'Video Files (*{ext})'
)
# 如果用户取消保存,则返回
if not filename:
return
# 获取录制参数
self.fps = self.fps_spin.value()
self.quality = self.quality_spin.value()
self.output_filename = filename
self.frames = [] # 清空帧列表
# 最小化窗口到后台,避免录制到自己的窗口
self.showMinimized()
time.sleep(0.5) # 等待窗口最小化完成
# 使用mss初始化屏幕捕获
with mss.mss() as sct:
if self.fullscreen_check.isChecked():
# 全屏录制:使用第一个显示器
monitor = sct.monitors[1]
self.screen_size = (monitor['width'], monitor['height'])
self.capture_rect = None
else:
# 窗口录制:获取当前激活窗口
window_rect = self.get_active_window_rect()
if window_rect:
# 设置捕获区域为窗口区域
self.capture_rect = {
'left': window_rect[0],
'top': window_rect[1],
'width': window_rect[2],
'height': window_rect[3]
}
self.screen_size = (window_rect[2], window_rect[3])
else:
# 如果获取窗口失败,使用全屏默认值
self.screen_size = (1920, 1080)
self.capture_rect = {'top': 0, 'left': 0, 'width': 1920, 'height': 1080}
# 设置录制状态
self.recording = True
self.start_time = time.time()
self.start_button.setEnabled(False)
self.stop_button.setEnabled(True)
self.status_label.setText('状态: 录制中...')
self.timer.start(100) # 每100ms更新一次计时器
# 创建并启动录制线程
self.recording_thread = threading.Thread(target=self.record_screen)
self.recording_thread.daemon = True # 设置为守护线程,主程序退出时自动结束
self.recording_thread.start()
def record_screen(self):
"""录制屏幕的线程函数
在后台线程中持续捕获屏幕并存储帧
"""
frame_duration = 1.0 / self.fps # 每帧的理论间隔时间
with mss.mss() as sct:
while self.recording:
frame_start = time.time()
# 捕获屏幕
if self.fullscreen_check.isChecked():
# 全屏捕获
monitor = sct.monitors[1]
screenshot = sct.grab(monitor)
else:
# 窗口捕获
screenshot = sct.grab(self.capture_rect)
# 转换为numpy数组并存储
frame = np.array(screenshot)
self.frames.append(frame)
# 控制帧率:计算需要等待的时间
elapsed = time.time() - frame_start
sleep_time = max(0, frame_duration - elapsed)
time.sleep(sleep_time)
def stop_recording(self):
"""停止录制并保存视频文件"""
self.recording = False
self.timer.stop() # 停止计时器
# 等待录制线程结束
if self.recording_thread:
self.recording_thread.join(timeout=2)
# 如果有捕获到帧,保存为视频文件
if self.frames:
codec, _ = self.get_codec()
# 转换质量参数(imageio使用1-10的范围)
imageio_quality = max(1, min(10, self.quality // 10))
# 根据编码器选择合适的写入器
if 'mp4v' in codec:
# MP4格式使用libx264编码
writer = imageio.get_writer(self.output_filename, fps=self.fps, codec='libx264', quality=imageio_quality)
else:
# AVI格式使用原始视频
writer = imageio.get_writer(self.output_filename, fps=self.fps, codec='rawvideo')
# 将所有帧写入视频
for frame in self.frames:
writer.append_data(frame)
writer.close() # 关闭写入器
# 添加到历史记录
self.add_to_history(self.output_filename)
# 恢复界面状态
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.status_label.setText('状态: 录制已停止')
self.time_label.setText('录制时长: 00:00:00')
def update_timer(self):
"""更新录制时长显示"""
if self.start_time:
elapsed = time.time() - self.start_time
hours = int(elapsed // 3600)
minutes = int((elapsed % 3600) // 60)
seconds = int(elapsed % 60)
self.time_label.setText(f'录制时长: {hours:02d}:{minutes:02d}:{seconds:02d}')
def init_shortcuts(self):
"""初始化快捷键"""
# Ctrl+R 开始录制
self.shortcut_start = QShortcut(QKeySequence("Ctrl+R"), self)
self.shortcut_start.activated.connect(self.start_recording_shortcut)
# Ctrl+S 停止录制
self.shortcut_stop = QShortcut(QKeySequence("Ctrl+S"), self)
self.shortcut_stop.activated.connect(self.stop_recording_shortcut)
def start_recording_shortcut(self):
"""快捷键开始录制(仅在未录制时有效)"""
if not self.recording:
self.start_recording()
def stop_recording_shortcut(self):
"""快捷键停止录制(仅在录制中有效)"""
if self.recording:
self.stop_recording()
def open_video(self, item):
"""双击历史记录项打开视频文件
Args:
item: 被双击的列表项
"""
# 从列表项获取文件路径
file_path = item.data(Qt.UserRole)
if file_path and os.path.exists(file_path):
# 使用系统默认程序打开文件
os.startfile(file_path)
else:
QMessageBox.warning(self, '错误', '视频文件不存在或已被删除')
def add_to_history(self, file_path):
"""添加文件到录制历史
Args:
file_path: 视频文件的路径
"""
if file_path and os.path.exists(file_path):
self.recorded_files.append(file_path)
file_name = os.path.basename(file_path)
# 创建列表项
item = QListWidgetItem(file_name)
item.setData(Qt.UserRole, file_path) # 存储完整路径
item.setToolTip(file_path) # 设置悬停提示
self.history_list.addItem(item)
def closeEvent(self, event):
"""窗口关闭事件处理
如果正在录制,先停止录制再关闭窗口
Args:
event: 关闭事件
"""
if self.recording:
self.stop_recording()
event.accept() # 接受关闭事件
def main():
"""主函数:创建并运行应用程序"""
app = QApplication(sys.argv)
recorder = ScreenRecorder()
recorder.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
1132

被折叠的 条评论
为什么被折叠?



