这款软件旨在让手机能够充当电脑的输入设备。
在家通过HDMI连接笔记本电脑观看电视节目时,由于没有无线键盘,输入操作变得十分繁琐。通常只能使用屏幕键盘进行输入,然而其操作起来也很不方便。
于是,我们开发出了这款软件。借助这款软件,就能够使用手机将输入内容传输到电脑当中。
其使用方法如下:
1. 首先对软件进行解压缩,然后双击运行程序。
2. 在处于同一局域网的手机浏览器中,输入软件所显示的地址。
3. 在手机浏览器的相应界面输入文字后,点击发送按钮,这样就可以在电脑上实现输入操作了。
下载地址:
您需要登录并回复后才可以查看隐藏的内容
main.py源代码:
import asyncio import json import pyautogui import pyperclip from aiohttp import web import os from pathlib import Path html = ''' <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>虚拟键盘</title> <style> * { box-sizing: border-box; } body { margin: 0; padding: 20px; touch-action: manipulation; user-select: none; font-family: Arial, sans-serif; } .container { max-width: 800px; margin: 0 auto; } .keyboard { display: grid; grid-template-columns: repeat(10, 1fr); gap: 5px; margin-top: 20px; } .key { background: #e0e0e0; border: none; border-radius: 5px; padding: 15px 5px; font-size: 16px; touch-action: manipulation; } .key:active { background: #bdbdbd; } .key.wide { grid-column: span 2; } #status { text-align: center; margin: 20px 0; padding: 10px; background: #f5f5f5; border-radius: 5px; } .text-input-section { margin: 20px 0; padding: 20px; background: #f5f5f5; border-radius: 5px; } .text-input-section textarea { width: 100%; height: 100px; margin: 10px 0; padding: 10px; border: 1px solid #ddd; border-radius: 5px; resize: vertical; } .button-group { display: flex; gap: 10px; margin: 10px 0; } .button-group button { background: #4CAF50; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; flex: 1; } .button-group button:active { background: #45a049; } .history-section { margin: 20px 0; padding: 20px; background: #f5f5f5; border-radius: 5px; } .history-list { max-height: 200px; overflow-y: auto; border: 1px solid #ddd; border-radius: 5px; background: white; } .history-item { padding: 10px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; } .history-item:last-child { border-bottom: none; } .history-text { flex: 1; margin-right: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .history-actions { display: flex; gap: 5px; } .history-actions button { background: #2196F3; color: white; border: none; padding: 5px 10px; border-radius: 3px; cursor: pointer; font-size: 12px; } .history-actions button.delete { background: #f44336; } .history-actions button:active { opacity: 0.8; } .link { display: inline-block; margin-top: 5px; /* 调整间距 */ font-size: 14px; /* 调整字体大小 */ color: #007BFF; text-decoration: none; border-bottom: 2px solid #007BFF; padding-bottom: 3px; /* 调整间距 */ transition: color 0.3s ease; } .link:hover { color: #0056b3; } </style> </head> <body> <div class="container"> <div id="status">等待连接...</div> <div class="keyboard" id="keyboard"> <!-- 键盘布局将通过JavaScript生成 --> </div> <div class="text-input-section"> <h3>文本输入</h3> <a href="http://www.3qpd.com" class="link">更多黑科技资源,请访问www.3qpd.com!</a> <textarea id="customText" placeholder="在这里输入要发送的文本..."></textarea> <div class="button-group"> <button onclick="sendCustomText()">发送文本</button> <button onclick="clearInput()">清空输入</button> </div> </div> <div class="history-section"> <h3>历史记录</h3> <div class="history-list" id="historyList"> <!-- 历史记录将通过JavaScript动态添加 --> </div> </div> </div> <script> let ws = null; const keyboard = document.getElementById('keyboard'); const status = document.getElementById('status'); const historyList = document.getElementById('historyList'); const MAX_HISTORY = 10; // 从localStorage加载历史记录 let inputHistory = JSON.parse(localStorage.getItem('inputHistory') || '[]'); // 键盘布局 // const keys = [ // '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', // 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', // 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', // 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', // 'Space', 'Backspace', 'Enter' // ]; const keys = [ 'Space', 'Backspace', 'Enter' ]; // 生成键盘按钮 keys.forEach(key => { const button = document.createElement('button'); button.className = 'key'; if (key === 'Space' || key === 'Backspace' || 'Enter') { button.className = 'key wide'; } button.textContent = key === 'Space' ? '空格' : key === 'Backspace' ? '删除' : key === 'Enter'? '回车' : key; button.addEventListener('touchend', (e) => { e.preventDefault(); sendKey(key); }); keyboard.appendChild(button); }); // 连接WebSocket服务器 function connect() { const protocol = location.protocol === 'https:' ? 'wss://' : 'ws://'; ws = new WebSocket(protocol + location.host + '/ws'); ws.onopen = () => { status.textContent = '已连接'; status.style.background = '#c8e6c9'; }; ws.onclose = () => { status.textContent = '连接断开,尝试重新连接...'; status.style.background = '#ffcdd2'; setTimeout(connect, 3000); }; } // 发送按键信息 function sendKey(key) { if (ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'keypress', key: key })); } } // 更新历史记录显示 function updateHistoryDisplay() { historyList.innerHTML = ''; inputHistory.forEach((text, index) => { const historyItem = document.createElement('div'); historyItem.className = 'history-item'; const textSpan = document.createElement('span'); textSpan.className = 'history-text'; textSpan.textContent = text; const actions = document.createElement('div'); actions.className = 'history-actions'; const sendButton = document.createElement('button'); sendButton.textContent = '发送'; sendButton.onclick = () => resendHistoryText(text); const deleteButton = document.createElement('button'); deleteButton.textContent = '删除'; deleteButton.className = 'delete'; deleteButton.onclick = () => deleteHistoryItem(index); actions.appendChild(sendButton); actions.appendChild(deleteButton); historyItem.appendChild(textSpan); historyItem.appendChild(actions); historyList.appendChild(historyItem); }); } // 添加到历史记录 function addToHistory(text) { if (text && !inputHistory.includes(text)) { inputHistory.unshift(text); if (inputHistory.length > MAX_HISTORY) { inputHistory.pop(); } localStorage.setItem('inputHistory', JSON.stringify(inputHistory)); updateHistoryDisplay(); } } // 删除历史记录项 function deleteHistoryItem(index) { inputHistory.splice(index, 1); localStorage.setItem('inputHistory', JSON.stringify(inputHistory)); updateHistoryDisplay(); } // 重新发送历史记录中的文本 function resendHistoryText(text) { if (ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'text', content: text })); } } // 发送自定义文本 function sendCustomText() { const textarea = document.getElementById('customText'); const text = textarea.value; if (text && ws && ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'text', content: text })); addToHistory(text); textarea.value = ''; // 清空输入框 } } // 清空输入框 function clearInput() { document.getElementById('customText').value = ''; } // 初始化 connect(); updateHistoryDisplay(); </script> </body> </html> ''' # WebSocket处理函数 # WebSocket处理函数 async def websocket_handler(request): ws = web.WebSocketResponse() await ws.prepare(request) try: async for msg in ws: if msg.type == web.WSMsgType.TEXT: data = json.loads(msg.data) if data['type'] == 'keypress': key = data['key'] if key == 'Space': pyautogui.press('space') elif key == 'Backspace': pyautogui.press('backspace') elif key == 'Enter': pyautogui.press('enter') else: pyautogui.press(key) elif data['type'] == 'text': # 使用剪贴板来处理文本输入 text = data['content'] original_clipboard = pyperclip.paste() # 保存原始剪贴板内容 try: pyperclip.copy(text) # 复制新文本到剪贴板 pyautogui.hotkey('ctrl', 'v') # 模拟粘贴操作 finally: # 恢复原始剪贴板内容 pyperclip.copy(original_clipboard) except Exception as e: print(f"WebSocket error: {e}") finally: return ws # HTTP首页处理函数 async def index_handler(request): # 从文件读取HTML内容 # html_path = Path(__file__).parent / 'templates' / 'keyboard.html' # with open(html_path, 'r', encoding='utf-8') as f: # content = f.read() return web.Response(text=html, content_type='text/html') # 获取本地IP地址 def get_local_ip(): import socket try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('8.8.8.8', 80)) ip = s.getsockname()[0] s.close() return ip except: return '127.0.0.1' async def init_app(): app = web.Application() app.router.add_get('/', index_handler) app.router.add_get('/ws', websocket_handler) return app if __name__ == '__main__': # 需要安装: pip install aiohttp pyautogui ip = get_local_ip() port = 8080 print(f"正在启动服务器...") print(f"请在手机浏览器访问: http://{ip}:{port}") app = init_app() # asyncio.get_event_loop().run_until_complete(init_app()) web.run_app(app, host='0.0.0.0', port=port)
请求资源或报告无效资源,请点击[反馈中心]