I have a problem with a pygame window disappearing immediately after it has been opened I know this can be resolved with a loop around the pygame.quit but i cant be able to solve it.
enter code he enter codeimport sys
import pygame
pygame.init()
quit = 1
if(quit == 2):
pygame.quit
if (quit == 1):
wind = pygame.display.set_mode((600,600))
width = 300
height = 300
x = 300
y = 300
vel = 1re
You window closes immediately, because your application is immediately terminated. You need an application loop. The typical PyGame application loop has to:
limit the frames per second to limit CPU usage with pygame.time.Clock.tick
handle the events by calling either pygame.event.pump() or pygame.event.get().
update the game states and positions of objects dependent on the input events and time (respectively frames)
clear the entire display or draw the background
draw the entire scene (blit all the objects)
update the display by calling either pygame.display.update() or pygame.display.flip()
e.g.:
import pygame
pygame.init()
window = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
# main application loop
run = True
while run:
# limit frames per second
clock.tick(100)
# event loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# clear the display
window.fill(0)
# draw the scene
pygame.draw.circle(window, (255, 0, 0), (250, 250), 100)
# update the display
pygame.display.flip()
pygame.quit()
exit()
I think it quits because the code has finished executing, and I don't see a loop in your code currently...
Maybe something like:
# Initialise your pygame stuff
isRunning = true
while isRunning:
# Respond to events from pygame
if certainCondition:
isRunning = false
pygame.quit()
Related
I would like to show/hide a grid on a tkinter canvas, as seen in Gimp for instance.
I have this code:
is_on = True
def my_grid():
global is_on
global hor
global ver
if is_on:
y=0
x=0
for i in range(8):
x+=64
y+=64
hor=A.create_line(0,y,512,y, fill="black")
ver=A.create_line(x,0, x, 512, fill="black")
is_on=False
else:
A.delete(hor)
A.delete(ver)
is_on=True
This delete only the last row and the last column. I've also tried to loop the delete action but it doesn't work.
Hope someone could help me.
Best
If you add a tag to the lines, you can manipulate all of the lines with a single statement.
Also, there's no need to delete and recreate the lines each time. You can hide the grid by setting their state to "hidden".
For example, here's how to create, hide, and show the grid:
def create_grid():
x = y = 0
for i in range(8):
x+=64
y+=64
A.create_line(0,y,512,y, fill="black", tags=("grid",))
A.create_line(x,0, x, 512, fill="black", tags=("grid",))
def show_grid():
A.itemconfigure("grid", state="normal")
def hide_grid():
A.itemconfigure("grid", state="hidden")
Try something like this:
import tkinter as tk
class CanvasGrid(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
self.lines = []
self.lines_displayed = False
def display_lines(self):
# If the lines are already displayed don't do anything
if self.lines_displayed:
return None
self.lines_displayed = True
# Clear the list of the lines as they are no longer on the screen
self.lines.clear()
# Get the width and height of the canvas
width = super().winfo_reqwidth()
height = super().winfo_reqheight()
# Loop through and create the vertical lines
for x in range(0, width-width//8, width//8):
line = super().create_line(x, 0, x, height, fill="black")
self.lines.append(line)
# Loop through and create the horizontal lines
for y in range(0, height-height//8, height//8):
line = super().create_line(0, y, width, y, fill="black")
self.lines.append(line)
def hide_lines(self):
# For each line that is on the screen
for line in self.lines:
# Delete the line
super().delete(line)
self.lines_displayed = False
# Example code that uses the class
def toggle_lines(event):
if canvas.lines_displayed:
canvas.hide_lines()
else:
canvas.display_lines()
root = tk.Tk()
canvas = CanvasGrid(root, width=400, height=400)
canvas.pack()
canvas.display_lines()
canvas.bind("<Button-1>", toggle_lines)
root.mainloop()
I created an extension to the tkinter.Canvas class by adding display_lines and hide_lines. You can use the class as if you are using tkinter.Canvas but it has those 2 methods. To try out the example code click on the canvas to show/hide the lines.
Here is another way of doing this:
from tkinter import Tk, Canvas, Button
vertical = 8
horizontal = 8
canvas_width = 700
canvas_height = 500
grid_lst = []
def grid(mode):
if mode == 'show':
if len(grid_lst):
return
for hor in range(horizontal):
y = (hor + 1) * (canvas_height / (horizontal + 1))
grid_lst.append(canvas.create_line((0, y, canvas_width, y), fill='black'))
for ver in range(vertical):
x = (ver + 1) * (canvas_width / (vertical + 1))
grid_lst.append(canvas.create_line((x, 0, x, canvas_height), fill='black'))
if mode == 'hide':
for line in grid_lst:
canvas.delete(line)
grid_lst.clear()
root = Tk()
canvas = Canvas(root, width=canvas_width, height=canvas_height, bg='grey')
canvas.pack()
Button(root, text='Show Grid', command=lambda: grid('show')).pack(side='left', fill='x', expand=True)
Button(root, text='Hide Grid', command=lambda: grid('hide')).pack(side='right', fill='x', expand=True)
root.mainloop()
This method uses just one function that has an argument which determines what to do with grid: either show or hide. It also uses variables in a way that changing them would also auto-adjust the grid: vertical, horizontal, canvas_width, canvas_height. And they do what they say. vertical determines the amount of vertical lines and horizontal determines the amount of horizontal lines so this is pretty adjustable and "dynamic".
EDIT: improved performance so that lines don't add up to the list if the grid('show') is called multiple times and so that the list gets cleared when grid('hide') is called.
I have found that time of the string colouring depends on how many different NSColors are used. In code below if I use only one colour for the three cases then the text colouring process is 3 times faster than in the case when three different colours are used for these three cases, each colour for each case. Why ? Is there a way not to slow down the colouring for three different colours ?
for i in 0..<arrayOfNSRangesForA.count
{
textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForA[i])
}
for i in 0..<arrayOfNSRangesForT.count
{
textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForT[i])
}
for i in 0..<arrayOfNSRangesForC.count
{
textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForC[i])
}
Update
I have found one more BAD thing. When I changed colouring from NSForegroundColorAttributeNameto NSBackgroundColorAttributeName the running time has increased significantly - 10 times. For 20 000 characters, it was for one colour, for NSForegroundColorAttributeName- 1 sec, for NSBackgroundColorAttributeName - 10 sec; if three colours - 3 and 30 sec accordingly. For me it is very bad feature of Swift !!! It is not possible to do normal work with DNA (ATGC sequence) colouring, since the length of DNA is thousands of A,T,G,C characters!
Update
In comments I have a suggestion to colour only visible part of text. I have tried this approach and it is much worse even for shorter text in comparison with what I did in standard way. So, I had NSRange of text for visible part of text, and did colouring on fly while scrolling by using notification when scrolling is on. It is a bad way.
The biggest obstacle is laying out all these attributed characters in the text view. Colorize the DNA sequence takes minimal amount of time. Instead of writing your own layout manager or text storage class, you can adopt a divide-and-conquer approach by colorizing the text view in chunks at a time:
#IBOutlet var textView: NSTextView!
var dnaSequence: String!
var attributedDNASequence: NSAttributedString!
#IBAction func colorize(_ sender: Any) {
self.dnaSequence = "ACGT" // your plaintext DNA sequence
self.attributedDNASequence = self.makeAttributedDNASequence()
// Rendering long string with the same attributes throughout is extremely fast
self.textView.textStorage?.setAttributedString(NSAttributedString(string: dnaSequence))
let step = 10_000 // colorize 10k characters at a time
let delay = 0.2 // delay between each render
for (i, location) in stride(from: 0, to: self.dnaSequence.characters.count, by: step).enumerated() {
let length = min(step, self.dnaSequence.characters.count - location)
let range = NSMakeRange(location, length)
// Since we are modifying the textStorage of a GUI object (NSTextView)
// we should do it on the main thread
DispatchQueue.main.asyncAfter(deadline: .now() + (delay * Double(i))) {
let subtext = self.attributedDNASequence.attributedSubstring(from: range)
print("Replacing text in range \(location) to \(location + length)")
self.textView.textStorage?.replaceCharacters(in: range, with: subtext)
}
}
}
// MARK: -
var colorA = NSColor.red
var colorC = NSColor.green
var colorG = NSColor.blue
var colorT = NSColor.black
func makeAttributedDNASequence() -> NSAttributedString {
let attributedText = NSMutableAttributedString(string: dnaSequence)
var index = dnaSequence.startIndex
var color: NSColor!
for i in 0..<dnaSequence.characters.count {
switch dnaSequence[index] {
case "A":
color = colorA
case "C":
color = colorC
case "G":
color = colorG
case "T":
color = colorT
default:
color = NSColor.black
}
attributedText.addAttribute(NSForegroundColorAttributeName, value: color, range: NSMakeRange(i,1))
index = dnaSequence.index(after: index)
}
return attributedText
}
The trick is to make the application as responsive as possible so the user is unaware that things are still being done in the background. With a small delay (<= 0.3 second) I couldn't scroll my mouse fast enough to reach the end of text view before everything has been colorized (100k characters).
On a 100k-character test, it took 0.7 seconds to until the colorized string first appeared inside the text view instead of the 7 seconds if I did everything at once.
Have you tried using a ciColor instead of an attribute? ciColors can be used with text, images and backgrounds.
You can try like this:
txtField.textColor?.ciColor.red
I have a beat em up game and a 'punch' sound is played on a timer if an enemy collides with my character as shown below:
local function hitSound()
local hsound = math.random(#sfx.hit)
audio.play(sfx.hit[hsound])
end
--------------------------------------------------CHARACTER COLLISION---------------
local function guyCollision(self, event)
if event.other.type == "enemy1"then
if event.phase == "began" then
hitTimer = timer.performWithDelay(500,hitSound,0)
event.other:setSequence("punch")
event.other:play()
end
if event.phase == "ended" then
timer.pause(hitTimer)
end
This works fine when my character is taking down just one enemy at a time but if there are more than one spawned enemy (which there usually is) fighting my character when i kill them the punching sound remains.
Also when i call audio.fadeout() when my character dies the punch sounds don't fade out with the other sounds/music :s
Someone suggested to me to assign the timer to the specific enemy in question using its index in its table but im unsure of how to do this... is this the best way? I just don't know how to get that enemys current index.. any help much appreciated!
Heres my enemy spawn code:
local spawnTable2 = {}
local function spawnEnemy()
enemy1 = display.newSprite( group, sheetE, sequenceData2 )
enemy1.x=math.random(100,1300)
enemy1.y=math.random(360,760)
enemy1.gravityScale = 0
enemy1:play()
enemy1.type="coin"
enemy1.objTable = spawnTable2
enemy1.index = #enemy1.objTable + 1
enemy1.myName = "enemy" .. enemy1.index
physics.addBody( enemy1, "kinematic",{ density = 1.0, friction = 0.3, bounce = 0.2 })
enemy1.isFixedRotation = true
enemy1.type = "enemy1"
enemy1.enterFrame = moveEnemy
Runtime:addEventListener("enterFrame",enemy1)
enemy1.objTable[enemy1.index] = enemy1
return enemy1
end
I think your problem is that your hitTimer variable is probably being overwritten and you can only cancel the last one. You could probably do: self.hitTimer to get around it.
Rob
So I tried to automate running in a game, where the map is huge, and I have to run miles. I wanted to toggle on the hotkey (Ctrl+Shift+A or something else) press the running (in the game, I can run with w).
I tried code, like:
Pause On
Loop
Send w
+^a::Pause
(it can press the w, but it can't release) and like this:
+^a::
toggle := !toggle
while toggle
Send {w down}
(same problem).
It's just my problem, or these codes are wrong?
I have a (at least i think) much simpler solution :)
#NoTrayIcon
ScrollLock::
Input, Key, ,{Enter}
Send, {%Key% Down}
return
You press ScrollLock (which I doubt you use for anything else, otherwise set it to a free key), and then enter the name of button to be held down.
If you want to hold down a single character, you just write it in.
For other keys you can find the names here: http://www.autohotkey.com/docs/KeyList.htm
Mouse: LButton for left, RButton for right and MButton for middle
You end the input with the Enter key, and after that the program will hold down the entered key.
If you want to "lift up" the key, just simply press it once, and it will be held down no more. :)
ps.:I have #NoTrayIcon, because I'm running it permanently in the background, but if you wanted to be able to exit then simply add something like this:
F12::
ExitApp
return
This is my stock function. I usualy map it to ^W or Q. Pressing w or s will cancel it. Easy peasy.
HoldW(){
SendInput {w up}{w down}
Loop
{
Sleep 100
GetKeyState state, w
if state = u
return
If GetKeyState("s")
{
SendInput {w up}
return
}
}
}
+^vk41:: ; shift+ctrl+a
SetTimer, % "SomeLable", % (bToggle:=!bToggle) ? 25:"Off"
KeyWait, % "vk41"
Return
SomeLable:
SendInput, % "{vk57}" ; w
Return
A silly noob example where F10 is the toggle hotkey, and the up/down state is a variable. The variable needs to be pre-declared to give the initial value.
To be honest I expected an error message, but it seemed to run fine.
keystate=down
F10::
Send {w %keystate%}
if keystate = down
SetEnv, keystate, up
else if keystate = up
SetEnv, keystate, down
return
Toggle := 1
Q::Send, % Toggle = 1 ? ( "0", Toggle := 0 ) : ( "9", Toggle := 1 )
Change Q to your preferred hotkey, and change "0" and "9" to the keys you want to toggle through. Make sure to set your abilities or weapons to the keys you replace in "0" and "9".
So, lets say I have a primary and secondary weapon. I bind them in game to 9 and 0.
I press Q to cycle between them for fast weapon switching. Or w/e else you want.
I am trying to update the list of items in one combobox2 depending on the item selected in another - combobox1.
For example, if the user selects a file.mp3 in combobox1 then combobox2 will display a list of audio extension (.aac, .wav, .wma). However, if the user selects a file.flv from combobox1, combobox2 will display a list of video extensions (.mpg, mp4, .avi, .mov).
I initially thought I could accomplish this with if statements. The initial selection works, but there after, if you continue to choose different files, the combobox2 is not updated. I tried using an Event, but it didn't work.
Below if a very stripped-down version of the code so that you can get the gist:
import wx
import os
import sys
import time
from wx.lib.delayedresult import startWorker
class udCombo(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'd-Converter', size=(500, 310))
panel = wx.Panel(self, wx.ID_ANY)#Creates a panel over the widget
toolbar = self.CreateToolBar()
toolbar.Realize()
font = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD)
font2 = wx.Font(7, wx.DECORATIVE, wx.NORMAL, wx.NORMAL)
directory = wx.StaticText(panel, -1, 'Path to media files: c:\\ffmpeg\\bin', (300, 13))
directory.SetFont(font2)
convertfile = wx.StaticText(panel, -1, 'File:', (270, 53))
convertfile.SetFont(font)
convertfile2 = wx.StaticText(panel, -1, 'Format:', (245, 83))
#Select Media
os.chdir("c:\\ffmpeg\\bin")
wrkdir = os.getcwd()
filelist = os.listdir(wrkdir)
self.formats1 = []
for filename in filelist:
(head, filename) = os.path.split(filename)
if filename.endswith(".avi") or filename.endswith(".mp4") or filename.endswith(".flv") or filename.endswith(".mov") or filename.endswith(".mpeg4") or filename.endswith(".mpeg") or filename.endswith(".mpg2") or filename.endswith(".wav") or filename.endswith(".mp3"):
self.formats1.append(filename)
self.format_combo1=wx.ComboBox(panel, size=(140, -1),value='Select Media', choices=self.formats1, style=wx.CB_DROPDOWN, pos=(300,50))
self.Bind(wx.EVT_COMBOBOX, self.fileFormats, self.format_combo1)
self.format_combo2=wx.ComboBox(panel, size=(100, -1),pos=(300,81))
self.Bind(wx.EVT_COMBOBOX, self.fileFormats, self.format_combo2)
def fileFormats(self, e):
myFormats = {'audio': ('.wav', '.wma', '.mp3'), 'video': ('.mpg', '.mp4', '.mpeg')}
bad_file = ['Media not supported']
myFile = self.format_combo1.GetValue()
f_exten = [x for x in myFormats['audio'] or myFormats['video'] if myFile.endswith(x)]
if f_exten[0] in myFormats['audio']:
self.format_combo2.SetItems(myFormats['audio'])
elif f_exten[0] in myFormats['video']:
self.format_combo2.SetItems(myFormats['video'])
else:
self.format_combo2.SetItems(bad_file)
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = udCombo()
frame.SetSizeHints(500,310,500,310)
frame.Show()
app.MainLoop()
Traceback error:
Traceback (most recent call last):
File "C:\Users\GVRSQA004\Desktop\udCombo.py", line 86, in fileFormats
if f_exten[0] in myFormats['audio']:
IndexError: list index out of range
Use a dictionary to hold the two lists. Then when the user clicks something in the first widget, you can call the second combobox's SetItems(myDict[selection]) method or something along those lines. The error message is because you're trying to do something with a CommandEvent that it doesn't support. They don't have an "rfind" attribute, for example.
EDIT: The new code the OP posted doesn't work because it's only running the list comprehension against the first half of the OR statement. It never runs against the "video" portion, so it returns an empty list if the user chooses anything with a video format extension. It WILL work if you select an audio selection.
Personally, I would recommend creating a video extension list and an audio list. That would be easier to understand in the future should you need to fix it later.