Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【求助】使用Python脚本如何获取Windows中选中的文本 #38

Closed
simpleowen opened this issue Dec 25, 2016 · 9 comments
Closed
Assignees
Labels

Comments

@simpleowen
Copy link

Title:使用Python脚本如何获取Windows中选中的文本

背景

  • 我期待实现的效果/欲达成的目的是:
    在任意位置选中文本后,通过右键菜单获取这段选中的文本
    获取选中文本

到时我会加入一个右键菜单项,功能就是获取已选中的文本,类似复制功能,复制后保存到剪贴板中,但我会用一个变量来接收这段选中的字符串

  • 我的系统、版本等环境配置是:
    Windows 7、Windows XP

现象

我想实现上述效果/目的时,遇到了这样的情况:

  • 输入:
  • 触发:
  • 问题:没法下手,在网上搜索

分析

我已经做了以下尝试:

  • 尝试 0:

    • 参考:
    • 判断:
    • 实验:使用Google搜索中文关键词“Python 获取Windows中选中的文本”、“Python 获取选中的文本”
    • 结论:没有找到合适的信息
  • 尝试 1:

  • 尝试 2:

    • 参考:如何获取鼠标选中文本内容Windows SDK 教程(四) 记事本与SendMessage
    • 判断:从了解Windows获取文本原理入手
    • 实验:使用Google搜索关键词“windows如何获取选中的文本”、“windows 读取选中文本”、”windows API 读取选中文本”、“windows api 选中文本”
    • 结论:通过网络搜索,隐约觉得可能用windows api的方式通过消息机制可以获取选中的文本,因为没这方面基础,只是停留在感觉阶段。希望能用Python实现。

方案

我推测可以有以下几种解决方案,请问大家哪种思路正确,或有其它思路建议?

  • 解决思路 0:
    • 参考:
    • 设想:Python是不是有模块可以用来调用Windows API从而获取选中的文本
  • 解决思路 1:

卡在这两天了,请同学们帮忙看下。
若有可能,还请指出我在上述分析问题和解决思路过程中的bug,多谢啦。。。

@simpleowen
Copy link
Author

在报名三期之前有看过二期的一些资料

这两天在看同学陆续提交入学任务的时候也发现有同学碰到了windows操作系统下环境变量的坑,于是想着用Python做一个脚本轻松简便的将路径设置为用户的环境变量。

想法是这样的:用户在windows资源管理器地址栏中,选中路径文本后右键弹出菜单项“加入到当前用户的PATH环境变量”,单击该菜单即完成环境变量的设置

分解任务:

任务1.在windows的资源管理器中选中路径或选中一个目录

任务2.获取选中的路径文本或当前目录所在路径的字符串

任务3.右键为系统添加一个右键菜单项“加入到当前用户的PATH环境变量”,同时弹出菜单

任务4.执行设置环境变量脚本

以上4个任务中,第1个选中文本的任务可以由操作系统完成,后面3个任务需要编程

先来做第4个任务。

将第4个任务进行细分

4.1 取出当前环境变量PATH的值
4.2 根据分号分解字符串成列表
4.3 查询列表中的每个值,若已存在相同的值则提示“已有当前路径设置”,否则添加到列表末尾
4.4 将列表中的值重新组合为环境变量PATH的值
4.5 赋值给PATH

环境变量属于系统级的东西,于是想到python中的os模块应该有相应的方法可以用。

打开python官方文档搜索os模块,找到两个函数,一个是获取环境变量os.getenv(),一个是设置环境变量os.putenv(),果断用上先

少废话,上代码

version 1.0

# coding:utf-8
"""
使用getenv和putenv函数取出和设置环境变量
"""
import os

waitstr = 'c:\python271;'#假设一个待新增到环境变量中的路径
pathstr = ''
p = []

tempstr = os.getenv('path') #取得环境变量值

p = tempstr.split(';') #根据分号分解path环境变量的值

if waitstr in p:#若待新增的路径已经存在在path的值中则退出,否则添加到值列表中
	print "exist"
	exit()
else:
	p.append(waitstr)

for s in p:#重新组合列表中的值为一个字符串
	if s == '':
		continue
	pathstr += s + ';'

os.putenv('path',pathstr)#设置环境变量path设置值

print os.getenv('path')

最后一句打印出来的path的值始终没有新增的那条路径,也就是说putenv设置没有成功。

@simpleowen
Copy link
Author

打开windows查看环境变量,发现path的值没有发生变化,于是再去文档中查看putenv函数,发现有这么一句话

Such changes to the environment affect subprocesses started with os.system(), popen() or fork() and execv().
......
however, calls to putenv() don’t update os.environ, so it is actually preferable to assign to items of os.environ.

这个变化会影响到以os.system(), popen() or fork() and execv()开启的子进程
......
调用putenv()函数并不更新os.environ

不太明白啥意思,反正跟进程什么鬼有关系,感觉可能需要更新os.environ才会起作用

所以在准备尝试通过修改os.environ的值来设置为环境变量path设置值

少废话,上代码

version 1.1

# coding:utf-8
"""
使用os.environ取出和设置环境变量
"""
import os

# print os.environ

waitstr = 'c:\python271;'

os.environ['PATH'] = os.environ['PATH'] + waitstr

print os.getenv('path')

这下简单多了,但是最后发现跟1.0版本一样的现象,还是没法更改windows环境变量PATH的值。

此时有点困惑了,这到底咋回事,为什么不起作用的?

@simpleowen
Copy link
Author

只能再用google搜了

发现一段话,也许可能大概差不多解释了不起作用的原因

环境变量表是保存在每个进程的用户空间的最高地址的,一个进程无权修改别的进程的用户空间内容

没办法了,只能出大招了。

最终,考虑到在规划的任务中第3个任务需要修改注册表值以添加右键菜单,于是想到也许环境变量也可以通过修改注册表来实现,简单、暴力

在网上搜索python操作注册表的模块,还真有。

python有一个内置模块_winreg可以用来操作注册表。

工具有了,下面来了解操作方法,操作之前得先了解环境变量在注册表中的位置、键值等等信息

搜索一通,找到了位置

对于用户环境变量,写入注册表的 HKEY_CURRENT_USER/Environment
对于系统环境变量,写入注册表的 HKEY_LOCAL_MACHINE/SYSTEM/ControlSet001/Control/SessionManager/Environment

ControlSet分几种情况
ControlSet:表示运行时的配置
ControlSet001:表示系统真实的配置信息
ControlSet002:表示最近一次成功的配置信息

少废话,上代码

version 1.2

# coding:utf-8
"""
使用注册表操作模块取出和设置环境变量
"""
import _winreg

newenv = 'c:\\python271;'

user_env = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,'Environment')
# print user_env

value,type = _winreg.QueryValueEx(user_env,'PATH')
# print value,type

tempenv = value + newenv
# print tempenv

try:
	_winreg.SetValueEx(user_env,'PATH',0,type,tempenv)

except WindowsError:
	print 'excetion'
finally:
	_winreg.CloseKey(user_env)


执行后,SetValueEx一直抛异常,不知是什么原因

搜索一通,将OpenKey函数换成CreateKey函数后执行正常,PATH的值发生了变化

对比两个函数

_winreg.CreateKey(key, sub_key)

    Creates or opens the specified key, returning a handle object.

    key is an already open key, or one of the predefined HKEY_* constants.

    sub_key is a string that names the key this method opens or creates.

    If key is one of the predefined keys, sub_key may be None. In that case, the handle returned is the same key handle passed in to the function.

    If the key already exists, this function opens the existing key.

    The return value is the handle of the opened key. If the function fails, a WindowsError exception is raised.


_winreg.OpenKey(key, sub_key[, res[, sam]])

    Opens the specified key, returning a handle object.

    key is an already open key, or any one of the predefined HKEY_* constants.

    sub_key is a string that identifies the sub_key to open.

    res is a reserved integer, and must be zero. The default is zero.

    sam is an integer that specifies an access mask that describes the desired security access for the key. Default is KEY_READ. See Access Rights for other allowed values.

    The result is a new handle to the specified key.

    If the function fails, WindowsError is raised.

网上说这个可能跟_winreg模块与系统之间的配合有关系

再优化一下version 1.2

version 1.2.1

# coding:utf-8
"""
使用注册表操作模块取出和设置环境变量
"""
import _winreg

envlist = []
newenv = 'c:\\python271;'

user_env = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,'Environment')
# print user_env

value,type = _winreg.QueryValueEx(user_env,'PATH')
# print value,type

envlist = value.split(';')

for env in envlist:
	if env == newenv:
		print 'exist'
		exit()

tempenv = value + newenv + ';'
# print tempenv

try:
	_winreg.SetValueEx(user_env,'PATH',0,type,tempenv)

except WindowsError:
	print 'excetion'
finally:
	_winreg.CloseKey(user_env)


至此,任务4(设置环境变量的脚本)算是有了影。

@simpleowen
Copy link
Author

