我正在编写一个使用python和webkit的游戏,并且网页是前端/ GUI。 PC连接到一个控制硬币斗和其他I / O的Arduino。当Arduino通过串行发送“ coinin”时,我在一个串行监视线程中捕获了它,然后在网页上运行一些javascript以“向游戏添加硬币”。
为了简化故障排除,我设置了一个运行测试线程而不是读取串行的示例,但是问题是相同的。该线程尝试通过在网页上运行“ addcoin()”来每秒添加一枚硬币。如果取消注释run_javascript()行,则程序核心将转储。
我想出了一个键盘破解的解决方法。测试线程没有尝试直接运行run_javascript(),而是对xdotool进行了os.system调用,以在程序窗口中键入字母“ conn”。该窗口具有一个键事件侦听器,当它在keybuffer []中获得字母“ conn”时,它将对网页运行所需的run_javascript()调用。如果将两个文件复制到一个文件夹中,然后运行python程序,则会看到硬币文本每秒计数一次(单击BackSpace结束该程序)。如果您尝试从线程中运行javascript,则会看到程序核心转储。
问题是,有没有一种更好的方法可以执行此操作,而不必使用键盘hack来运行javascript?尽管黑客解决了问题,但它在游戏中带来了缺陷。您可以通过在键盘上键入“ conn”来欺骗硬币。我想找到其他触发事件的方式,而不必使用键盘事件。
示例网页index.htm
<html>
<script language="JavaScript" type="text/javascript">
var mycoins=0;
document.onkeydown = function(evt) {
evt = evt || window.event;
cancelKeypress = (evt.ctrlKey && evt.keyCode == 84);
return false;
};
function addcoin()
{
mycoins+=1;
id('mycoins').innerHTML="You Have "+mycoins.toString()+" coins"
}
function id(myID){return document.getElementById(myID)}
</script>
<html>
<body>
<div id=mycoins>You Have 0 Coins</div>
</body>
</html>
sample python
#!/usr/bin/python
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
gi.require_version('WebKit2', '4.0')
from gi.repository import WebKit2
import os,time,sys,threading,serial
defaultpath = os.path.dirname(os.path.realpath(__file__))
killthread=False
keybuffer=[]
buffkeys=['c','o','n','h','p','e']
myname=os.path.basename(__file__)
serial_ports=['/dev/ttyUSB0','/dev/ttyUSB1','/dev/ttyACM0','/dev/ttyACM1']
checkserial=True;
class BrowserView:
def __init__(self):
global checkserial
window = Gtk.Window()
window.connect("key-press-event", self._key_pressed, window)
self.view = WebKit2.WebView()
self.view.load_uri('file:///'+defaultpath+'/index.htm')
self.view.connect("notify::title", self.window_title_change)
window.add(self.view)
window.fullscreen()
window.show_all()
'''
######not used for this example#######################################
serial_port=""
for x in serial_ports:
#print 'trying ',x
if os.popen('ls '+x+' >/dev/null 2>&1 ; echo $?').read().strip()=='0':
serial_port=x
break;
baud=9600
if len(serial_port)>1:
self.ser = serial.Serial(serial_port, baud, timeout=0)
else:
self.view.load_uri('file:///'+defaultpath+'/signDOWN.htm?Serial%20Port%20Error|Keno%20will%20auto%20close')
checkserial=False;
if checkserial:
thread = threading.Thread(target=self.read_from_port)
thread.start()
#######################################################################
'''
#####thread test#############
thread = threading.Thread(target=self.testthread)
thread.start()
def testthread(self):
while True:
os.system('xdotool search --name '+myname+' type conn')
#self.view.run_javascript('addcoin()') #causes core dump
if killthread==True:
break;
time.sleep(1)
def read_from_port(self):
while True:
if self.ser.inWaiting()>0:
response=self.ser.readline()
print(response)
if 'coinin' in response:
os.system('xdotool search --name '+myname+' type conn')
#self.view.run_javascript('addcoin()') #causes core dump
if killthread==True:
break;
time.sleep(1)
def checkbuffer(self):
global keybuffer
if 'conn' in ''.join(str(x) for x in keybuffer):
self.view.run_javascript('addcoin()')
keybuffer=[]
def window_title_change(self, widget, param):
if not self.view.get_title():
return
os.chdir(defaultpath)
if self.view.get_title().startswith("pythondiag:::"):
message = self.view.get_title().split(":::",1)[1]
os.system('zenity --notification --text='+message+' --timeout=2')
def _key_pressed(self, widget, event, window):
global keybuffer
mykey=Gdk.keyval_name(event.keyval)
isakey=False
for x in buffkeys:
if mykey==x:
isakey=True;
if isakey:
keybuffer.append(Gdk.keyval_name(event.keyval))
else:
keybuffer=[]
self.checkbuffer()
if mykey == 'BackSpace':
self.myquit()
def myquit(self):
global killthread
killthread=True
try:
self.ser.write('clear\n')
except:
pass
Gtk.main_quit()
if __name__ == "__main__":
BrowserView()
Gtk.main()
[似乎是由于run_javascript
不是MT安全的(例如,与run_javascript
不同)。
尽管GIL在给定的时间只允许运行一个python线程,但在上下文切换时我们对其他线程状态一无所知。这就是为什么只应从主循环调用this one的原因。如果要调用此函数,则应在主循环中安排其执行时间。可能最简单的方法是使用run_javascript
。请尝试下面的代码段。
idle_add