首页 > temp > python入门教程 >
-
教你写页游自动化Python脚本,取色,大漠识别和后台点击
import tkinter as tk#[size=3]首先导入tkinter,需要事先用pip安装进python里(方法自行百度)[/size]
def init_window():
global cs,wd
wd = tk.Tk()
cs = tk.Canvas(wd,
width = 800,
height = 500,
bg = 'white')
wd.minsize(800, 500) # 最小尺寸
wd.maxsize(800, 500)#最大尺寸,使最大化失效
wd.title('DDTHelper')
pic = tk.PhotoImage(file="pic.png")#设置背景图片,最好是800*500和png格式的
cs.create_image(400,250,image = pic)
cs.pack()
bt = tk.Button(wd,
text='初始化',
bg=('white'),
font=('微软雅黑',20),
width=155,
height=48,
command=BT_onCreat)
bt.pack()
cs.create_window(530,70,
width=155,
height=48,
window=bt)
wd.mainloop()
def BT_onCreat():
print("初始化。。。")
#入口,这行代码需要一直都待在脚本的最底下
#设置字典
hwnd_title = dict()
init_window()
(不过在图片上叠加控件其实有更好的方案,使控件的背景为透明的,但是那篇文章的代码运行不来)
运行效果
现在我们为点击 初始化 按钮添加一些事项
让他在被点击的时候识别当前的游戏窗口
(因为我用的是36jb大厅登录的游戏,抓取句柄的时候可以根据他的title来区别游戏窗口)
这里我偷了个懒,利用该登录器游戏窗口的title来获取
更改上面的导入库和 BT_onCreat()方法
import win32com.client as wc,win32gui as wg,threading as xc,time,tkinter as tk,win32api as wa,win32con as wn#需要事先用pip安装pywin32插件进python里(方法自行百度)
def init_window():
global cs,wd
wd = tk.Tk()
cs = tk.Canvas(wd,
width = 800,
height = 500,
bg = 'white')
wd.minsize(800, 500) # 最小尺寸
wd.maxsize(800, 500)#最大尺寸,使最大化失效
wd.title('DDTHelper')
pic = tk.PhotoImage(file="pic.png")#设置背景图片,最好是800*500和png格式的
cs.create_image(400,250,image = pic)
cs.pack()
bt = tk.Button(wd,
text='初始化',
bg=('white'),
font=('微软雅黑',20),
width=155,
height=48,
command=BT_onCreat)
bt.pack()
cs.create_window(530,70,
width=155,
height=48,
window=bt)
wd.mainloop()
def BT_onCreat():
global is_run,Znum,t1,t2,t3
Znum = 0#当前已经登陆的游戏账号数量
wg.EnumWindows(get_all_hwnd, 0)
for h,t in hwnd_title.items():
if "4399" in t:#根据title里包含的 4399 来提取游戏窗口
hwnd = t.split("|")[3]
name = t.split("|")[2]
print("账号:" + name + "句柄:" + hwnd)
Znum = Znum + 1
hwnd = int(hwnd)#将句柄转化为int,因为句柄是从标题获取的string,导致了类型错误,我就是被这个坑了好久。。
if Znum==1:#为每一个游戏界面创建一个单独的操作线程,为了方便用global传递,没有用exec。
t1 = xc.Thread(target=Con,args=(hwnd,name,Znum))
elif Znum==2:
t2 = xc.Thread(target=Con,args=(hwnd,name,Znum))
elif Znum==3:
t3 = xc.Thread(target=Con,args=(hwnd,name,Znum))
init_control(Znum,name)
#下面再添加几个方法进去
#获取句柄用的
def get_all_hwnd(hwnd,mouse):
if wg.IsWindow(hwnd) and wg.IsWindowEnabled(hwnd) and wg.IsWindowVisible(hwnd):
hwnd_title.update({hwnd:wg.GetWindowText(hwnd)})
#为每一个线程创建一个对应的控件来控制线程的运行
def init_control(Znum,name):
global cs,wd,v1,v2,v3,tx1,t2,tx2,t3,tx3,txn1,txn2,txn3
if Znum==1:
v1=tk.IntVar()
tx1=tk.StringVar()
txn1=tk.StringVar()
elif Znum==2:
v2=tk.IntVar()
tx2=tk.StringVar()
txn2=tk.StringVar()
elif Znum==3:
v3=tk.IntVar()
tx3=tk.StringVar()
txn3=tk.StringVar()
exec('tx{}.set("未运行")'.format(Znum))
exec('lb{} = tk.Label(wd,text="{}",bg=("#ffffff"),font=("微软雅黑",20))'.format(Znum,name))
exec('lbn{} = tk.Label(wd,textvariable=txn{},bg=("#ffffff"),font=("微软雅黑",10))'.format(Znum,Znum))
exec('cb{} = tk.Checkbutton(wd,textvariable=tx{},bg=("#ffffff"),font=("微软雅黑",10),variable = v{}, height=5,width = 0,command=BT_onRun{})'.format(Znum,Znum,Znum,Znum))
exec('cb{}.pack()'.format(Znum))
exec('lb{}.pack()'.format(Znum))
exec('lbn{}.pack()'.format(Znum))
Ytmp=Znum*100
Ytmp=Ytmp+70
exec('cs.create_window(630,{},width=0,height=0,window=lb{})'.format(Ytmp,Znum))
Ytmp=Ytmp+40
exec('cs.create_window(630,{},width=35,height=25,window=lbn{})'.format(Ytmp,Znum))
exec('cs.create_window(710,{},width=70,height=25,window=cb{})'.format(Ytmp,Znum))
#线程方法
def Con(hwnd,name,xc):
print("启动成功")
#多选框点击事件
def BT_onRun1():
global v1,tx1,t1,ct1
if v1.get()==1:#判断是否被选中
ct1=0
tx1.set('正运行')
t1.start()
else:
ct1=1#用来控制线程终止
tx1.set('未运行')
def BT_onRun2():
global v2,tx2,ct2
if v2.get()==1:#判断是否被选中
ct2=0
tx2.set('正运行')
t2.start()
else:
ct2 = 1
tx2.set('未运行')
def BT_onRun3():
global v3,tx3,ct3
if v3.get()==1:#判断是否被选中
ct3=0
tx3.set('正运行')
t3.start()
else:
ct3=1
tx3.set('未运行')
#入口,这行代码需要一直都待在脚本的最底下
#设置字典
hwnd_title = dict()
init_window()
运行后,点击初始化的效果
可以看到,当只有一个游戏窗口的时候,脚本就自动识别出了该游戏窗口。(目前最多识别3个,且不能二次点击初始化,否则会报错。听说用exce动态封装线程时可以用dict来接收,而目前二次识别也有了大致方案)
并在勾选 未运行 旁边的 框框 时,运行对应的线程。
接下来就要到脚本的线程模块了,而有过py基础的人都知道,py的线程是没有stopThread的
但我们将要实现如何控制脚本执行游戏操作的线程,让它收放自如
下面教程开始
因为接下来的脚本是精简过的,和上次帖子略有不同,以这次帖子为准
我们先像上个帖子一样搭建好一个界面的代码,以此作为平台
import win32com.client as wc,win32gui as wg,threading as xc,time,tkinter as tk,win32api as wa,win32con as wn,multiprocessing as jc
def init_window():
global cs,wd
wd = tk.Tk()
cs = tk.Canvas(wd,
width = 800,
height = 500,
bg = 'white')
wd.minsize(800, 500) # 最小尺寸
wd.maxsize(800, 500)
wd.title('DDTHelper')
pic = tk.PhotoImage(file="pic.png")
cs.create_image(400,250,image = pic)
cs.pack()
bt = tk.Button(wd,
text='初始化',
bg=('white'),
font=('微软雅黑',20),
width=155,
height=48,
command=BT_onCreat)
bt.pack()
cs.create_window(530,70,
width=155,
height=48,
window=bt)
wd.mainloop()
def init_control(Znum,name):
global v1,v2,v3,tx1,t2,tx2,t3,tx3,txn1,txn2,txn3
if Znum==1:
v1=tk.IntVar()
tx1=tk.StringVar()
#txn1=tk.StringVar()
elif Znum==2:
v2=tk.IntVar()
tx2=tk.StringVar()
#txn2=tk.StringVar()
elif Znum==3:
v3=tk.IntVar()
tx3=tk.StringVar()
#txn3=tk.StringVar()
exec('tx{}.set("未运行")'.format(Znum))
exec('lb{} = tk.Label(wd,text="{}",bg=("#ffffff"),font=("微软雅黑",20))'.format(Znum,name))
#exec('lbn{} = tk.Label(wd,textvariable=txn{},bg=("#ffffff"),font=("微软雅黑",10))'.format(Znum,Znum))
exec('cb{} = tk.Checkbutton(wd,textvariable=tx{},bg=("#ffffff"),font=("微软雅黑",10),variable = v{}, height=5,width = 0,command=BT_onRun{})'.format(Znum,Znum,Znum,Znum))
exec('cb{}.pack()'.format(Znum))
exec('lb{}.pack()'.format(Znum))
#exec('lbn{}.pack()'.format(Znum))
Ytmp=Znum*100
Ytmp=Ytmp+70
exec('cs.create_window(630,{},width=0,height=0,window=lb{})'.format(Ytmp,Znum))
Ytmp=Ytmp+40
#exec('cs.create_window(630,{},width=35,height=25,window=lbn{})'.format(Ytmp,Znum))
exec('cs.create_window(710,{},width=70,height=25,window=cb{})'.format(Ytmp,Znum))
def BT_onCreat():
global Znum,D1,D2,D3,conT
Znum = 0
wg.EnumWindows(get_all_hwnd, 0)
conT=jc.Manager().Array("i",[3,0,0,0])#用来控制进程
#lock = jc.Lock()#用来给进程运行顺序排序,防止显示错乱,打包成exe时可以去除(如果出现错误 windos 什么的就改成lock = jc.Manager.Lock() 这样就可以了,或者删掉Manager)
#lock不稳定,弃用
for h,t in hwnd_title.items():
if "4399" in t:
hwnd = t.split("|")[3]
name = t.split("|")[2]
print("账号:" + name + "句柄:" + hwnd)
Znum = Znum + 1
hwnd = int(hwnd)
init_control(Znum,name)
if Znum==1:
D1 = jc.Manager().Array("i",[1,hwnd])
elif Znum==2:
D2 = jc.Manager().Array("i",[2,hwnd])
elif Znum==3:
D3 = jc.Manager().Array("i",[3,hwnd])
def get_all_hwnd(hwnd,mouse):
if wg.IsWindow(hwnd) and wg.IsWindowEnabled(hwnd) and wg.IsWindowVisible(hwnd):
hwnd_title.update({hwnd:wg.GetWindowText(hwnd)})
def Con(data,conT):
#l.acquire()#锁
#try:
print("运行成功")
#finally:
#l.release()
def onRunMan(Znum):
if onRunMan2(Znum) == 1:
conT[Znum]=0
exec('p{} = jc.Process(target=Con,args=(D{},conT))'.format(Znum,Znum))
exec('p{}.daemon=True'.format(Znum))
exec('tx{}.set("运行中")'.format(Znum))
exec('p{}.start()'.format(Znum))
else:
conT[Znum]=1
exec('tx{}.set("未运行")'.format(Znum))
def onRunMan2(Znum):
if Znum ==1:
return v1.get()
elif Znum == 2:
return v2.get()
elif Znum ==3:
return v3.get()
def BT_onRun1():
onRunMan(1)
def BT_onRun2():
onRunMan(2)
def BT_onRun3():
onRunMan(3)
if __name__ == '__main__':
hwnd_title = dict()
init_window()
成功识别后,我们勾上运行的钩子
成功的话会在终端显示 成功运行
这次我在onCreat方法里封装需要发送给进程的数据
然后在onRunMain中动态拼装进程并启动它
再让产生的子进程来生成守护线程,让守护线程去操控游戏
然后子进程循环检测我们是不是发出了停止命令,如果线程检测到我们发出了停止的命令
自身的代码就执行完了,然后带动他产生的守护线程也被kill掉了。
这样就可以实现多线程的随时停止了
代码还巧妙借用了exec指令的“特性”:输出变量只能在该方法内可见,一旦该方法被重启,变量就没了
也就是说,如果我们直接用 p1 = jc.Process(target=Con,args=(D1,conT))来产生进程
那么在进程结束后,需要用 del p1来清除掉进程的“尸体”,然后再重新创建它
设置的Con方法代码,让它会自己生产守护线程
def Con(hwnd,Znum,conT,l):
#设置守护线程
time.sleep(1)
exec('t{} = xc.Thread(target=RunMain,args=(hwnd,Znum))'.format(Znum))#依靠Znum(游戏账号分配到的id)来动态生成不同的线程
exec('t{}.setDaemon(True)'.format(Znum))
exec('t{}.start()'.format(Znum))
while True:#开始接收我们是否发出了停止的命令
if conT[Znum] == 0:
time.sleep(1)
else:
break
print('进程' + str(Znum) +':已退出')
再补充它生产出的子线程所执行的方法(不可用)
def RunMain(hwnd,Znum):
RM=0#运行次数,因为用多进程后无法向用户节目输出,所以已弃用
hdc=wg.GetWindowDC(int(hwnd))#获取目标页游(flash)的hdc,用来获取指定坐标的颜色
while True:
while str(wg.GetPixel(hdc,919,280))!=str(10248996):#检测游戏角色是否处在房间界面(初始需要用户手动将游戏角色进入房间界面),用于检测游戏角色是否退出了副本回到了游戏房间
print("房间")
doClick(hwnd,5,5)
time.sleep(1)
if Chose_FB(hwnd,hdc) == 1:#查看当前两个副本中又那个副本开放,其实这个设计并不合理,如果当前没副本开放就出bug了,不过我只会在有副本开放才会运行这个脚本对吧-,-
FB_MS(hwnd,hdc)#启动1号副本方案
else:
FB_JD(hwnd,hdc)#二号副本方案
RM = RM + 1
当然,,现在由于主题和篇幅原因,我就不补充副本的流程方法了, 但这样可能会导致运行时报错
我们可以将它删减成
def RunMain(hwnd,Znum):
white True:
print("我在运行")
time.sleep(1)
这样在勾选运行的时候,
终端就会不停地显示 我在运行
直到我们把运行的钩子取消后,就不会再显示了(线程被kill掉了)
熟悉按键精灵的大佬们都应该用过一个叫大漠的插件
但先讲不依赖大漠的情况下,用微软官方的指令来实现脚本的操作
import win32com.client as wc,win32gui as wg,threading as xc,time,tkinter as tk,win32api as wa,win32con as wn,multiprocessing as jc
def init_window():
global cs,wd
wd = tk.Tk()
cs = tk.Canvas(wd,
width = 800,
height = 500,
bg = 'white')
wd.minsize(800, 500) # 最小尺寸
wd.maxsize(800, 500)
wd.title('DDTHelper')
pic = tk.PhotoImage(file="pic.png")
cs.create_image(400,250,image = pic)
cs.pack()
bt = tk.Button(wd,
text='初始化',
bg=('white'),
font=('微软雅黑',20),
width=155,
height=48,
command=BT_onCreat)
bt.pack()
cs.create_window(530,70,
width=155,
height=48,
window=bt)
wd.mainloop()
def init_control(Znum,name):
global v1,v2,v3,tx1,t2,tx2,t3,tx3,txn1,txn2,txn3
if Znum==1:
v1=tk.IntVar()
tx1=tk.StringVar()
#txn1=tk.StringVar()
elif Znum==2:
v2=tk.IntVar()
tx2=tk.StringVar()
#txn2=tk.StringVar()
elif Znum==3:
v3=tk.IntVar()
tx3=tk.StringVar()
#txn3=tk.StringVar()
exec('tx{}.set("未运行")'.format(Znum))
exec('lb{} = tk.Label(wd,text="{}",bg=("#ffffff"),font=("微软雅黑",20))'.format(Znum,name))
#exec('lbn{} = tk.Label(wd,textvariable=txn{},bg=("#ffffff"),font=("微软雅黑",10))'.format(Znum,Znum))
exec('cb{} = tk.Checkbutton(wd,textvariable=tx{},bg=("#ffffff"),font=("微软雅黑",10),variable = v{}, height=5,width = 0,command=BT_onRun{})'.format(Znum,Znum,Znum,Znum))
exec('cb{}.pack()'.format(Znum))
exec('lb{}.pack()'.format(Znum))
#exec('lbn{}.pack()'.format(Znum))
Ytmp=Znum*100
Ytmp=Ytmp+70
exec('cs.create_window(630,{},width=0,height=0,window=lb{})'.format(Ytmp,Znum))
Ytmp=Ytmp+40
#exec('cs.create_window(630,{},width=35,height=25,window=lbn{})'.format(Ytmp,Znum))
exec('cs.create_window(710,{},width=70,height=25,window=cb{})'.format(Ytmp,Znum))
def BT_onCreat():
global Znum,D1,D2,D3,conT
Znum = 0
wg.EnumWindows(get_all_hwnd, 0)
conT = jc.Manager().Array("i",[3,0,0,0])
for h,t in hwnd_title.items():
if "4399" in t:
hwnd = t.split("|")[3]
name = t.split("|")[2]
print("账号:" + name + "句柄:" + hwnd)
Znum = Znum + 1
hwnd = int(hwnd)
init_control(Znum,name)
if Znum == 1:
D1 = jc.Manager().Array("i",[1,hwnd])
elif Znum == 2:
D2 = jc.Manager().Array("i",[2,hwnd])
elif Znum == 3:
D3 = jc.Manager().Array("i",[3,hwnd])
def get_all_hwnd(hwnd,mouse):
if wg.IsWindow(hwnd) and wg.IsWindowEnabled(hwnd) and wg.IsWindowVisible(hwnd):
hwnd_title.update({hwnd:wg.GetWindowText(hwnd)})
def all_run(Znum):
while Znum >0:
exec('t{}.start()'.format(Znum))
Znum = Znum - 1
#操作类--------------------------------------------------------------------------------------------------------------
def climb(hwnd,jl,fx):
if fx==1:#右边
#适应方向及防止无效
wa.SendMessage(hwnd,wn.WM_KEYDOWN,68,None)
wa.SendMessage(hwnd,wn.WM_KEYUP,68,None)
#1.3=1屏距
wa.SendMessage(hwnd,wn.WM_KEYDOWN,68,None)
time.sleep(jl*1.3)
wa.SendMessage(hwnd,wn.WM_KEYUP,68,None)
else:
#适应方向及防止无效
wa.SendMessage(hwnd,wn.WM_KEYDOWN,65,None)
wa.SendMessage(hwnd,wn.WM_KEYUP,65,None)
#1.3=1屏距
wa.SendMessage(hwnd,wn.WM_KEYDOWN,65,None)
time.sleep(jl*1.3)
wa.SendMessage(hwnd,wn.WM_KEYUP,65,None)
def doAngle(hwnd,jd):
for i in range(jd):
time.sleep(0.05)
wa.SendMessage(hwnd,wn.WM_KEYDOWN,87,None)
wa.SendMessage(hwnd,wn.WM_KEYUP,87,None)
def doClick(hwnd,cx,cy):
long_position = wa.MAKELONG(cx, cy)
wa.SendMessage(hwnd, wn.WM_LBUTTONDOWN, wn.MK_LBUTTON, long_position)
wa.SendMessage(hwnd, wn.WM_LBUTTONUP, wn.MK_LBUTTON, long_position)
def doFire(hwnd,ld):
wa.SendMessage(hwnd,wn.WM_KEYFIRST,66,None)#先摁大
wa.SendMessage(hwnd,wn.WM_KEYFIRST,69,None)#先摁技能
wa.SendMessage(hwnd,wn.WM_KEYFIRST,97,None)
wa.SendMessage(hwnd,wn.WM_KEYFIRST,98,None)
wa.SendMessage(hwnd,wn.WM_KEYFIRST,97,None)#11大招
wa.SendMessage(hwnd,wn.WM_KEYFIRST,100,None)
wa.SendMessage(hwnd,wn.WM_KEYDOWN,32,None)
time.sleep(ld * 0.04)
wa.SendMessage(hwnd,wn.WM_KEYUP,32,None)
#游戏流程处理类---------------------------------------------------------------------------------------------------------
def Chose_FB(hwnd,hdc):
doClick(hwnd,600,200)#打开菜单
time.sleep(1)
doClick(hwnd,626,188)#单人副本
time.sleep(1)
while True:
doClick(hwnd,5,5)
if str(wg.GetPixel(hdc,244,237))==str(2041582):
doClick(hwnd,289,243)#魔石
FBn=1
break
elif str(wg.GetPixel(hdc,337,278))==str(13298869):
doClick(hwnd,292,299)#技能丹
FBn=2
break
time.sleep(1)
doClick(hwnd,726,501)#难度
time.sleep(1)
doClick(hwnd,504,563)#确定
time.sleep(1)
doClick(hwnd,951,491)
return(FBn)
def FB_MS(hwnd,hdc):
time.sleep(24)
while str(wg.GetPixel(hdc,497,169))!=str(5418993):#回合检测
doClick(hwnd,5,5)
time.sleep(0.5)
while True:
doClick(hwnd,5,5)
colx=wg.GetPixel(hdc,917,