通过前面几个版本的迭代,发现任务应该这样分解

分成两个脚本,脚本2中有两个任务

脚本1.要为右键菜单添加一个“添加到当前用户环境变量PATH中”的项目,将该菜单项的执行链接为python的脚本2所在路径

脚本2.获取选中的文本或获取选中文件夹所在路径

脚本2.单击该菜单时要执行另一段python设置环境变量PATH的脚本

原计划是将右键菜单项添加到资源管理器的右键菜单中,获取当前选中文本作为要添加到PATH中的值,比如
地址栏右键菜单

但是经过两天的搜索,依然没有清晰的解决方案,先时间止损,采用剪贴板的方案实现文本的获取

再次调整任务

脚本1.添加一个右键菜单项“将剪贴板内容添加到PATH值中”到所有文件、文件夹的右键菜单中

脚本2.实现将剪贴板内容添加到当前用户环境变量PATH值的末尾

用python操作剪贴板资料比较多,所以将剪贴板操作代码合并到version1.2.1中形成version1.3(脚本2)

version 1.3

# coding:utf-8
"""
将剪贴板内容设置为环境变量PATH的值
"""
import _winreg
import win32clipboard as w
import win32con

def clearClipboard():
	w.OpenClipboard()
	w.EmptyClipboard()
	w.CloseClipboard()


def getTextFromClipboard():
	w.OpenClipboard()
	d = w.GetClipboardData(win32con.CF_TEXT)
	w.CloseClipboard()
	return d
try:
	strtemp = getTextFromClipboard()
except TypeError:
	print 'maybe no data in Clipboard'
	exit()


clearClipboard()


newenv = strtemp + ';' #'c:\\python271;'#用变量代替取到的字符串



user_env = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,'Environment')
# user_env = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,'Environment')
# print user_env

value,type = _winreg.QueryValueEx(user_env,'PATH')
# print value,type

tempenv = value + newenv
# print tempenv

try:
	_winreg.SetValueEx(user_env,'PATH',0,type,tempenv)

except WindowsError:
	print 'excetion'
finally:  
	_winreg.CloseKey(user_env)


@simpleowen
Copy link
Author

simpleowen commented Dec 27, 2016

重新屡一下需要编程实现的点

1.编程查找注册表特定位置添加键值

2.右键菜单执行脚本2生成的exe文件实现添加环境变量PATH的值

由于脚本2已基本完工,所以先将脚本2生成exe文件,以便能在windows上运行。

搜索发现cx-freeze模块可以干这个事,使用pip install cx-Freeze安装包

生成exe需要在脚本2所在目录再新增一个setup.py脚本

访问cx-Freeze的官方文档,上面有setup.py的模板,复制下来

import sys
from cx_Freeze import setup, Executable

# Dependencies are automatically detected, but it might need fine tuning.
build_exe_options = {"packages": ["os"], "excludes": ["tkinter"]}

# GUI applications require a different base on Windows (the default is for a
# console application).
base = None
if sys.platform == "win32":
    base = "Win32GUI"

setup(  name = "guifoo",
        version = "0.1",
        description = "My GUI application!",
        options = {"build_exe": build_exe_options},
        executables = [Executable("guifoo.py", base=base)])

将setup函数的参数修改为自己的信息,必须要修改的地方是executables = [Executable("guifoo.py", base=base)])中的guifoo.py,这里应该改成脚本2的名字,确保cx_Freeze在生成exe时能找到脚本2

脚本2和setup.py都已经准备好。

命令行中输入命令

python setup.py bdist_msi

在脚本所在的目录中会生成两个文件夹

生成两个文件夹

dist目录中有一个生成的安装文件

windows安装文件

双击安装,默认路径安装完成后,在C盘下能找到安装后的可执行exe程序

exe文件

验证是否可以成功执行,复制一条待设置环境变量的路径到剪贴板

复制一条路径

执行生成的exe,查看环境变量PATH值的变化

执行成功

PATH值的末尾新增了刚刚复制的一条路径,说明exe执行成功。

@simpleowen
Copy link
Author

截至目前,整个任务还剩下编程实现添加右键菜单及一些优化未实现

现在就来实现添加右键菜单的功能。

考虑后,决定将右键菜单项添加到所有文件和文件夹的右键菜单中。

经过搜索,文件和文件夹的右键菜单位于注册表 HKEY_CLASSES_ROOT\AllFilesystemObjects\shell下

要想右键菜单执行指定的文件,需在shell下设置好菜单项后在其下新建command键,并将指定的文件路径赋值给command的值

