How to delay blits being iterated from a list - python

I'm trying to create a typewriter effect for text being blitted. By typewriter effect, I simply mean that Im trying to avoid the entirety of the text being blitted on screen at once. Instead, im trying to have each letter appear individually, with a slight delay before the next character in the string appears.
The catch is that im not using pygame's font.render. Instead, i've made my own custom fonts, each letter being saved as a separate image file. Now each alphanumeric character has it's own variable to which it's image is attached and each is appended to a list.
e.g:
letter_IMGs = []
a = "a" == pygame.image.load("IMG/letter_a.gif)
letter_IMG.append(a)
Lower, I have something along these lines:
letter_pos_x = 0
text = "Hello"
for i, c in enumerate(text):
screen.blit(letter_IMGs[i], (letter_pos_x,0))
letter_pos_x += 20
scroll_wait #this is a clock.delay variable. It's value was set outside the loop. I'm just calling it here.
Now as you'd guess, the result with that code is that the entire line of text appears simultaneously after the delay. I've been trying to code it as needed from there, but most of what I come up with returns with a "cannot iterate through surface objects" error.
I'm pretty much at a loss on how I should proceed next. Note that, ive been learning a bit of code on my own, on and off, for the past year and that I don't really know what im doing yet. Any and all help will be much appreciated.
Thanks in advance for your time.

Without getting into the pygame specifices too much, you just need to change the iterator so it returns substrings rather than letters:
def iterate_text(text):
for r in range(len(text)):
yield text[:r + 1]
which will return the substring iteratively:
for t in iterate_text('hello'):
print t
# h
# he
# hel
# hell
# hello
use a separate function to draw the string:
def draw_text(x, y, text):
characters = [letter_IMGs[t] for t in text]
cursor = x
for char in characters:
screen.blit(char, cursor, y)
cursor += 20
in your main loop you can decide when to get the next character. You'll basically do something like:
typewriter = iter_text('hello world')
text_to_draw = None
advance_text = False
at a level outside the loop that survive from frame to frame. When you want to draw the next character, you set advance_text to True, in and in the main loop:
if typewriter and advance_text:
text_to_draw = typewriter.next()
advance_text = False # until you set it again
if text_to_draw :
draw_text(0,0, draw_text)
You can start over by resetting the typewriter with new text, and control the timing of the new character appearing by setting advance_text to True before the next frame

Related

Create a function in python that replaces "to be honest" in a sentence with "TBH"

Create a function in python that replaces at least four different words or phrases with internet slang acronyms such as LOL, OMG, TBH. For example, if the user enters a sentence "Oh my god, I am scared to be honest." The output should be "OMG I am scared TBH". The program must not use any built-in find, replace, encode, index, or translate functions. The program can use indexing (i.e., [ ] ), slicing (i.e., :), the in operator, and the len() function.
This is what I have so far:
user_string = (input("Please enter a string: ")).lower()
punctuations = '''.,!##$%^&*()[]{};:-'"\|<>/?_~'''
new_string = ""
list = []
for i in range(0, len(user_string)):
if (user_string[i] not in punctuations):
new_string = new_string + user_string[i]
print(new_string)
slang = "to be honest"
for i in range(0, len(slang)):
for j in range(0, len(new_string)):
if (new_string[j] == slang[i]):
list.append(j)
if (i < len(slang)):
i = i + 1
elif (new_string[j] != slang[i]):
if (len(list) > 0):
list.pop()
print(list)
First I am getting the sentence from the user and removing all the punctuations from the sentence. Then I have created a variable called slang which holds the slang that I want to replace in the sentence with the acronym "TBH".
I have nested for loops which compare the string that the user has entered to the first letter of the slang variable. If the letters are the same, it compares the next letter of the string with the next letter of the slang.
I'm getting an error from the last part. How do I check if "to be honest" is in the string that the user has entered? And if it is in the string, how do I replace it with "TBH"?
I cannot see any python errors that your code will actually produce, given the number of guard clauses, so I will assume what you mean by error is actually the program not working as you intended.
With that in mind, the main problem with your code is that you have nested for loops. This means that for any one character in slang, you check it against every character in new_string.
If you run through your code with this in mind, you will see that for every character in slang, you are attempting to add one value to the list and remove len(slang) - 1 values from it. Your clause, however, prevents this from causing an python error.
I would also like to mention that the statement
if (i < Len(slang)):
i = i + 1
is completely unnecessary because i is already automatically incremented by the for loop, which could cause issues later. It is guarded by a clause though, which is why it isn't a problem yet.
If you're still stuck on this problem, here's my version on how to solve this exercise:
# This is a dictionary so we can automate the replacement on the `__main__` scope
targets = {'to be honest': 'TBH', 'oh my god': 'OMG'}
# Returns a list of intervals that tells where all occurences of the
# `sequence` passed as parameter resides inside `source`.
#
# If `sequence` is not present, the list will be empty.
def findSequences(source, sequence):
# This is our return value.
intervals = []
# len is O(1). But if you need to implement your own len function,
# this might be handy to save for the linear complexity.
srcLength = len(source)
seqLength = len(sequence)
# If the sequence is larger than source, it's not inside
if (seqLength > srcLength):
return intervals
# If it's smaller or equal than source, it might be
else:
buffer = ''
for i in range(srcLength):
buffer = ''
# From a starting character on index `i`, we will create
# a temporary buffer with the length of sequence.
for j in range(seqLength):
# We must take care to not go out of the source string
# otherwise, there's no point in continuing on building
# buffer.
if (i+j >= srcLength):
break
else:
buffer += source[i+j]
# If this temporary buffer equals sequence, we found the
# substring!
if (buffer == sequence):
# Return the interval of the substring
intervals.append((i, i+j))
# Out of the for-loop.
return intervals
# Takes out any characters inside `punctuation` from source.
#
# Uses the `in` keyword on the if-statement. But as the post says,
# it's allowed.
def takeOutPunctuation(source, punctuation='.,!##$%^&*()[]{};:-\'"\\|<>/?_~'):
buffer = ''
for char in source:
if (char not in punctuation):
buffer += char
return buffer
# A naive approach would not to find all intervals, but to find the first
# `phrase` occurence inside the `source` string, and replace it. If you do
# that, it will get replacing "TBH" to "TBH2" infinitelly, always append "2"
# to the string.
#
# This function is smart enough to avoid that.
#
# It replaces all occurences of the `phrase` string into a `target` string.
#
# As `findSequences` returns a list of all capture's intervals, the
# replacement will not get stuck in an infinite loop if we use
# parameters such as: myReplace(..., "TBH", "TBH2")
def myReplace(source, phrase, target):
intervals = findSequences(source, phrase)
if (len(intervals) == 0):
return source
else:
# Append everything until the first capture
buffer = source[:intervals[0][0]]
# We insert this first interval just for writting less code inside the for-loop.
#
# This is not a capture, it's just so we can access (i-1) when the iteration
# starts.
intervals.insert(0, (0, intervals[0][0]))
# Start a the second position of the `intervals` array so we can access (i-1)
# at the start of the iteration.
for i in range(1, len(intervals)):
# For every `phrase` capture, we append:
# - everything that comes before the capture
# - the `target` string
buffer += source[intervals[i-1][1]+1:intervals[i][0]] + target
# Once the iteration ends, we must append everything that comes later
# after the last capture.
buffer += source[intervals[-1][1]+1:]
# Return the modified string
return buffer
if __name__ == '__main__':
# Note: I didn't wrote input() here so we can see what the actual input is.
user_string = 'Oh my god, I am scared to be honest and to be honest and to be honest!'.lower()
user_string = takeOutPunctuation(user_string)
# Automated Replacement
for key in targets:
user_string = myReplace(user_string, key, targets[key])
# Print the output:
print(user_string)
# -> OMG i am scared TBH and TBH and TBH
Note: I used Python 3.10.2 to run this script.

Python docx - find and replace words with italicized version

I have thought of a few ways to accomplish this, but each is uglier than the next. I'm trying to think of a way to search for all instances of a word in a word document and italicize them.
I can't upload a word document, but here's what I had in mind:
A working example would find all instances of billybob, including the one in the table, and italicize them. The problem is the way the runs are frequently aligned means that one run might have billy and the next one might have bob so there's no straightforward way to find all of them.
I'm going to leave this open because the approach I came up with isn't perfect, but it works in the vast majority of the cases. Here is the code:
document = Document(<YOUR_DOC>)
# Data will be a list of rows represented as dictionaries
# containing each row's data.
characters = {}
for paragraph in <YOUR_PARAGRAPHS>:
run_string = ""
run_index = {}
i = 0
for x, run in enumerate(paragraph.runs):
# Create a string consisting of all the runs' text. Theoretically this
# should always be the same as parapgrah.text, but I didn't check
run_string = run_string + run.text
# The index i represents the starting position of the run in question
# within the string. We are creating a dictionary of form
# {<run_start_location>: <pointer_to_run>}
run_index[i] = x
# This will be the start of the next run
i = i + len(run.text)
word_you_wanted_to_find = re.findall("some_regex", paragraph.text)
for word in word_you_wanted_to_find:
# [m.start() for m in re.finditer(word, run_string)] returns the starting
# positions of each word that was found
for word_start in [m.start() for m in re.finditer(word, run_string)]:
word_end = word_start + len(word)
# This will be a list of the indices of the runs which have part
# of the word we want to include
included_runs = []
for key in run_index.keys():
# Remember, the key is the location in the string of the start of
# the run. In this case, the start of the word start should be less than
# the key+len(run) and the end of the word should be greater
# than the key (the start of the run)
if word_start <= (key + len(paragraph.runs[run_index[key]].text)) and key < word_end:
included_runs.append(key)
# If the key is larger than or equal to the end of the word,
# this means we have found all relevant keys. We don't need
# to loop over the rest (we could, it just wouldn't be efficient)
if key >= word_end:
break
# At this point, included_runs is a full list of indices to the relevant
# runs so we can modify each one in turn.
for run_key in included_runs:
paragraph.runs[run_index[run_key]].italic = True
document.save(<MODIFIED_DOC>)
Problem 1
The problem with this approach is that, while uncommon (at least in my doc), it is possible for a single run to contain more than just your target word. So you might end up italicizing an entire run that includes your run and then some. For my use case it didn't make sense to fix that problem here.
Solution
If you were to perfect what I did above you would have to change this code block:
if word_start <= (key + len(paragraph.runs[run_index[key]].text)) and key < word_end:
included_runs.append(key)
Here you have identified the run that has your word. You would need to extend the code to separate the word into its own run and remove it from the current run. Then you could separately italicize that run.
Problem 2
The code shown above doesn't handle both the table and normal text. I didn't need to for my use case, but in the general case you would have to check both.

Python issue with replace statement?

I've been write this practice program for while now, the whole purpose of the code is to get user input and generate passwords, everything almost works, but the replace statements are driving me nuts. Maybe one of you smart programmers can help me, because I'm kinda new to this whole field of programming. The issue is that replace statement only seems to work with the first char in Strng, but not the others one. The other funcs blower the last run first and then the middle one runs.
def Manip(Strng):
#Strng = 'jayjay'
print (Strng.replace('j','h',1))
#Displays: 'hayjay'
print (Strng.replace('j','h',4))
#Displays: 'hayhay'
return
def Add_nums(Strng):
Size=len(str(Strng))
Total_per = str(Strng).count('%')
# Get The % Spots Position, So they only get replaced with numbers during permutation
currnt_Pos = 0
per = [] # % position per for percent
rGen = ''
for i in str(Strng):
if i == str('%'):
per.append(currnt_Pos)
currnt_Pos+=1
for num,pos in zip(str(self.ints),per):
rGen = Strng.replace(str(Strng[pos]),str(num),4);
return rGen
for pos in AlphaB: # DataBase Of The Positions Of Alphabets
for letter in self.alphas: #letters in The User Inputs
GenPass=(self.forms.replace(self.forms[pos],letter,int(pos)))
# Not Fully Formatted yet; you got something like Cat%%%, so you can use another function to change % to nums
# And use the permutations function to generate other passwrds and then
# continue to the rest of this for loop which will generate something like cat222 or cat333
Add_nums(GenPass) # The Function That will add numbers to the Cat%%%
print (rGen);exit()

detect an alphabetical character in a picture using JES

I have a task that asked me to making two TEXT picture 1 which one of them is only one character 2 to detect on the TEXT picture.
the first task is to detect only one character location and I sorted it out, but the second task is to detect all the characters location in it.
It says that I have to copy, and paste the detectOneChar() and extend it by numbers of alphabetical including "space" meaning 27 times. but I did not understand how to do.
This is my first code:
def driver():
src=makePicture(pickAFile())
tgt=makePicture(pickAFile())
for myOffset in range(0,getWidth(tgt)-getWidth(src)):
detectOneChar(src,tgt,myOffset,0)
explore(tgt)
return tgt
def detectOneChar(src,tgt,xOffset,yOffset):
sWidth=getWidth(src)
sHeight=getHeight(src)
matchPixels=0
perfectMatch=sWidth*sHeight
for sX in range (0,sWidth):
for sY in range (0,sHeight):
tX=sX+xOffset
tY=sY+yOffset
sPx=getPixel(src,sX,sY)
tPx=getPixel(tgt,tX,tY)
if getColor(tPx) == getColor(sPx):
matchPixels=matchPixels+1
if matchPixels == perfectMatch:
print "Found L at position", tX
setColor(tPx,getColor(sPx))
i cant see if your functions works, everything it prints out just the file path, however, it is recommended using an array and create a list of letter inside

python display a character at a time

So I'm recreating a part of pokemon yellow (trying to make it as close to the original as possible) And for 2 days now I'm searching a smart and efficient way to render and display a string one character at a time in the textbox, just like the pokemon games!(By the way I'm using pygame and python). Does anyone know any way of achieving this? I've tried many things but when rendering one character at a time, there is always inadequate space between them.
Sorry for the long question!
Cheers,
Alex
(edit) Thanks for your interest guys!!!
I'm not sure if I know the correct way to display my code here, If I should just
copy paste it in here or upload in dropbox or somewhere else..
(edit2) Just to clarify, i use the font at size 28, so the way I'm trying to render the characters right now, is to make a list where every element has the format (character_to_render,x_pos_to_render,y_pos_to_render). The next character would be (character2_to_render,x_pos_to_render + 28,y_pos_to_render). But approaching the problem this way, leaves inadequate space between some characters and some others are just fine.
(Edit 3) : Thanks for all your answers guys ! After closely observing the emulator, I noticed that the inadequate spacing between rendered characters is apparent there as well! So I'll just ignore this issue Andover on with my project !! Cheers and have a nice day!
Ok so here is the best solution that I have come up with so far.
You want to be able to display a string, but you only want to do it one character at a time. With strings you can do something like string[0:len(string)] which will return the whole string. So what I am thinking and please correct me if I'm wrong, but say you lower the FPS for a couple seconds, or If you do not want to do this because you still want to accept user input to skip the text.
So you have your while loop, and you check for if text is being displayed. If it is, you want to add a new letter to the string that you are displaying to the screen. I would recommend using a class for the text displayed on the screen.
surf = pygame.Surface(80, 80)
Text = TextBoxString("Hello World")
font = pygame.font.SysFont("Arial", 18)
while true:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
elif event.type == MOUSEBUTTONUP:
Text.showAll()
surf.fill((0,0,0))
text = font.render(Text.currentString, (0,0,0))
surf.blit(text, (0,0))
Text.addOn()
class TextBoxString:
def __init__(self, string):
#string that you will be dealing with
self.totalString = string
self.currentString = string[0]
#how many characters you want shown to the screen
self.length = 0
#this means that every four times through your 
#while loop a new char is displayed
self.speed = 4
def addOn(self) #adds one to the loop num and then checks if the loop num equals the speed
self.loopNum += 1
if self.loopNum == self.speed:
self.length += 1
self.loopNum=0
self.currentString = totalString[0: self.length]
def showAll(self):
self.length = len(self.totalString)
self.currentString = [0: self.length]

Categories