Python和Webkit,监视串行端口线程,如何避免核心转储运行javascript

问题描述 投票:0回答:1

我正在编写一个使用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()
python multithreading serial-port webkit gtk3
1个回答
0
投票

[似乎是由于run_javascript不是MT安全的(例如,与run_javascript不同)。

尽管GIL在给定的时间只允许运行一个python线程,但在上下文切换时我们对其他线程状态一无所知。这就是为什么只应从主循环调用this one的原因。如果要调用此函数,则应在主循环中安排其执行时间。可能最简单的方法是使用run_javascript。请尝试下面的代码段。

idle_add
© www.soinside.com 2019 - 2024. All rights reserved.