簡體   English   中英

如果線程運行了webbrowser.open(),則無法使用Ctrl-C退出Python腳本

[英]Can't quit Python script with Ctrl-C if a thread ran webbrowser.open()

我正在使用Python的Bottle Web應用程序框架( pip install bottle ),並希望運行一個可以從本地計算機訪問的Web應用程序(本質上是一個使用GUI瀏覽器的桌面應用程序)。 要啟動bottle Web應用程序,我必須調用bottle.run()但是只要腳本正在運行,它就會一直阻塞。 您可以通過按Ctrl-C停止它。

但是,我也希望該應用通過調用webbrowser.open()將網絡瀏覽器打開到localhost。 問題是,我不能先調用webbrowser.open()因為該Web應用程序無法運行,但是如果我先調用bottle.run() ,則只要該Web應用程序正在運行,它就不會返回無法繼續調用webbrowser.open()

我的解決方案是將對webbrowser.open()的調用放在線程內:

import bottle
import threading
import webbrowser
import time

class BrowserOpener(threading.Thread):
  def run(self):
    time.sleep(1) # waiting 1 sec is a hack, but it works
    webbrowser.open('http://localhost:8042')
    print('Browser opened')

@bottle.route('/')
def index():
  return 'hello world!'

BrowserOpener().start()
bottle.run(host='localhost', port=8042)

現在出現的問題是,在終端中按Ctrl-C似乎不起作用,因此除了完全關閉終端外,我無法停止Web應用程序。 我不確定為什么會這樣: 'Browser opened'被打印到屏幕上,所以我知道webbrowser.open()正在返回。

我在Windows 7上。

我已經嘗試self._running = False 如何終止在設置self._running = False python調用webbrowser的線程的解決方案,但這並沒有改變任何東西。 我可以從中調用join()的線程之外也沒有地方。

即使我擺脫了單獨的線程並使用os.system('python openbrowser.py')運行了等待一秒鍾並打開os.system('python openbrowser.py')瀏覽器的腳本,這仍然會阻止Ctrl-C正常工作。

我也嘗試使用threading.Timer(1, webbrowser.open, ['http://localhost:8042']).start()啟動瀏覽threading.Timer(1, webbrowser.open, ['http://localhost:8042']).start()但這仍然會阻止Ctrl-C正常工作。

有沒有我沒有的解決方案?

關於此答案的兩個立即警告:

  1. 可能有一種方法可以更接近您的原始設計來完成您想要的工作。 如果您不希望偏離您的最初想法那么多,也許另一個回答者可以提供更好的解決方案。
  2. 此解決方案尚未在Windows上進行測試,因此可能無法識別Ctrl-C信號而遇到相同或類似的問題。 不幸的是,我沒有手頭有Python解釋器的Windows機器。

順便說一句:

您可能會發現將服務器放置在單獨的線程中,然后通過一些簡單的信號從主線程(非阻塞線程)對其進行控制,從而可以輕松地進行工作。 我在下面創建了一個玩具示例來演示我的意思。 您可能更喜歡將類本身放在文件中,然后簡單地將其導入並將該類的新實例實例化到其他腳本中。

import ctypes
import threading
import webbrowser
import bottle


class SimpleExampleApp():
    def __init__(self):
        self.app = bottle.Bottle()

        #define all of the routes for your app inside the init method
        @self.app.get('/')
        def index():
            return 'It works!'

        @self.app.get('/other_route')
        def alternative():
            return 'This Works Too'

    def run(self):
        self.start_server_thread()
        #depending upon how much configuration you are doing
        #when you start the server you may need to add a brief
        #delay before opening the browser to make sure that it
        #is ready to receive the initial request
        webbrowser.open('http://localhost:8042')

    def start_server_thread(self):
        self.server_thread = threading.Thread(
            target = self.app.run, 
            kwargs = {'host': 'localhost', 'port': 8042}
        )
        self.server_thread.start()

    def stop_server_thread(self):
        stop = ctypes.pythonapi.PyThreadState_SetAsyncExc(
            ctypes.c_long(self.server_thread.get_ident()), 
            ctypes.py_object(KeyboardInterrupt)
        )
        #adding a print statement for debugging purposes since I
        #do not know how well this will work on Windows platform
        print('Return value of stop was {0}'.format(stop))
        self.server_thread.join()


my_app = SimpleExampleApp()
my_app.run()

#when you're ready to stop the app, simply call
#my_app.stop_server_thread()

您可能需要為實際應用程序進行相當大的修改,但是希望它可以幫助您入門。 祝好運!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM