take screenshot for label using for loop - python

I want to take screenshot for each time random create string.
I mad a gui program using python and pyqt5. I have a label which I insert an image into it and I have two other labels which set to set text randomly using random and string module.
so, I want to take screenshot for that label which ( lets consider that) has an image and two label for every time random string set on label.
def createCards(self):
cardsNum = int(self.lineEdit_23.text())
S = 5
while cardsNum != 0:
self.label_8.setFrameShape(QFrame.NoFrame)
self.label_9.setFrameShape(QFrame.NoFrame)
self.label_8.setText(''.join(random.choices(string.ascii_lowercase + string.digits, k = S)))
self.label_9.setText(''.join(random.choices(string.ascii_lowercase + string.digits, k = S)))
pics_path = 'pics/%s.jpg'%(str(pics))
screen = QtWidgets.QApplication.primaryScreen()
screenshot = screen.grabWindow( self.label_7.winId())
screenshot.save(pics_path, 'jpg')
cardsNum -= 1

You have to implement the same logic using signals, for example through a QTimer.
from functools import cached_property
#cached_property
def timer(self):
timer = QTimer()
timer.setInterval(100)
timer.timeout.connect(self.take_screenshot)
return timer
def createCards(self):
self.cardsNum = int(self.lineEdit_23.text())
self.timer.start()
def take_screenshot(self):
S = 5
if self.cardsNum == 0:
self.timer.stop()
else:
self.label_8.setFrameShape(QFrame.NoFrame)
self.label_9.setFrameShape(QFrame.NoFrame)
self.label_8.setText(
"".join(random.choices(string.ascii_lowercase + string.digits, k=S))
)
self.label_9.setText(
"".join(random.choices(string.ascii_lowercase + string.digits, k=S))
)
pics_path = "pics/%s.jpg" % (str(pics))
screen = QtWidgets.QApplication.primaryScreen()
screenshot = screen.grabWindow(self.label_7.winId())
screenshot.save(pics_path, "jpg")
self.cardsNum -= 1

Related

Cannot Display two custom Progress Bars at once

I'm currenty working on a progress bar for another project. I pretty much have everything done and working as intended.
However, i am having an issue, where i can't have two (or more) bars displaying simultaneously. The second one always seems to overwrite the first.
Here's the code (excuse my crappy coding skills):
Progress Bar Class:
import sys
class ProgressBar():
full_size = 100
standard = 50
small = 25
def __init__(self, pretext, length=50, total=100):
self.pretext = pretext
self.length = length
self.total = total
def update_bar(self, fill_amount):
real_fill_amount = int(self.length * fill_amount / self.total)
sys.stdout.write("\r%s |%s%s| %s" % (self.pretext,
"█" * real_fill_amount,
"░" * (self.length - real_fill_amount),
str(fill_amount) + "%"))
sys.stdout.flush()
Example code (CPU and Memory Usage Meter):
from pout_progressbar import ProgressBar
import psutil
import time
bar1 = ProgressBar("CPU Usage", length=ProgressBar.standard, total=100)
bar2 = ProgressBar("Memory Usage", length=ProgressBar.standard, total=100)
while True:
bar1.update_bar(int(psutil.cpu_percent(interval=1)))
bar2.update_bar(int(psutil.virtual_memory().percent))
time.sleep(0.5)
Note: pout_progressbar is the filename of the ProgressBar module.
In the example script, two bars are supposed to be showing, but only the second one is showing.
Is there any way i can fix this ?
Thanks in advance.
That is because your second progress bar output overwrites that of the first. Here is a way to fix that:
import sys
import psutil
import time
class ProgressBar:
full_size = 100
standard = 50
small = 25
def __init__(self, pretext, length=50, total=100):
self.pretext = pretext
self.length = length
self.total = total
def update_bar(self, fill_amount):
real_fill_amount = self.length * fill_amount // self.total
progress = f"{self.pretext} |{'█'*real_fill_amount}" \
f"{'░'*(self.length - real_fill_amount)}| {fill_amount}%"
return progress
bar1 = ProgressBar("CPU Usage")
bar2 = ProgressBar("Memory Usage")
while True:
progress1 = bar1.update_bar(int(psutil.cpu_percent(interval=1)))
progress2 = bar2.update_bar(int(psutil.virtual_memory().percent))
sys.stdout.write(f'\r{progress1}\t{progress2}')
sys.stdout.flush()
time.sleep(0.5)
However, there is another problem. It is not trivial to overwrite more than just the previous line. So for now, both progess bars are displayed in the same line. If that is not ok for you, you need to look into one of the options to overwrite multiple lines, however how to do that is system dependent.
As output of next bar depends on previous one, separating these is not able. So if I write:
class ProgressBars():
full_size = 100
standard = 50
small = 25
def __init__(self, pretexts, length=50, total=100):
self.pretexts = pretexts
self.length = length
self.total = total
def update_bar(self, fill_amounts):
real_fill_amounts = [int(self.length * x / self.total) for x in fill_amounts]
line = "%s |%s%s| %s" % (self.pretext,
"█" * real_fill_amount,
"░" * (self.length - real_fill_amount))
str(fill_amount) + "%"))
sys.stdout.write("\b" * (len(line) + len(fill_amounts))
sys.stdout.write(line)
sys.stdout.flush()
If you wan to control console in advance, There is curses.

How can I test a function that is embedded in a method

I recently started doing python. The course I was on ended with an introduction to testing with doctest. I have written a program that uses Tkinter to display widgets and it works :-) . I am using version 3.7. However, testing it is another matter. I can test simple functions and methods, but I hit difficulties when I have a function inside a method. I am pasting below a stripped-down version of what I am trying to achieve. I tried first with doctest and it threw up an error:
"AttributeError: 'function' object has no attribute 'c_square'".
# counter.py
from tkinter import *
import doctest
count = 0
delay = 1000
class MyClass:
def __init__(self, master):
master.geometry("1000x500")
master.resizable(0, 0)
master.title("Display a counter")
frame1 = Frame(master)
frame1.pack()
label1 = Label(frame1, font = ('Courier', 15 , 'bold'))
label1.grid(row = 0, column = 0)
self.my_counter(label1)
label2 = Label(frame1, font = ('Courier', 15 , 'bold'))
label2.grid(row = 0, column = 1)
self.square_of_count(label2)
# This method recursively increments a counter and displays the count.
def my_counter(self, lbl):
def increment_count():
global count
global delay
count += 1
string = str(count)
lbl.config(text = string)
lbl.after(delay, increment_count)
increment_count()
# This method takes the square of the counter and displays the result.
def square_of_count(self, lbl):
def c_square():
global count
squ = count * count
string = str(squ)
lbl.config(text=string)
lbl.after(delay, c_square)
return squ
c_square()
def test_c_square(number):
"""
>>> test_c_square(2)
4
"""
global count
count = number
master = Tk()
frame1 = Frame(master)
label = Label(frame1, font = ('Courier', 15 , 'bold'))
return MyClass.square_of_count.c_square(MyClass.square_of_count.c_square)
def main():
""" # main body commented out for test purposes.
root = Tk()
a = MyClass(root)
root.mainloop()
"""
doctest.testmod(verbose=True)
if __name__ == "__main__":
main()
I am using a separate test function, so that I can initialise my counter.
Then someone suggested that I try unittest, so I wrote this :
import unittest
import counter
class TestCounter(unittest.TestCase):
counter.count = 2
print("count = ", counter.count)
def square_of_count(self):
result = counter.c_square()
self.assertEqual(result, 4)
result = counter.c_square()
self.assertNotEqual(result, 3)
if __name__ == '__main__':
unittest.main()
This runs without throwing up any errors, the purpose of it is to set a value to the variable 'count' and read back the result. But I get the same response whatever value I test for, so I do not believe it is working right. I also tried variations on a theme, but I just got error messages.
Can someone please point out what I am doing wrong, I have looked about various forums and tutorials but have not seen this question asked before.
I would appreciate an answer that is easy to follow, I am asperger / dyslexic and find it difficult to learn new material. A correction with explanation would be most helpful. Thank you.
First of all, avoid this kind of nesting the functions. In your particular case I would highly suggest refactoring of a code in manner of creating some help private methods which you will call from the main ones, or even create whole new utility class:
class Util:
def _init_(self):
self.name = "Utility"
def add_two_numbers(self, first, second):
if(isinstance(first, int) and isinstance(second, int)):
return first+second
class SomeFancyClass:
def __init__(self):
self.util = Util()
self.constant = 4
# This method recursively increments a counter and displays the count.
def my_fancy_math(self, first, second):
return self.constant * self.util.add_two_numbers(first, second)
FancyVar = SomeFancyClass()
print(FancyVar.my_fancy_math(5, 6))
In case you dont want to change your code (for some reason), there is extremely dirty way to access your inner function. Again, a bit stupidly modified example made from your code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# counter.py
from tkinter import *
import doctest
import types
count = 0
delay = 1000
class MyClass:
def __init__(self, smth1):
self.something = smth1
# This method recursively increments a counter and displays the count.
def my_counter(self, lbl):
def increment_count():
global count
global delay
count += 1
string = str(count)
lbl.config(text=string)
lbl.after(delay, increment_count)
increment_count()
# This method takes the square of the counter and displays the result.
def square_of_count(self, lbl):
def test_function1(self, first, second):
return first+second
def c_square():
global count
squ = count * count
string = str(squ)
lbl.config(text=string)
lbl.after(delay, c_square)
return squ
c_square()
def test_function(self, st1):
print(st1)
def test_c_square(number):
global count
count = number
master = Tk()
frame1 = Frame(master)
label = Label(frame1, font=('Courier', 15, 'bold'))
return MyClass.square_of_count.c_square(MyClass.square_of_count.c_square)
def main():
doctest.testmod(verbose=True)
if __name__ == '__main__':
# main()
print('done')
test_function = types.FunctionType(MyClass.square_of_count.__code__.co_consts[1],
{}, None, (), ())
obj = MyClass("Hi")
sum1 = test_function("", 1, 2)
print(sum1)

Need Help Making Buttons to perform for loops when you input a number

I am trying to make a button in Maya using Python that when you type in a number the for loop would loop for that many times. For example, I would put 5 in the box so the for loop would loop 5 times resulting in 50 cubes since it is for i in range (1,10).
This is my code:
import maya.cmds as cmds
import random
handle = "cubeUI"
if cmds.window(handle, exists=True):
print ("deleting old window...\n")
cmds.deleteUI(handle)
cmds.window(handle, title = "make random cubes")
cmds.columnLayout()
cmds.text(label = "amount")
amount_range_text_field = cmds.intField()
cmds.button(label = "random cube", command = "giveMeCube()")
cmds.showWindow(handle)
def giveMeCube():
cmds.polyCube()
amount_range = cmds.intField( amount_range_text_field, query=True, value = True )
for i in range (1,10):
print i
temp = cmds.polyCube()
cmds.xform(temp, t = (random.uniform(-1 *amount_range, amount_range),
random.uniform(-1 * amount_range, amount_range), random.uniform(-1 *
amount_range, amount_range) ) )
My answer is a bit complex, Green Cell answer should work for you.
Here is an example on how you should think your scripts to be more 'clean'
I've put some annotation to help to understand why this
import maya.cmds as cmds
# This module can pass data throughts ui
from functools import partial
import random
# your function that have the amount set as variable that you can set easily :
# giveMeCube(2) result into 20 cubes
def giveMeCube(amount_range = 1):
nb = amount_range * 10
for i in range (nb):
print(i)
temp = cmds.polyCube()
cmds.xform(temp, t = (random.uniform(-1 *amount_range, amount_range),
random.uniform(-1 * amount_range, amount_range), random.uniform(-1 *
amount_range, amount_range) ) )
# this function is just to separate your function from ui control
# so if you want to use giveMeCube in command line or in another script, you don't have your ui polluting the function
# *args is here because the command flag from maya ui give a default True as last argument that need to be dismissed
# most of the time, im putting the intfield query in another function
def uiGiveMeCube(fieldname, *args):
amount = cmds.intField(fieldname, q=True, value=True)
giveMeCube(amount)
def showUI():
handle = "cubeUI"
if cmds.window(handle, exists=True):
print ("deleting old window...\n")
cmds.deleteUI(handle)
cmds.window(handle, title = "make random cubes")
cmds.columnLayout()
cmds.text(label = "amount")
amount_range_text_field = cmds.intField(value=1, min=1)
# you should not use string to set your function
# you could have write : cmds.button(label = "random cube", command = giveMeCube)
# so how partial is used : partial(function, argument1, argument2, ...etc)
cmds.button(label = "random cube", command = partial(uiGiveMeCube, amount_range_text_field))
cmds.showWindow(handle)
showUI()
You already got the value from your spinner in the amount_range variable, so just use that in an another loop. You can also remove the cmds.polyCube() at the beginning of your function since there's no reason for it. Your for loop is actually iterating 9 times right now, instead just change it to for i in range(10) and that will iterate 10 times. You also need to indent the last portion of the code so that it's in the for loop.
import maya.cmds as cmds
import random
handle = "cubeUI"
if cmds.window(handle, exists=True):
print ("deleting old window...\n")
cmds.deleteUI(handle)
cmds.window(handle, title = "make random cubes")
cmds.columnLayout()
cmds.text(label = "amount")
amount_range_text_field = cmds.intField()
cmds.button(label = "random cube", command = "giveMeCube()")
cmds.showWindow(handle)
def giveMeCube():
# Remove cube creation that was here.
amount_range = cmds.intField(amount_range_text_field, query=True, value=True)
for j in range(amount_range): # Use your spinner value to loop x many times.
for i in range(10): # Just need to specify max count to get the proper amount.
print i
temp = cmds.polyCube()
cmds.xform(temp, t = (random.uniform(-1 *amount_range, amount_range),
random.uniform(-1 * amount_range, amount_range), random.uniform(-1 *
amount_range, amount_range)))

Python - returning from a While Loop after its end

I am trying to code a menu routine for an alarm-clock in python, which displays the relevant information on a 7-segment display according to some inputs via pushbuttons.
I managed to create a loop which displays the current time, and whe the button "debMenu" is clicked 3 menu options are displayed. This works well only for the first time untill the 3rd. menu option is reached. When I push the button again the routine does not work - so the function "main_menu" is not called again.
What I am doing wrong...? Thanks !!
stop_loop = [False]
def main_menu(stop_loop, n=[0]):
stop_loop[0] = True
debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
menuList = ['HO ', 'AL ', 'UP ']
if n[0] < 3:
display.scroll(menuList[n[0]], 200) #display menu on 7-seg display
display.show(menuList[n[0]])
n[0] += 1
elif n[0] == 3:
n=[0]
stop_loop[0] = False
main()
def main():
stop_loop[0] = False
debMenu = DebouncedSwitch(butMenu, main_menu, stop_loop)
while not stop_loop[0]: # display current time on 7-seg display
curTime = rtc.datetime()
display.numbers(curTime.hour, curTime.minute, False)
time.sleep_ms(500)
display.numbers(curTime.hour, curTime.minute)
time.sleep_ms(500)
main()
I guess the default value mutable variable is the one killing you. Check here: http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments.
I don't even understand why you need a list variable in this case, why not just n=0 without having to use a list?.
maybe make a new global variable or class to encapsulate both state-related variables (stop_loop and n). Like:
loop = dict(stop=False, n=0)
now you pass this instead of stop_loop to main_menu.
Or use a class that encapsulates a circular array for menu like:
class LoopMenu:
""" circular array menu list with pause/continue/next"""
def __init__(self, menu):
self.menu = menu
self.stop = False
self.n = 0
def next(self):
menu = self.menu[self.n]
self.n += 1
if self.n > len(self.menu):
self.n = 0
return menu
def pause(self):
self.stop = True
def play(self):
self.stop = False
then you main_menu method could be:
my_menu_loop = LoopMenu(['HO ', 'AL ', 'UP '])
def main_menu(menu_loop):
menu_loop.pause()
debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
menu = menu_loop.next()
display.scroll(menu, 200) #display menu on 7-seg display
display.show(menu)
main()
def main():
my_menu_loop.play()
debMenu = DebouncedSwitch(butMenu, main_menu, my_menu_loop)
while not loop_menu.stop: # display current time on 7-seg display
curTime = rtc.datetime()
display.numbers(curTime.hour, curTime.minute, False)
time.sleep_ms(500)
display.numbers(curTime.hour, curTime.minute)
time.sleep_ms(500)
main()

How to interact with mayavi window while animating particles? (during Particle Swarm Optimization)

I am trying to do an animation of a Particle Swarm Optimization using Python and Mayavi2.
The animation is working fine, my problem is that it is not possible to interact with the plot while it is animating the movement. Specifically i would like to turn the graph and zoom. Maybe someone has experience doing animations?
The way i do it is first to calculate the positions of the particles and then to store them. After the calculation is finished i plot the positions of the particle at the first instace of time with point3d() and then i iterate through time updating the data using the set() method.
Is there a way to make it possible to turn the graph? I have heard about something with threads, disabeling the the rendering, but i could not figure out how to do it in my code. Besides lots of other stuff, I have read:
http://code.enthought.com/projects/mayavi//docs/development/html/mayavi/mlab_animating.html
http://code.enthought.com/projects/mayavi//docs/development/html/mayavi/tips.html#acceleration-mayavi-scripts
but it can't see how to use it.
Any suggestions?
Here is my code:
#!/usr/bin/env python
'''
#author rt
'''
import pylab as plt
from numpy import *
from mayavi import mlab
from threading import Thread # making plotting faster?
import ackley as ac
class Swarm(Thread, object):
'''
constructor for the swarm
initializes all instance variables
'''
def __init__(self,objective_function):
Thread.__init__(self)
# optimization options
self.omega = 0.9 # inertial constant
self.c1 = 0.06 # cognitive/private constant
self.c2 = 0.06 # social constant
self.objective = objective_function # function object
self.max_iteration = 100 # maximal number of iterations
# Swarm stuff
self.number = 0
self.best = [] # gbest; the global best position
self.particles = [] # empty list for particles
# temporary
self.min = self.objective.min
self.max = self.objective.max
self.best_evolution = []
# self.dimensions = 2 # dimensions NB!
'''
add particles to the swarm
find the best position of particle in swarm to set global best
'''
def add_particles(self, n):
for i in range(n):
particle = Particle(self)
if i == 0: # initialize self.best
self.best = particle.position
if particle.eval() < self._eval(): # check if there is a better and if, set it
self.best = copy(particle.position)
self.particles.append(particle) # append the particle to the swarm
def _eval(self):
return self.objective.evaluate(self.best)
def plot(self):
for i in range(self.max_iteration):
pos_x = []
pos_y = []
pos_z = []
#print pos_x
for particle in self.particles:
[x,y,z] = particle.trail[i]
pos_x.append(x)
pos_y.append(y)
pos_z.append(z)
#print pos_x
if i ==0:
g = mlab.points3d(pos_x, pos_y,pos_z, scale_factor=0.5)
ms =g.mlab_source
ms.anti_aliasing_frames = 0
ms.set(x=pos_x, y = pos_y, z = pos_z,scale_factor=0.5) #updating y value
#print pos_y
#ms.set(x=pos_x) # update x values
#ms.set(y=pos_y) #updating y value
#ms.set(z=pos_z) #updating y value
#for p in self.particles:
#p.plot()
def plot_objective(self):
delta = 0.1
v = mgrid[self.min:self.max:delta,self.min:self.max:delta]
z = self.objective.evaluate(v)
#mlab.mesh(v[0],v[1],z)
mlab.surf(v[0],v[1],z) # surf creates a more efficient data structure than mesh
mlab.xlabel('x-axis', object=None)
mlab.ylabel('y-axis', object=None)
mlab.zlabel('z-axis', object=None)
def _info(self):
self.plot()
print '----------------------------'
print 'The best result is:'
print 'Coordinates:', self.best
print 'Value: ', self._eval()
#print 'with ', nreval, 'evaluations'
print 'nr of particles: ', len(self.particles)
print '----------------------------'
def run(self):
self.plot_objective()
self.best = self.particles[0].get_position()
iteration = 0
while iteration < self.max_iteration:
#if iteration!= 0: obj.scene.disable_render = True
#disable_render = True
for particle in self.particles:
rnd_c1 = array([random.uniform(0,1),random.uniform(0,1)])
rnd_c2 = array([random.uniform(0,1),random.uniform(0,1)])
particle.velocity = self.omega * array(particle.velocity) + \
self.c1 * rnd_c1 * (array(particle.best) - array(particle.position)) + \
self.c2 * rnd_c2 * (array(self.best) - array(particle.position)) # TODO: change so independent rnd for components
particle.position = array(particle.position) + particle.velocity
if particle.eval() < particle.best_eval():
particle.best = copy(particle.position)
if particle.eval() < self._eval():
self.best = copy(particle.position)
particle.update() # add the point to the trail
iteration +=1
self.best_evolution.append(self._eval())
#obj.scene.disable_render = False
print 'finished: ', iteration
self._info()
'''
Class modeling particle
'''
class Particle():
def __init__(self, swarm):
self.swarm = swarm
x_rand = random.uniform(self.swarm.min,self.swarm.max)
y_rand = random.uniform(self.swarm.min,self.swarm.max)
self.position = array([x_rand,y_rand])
v_x_rand = random.uniform(self.swarm.min,self.swarm.max)
v_y_rand = random.uniform(self.swarm.min,self.swarm.max)
self.velocity = array([v_x_rand, v_y_rand])
self.size = 0.5
self.best = self.position
# visualization
self.trail = []
def plot(self):
[x,y] = self.position
z = self.eval()
mlab.points3d(x,y,z,scale_factor=self.size)
def eval(self):
return self.swarm.objective.evaluate(self.position)
def best_eval(self):
return self.swarm.objective.evaluate(self.best)
def get_position(self):
return self.position
def update(self):
[x,y] = self.position
z = self.eval()
#print [x,y,z]
self.trail.append([x,y,z])
def plot_trail(self,index):
[x,y,z] = self.trail[index]
mlab.points3d(x,y,z,scale_factor=self.size)
# Make the animation
mlab.figure(1, bgcolor=(0, 0, 0), size=(1300, 700)) # create a new figure with black background and size 1300x700
objective = ac.Ackley() # make an objective function
swarm = pso.Swarm(objective) # create a swarm
nr_of_particles = 25 # nr of particles in swarm
swarm.add_particles(nr_of_particles)
swarm.run()
#swarm.start()
mlab.show()
print '------------------------------------------------------'
print 'Particle Swarm Optimization'
#objective.info()
print 'Objective function to minimize has dimension = ', objective.get_dimension()
print '# of iterations = ', 1000
print '# of particles in swarm = ', nr_of_particles
print '------------------------------------------------------'
In my case, even though I was somewhat able to do what Brandon Rhodes suggested for a mock program (https://stackoverflow.com/questions/16617814/interacting-with-mlab-scene-while-it-is-being-drawn), I could not manage to convert my already existing larger program.
Then I found this link: http://wiki.wxpython.org/LongRunningTasks
So, I just sprinkled a lot of wx.Yield() s inside my loops. This way I did not need to change my program structure, and I am able to interact with the window. I think better ways are explained in the link.
Your problem is that the wx event loop, which runs the Mayavi GUI window and listens for mouse clicking and dragging and responds by moving the scene, is not getting any time to run during your animation because you are keeping Python captive in your loop without ever letting it return control.
Instead of keeping control of the program with a loop of your own, you need to create a wx.Timer class that advances the scene by one frame update, and that then returns control to the wx event loop after scheduling itself again. It will look something like this:
import wx
...
class Animator(wx.Timer):
def Notify(self):
"""When a wx.Timer goes off, it calls its Notify() method."""
if (...the animation is complete...):
return
# Otherwise, update all necessary data to advance one step
# in the animation; you might need to keep a counter or
# other state as an instance variable on `self`
# [DATA UPDATE GOES HERE]
# Schedule ourselves again, giving the wx event loop time to
# process any pending mouse motion.
self.Start(0, oneShot=True) # "in zero milliseconds, call me again!"
I played with slightly higher values like 1 for the number of milliseconds that wx gets to run the UI with, but could not really tell a difference between that and just choosing 0 and having control returned "immediately".

Categories