I am trying to save the mouse hover positions in a given video. I need to load the video on a 'Load Video' button press. When the mouse is outside the canvas no (x,y) should be saved. I also want the video to streamed at much lower rate (say, 4 times slower). Presently, I have the following code:`
import Tkinter as tk
from Tkinter import *
import PIL.Image,PIL.ImageTk
import time
import cv2
class App:
def __init__(self, window, window_title, video_source=0):
self.window = window
self.window.title(window_title)
self.video_source = video_source
self.video_loaded=False
# open video source (by default this will try to open the computer webcam)
self.vid = MyVideoCapture(self.video_source)
# Create a canvas that can fit the above video source size
self.canvas = tk.Canvas(window, width = self.vid.width, height =
self.vid.height)
self.canvas.pack()
self.canvas.bind('<Motion>',self.canvas.motion)
#self.canvas.bind("<Enter>", self.on_enter)
#self.canvas.bind("<Leave>", self.on_leave)
# Button that lets the user take a snapshot
self.btn_snapshot=tk.Button(window, text="Snapshot", width=50,
command=self.snapshot)
self.btn_snapshot.pack(anchor=tk.CENTER, expand=True)
self.btn_collapse=tk.Button(window, text="Collapse", width=50,
command=self.collapse)
self.btn_collapse.pack(anchor=tk.CENTER, expand=True)
self.btn_load_video=tk.Button(window, text="Load Video", width=50,
command=self.load_video)
self.btn_load_video.pack(anchor=tk.CENTER, expand=True)
#if self.video_loaded==True:
# After it is called once, the update method will be automatically
called every delay milliseconds
self.delay = 15
self.update()
self.window.mainloop()
def load_video(self):
# open video source (by default this will try to open the computer
webcam)
self.vid = MyVideoCapture(self.video_source)
self.video_loaded=True
def snapshot(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg",
cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
def collapse(self):
self.window.quit()
def motion(self):
self.x=self.canvas.winfo_pointerx
self.y=self.canvas.winfo_pointery
print('{},{}'.format(self.x, self.y))
#self.canvas.itemconfigure(text='({x},{y})'.format(x = self.x,
y=self.y))
#print('{},{}'.format(self.x, self.y))
#def motion(self):
# x, y = self.x, self.y
# print('{}, {}'.format(x, y))
#def on_enter(self, event):
# self.l2.configure(text="Hello world")
#def on_leave(self, enter):
# self.l2.configure(text="")
def update(self):
# Get a frame from the video source
ret, frame = self.vid.get_frame()
if ret:
self.photo = PIL.ImageTk.PhotoImage(image =
PIL.Image.fromarray(frame))
self.canvas.create_image(0, 0, image = self.photo, anchor = tk.NW)
self.window.after(self.delay, self.update)
class MyVideoCapture:
def __init__(self, video_source=0):
# Open the video source
video_source='./videofilename.wmv'
self.vid = cv2.VideoCapture(video_source)
if not self.vid.isOpened():
raise ValueError("Unable to open video source", video_source)
# Get video source width and height
self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
def get_frame(self):
if self.vid.isOpened():
ret, frame = self.vid.read()
if ret:
# Return a boolean success flag and the current frame converted
to BGR
return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
else:
return (ret, None)
else:
return (ret, None)
# Release the video source when the object is destroyed
def __del__(self):
if self.vid.isOpened():
self.vid.release()
# Create a window and pass it to the Application object
root = tk.Tk()
App(root, "Tkinter and OpenCV")
When I run this, I get the following error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/home/anaconda2/envs/my_env/lib/python2.7/lib-tk/Tkinter.py", line
1541, in __call__
return self.func(*args)
TypeError: motion() takes exactly 1 argument (2 given)
I want the function motion() to return the mouse hover positions. Appreciate help. Thanks in advance.
Here is the link from which the I got the main code.
The bind() function sends an event object to the callback function, but the motion() function only accepts self. Try:
def motion(self, event):
self.x=event.x
self.y=event.y
Binding the function for saving mouse positions can be done as in teh example below.
from tkinter import *
root = Tk()
root.geometry('300x200+800+50')
c = Canvas(root, bg='tan')
c.pack(fill='both', expand='yes')
def motion(event):
if follow:
print(event.x, event.y)
follow = False
def follow_motion(event):
global follow
follow = not follow
c.bind('<Motion>', motion)
c.bind('<Button-1>', follow_motion)
root.mainloop()
Click the left mouse button on the canvas and the function motion() is enabled. One more click disables it.
Related
Shortened the code to minimum for the question. I want to toggle between images to apply on the canvas using radiobuttons. The code will only apply the images if I enter the image name, (ex. tkimg2) into the 'stamp' event. The radiobuttons are not selecting the images, nor does the link work if I enter the image name in picture=[]. Do you know why the image name is sufficient in one location and not the other and why the radiobuttons don't work? Thank you for any help
from Tkinter import *
import PIL
from PIL import ImageTk, Image
import random
import os.path
root = Tk()
shapes = []
#load 2 images for stamping
__dir__ = os.path.dirname(os.path.abspath(__file__))
filename = os.path.join(__dir__, 'balloon.jpg')
img = PIL.Image.open(filename)
tkimg=PIL.ImageTk.PhotoImage(img)
filename2 = os.path.join(__dir__, 'bird1.jpg')
img2 = PIL.Image.open(filename2)
tkimg2=PIL.ImageTk.PhotoImage(img2)
picture =[]
image=picture
# A Radiobutton to toggle between images
radio = [0]*2
v = IntVar()
def call():
if int(float(str(v.get())))==1:
picture=tkimg
else:
picture=tkimg2
Label(root, text ="Select an image to place.").grid(row=1, column=0,
columnspan=5, sticky=S)
R1=Radiobutton(root, text="Bird 1", variable=v, value=1, command=call)
R1.grid(row=2, column=0, sticky=N+E)
R1.select()
R2=Radiobutton(root, text="Bird 2", variable=v, value=2, command=call)
R2.grid(row=2, column=1, sticky=N+E)
# A canvas for mouse events and image drawing
canvas = Canvas(root, height=1000, width=1000, bg='#2EEAFF')
canvas.grid(column=5, row=0, rowspan=4, sticky=W)
# Bind a function to the left mouse button down event.
def stamp(event):
canvas.create_image(event.x,event.y,image)
canvas.bind('<ButtonPress-1>', stamp)
# Enter event loop
root.mainloop()
I removed most of the code not relevant to the question and changed the images to ordinary PhotoImages for simplicity. Also I changed the first positioning of the image on the canvas.
If you want to assign a value to a variable inside a function you'll have to make it global or it will not work. The variable will be defined in the local function scope and will be garbage collected when the function ends.
I don't think you can change an image on a canvas by updating the variable you used to create it. That's how a StringVar functions.
As the image is the only widget on the canvas I delete ALL items and then create a new image when I toggle images.
Also: I use Python 3.6 so I spell tkinter without the capital T.
from tkinter import *
root = Tk()
#load 2 images for stamping
tkimg = PhotoImage(file='test.gif') # Test image
tkimg2 = PhotoImage(file='tesu.gif') # Test image
# A Radiobutton to toggle between images
v = IntVar()
def call():
canvas.delete(ALL)
if v.get() == 1:
canvas.create_image((2, 2), image=tkimg, anchor=NW)
else:
canvas.create_image((2, 2), image=tkimg2, anchor=NW)
Label(root, text ="Select an image to place.").grid(row=1, column=0, columnspan=5, sticky=S)
R1=Radiobutton(root, text="Bird 1", variable=v, value=1, command=call)
R1.grid(row=2, column=0, sticky=N+E)
R1.select()
R2=Radiobutton(root, text="Bird 2", variable=v, value=2, command=call)
R2.grid(row=2, column=1, sticky=N+E)
# A canvas for mouse events and image drawing
canvas = Canvas(root, height=200, width=200, bg='#2EEAFF')
canvas.grid(column=5, row=0, rowspan=4, sticky=W)
canvas.create_image((2, 2), image=tkimg, anchor=NW)
# Enter event loop
root.mainloop()
I've hit a block when making a pygame project. the display.py file cant find the render() attribute in the player.py file.
Traceback (most recent call last):
File "C:\Users\ethan\Desktop\pyprojects\pygame\display.py", line 34, in <module>
player.render(screen)
AttributeError: Player instance has no attribute 'render'
I tried removing the reference to rendering the player. and it worked, plus the screen rendered properly. when I put the reference back in, the screen didn't render properly, and crashed all together
It would be greatly appreciated if you could help me.
here's the code:
Windows 10
Python-2.7
pygame-1.9.3
display.py
import pygame
from player import *
import sys
pygame.init()
black = (0,0,0)
red = (255,0,0)
green = (0,255,0)
blue = (0,0,255)
white = (255,255,255)
screen_x = 800
screen_y = 600
screen = pygame.display.set_mode([screen_x, screen_y])
pygame.display.set_caption("Test")
clock = pygame.time.Clock()
player = Player(0,0)
gameLoop = True
while gameLoop:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameLoop = False
print(event)
clock.tick(30)
pygame.display.update()
player.render(screen)
screen.fill(white)
pygame.quit()
player.py
import pygame
from display import *
import sys
class Player:
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 32
self.height = 32
def render(self, window):
pygame.draw.rect(window, (0,0,255), (self.x, self.y, self.width, self.height))
again thanks!
I get a different error:
player = Player(0,0)
NameError: name 'Player' is not defined
That happens because you import from display in the player.py file and that runs the display.py file before the Player is defined in player.py.
You could put the code inside display.py into a main function and call it in a special if clause:
if __name__ == '__main__':
main()
That makes sure that the main function doesn't run when the display module gets imported.
Or you could remove the line from display import * in the player module. If there are things that both the player and display modules need, put them into another separate module.
I developed a simple Python application doing some stuff, then I decided to add a simple GUI using Tkinter.
The problem is that, while the I call a function called startprocess and begin doing stuff which is processor heavy and the window freezes.
I know it's a common problem and I've already read that I should use multithreads (very complicated, because the function updates the GUI too) or divide my code in different function, each one working for a little time. anyways is there any modification needed in below code to avoid GUI freezing?
import threading
import tkinter as tk
from tkinter import filedialog
from tkinter import messagebox
import os, datetime, sys, subprocess
import parselog_v1
# diplay messagebox window
def MessageBox(windowLable,msg):
messagebox.showinfo(windowLable, msg)
# check if Dir empty
def checkDirEmpty(work_path):
if os.path.isdir(work_path):
if not os.listdir(work_path):
print ("No Files found in directory")
MessageBox('Log Parser', 'No Files found in directory.')
else:
return True
# launch app in center of screen
def center_window(width=300, height=200):
# get screen width and height
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# calculate position x and y coordinates
x = (screen_width/2) - (width/2)
y = (screen_height/2) - (height/2)
root.geometry('%dx%d+%d+%d' % (width, height, x, y))
# application frame
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.master.title("Log Parser")
def createWidgets(self):
self.Run_Main = tk.Button(self)
self.Run_Main["text"] = "Browse for logs"
self.Run_Main["fg"] = "blue"
self.Run_Main["command"] = self.startProcess
self.Run_Main.pack(side='left',padx=0)
self.QUIT = tk.Button(self)
self.QUIT["text"] = "Quit!"
self.QUIT["fg"] = "red"
self.QUIT["command"] = self.quit
self.QUIT.pack(side='right',padx=5)
def startProcess(self):
global Src_foldername
Src_foldername = filedialog.askdirectory()
Src_foldername = Src_foldername.replace("/", "\\")
print("Source folder: " + Src_foldername)
if checkDirEmpty(Src_foldername):
# process logs
# multithread
print("Processing...")
self.refresh()
threading.Thread(target=parselog_v1.main(Src_foldername))
# scroll text inside application frame
class scrollTxtArea:
def __init__(self, root):
frame = tk.Frame(root)
frame.pack()
self.textPad(frame)
return
class IORedirector(object):
'''A general class for redirecting I/O to this Text widget.'''
def __init__(self, text_area):
self.text_area = text_area
class StdoutRedirector(IORedirector):
'''A class for redirecting stdout to this Text widget.'''
def textPad(self, frame):
# add a frame and put a text area into it
textPad = tk.Frame(frame)
self.text = tk.Text(textPad, height=21, width=68)
self.text.config()
# add a vertical scroll bar to the text area
scroll = tk.Scrollbar(textPad)
self.text.configure(yscrollcommand=scroll.set,background="black", foreground="green")
# pack everything
self.text.pack(side=tk.LEFT, pady=2)
scroll.pack(side=tk.RIGHT, fill=tk.Y)
textPad.pack(side=tk.TOP)
self.text.insert("end", "Begin by selecting log folder..." + "\n")
self.text.configure(state='disabled') # disable text editing
sys.stdout = (self) # to begin logging stdio to GUI
return
def write(self, txt):
self.text.configure(state='normal')
self.text.insert('end', txt)
self.text.configure(state='disabled')
root = tk.Tk()
root.resizable(width=False, height=False)
center_window(500, 300) # launch in center of screen
app = Application(master=root)
scrollFrame = scrollTxtArea(root)
app.mainloop()
root.destroy()
You use thread in wrong way.
First: target= needs function name without () and arguments.
You can assign arguments to args= (it have to be tuple even if you have only one argument)
threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
Now your code runs parselog_v1.main as normal function, waits for result and it will assign this result as function name to taget= - so you have something like this:
result = parselog_v1.main(Src_foldername)
threading.Thread(target=result)
It stops mainloop so it can't get mouse/keyboard events, refresh window, etc. so it looks like window freeze.
Second: after you create thread correctly you have to start it
my_thread = threading.Thread(target=parselog_v1.main, args=(Src_foldername,) )
my_thread.start()
Hy guys,
in my executable program there is a toolbar. Well, the user decides to move the toolbar. Now the toolbar is floating. I know I have to conntect the floating-signals that is emittted when the toolbar ist arranged by the user. How can I save the new position of the toolbar? I know the method of adding the toolbar to the main window with a position:self.addToolBar( Qt.LeftToolBarArea , toolbar_name). In the handle_floating()-method you see what I want: There I want to get the position currently, but how? You also see I have just added one member variable, named self.toolbar_pos, to hold the position of the toolbar. My idea is, when application is terminated I want to serialize this value to a file, and later, when application is ran again its will read that file and set the toolbar accordingly. But this is no problem. Currently I don't have no idea to get the position of the toolbar.
I need your help :)
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.toolbar_pos = None
self.initUI()
def initUI(self):
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(QtGui.qApp.quit)
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.addToolBar(self.toolbar )
self.toolbar.addAction(exitAction)
self.toolbar.setAllowedAreas(QtCore.Qt.TopToolBarArea
| QtCore.Qt.BottomToolBarArea
| QtCore.Qt.LeftToolBarArea
| QtCore.Qt.RightToolBarArea)
self.addToolBar( QtCore.Qt.LeftToolBarArea , self.toolbar )
self.toolbar.topLevelChanged.connect(self.handle_floating)
def handle_floating(self, event):
# The topLevel parameter is true
# if the toolbar is now floating
if not event:
# If the toolbar no longer floats,
# then calculate the position where the
# toolbar is located currently.
self.toolbar_pos = None
print "Get position: ?"
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.setGeometry(300, 300, 300, 200)
ex.setWindowTitle('Toolbar example')
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The QMainWindow class already has APIs for this: i.e. saveState and restoreState. These can be used to save and restore the state of all the toolbars and dock-widgets in your application.
To use them, you first need to make sure that all your toolbars and dock-widgets are given a unique object-name when they are created:
class Example(QtGui.QMainWindow):
...
def initUI(self):
...
self.toolbar = QtGui.QToolBar(self)
self.toolbar.setObjectName('foobar')
Then you can override closeEvent to save the state:
class Example(QtGui.QMainWindow):
...
def closeEvent(self, event):
with open('/tmp/test.conf', 'wb') as stream:
stream.write(self.saveState().data())
(NB: I've just used a temporary file here for testing, but it would obviously be much better to use something like QSettings in your real application).
Finally, you can restore the state that was saved previously:
class Example(QtGui.QMainWindow):
def __init__(self, parent=None):
...
self.initUI()
try:
with open('/tmp/test.conf', 'rb') as stream:
self.restoreState(QtCore.QByteArray(stream.read()))
except IOError:
pass
How can you fade in and/or out on a Tkinter.Frame or any other widget for that matter. All of the examples that I have seen have been for either root (Tkinter.Tk) or Toplevel setting the alpha e.g.
https://stackoverflow.com/a/22491808/1552953
Is it possible to apply this to an individual widget?
You can't do it on individual widgets. Transparency in Tkinter is only available for instances of Tk and Toplevel.
Based on Bryan's answer I came up with this solution, which he inadvertently provided most of the code for also.
One thing to note is if you move the main window the toplevel doesn't move with it...
import Tkinter
import Queue
class Flash(Tkinter.Toplevel):
def __init__(self, root, **options):
Tkinter.Toplevel.__init__(self, root, width=100, height=20, **options)
self.overrideredirect(True) # remove header from toplevel
self.root = root
self.attributes("-alpha", 0.0) # set transparency to 100%
self.queue = Queue.Queue()
self.update_me()
def write(self, message):
self.queue.put(message) # insert message into the queue
def update_me(self):
#This makes our tkinter widget threadsafe
# http://effbot.org/zone/tkinter-threads.htm
try:
while 1:
message = self.queue.get_nowait() # get message from the queue
# if a message is received code will execute from here otherwise exception
# http://stackoverflow.com/questions/11156766/placing-child-window-relative-to-parent-in-tkinter-pythons
x = root.winfo_rootx() # set x coordinate of root
y = root.winfo_rooty() # set y coordinate of root
width = root.winfo_width() # get the width of root
self.geometry("+%d+%d" % (x+width-self.winfo_width() ,y)) # place in the top right cornder of root
self.fade_in() # fade in when a message is received
label_flash = Tkinter.Label(self, text=message, bg='black', fg='white', padx=5, pady=5)
label_flash.pack(anchor='e')
self.lift(self.root)
def callback():
label_flash.after(2000, label_flash.destroy) # destroy the label after 5 seconds
self.fade_away() # fade away after 3 seconds
label_flash.after(3000, callback)
except Queue.Empty:
pass
self.after(100, self.update_me) # check queue every 100th of a second
# http://stackoverflow.com/questions/3399882/having-trouble-with-tkinter-transparency
def fade_in(self):
alpha = self.attributes("-alpha")
alpha = min(alpha + .01, 1.0)
self.attributes("-alpha", alpha)
if alpha < 1.0:
self.after(10, self.fade_in)
# http://stackoverflow.com/questions/22491488/how-to-create-a-fade-out-effect-in-tkinter-my-code-crashes
def fade_away(self):
alpha = self.attributes("-alpha")
if alpha > 0:
alpha -= .1
self.attributes("-alpha", alpha)
self.after(10, self.fade_away)
if __name__ == '__main__':
root = Tkinter.Tk()
root.minsize(700, 300)
root.geometry("700x500")
flash = Flash(root) # create toplevel instance
def callback():
# put a delay between each message so we can check the behaviour depending on the lenght of the delay between messages
import time
flash.write('Hello World')
time.sleep(1)
flash.write('Ready!')
time.sleep(2)
flash.write('Steady!')
time.sleep(4)
flash.write('Go!')
# create a thread to prevent the delays from blocking our GUI
import threading
t = threading.Thread(target=callback)
t.daemon = True
t.start()
root.mainloop()
exit()