依然使用_winreg模块来操作注册表

CreateKeyEx函数用于打开已存在的或新建一个键

SetValueEx函数用于为键设定一个值

少废话,上代码

# coding:utf-8
import _winreg

cKey = _winreg.CreateKeyEx(_winreg.HKEY_CLASSES_ROOT,"AllFilesystemObjects\shell\setpath\command")

mKey = _winreg.CreateKeyEx(_winreg.HKEY_CLASSES_ROOT,"AllFilesystemObjects\shell\setpath")

mstr = 'add clipboard content to the path'
cstr = "\"C:\\Program Files\\envset\\env.exe\" \"%1\"" # %1表示该文件不带参数

_winreg.SetValueEx(oKey,'',0,_winreg.REG_SZ, mstr) # menu name
_winreg.SetValueEx(cKey,'',0,_winreg.REG_SZ, cstr) #


_winreg.CloseKey(cKey)
_winreg.CloseKey(mKey)

执行代码后,打开注册表

菜单键

菜单链接

注册表更新成功。再看右键菜单效果

右键菜单

右键菜单添加成功。

@simpleowen
Copy link
Author

simpleowen commented Dec 27, 2016

优化

1.PATH中已有的路径不再添加
2.原本打印信息的地方改为弹出windows消息框

version 2.0

# coding:utf-8
"""
将剪贴板中的内容添加到环境变量PATH的值中
"""
import sys
import _winreg
import win32clipboard as w
import win32con
import win32api

def clearClipboard():
	w.OpenClipboard()
	w.EmptyClipboard()
	w.CloseClipboard()

def getTextFromClipboard():
	w.OpenClipboard()
	d = w.GetClipboardData(win32con.CF_TEXT)
	w.CloseClipboard()
	return d

try:
	strtemp = getTextFromClipboard()
except TypeError:
	win32api.MessageBox(0, "Maybe no data in Clipboard", "Tips",win32con.MB_OK)
	sys.exit()

print strtemp
clearClipboard()

newenv = strtemp + ';'

user_env = _winreg.CreateKey(_winreg.HKEY_CURRENT_USER,'Environment')
value,type = _winreg.QueryValueEx(user_env,'PATH')

vlist = value.split(';')
print vlist
for v in vlist:
	if strtemp == v:
		win32api.MessageBox(0, "exist same path", "Tips",win32con.MB_OK)
		sys.exit()

tempenv = value + newenv

try:
	_winreg.SetValueEx(user_env,'PATH',0,type,tempenv)
	win32api.MessageBox(0, "Successfully", "Tips",win32con.MB_OK)
except WindowsError:
	win32api.MessageBox(0, "Set failed", "Tips",win32con.MB_OK)
finally:  
	_winreg.CloseKey(user_env)
# coding:utf-8
"""
添加菜单项到windows所有文件和文件夹的右键菜单中
"""

import _winreg

cKey = _winreg.CreateKeyEx(_winreg.HKEY_CLASSES_ROOT,"AllFilesystemObjects\shell\setpath\command")

mKey = _winreg.CreateKeyEx(_winreg.HKEY_CLASSES_ROOT,"AllFilesystemObjects\shell\setpath")

mstr = 'add clipboard content to the path'
cstr = "\"C:\\Program Files\\envset\\env.exe\" \"%1\"" # %1表示该文件不带参数

_winreg.SetValueEx(mKey,'',0,_winreg.REG_SZ, mstr) # menu name
_winreg.SetValueEx(cKey,'',0,_winreg.REG_SZ, cstr) #

_winreg.CloseKey(cKey)
_winreg.CloseKey(mKey)

最终,实现这个设置环境变量的功能需要安装一个文件,执行一个脚本

@simpleowen
Copy link
Author

总结

原本以为很简单的‘获取选中的文本’的功能却卡了很长时间,到现在心里面还有影子在。

通过一系列的搜索发现这个获取选中文本的功能有更专业的名词来表达,如“屏幕划词”、“鼠标划词”、“鼠标取词”等,在一些翻译软件中,这是很基础的一个功能。

用到的主要模块

_winreg:操作windows注册表
win32clipboard:操作windows剪贴板
win32api:调用windows api接口函数
cx_Freeze:打包python脚本成exe

了解了

windows注册表结构
windows环境变量在注册表中的位置
windows右键菜单在注册表中的位置

小技巧

python弹出提示消息框
python脚本打包成exe

@HiIcy
Copy link

HiIcy commented Jun 13, 2017

请问是怎么实现选中文本的啊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants