当从菜单事件发出信号时停止线程时出现问题?
该样本来自我正在从事的一个项目。我的客户使用的软件,如果在其视图中检测到运动,则使用他的网络摄像头拍摄快照。问题是该软件无法在拍摄图像时将其通过电子邮件发送到他的帐户。我编写了这个项目来监视保存快照的文件夹,并将其设置为将电子邮件中的任何新快照发送到定义的帐户,这样他就会在发生事件时收到有关他的单元格的警报以及快照运动中的物体。是的,我知道有许多应用程序的网络摄像头软件中都包含此功能,但我的客户雇用了我,这样他就可以避免更换当前的软件,因为他使用起来很舒服。
Start() 函数有两个步骤。步骤 1 是监视器启动前的延迟,步骤 2 是监视器本身的启动。到目前为止,我无法使用 Stop() 函数来终止 GUI 状态栏中倒计时的第 1 步。
Monitor.Stop() 函数在控制台中单独运行时工作正常,但是当它在界面内的 self.OnConnect() 事件处理程序中运行时它不起作用?我尝试过使用线程结构的多种变体:线程、线程、kthread 等,但都以相同的结果结束。
理想情况下,我想单击“文件”菜单下的“连接”->“连接”连接标签更改为断开连接->监控从步骤 1 -> 开始状态栏显示到步骤 2 为止的剩余时间。
此时,我希望能够通过单击“文件”菜单下的“断开连接”来停止倒计时,但是当我单击它时,倒计时会继续吗?我尝试过的线程函数的每个变体在从控制台中运行时都成功停止了线程,但到目前为止我无法弄清楚为什么从我的 gui 中调用时倒计时无法停止?
任何帮助将不胜感激! :D
顺便说一句,如果您选择运行脚本,请确保将脚本底部的两个“C:\Replace\With\Valid\Path”条目替换为系统上的有效路径。
import os, sys, thread, time, wx
class Frame(wx.Frame):
def __init__(self, parent, id=-1, title="A Frame", path="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, id, title, pos, size, style)
self.path = path
self.StatusBar = wx.StatusBar(self, -1)
self.StatusBar.SetFieldsCount(3)
self.StatusBar.SetStatusWidths([-2, -1, -1])
self.InitMenuBar()
def InitMenuBar(self):
menuBar = wx.MenuBar()
menuFile = wx.Menu()
menuHelp = wx.Menu()
self._Connect = menuFile.Append(101, "&Connect", kind=wx.ITEM_CHECK)
menuFile.AppendSeparator()
menuFile.Append(104, "E&xit")
menuBar.Append(menuFile, "&File")
menuBar.Append(menuHelp, "&Help")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnConnect, self._Connect)
def OnConnect(self, event):
#print [event.IsChecked()]
mon = Monitor("", "", "", self.path, "60", self.StatusBar)
if event.IsChecked():
print "Set Menu Label Disconnected"
self._Connect.SetItemLabel("Disconnect")
print "Start Monitor"
mon.Start()
print "Start Finished"
else:
print "Set Menu Label Connected"
self._Connect.SetItemLabel("Connect")
print "Stop Monitor"
mon.Stop()
print "Stop Finished"
class Monitor:
def __init__(self, email, password, recipient, path, timeout, statusBar=None):
self.email = email
self.password = password
self.recipient = recipient
self.path = path
self.timeout = timeout
self.statusBar = statusBar
#self.lock = thread.allocate_lock()
def Start(self):
#self.lock.acquire()
self.running = True
thread.start_new_thread(self.Run, ())
#self.lock.release()
def Stop(self):
#self.lock.acquire()
self.running = False
#self.lock.release()
def IsRunning(self):
return self.running
def Run(self):
start = NewestByModTime(self.path)
count = int(self.timeout)
while self.running:
#print self.running
# Step 1 - Delay the start of the monitor for X amount of seconds, updating the
# statusbar/console each second to relfect a countdown. remove one from count each
# loop until the count equals 0, than continue on to Step 2.
if count > 0:
if self.statusBar:
self.statusBar.SetStatusText("Monitoring will begin in %s seconds" % (count))
else:
sys.stdout.write("Monitoring will begin in %s seconds\r" % (count))
#sys.stdout.flush()
count -= 1
time.sleep(1)
# Step 2 - Start the monitor function which monitors the selected folder for new
#files. If a new file is detected, send notification via email with the new file
#as an attachment. (for this project, files in the folder will always be jpg images)
# *NOTE* I Have not tested the Stop() function during Step 2 just yet, but I would
# assume it would fail just the same as . *NOTE*
if count == 0:
current = NewestByModTime(self.path)
if current[1] > start[1]:
print "Activity Detected"
start = current
print "Sending Notification Email"
#sendMail(self.email, self.password, self.recipient, "JERK ALERT!!",
# "Some jerkoff is in your place right now, wanna see who it is??", "%s\\%s" % (self.path, start[0]))
print "Notification Email Sent!"
print
self.running = False
def NewestByModTime(path):
stat = ["", 0]
for a in os.listdir(path):
new = os.path.getmtime("%s\\%s" %(path, a))
if new > stat[1]:
stat = [a, new]
return stat
if __name__ == "__main__":
# Run GUI
app = wx.PySimpleApp()
frame = Frame(None, -1, "Test Frame", "C:\\Replace\\With\\Valid\\Path", size=(800, 600))
frame.Show()
app.MainLoop()
del app
## Run Console
#mon = Monitor("", "", "", "C:\\Replace\\With\\Valid\\Path", "60", None)
#mon.Start()
#time.sleep(10)
#mon.Stop()
This samplet is from a project I am working on. My client uses software which uses his webcam to take snapshots if motion is detected within it's view. The problem is that the software is unable to email the images to his account as they are taken. I wrote this project to monitor the folder where the snapshots are saved, and set it up to send any new snapshots in an email to the defined account, this way he'll get an alert on his cell as it happens, along with a snapshot of what was caught in motion. Yes I am aware of the fact that there are numerous applications that have this feature included within their webcam software, but my client has hired me so he can avoid having to replace his current software as he is comfortable using.
There are two steps to the Start() function. Step 1 is a delay before the monitor starts, Step 2 is the launch of the monitor itself. So far I am unable to get the Stop() function to kill Step 1 from counting down in the statusBar of the GUI.
The Monitor.Stop() function works fine when run by itself within the console, but it doesn't work when it's run from within the self.OnConnect() event handler within the interface? I have tried multiple variations of threading structures using: threading, thread, kthread, etc., but all have ended with the same result.
Ideally I want to click Connect under the File menu -> Connect label changes to Disconnect -> monitor starts with Step 1 -> statusBar displays time remaining until Step 2.
At this point I want to be able to stop the countdown by hitting Disconnect under the File menu but when I click it the countdown continues on? Each variation of the thread functions I have tried have successfully stopped the thread when run from within a console, but I am so far unable to figure out why the countdown fails to stop when called from within my gui?
Any help would be greatly appreciated! :D
Btw, make sure to replace the two "C:\Replace\With\Valid\Path" entries at the bottom of the script with a valid path on your system if you chose to run it.
import os, sys, thread, time, wx
class Frame(wx.Frame):
def __init__(self, parent, id=-1, title="A Frame", path="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, id, title, pos, size, style)
self.path = path
self.StatusBar = wx.StatusBar(self, -1)
self.StatusBar.SetFieldsCount(3)
self.StatusBar.SetStatusWidths([-2, -1, -1])
self.InitMenuBar()
def InitMenuBar(self):
menuBar = wx.MenuBar()
menuFile = wx.Menu()
menuHelp = wx.Menu()
self._Connect = menuFile.Append(101, "&Connect", kind=wx.ITEM_CHECK)
menuFile.AppendSeparator()
menuFile.Append(104, "E&xit")
menuBar.Append(menuFile, "&File")
menuBar.Append(menuHelp, "&Help")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnConnect, self._Connect)
def OnConnect(self, event):
#print [event.IsChecked()]
mon = Monitor("", "", "", self.path, "60", self.StatusBar)
if event.IsChecked():
print "Set Menu Label Disconnected"
self._Connect.SetItemLabel("Disconnect")
print "Start Monitor"
mon.Start()
print "Start Finished"
else:
print "Set Menu Label Connected"
self._Connect.SetItemLabel("Connect")
print "Stop Monitor"
mon.Stop()
print "Stop Finished"
class Monitor:
def __init__(self, email, password, recipient, path, timeout, statusBar=None):
self.email = email
self.password = password
self.recipient = recipient
self.path = path
self.timeout = timeout
self.statusBar = statusBar
#self.lock = thread.allocate_lock()
def Start(self):
#self.lock.acquire()
self.running = True
thread.start_new_thread(self.Run, ())
#self.lock.release()
def Stop(self):
#self.lock.acquire()
self.running = False
#self.lock.release()
def IsRunning(self):
return self.running
def Run(self):
start = NewestByModTime(self.path)
count = int(self.timeout)
while self.running:
#print self.running
# Step 1 - Delay the start of the monitor for X amount of seconds, updating the
# statusbar/console each second to relfect a countdown. remove one from count each
# loop until the count equals 0, than continue on to Step 2.
if count > 0:
if self.statusBar:
self.statusBar.SetStatusText("Monitoring will begin in %s seconds" % (count))
else:
sys.stdout.write("Monitoring will begin in %s seconds\r" % (count))
#sys.stdout.flush()
count -= 1
time.sleep(1)
# Step 2 - Start the monitor function which monitors the selected folder for new
#files. If a new file is detected, send notification via email with the new file
#as an attachment. (for this project, files in the folder will always be jpg images)
# *NOTE* I Have not tested the Stop() function during Step 2 just yet, but I would
# assume it would fail just the same as . *NOTE*
if count == 0:
current = NewestByModTime(self.path)
if current[1] > start[1]:
print "Activity Detected"
start = current
print "Sending Notification Email"
#sendMail(self.email, self.password, self.recipient, "JERK ALERT!!",
# "Some jerkoff is in your place right now, wanna see who it is??", "%s\\%s" % (self.path, start[0]))
print "Notification Email Sent!"
print
self.running = False
def NewestByModTime(path):
stat = ["", 0]
for a in os.listdir(path):
new = os.path.getmtime("%s\\%s" %(path, a))
if new > stat[1]:
stat = [a, new]
return stat
if __name__ == "__main__":
# Run GUI
app = wx.PySimpleApp()
frame = Frame(None, -1, "Test Frame", "C:\\Replace\\With\\Valid\\Path", size=(800, 600))
frame.Show()
app.MainLoop()
del app
## Run Console
#mon = Monitor("", "", "", "C:\\Replace\\With\\Valid\\Path", "60", None)
#mon.Start()
#time.sleep(10)
#mon.Stop()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
刚刚想通了..为什么我最大的问题总是一行解决? :P
显然我忽略了这样一个事实:每次调用 self.OnConnect 时,我都会重新创建变量 mon,当我非常沮丧地疯狂地单击连接/断开连接十亿次并观察计数器在不同倒计时 59、43、58、42 等之间交换时,我有点发现了这个错误。哈哈,
我将 mon 更改为 self.mon 并添加if not hasattr(self, "mon") 来阻止 self.mon 每次重新创建自身。至此问题解决,倒计时完美停止。
到:
Just figured it out.. Why are my biggest problems always one line fixes?? :P
Apparently I overlooked the fact that I was recreating the variable mon each time self.OnConnect was called, I kinda clued in on this mistake when I got frustrated enough to furiously click connect/disconnect a billion times and watched the counter swap between different countdowns 59, 43, 58, 42, etc. LOL
I changed mon to self.mon and added in if not hasattr(self, "mon") to stop self.mon from recreating itself each time. So far the problem is solved and the countdown stops perfectly.
To: