Moviepy bugs out when concatenating in a for loop - python

I want to concatenate 4 clips of the same videoclip, Fimage, for a certain duration of time. The print tests and logic tests say that this should work, but it generates a 7 second clip with half of it bugging out. My suspicion is that the concatenate_videoclips is messing up in the for loop as print statements are working inside the for loop, so is there another way I can concatenate it?
This should be really straight forward, but it bugs out on me. In theory it should generate a 23 second video clip of the same image, but instead it generates a 7 second image with it bugging at 4 second mark.
from moviepy.editor import *
# Create an ImageClip object
Fimage = ImageClip("Z:/Programming Stuff/Images/Testing/Char3.png")
Fimage = Fimage.set_duration(5)
Fimage.fps = 30
durations = [5,10,5,3] #Durations of each clip
clips = [Fimage, Fimage, Fimage, Fimage] #The same clip 4 times
Final = clips[0].set_duration(durations[0])
#IT SEEMS THAT WHENEVER I PUT FOR LOOP CONCATENATE, IT DOESNT WORK
for i in range(1, len(clips)):
clip = clips[i].set_duration(durations[i])
Final = concatenate_videoclips([Final, clip])
#The print statements oddly work fine so the loop isn't the problem, I suspect it's the concatenate logic.
Final.write_videofile("wiggles1.mp4")

My suspicions were correct. concatenate_videoclips does not like being in a for loop. It is not in the documentation, but from testing, it completes a single iteration and then freaks out for the second iteration and breaks, thus causing a 7 second clip with 5 seconds as the first clip and 2 seconds as the attempted second iteration. The correct way to do this is to alter each element's duration and concatenate outside of the loop. For all those who will attempt to do this, remember, concatenate just really hates being in loops and must be outside of it.
Final = clips[0].set_duration(durations[0]) #IT SEEMS THAT WHENEVER I PUT FOR LOOP CONCATENATE, IT DOESNT WORK
for i in range(1, len(clips)):
clips[i] = clips[i].set_duration(durations[i])
Final = concatenate_videoclips(clips) #Never put this in a the loop

Related

Double ```for``` loop

I'm trying to make a nesting for loop to calculate points for my cribbage game. Right now I'm trying to figure out a method for counting all the '15's in the players hand. The way I currently have the system set up makes it so if 'dT' is in the hand, the card art for the ten of diamonds will print.
What I currently have (that is related to this question) is
hand = [] # this is filled by the rest of the program
handCombos = []
def count15s():
for _ in range(6):
possiblePairs = {'T5','J5','Q5','K5','78','96','5T','5J','5Q','5K','87','69'}
first = hand[0][1]
second = hand[2][1]
combo = ''.join(first+second)
if(combo in possiblePairs):
print('ham') # placeholder so the program can run without an error
print(combo)
This works for what it is supposed to--if the second part of the first term + the second part of the third term is in the list of possible pairs, then it prints 'ham' to the console. The problem with this, however, is that 1) It prints 'ham' 6 times, and 2) It actually does what it's supposed to.
I'm looking for a way that I can nest a for loop inside another one so it can scan every combination of '15's in the hand, then that specific combination to the handCombos array so that it can't be used again. I know that I need a for loop somewhere but the way I'm thinking of doing it would end up with a triple loop and I don't think that this is the best way to do it.

Most efficient way to loop over python list and compare and adjust element values

I have a list of elements with a certain attribute/variable. First of all I need to check if all attributes have the same value, if not I need to adapt this attribute for every element to the highes value.
The thing is, it is not difficult to programm such a loop. However, I would like to know what the most efficient way is to do so.
My current approach works fine, but it loops through the list 2 times, has two local variables and just does not feel efficient enough.
I simplified the code. This is basically what I've got:
biggest_value = 0
re_calc = 0
for _, element in enumerate(element_list):
if element.value > biggest_value :
biggest_value = element.value
re_calc += 1
if re_calc > 1:
for _, element in enumerate(element_list):
element.value = adjust_value(biggest_value)
element_list(_) = element
The thing annoying me is the necessity of the "re_calc" variable. A simple check for the biggest value is no big deal. But this task consists out of 3 steps:
"Compare Attributes--> Finding Biggest Value --> possibly Adjust Others". However I do not want to loop over this list 3 times. Not even two times as my current suggestion does.
There has to be a more efficient way. Any ideas? Thanks in advance.
The first loop is just determing the largest value of the element_list. So an approach can be:
transform the element_list into a numpy array. Unfortunately you do not tell, how the list looks like. But if the list contains numbers then
L = np.array(element_list)
can probably do it.
After that use np.max(L). Numpy commands without for loops are usually much faster.
import numpy as np
nl = 10
L = np.random.rand(nl)
biggest_value = np.max(L)
L, biggest_value
gives
(array([0.70047074, 0.14160459, 0.75061621, 0.89013494, 0.70587705,
0.50218377, 0.31197993, 0.42670057, 0.67869183, 0.04415816]),
0.8901349369179461)
In the second for-loop it is not obvious what you want to achieve. Unfortunately you do not give an input and a desired output and do not not tell what adjust_value has to do. A minimal running code with data would be helpful to give a support.

Loop through array of shuffled WebElements without them getting stale

My dilemma is that if I use
a=[]
a=driver.find_elements_by_class_name("card")
random.shuffle(a)
for card in a:
nextup=(str(card.text) + '\n' + "_" * 15)
do a bunch of stuff that takes about 10 min
The first round works but then I get a StaleElementException because it clicks links and goes to diff pages. So then I switched to this:
a=[]
a=driver.find_elements_by_class_name("card")
i=0
cardnum=len(a)
while i != cardnum:
i += 1 #needed because first element thats found doesnt work
a=driver.find_elements_by_class_name("card") #also needed to refresh the list
random.shuffle(a)
nextup=(str(card.text) + '\n' + "_" * 15)
do a bunch of stuff that takes about 10 min
The problem with this one is the i variable because the same card could be clicked due to the shuffle with each loop. Then I added a catch to check if card had already been clicked and continue if it has. Sounds like it'd work but sadly the i variable counts these and then eventually counts past the index. I thought about periodically setting i back to 1 but I do not know if it will work. Edit: would make an infinite loop since once all are clicked i will be zero and it will never exit.
I know the code works it's been tested extensively, however, bots get banned for not being humanlike and random. The basics of this script is goes thru a list of categories and then goes thru all the cards in a category. Tried randomizing the categories but similar dilemma because to refresh the list you have to remake array in each loop like the above block then comes the problem with categories already being completed would be clicked again... Any advice would be appreciated.
What is happening here is that, as you interact with the page, the DOM gets refreshed, eventually causing the elements you've been storing to go stale.
Rather than keeping a list of the elements, keep a reference to their individual element paths, and reacquire the elements as needed:
# The base css path for all the cards
base_card_css_path = ".card"
# Get all the target elements. You are only doing this to
# get a count of the number of elements on the page
card_elems = driver.find_elements_by_css_selector(base_card_css_path)
# Convert this to a list of indexes, starting with 1
card_indexes = list(range(1, len(card_elems)+1))
# Shuffle it
random.shuffle(card_indexes)
# Use `:nth-child(X)` syntax to get these elements on an as needed basis
for index in card_indexes:
card_css = base_card_css_path + ":nth-child({0})".format(index)
card = driver.find_element_by_css_selector(card_css)
nextup=(str(card.text) + '\n' + "_" * 15)
# do a bunch of stuff that takes about 10 min
(The above is untested for obvious reasons)

Add threads to a Sudoku checker

