title: Python and Webkit, watching serial port thread, how to avoid core dump running javascript
body: <p>I'm writing a game that uses python and webkit, and a webpage is the front-end/GUI.  The PC is connected to an Arduino that controls a coin hopper and other i/o.  When the Arduino sends 'coinin' over serial, I capture this in a serial-watching thread, then run some javascript on the webpage to 'add' a coin to the game.  </p>
  
  <p>For simplicity in troubleshooting, I set up an example that runs a test thread instead of reading serial, but the problem is the same.  The thread tries to add a coin every second by running 'addcoin()' on the webpage.  If you uncomment the run_javascript() line, the program core dumps.  </p>
  
  <p>I came up with a keyboard hack workaround.  The test thread, instead of trying to run_javascript() directly, does an os.system call to xdotool to type the letters 'conn' to the program window.  That window has a key-event listener, and when it gets the letters 'conn' in keybuffer[], it then runs the desired run_javascript() call to the webpage.  If you copy the two files to a folder, and run the python program, you'll see the coin text count up every second (Hit BackSpace to end the program).  If you try to run the javascript from the thread instead, you'll see the program core dump.</p>
  
  <p>The question is, is there a better way to do this, without having to use the keyboard hack to run the javascript?  Although the hack gets around the problem, it introduces a weakness in the game.  You can cheat a coin in by typing 'conn' on the keyboard.  I'd like to find some other way to trigger the event, without having to use the keyboard event.</p>
  
  <p><strong>Sample webpage index.htm</strong></p>
  
  <pre><code>&lt;html&gt;
  &lt;script language="JavaScript"  type="text/javascript"&gt;
  var mycoins=0;
  document.onkeydown = function(evt) {
      evt = evt || window.event;
      cancelKeypress = (evt.ctrlKey &amp;&amp; evt.keyCode == 84);
          return false;
  };
  
  function addcoin()
  {
  mycoins+=1;
  id('mycoins').innerHTML="You Have "+mycoins.toString()+" coins"
  }
  
  function id(myID){return document.getElementById(myID)}
  &lt;/script&gt;
  &lt;html&gt;
  &lt;body&gt;
  &lt;div id=mycoins&gt;You Have 0 Coins&lt;/div&gt;
  &lt;/body&gt;
  &lt;/html&gt; 
  </code></pre>
  
  <p><strong>sample python</strong></p>
  
  <pre><code>#!/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+' &gt;/dev/null 2&gt;&amp;1 ; echo $?').read().strip()=='0':
               serial_port=x
               break;
         baud=9600
         if len(serial_port)&gt;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()&gt;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()
  </code></pre>