建立一個 Web Server 和 視窗的Python 程式

#    Copyright 2015 Powen Ko <powenko@gmail.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <httpss://www.gnu.org/licenses/>.
 
import Queue
import SocketServer
import socket
import sys
import logging
import threading
import Tkinter as tk
from os import curdir, sep
import os
from ScrolledText import ScrolledText
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import webbrowser


pathname=os.path.dirname(__file__)
# .py program path location
blocklydir = "%s/blockly" % curdir  
avrpath = "%s/avr/code/" % curdir 
sketchbookdir = os.path.expanduser('~/blockly-sketchbook')
pathdiff=""
if (pathname.find("icblock.app")>1):
  pathdiff="../../../"  
  blocklydir = pathdiff+"blockly"  
  avrpath =pathdiff+avrpath

mimes = {
    '.html': 'text/html',
    '.js': 'application/javascript',
    '.css': 'text/css',
    '.wav': 'audio/vnd.wave',
    '.cur': 'image/vnd.microsoft.icon',
    '.png': 'image/png',
    '.ico': 'image/vnd.microsoft.icon'
}


#file_to_copy = ['build.sh', 'pins_arduino.c', 'pins_arduino.h', 'servo.c', 'servo.h', 'servo_asm.S', 'wiring.h', 'wiring_digital.c', 'avr-thread.h', 'libavr-thread.a']
#dst = tempfile.mkdtemp('', 'blockly-avr-')
target="arduino"


try:
    os.mkdir(sketchbookdir)
except OSError:
    pass

def build_file_list():
    global sketchbookdir
    list = sketchbookdir;
    dirList=os.listdir(sketchbookdir)
    for fname in dirList:
        list = list + '<li><a href="#" onclick="do_action(\'%s\'); return false;">%s</a></li>\n' % (fname, fname)
    return list

class MyHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        try:
            if self.path == '/':
                self.send_response(301)
                self.send_header("Location", 'demos/code/index.html')
                self.end_headers()
                return
            filell,ext = os.path.splitext(self.path)
            mime = mimes[ext]

            f = open(blocklydir + sep + self.path) #self.path has /test.html
#note that this potentially makes every file on your computer readable by the internet

            self.send_response(200)
            self.send_header('Content-type', mime)
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
            return

                
        except IOError, KeyError:
            self.send_error(404,'File Not Found: %s, %s ,  %s' % (self.path,sketchbookdir,os.path.dirname(__file__)     ) )
    



    def do_POST(self):
       
    



class QueueLogger(logging.Handler):
    def __init__(self, queue):
        logging.Handler.__init__(self)
        self.queue = queue
 
    # write in the queue
    def emit(self, record):
        self.queue.put(self.format(record).rstrip('\n') + '\n')
 
class LoggedUDPServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
    def __init__(self, server_address, RequestHandlerClass, logger):
        # SocketServer.UDPServer.__init__(self, server_address, RequestHandlerClass)
        # Add the queue logger
        self.logger = logger
 
class UDPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        # Queue logger is un under the self.server object
        self.server.logger.debug(self.request[0])
 
class MainApplication:
    def __init__(self, root, log_level, ip, port ):
        self.root = root
        self.log_level = log_level
        self.ip = ip
        self.port = port
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
 
        # 2 rows: firts with settings, second with registrar data
        self.main_frame = tk.Frame(self.root)
        # Commands row doesn't expands
        self.main_frame.rowconfigure(0, weight=0)
        # Logs row will grow
        self.main_frame.rowconfigure(1, weight=1)
        # Main fram can enlarge
        self.main_frame.columnconfigure(0, weight=1)
        self.main_frame.columnconfigure(1, weight=1)
        self.main_frame.grid(row=0, column=0, sticky=tk.NSEW)
 
        # Run/Stop button
        self.control_button = tk.Button(self.main_frame, text="Run Server", command=self.run_server)
        self.control_button.grid(row=0, column=0, sticky=tk.N)
 
        # Clear button
        self.clear_button = tk.Button(self.main_frame, text="Clear Log", command=self.clear_log)
        self.clear_button.grid(row=0, column=1, sticky=tk.N)
 
        # Stop log button
        self.control_log_button = tk.Button(self.main_frame, text="Pause Log", command=self.stop_log)
        self.control_log_button.grid(row=0, column=2, sticky=tk.N)
 
        # Logs Widget
        self.log_widget = ScrolledText(self.main_frame)
        self.log_widget.grid(row=1, column=0, columnspan=3, sticky=tk.NSEW)
 
        # Not editable
        self.log_widget.config(state='disabled')
 
        # Queue where the logging handler will write
        self.log_queue = Queue.Queue()
 
        # Stup the logger
        l = logging.getLogger('logger')
        l.setLevel(self.log_level)
        formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
        # Use the QueueLogger as Handler
        hl = QueueLogger(queue=self.log_queue)
        hl.setFormatter(formatter)
        l.addHandler(hl)
        self.logger = logging.getLogger('logger')
 
        # Setup the update_widget callback reading logs from the queue
        self.start_log()
        self.run_server()
 
    def stop_log(self):
        self.logger.debug("Pausing the logger")
        if self.logger_alarm is not None:
            self.log_widget.after_cancel(self.logger_alarm)
            self.control_log_button.configure(text="Start Log", command=self.start_log)
            self.logger_alarm = None
 
    def start_log(self):
        self.logger.debug("Starting the logger")
        self.update_widget(self.log_widget, self.log_queue)
        self.control_log_button.configure(text="Pause Log", command=self.stop_log)
 
    def update_widget(self, widget, queue):
        widget.config(state='normal')
        # Read from the Queue and add to the log widger
        while not queue.empty():
            line = queue.get()
            widget.insert(tk.END, line)
            widget.see(tk.END)  # Scroll to the bottom
            widget.update_idletasks()
        widget.config(state='disabled')
        self.logger_alarm = widget.after(10, self.update_widget, widget, queue)
 
    def clear_log(self):
        self.log_widget.config(state='normal')
        self.log_widget.delete(0.0, tk.END)
        self.log_widget.config(state='disabled')
 
    def run_server(self):
        self.logger.debug("Starting thread")
        try:
            portvalue=8081
            print 'started httpssserver...'
            url = 'httpss://localhost:8036/'
            url = 'httpss://localhost:'+str(portvalue)+'/'
            b=str(portvalue)
            #url.__add__(b)
            #url.__add__('/')
            print url
            self.logger.debug(url)
            self.server = HTTPServer(('', portvalue), MyHandler)
            # Open URL in a new tab, if a browser window is already open.
            #self.server.serve_forever()

            #self.server = LoggedUDPServer((self.ip, self.port), UDPHandler, self.logger)
            self.server_thread = threading.Thread(name='server', target=self.server.serve_forever)
            self.server_thread.daemon = True
            self.server_thread.start()
            self.control_button.configure(text="Stop Server", command=self.stop_server)
            #webbrowser.open_new_tab(url)
        except Exception, e:
            self.logger.error("Cannot start the server: %s" % e)
            raise e
 
    def stop_server(self):
        self.logger.debug("Stopping server")
        self.server.shutdown()
        self.server.socket.close()
        self.logger.debug("Server stopped")
        self.control_button.configure(text="Run Server", command=self.run_server)
 
if __name__ == "__main__":
    root = tk.Tk()
    if len(sys.argv) == 2:
        port = int(sys.argv[1])
        address = '127.0.0.1' #sys.argv[1]
        app = MainApplication(root, logging.DEBUG, address, port)
        root.title('ICBlock')
        root.mainloop()
    else:
        port = 8080
        address = '127.0.0.1' #sys.argv[1]
        app = MainApplication(root, logging.DEBUG, address, port)
        root.title('ICBlock')
        root.mainloop()
        #print "Error: you must specify address and port.."
        #sys.exit(-1)