【本文章发布于CSDN,未经个人同意不得以任何形式进行转载】
【文章地址:【pyqt】自制的py命令交互终端】
【github项目地址:PyQt_InteractiveTerminal】
书承上文【Python】用eval、exec、compile函数做一个伪交互式编程窗口
这里呢就是将这个交互编程终端高大上化了而已,用起来还是有点费事的说实话。肝细节都是用爱发电的,现在没爱了不想继续弄。
这个也只是个阶段性成果而已(比较成熟但还不是很成熟),和我想要做出的成果还有点差距,最终目标是想做出个类似于Python IDLE的东西(其实是更偏向于“查询工具”的一个东西),可以查看变量(var)的类型type(var)、内容str(var)、数据成员dir(var)。
因为我python撸码基本就开个文件Notepad++编辑然后就跑了(除非干大事或者接触十分陌生的东西时才开IDE),甚至没开VS(因为这玩意儿搞python不好用)、VC(装了VC好几年,用倒没怎么用过),所以有时想查某个变量的数据成员啥的会很难受,但实际上如果这个查询工具真做出来的话估计用起来也不太理想吧毕竟可能有其他更为成熟(?)的工具可以使用,但肝不足而且ddl吹得我脊背发凉所以先不搞了。
【运行效果】
![](https://img-blog.csdnimg.cn/7bbc538ecdd64f7ca0e87d4112e2a538.png)
![](https://img-blog.csdnimg.cn/2687a2e3e747495ab7061ea8b60b30d0.png)
![](https://img-blog.csdnimg.cn/27c63aa0f2774e9f8f1a0f69a90f3e7d.png)
![](https://img-blog.csdnimg.cn/77d4e58db16245fc9d545134ddd82950.png)
![](https://img-blog.csdnimg.cn/b567c0d0e2844bd8bfe2812101038050.png)
![](https://img-blog.csdnimg.cn/d631ea00ae7d4a72b103ea6a59b94047.png)
![](https://img-blog.csdnimg.cn/8a5502b150204dc3a4410b74d102c47a.png)
![](https://img-blog.csdnimg.cn/4ec8dbfa1ca64e9ea2c5a2f7299620c6.png)
【使用说明】
程序很简单,就两个大组件,一个是输出端,一个是输入端(底部)
①、在输入端输入代码回车发送就得了
②、输入端发送过的代码(也就是历史记录)通过按下“Ctrl/Alt”+“↑/↓”进行切换
③、自定义了小数点指令(见图1 2 4 5 6 7)和问号指令(见图3)以及清屏指令(见图8),其实这指令效果在Main.py的TextPreprocess函数中实现
可以在Main.py文件中进行更多的预置函数设置(见下图),在该文件中设置一个函数然后设置好context后重新运行交互终端就可进行调用,神奇吧[doge]
在Main.py文件的中TextPreprocess设置好更多的自定义指令(见下图)(TextPreprocess函数用于完成字串的预处理)
好了,感觉也没啥说的,效果也就这样,要说完善的话这东西肯定还能继续完善的,反正对于我目前来说能用就行,至少比Win+R→cmd→py调出来的py命令行交互终端好用
【代码】
也是多文件的,先说一下每个文件的用途:
XJ_TextEdit.py:继承了QTextEdit,封装了一些特殊功能如“单行模式”、“文本搜索”(详见文件),可单独使用
XJ_Recorder.py:记录器,用来获取print()执行后输出的内容,实际上也就是把 sys.stdout 赋值为其他东西而已,单独使用无意义。导入该文件时必须用这句:import XJ_Recorder,因为要的就是“XJ_Recorder”这个引用,可以说是唯一一个看上去就很异端的模块了
XJ_ListAccessor.py:列表访问器,用来实现“历史输入”功能的,单独使用用处不大
XJ_InteractiveTool.py:终端工具,这是我前两天码出的代码(【Python】用eval、exec、compile函数做一个伪交互式编程窗口)的封装版,可以直接运行,单独使用用处不大,然后顺带一提的是,因为这涉及到公共资源的使用问题,所以在多线程下使用时可能会有意料之外的返回结果,这很正常,在多线程里print东西的话显示的内容是不可控的。虽然想直接限死它为单例类但不想再动大刀了,一是麻烦二是觉得很可能会跑出别的问题
XJ_InteractiveTerminal.py:终端工具(界面版),可以直接使用,效果和正常的py命令交互差不多。想要有更良好的使用体验的话建议运行Main.py(毕竟这只是一个裸类而已,没有额外的设置)
Main.py:在这个文件里干你想干的事吧,这文件是主文件,上面的五个XJ文件一般不需要修改,看一下函数成员啥的(函数名、参数、返回值)就好了
Order.py:这文件是卖萌的,没有一点意义。在程序运行时可以执行“LoadFile(‘Order.py’)”以减少枯燥的命令输入(见下图)
Main.py
#Main.py
import sys
from PyQt5.QtWidgets import QApplication
from XJ_InteractiveTerminal import XJ_InteractiveTerminal
from types import MethodType
import _sitebuiltins
#【关于python内open函数encoding编码问题】https://www.cnblogs.com/wangyi0419/p/11192593.html#:~:text=%E7%94%B3%E6%98%8Eopen%20%28%29%E5%87%BD%E6%95%B0%E7%9A%84%E7%BC%96%E7%A0%81%E6%96%B9%E5%BC%8F%E4%B8%BA%27utf-8%27%EF%BC%8C%E5%8D%B3encoding%3D%22utf-8%22.,%E5%9C%A8%E8%AF%BB%E5%8F%96%E6%96%87%E6%9C%AC%E6%96%87%E4%BB%B6%E7%9A%84%E6%97%B6%E5%80%99%EF%BC%8C%E5%A6%82%E6%9E%9Copen%20%28%29%E5%87%BD%E6%95%B0%E6%B2%A1%E6%9C%89%E5%A3%B0%E6%98%8E%E4%BB%96%E4%BB%AC%E5%A6%82%E4%BD%95%E7%BC%96%E7%A0%81%EF%BC%8Cpython3%E4%BC%9A%E9%80%89%E5%8F%96%E4%BB%A3%E7%A0%81%E6%89%80%E8%BF%90%E8%A1%8C%E7%9A%84%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%9A%84%E9%BB%98%E8%AE%A4%E7%BC%96%E7%A0%81%E4%BD%9C%E4%B8%BAopen%20%28%29%E5%87%BD%E6%95%B0%E7%9A%84%E7%BC%96%E7%A0%81%E6%96%B9%E5%BC%8F%E3%80%82
def ListPrint(lst,col=3,colWidth=40):#将列表内容打印
'''
打印列表内容(其实只要是可迭代可字符串化的都能传入该函数,不仅限列表
用小数点可以快速调用该函数。小数点调用法:.[变量]
执行
.[1,2,3]
. [1,2,3]
与执行
ListPrint([1,2,3])
等价
'''
print('_'*(colWidth*col+col+1))
cnt=0
for i in lst:
print('|{:^{}}'.format(i,colWidth),end='')
cnt=cnt+1
if(cnt==col):
cnt=0
print('|')
print()
def QuicklyInquiry(obj,keyword='',formatPrint=False):#XJ的快速查询小助手,如果格式化输出为真那么将执行print语句,否则将返回列表
'''
XJ的快速查询小助手,本质上是返回dir(obj),关键字keyword用于筛选列表中的元素。
如果formatPrint为真那么将打印结果,为假则返回列表。
用小数点可以快速调用该函数。小数点调用法:..[变量] (逗号/空格) [关键字]
执行
..list c
..list,c
.. list , c
与执行
QuicklyInquiry(list,'c',True)
等价
'''
lst=list(filter(lambda s:s.lower().find(keyword.lower())!=-1,dir(obj)))
if(formatPrint):
ListPrint(lst)
else:
return lst
def LoadFile(IT:XJ_InteractiveTerminal):#从文件中逐行执行命令。因为有了恶心的“IT”变量的缘故,所以这个函数看起来不是那么美观(使用了函数闭包)
def inner(file):
'''
读取文件,从文件中逐行执行命令
'''
with open(file,'r',encoding='utf-8') as f:#默认编码为gbk,要手动设为utf-8。详见下面链接:
for line in f:
line=line.strip('\n')#去除尾部换行符
if(len(line)==0):
line=' '#塞一个空格,至少让它不为空
IT.SendText(line)
IT.SendText('')
return inner
def TextPreprocess(self,text):#文本预处理,与“XJ_InteractiveTerminal.TextPreprocess”绑定,用于执行额外的功能(例如清空输出端文本、设置快速的函数调用、过滤掉有害命令
if(text=='help()'):#有害指令
return 'help'
if(text.find('...')==0):#【清空输出端内容】
self.ClearScreen()
return ''
elif(text.find('..')==0):#【调用QuicklyInquiry】
text=text[2:].strip()#吃掉首尾空白符
if(len(text)==0):
return 'help(QuicklyInquiry)'
keyword=''
sep=text.find(',')
if(sep==-1):
sep=text.find(' ')
if(sep!=-1):
keyword=''.join(list(filter(lambda ch:ch if ch!=' ' and ch!=',' else '',text[sep+1:])))#清除关键字中无用的逗号和空格
text=text[:sep]
return "QuicklyInquiry({},'{}',True)".format(text,keyword)
elif(text.find('.')==0):#【调用ListPrint】
text=text[1:].strip()#吃掉首尾空白符
if(len(text)==0):
return 'help(ListPrint)'
return