如果线程 运行 webbrowser.open() 无法使用 Ctrl-C 退出 Python 脚本
Can't quit Python script with Ctrl-C if a thread ran webbrowser.open()
我正在为 Python (pip install bottle
) 使用 Bottle 网络应用程序框架,并希望 运行 一个只能从本地计算机访问的网络应用程序(它本质上是使用浏览器作为 GUI 的桌面应用程序)。要启动 Bottle Web 应用程序,我必须调用 bottle.run()
,但只要脚本为 运行ning,就会一直阻塞。您可以按 Ctrl-C 停止它。
但是,我还希望此应用程序通过调用 webbrowser.open()
打开一个网络浏览器到本地主机。问题是,我不能先调用 webbrowser.open()
,因为 Web 应用程序不会 运行ning,但是如果我先调用 bottle.run()
,它就不会 return只要 Web 应用程序处于 运行ning 状态,我就无法继续调用 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 似乎不起作用,所以除了完全关闭终端之外我没有办法停止网络应用程序。我不确定这是为什么:'Browser opened'
被打印到屏幕上所以我知道 webbrowser.open()
是 returning.
我在 Windows 7.
我已经尝试了 how to terminate a thread which calls the webbrowser in python 中设置 self._running = False
的解决方案,但这并没有改变任何东西。在我可以从中调用 join()
的线程之外也没有地方。
即使我摆脱了单独的线程并使用 os.system('python openbrowser.py')
到 运行 等待一秒钟并打开网络浏览器的脚本,这仍然会阻止 Ctrl-C 工作。
我也尝试使用 threading.Timer(1, webbrowser.open, ['http://localhost:8042']).start()
启动浏览器,但这仍然会阻止 Ctrl-C 工作。
是否有我没有看到的解决方案?
关于此答案的两个直接警告:
- 可能有一种方法可以实现您想要的效果,它更接近您的原始设计。如果您不想偏离您最初的想法那么多,也许另一个回答者可以提供更好的解决方案。
- 此解决方案尚未在 Windows 上进行测试,因此可能 运行 遇到无法识别 Ctrl-C 信号的相同或类似问题。不幸的是,我手头没有带 Python 解释器的 Windows 机器,无法先试用一下。
除此之外:
您可能会发现将服务器放在一个单独的线程中,然后通过一些简单的信号从主(非阻塞)线程控制它会更轻松。
我在下面创建了一个玩具示例来演示我的意思。您可能更愿意将 class 单独放入一个文件中,然后简单地将其导入并将 class 的一个新实例实例化到您的其他脚本中。
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()
出于实际应用的目的,您可能需要对它进行相当大的修改,但它应该能让您入门。
祝你好运!
我正在为 Python (pip install bottle
) 使用 Bottle 网络应用程序框架,并希望 运行 一个只能从本地计算机访问的网络应用程序(它本质上是使用浏览器作为 GUI 的桌面应用程序)。要启动 Bottle Web 应用程序,我必须调用 bottle.run()
,但只要脚本为 运行ning,就会一直阻塞。您可以按 Ctrl-C 停止它。
但是,我还希望此应用程序通过调用 webbrowser.open()
打开一个网络浏览器到本地主机。问题是,我不能先调用 webbrowser.open()
,因为 Web 应用程序不会 运行ning,但是如果我先调用 bottle.run()
,它就不会 return只要 Web 应用程序处于 运行ning 状态,我就无法继续调用 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 似乎不起作用,所以除了完全关闭终端之外我没有办法停止网络应用程序。我不确定这是为什么:'Browser opened'
被打印到屏幕上所以我知道 webbrowser.open()
是 returning.
我在 Windows 7.
我已经尝试了 how to terminate a thread which calls the webbrowser in python 中设置 self._running = False
的解决方案,但这并没有改变任何东西。在我可以从中调用 join()
的线程之外也没有地方。
即使我摆脱了单独的线程并使用 os.system('python openbrowser.py')
到 运行 等待一秒钟并打开网络浏览器的脚本,这仍然会阻止 Ctrl-C 工作。
我也尝试使用 threading.Timer(1, webbrowser.open, ['http://localhost:8042']).start()
启动浏览器,但这仍然会阻止 Ctrl-C 工作。
是否有我没有看到的解决方案?
关于此答案的两个直接警告:
- 可能有一种方法可以实现您想要的效果,它更接近您的原始设计。如果您不想偏离您最初的想法那么多,也许另一个回答者可以提供更好的解决方案。
- 此解决方案尚未在 Windows 上进行测试,因此可能 运行 遇到无法识别 Ctrl-C 信号的相同或类似问题。不幸的是,我手头没有带 Python 解释器的 Windows 机器,无法先试用一下。
除此之外:
您可能会发现将服务器放在一个单独的线程中,然后通过一些简单的信号从主(非阻塞)线程控制它会更轻松。 我在下面创建了一个玩具示例来演示我的意思。您可能更愿意将 class 单独放入一个文件中,然后简单地将其导入并将 class 的一个新实例实例化到您的其他脚本中。
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()
出于实际应用的目的,您可能需要对它进行相当大的修改,但它应该能让您入门。 祝你好运!