I have just created a simple function in Python which checks whether or not a Sudoku board (input is given as a list) is valid or not. The way I did it is pretty straight forward:
check if the sudoku board is 9x9
check that each number appears only once per row
check that each number appears only once per column
check that each number appears exactly once per 3x3 grid
Now, once I started this, I wanted to take advantage and learn a bit about Python threads. I read the docs, and also some awesome general multithreading related posts here on SO, but I just couldn't think of a way of implementing them in my checker.
Now, the way I'd like the threads works (as I thought) is one thread to check that each column contains 1-9 digits, another one to check the lines for the same thing, and another nine threads to check each 3x3 sub-grid. Could you guys please tell me (eventually with some explanations) how I could achieve this ? Thanks
So to give some general pointers on how to achieve this, without taking away any challenge. Lets start by import Threading:
import threading
Which will let us use thread objects! Also, in order to know if the Sudoku grid will be valid after the fact, we need a variable to store the True/False condition in. You can either go for a single variable, and use Thread Locks to ensure no other threads access it at the same time, or go with three separate. For simplicity, I'll use three separate variables in this example
LinesValid = False
ColumnsValid = False
GridsValid = False
Then, since threads require a function or another callable to run as their target, and you desire a thread for columns, rows and for each 3x3 grid, we need three functions for each thread. However, since there are 9 columns, 9 rows and also 9 grids I believe it would be a lot better to just do a single thread for the grids as well, but for the purpose of the exercise I suppose it is fine to do one for each.
def CheckLines():
# Line Checking Code
def CheckColumns():
# ColumnCheckingCode
def CheckGrid(UpperLeft, BottomRight):
# GridCheckingCode
Anyway, here we define our three functions, with their appropriate line checking code. Like the lines would check the X axis, and the Columns the Y axis, but the idea is to split it up. For the CheckGrid you would need to specify corners if you want it to have a thread for each tile, or if you decide on a single thread you would just define it as:
def CheckGrid():
# GridCheckingCode
After that we need to make our Threads:
LineThread = threading.Thread(target=CheckLines)
ColumnThread = threading.Thread(target=CheckLines)
GridThread = threading.Thread(target=CheckLines, args=([0, 0], [2, 2]))
You can ignore the arguments from the GridThread if you do not need the grids. Otherwise you need to specify corners, and find a way to loop through the specified tiles.
After that it is a matter of starting the threads, and joining them with the main thread, before showing the data to the user:
LineThread.start()
ColumnThread.start()
GridThread.start()
LineThread.join()
ColumnThread.join()
GridThread.join()
if sum(LinesValid, ColumnsValid, GridsValid) == 3:
print("The grid is valid!")
else:
print("The grid is invalid!")
Here we check if all our Bools are True: ( 1 + 1 + 1 ) == 3 if they are, and print data to the user based on this. These bools would be set to True/False within their respective Check** functions!
If you desire some more direct solutions, rather than a general direction with explanations, please let me know and I'll throw something together! The final piece of code looks something like this:
import threading
def CheckLines(self):
# Line Checking Code
def CheckColumns(self):
# ColumnCheckingCode
def CheckGrid(self, UpperLeft, BottomRight):
# GridCheckingCode
LinesValid = False
ColumnsValid = False
GridsValid = False
LineThread = threading.Thread(target=CheckLines)
ColumnThread = threading.Thread(target=CheckLines)
GridThread = threading.Thread(target=CheckLines, args=([0, 0], [2, 2]))
LineThread.start()
ColumnThread.start()
GridThread.start()
LineThread.join()
ColumnThread.join()
GridThread.join()
if sum(LinesValid, ColumnsValid, GridsValid) == 3:
print("The grid is valid!")
else:
print("The grid is invalid!")

Separate the first loop append and the second loop append while doing double loop in Python

Now I am trying to make two dimesional array with double loop.
In my code:
for t in range(0,150):
for z in range(0,279):
QC1 = QC[t,z,:,:]
SUMQ =1000*np.mean(QC1)
QRAIN1.append(SUMQ)
print len(QRAIN1)
QRAIN.append(QRAIN1)
QR = np.array(QRAIN)
I would like to make 150X279 array, but the result is not, because I think that in every time of the first loop run, the results are appended in the QRAIN1.
I would like to separate each loop run of the list of 259 numbers and accumulate them to QRAIN resulting 150x279 array.
Any help or idea would be really appreciated.
Thank you,
Isaac
Just make a new empty list each time through the loop:
for t in range(0,150):
QRAIN1 = []
for z in range(0,279):
QC1 = QC[t,z,:,:]
SUMQ =1000*np.mean(QC1)
QRAIN1.append(SUMQ)
print len(QRAIN1)
QRAIN.append(QRAIN1)
QR = np.array(QRAIN)
BTW, any time you find yourself starting with an empty list and then appending to it in a for loop, consider the stylish alternative of a list comprehension:
for t in range(150):
QRAIN1 = [1000*np.mean(QC[t,z,:,:]) for z in range(279)]
print len(QRAIN1)
QRAIN.append(QRAIN1)
QR = np.array(QRAIN)
I'm also removing the redundant 0, in the range calls -- again just a matter of style, but I like Tufte's principle, "no wasted pixels":-)
Of course you could also build all of QRAIN with a nested list comprehension, but I understand that's starting to be a bit of a stretch, and the "middle way" of a listcomp inside, a for loop outside, may be considered more readable. Anyway, just in case you want to try...:
QRAIN = [ [1000*np.mean(QC[t,z,:,:]) for z in range(279)]
for t in range(150) ]
QR = np.array(QRAIN)
This one doesn't have the prints but I suspect you were only using them as a debugging aid, so their loss shouldn't be a big problem, I hope:-).

Categories