I am trying to write a script in python that would take control of Halo 5 forge in order to automatically create and script an in-game script brain object and use the game's built-in scripting system to script the object to randomize the movement of 2 agents (will be added later) in order to procedurally generate a map, saving me time in doing tedious manual work on creating a method from scratch I have repeated many times in Halo 5 Forge.
Basically I am trying to create a class representing this script brain object in python and under the init method it is supposed to follow all of the steps in Forge to create a script brain object.
At first I tried pyautogui as it initially worked previously on Halo 5. But now it doesn't seem to work. It is a known issue that pyautogui doesn't input certain things properly on directX games and was suggested pydirectinput as an alternative.
So I did and while the mouse responds properly in the game and opens the object menu using the press() method, it doesn't actually seem to respond to leftClick() for some reason, only repositioning the mouse to the correct position but not actually clicking the object menu like I had hoped.
Here is the code:
import os
import pyautogui as pygui
import pydirectinput as pydi
import time
class forgeObjectRandomizer:
def __init__(self):
#CREATE THE SCRIPT BRAIN
time.sleep(5)
screenWidth, screenHeight = pydi.size()
pydi.moveTo(round(screenWidth / 2), round(screenHeight / 2))
time.sleep(1)
# pydi.leftClick(1749, 44)
pydi.press('o')
time.sleep(0.5)
pydi.leftClick(1554, 337) # --- Extras
time.sleep(0.5)
pydi.leftClick(1605, 266) # --- Scripting
time.sleep(0.5)
pydi.leftClick(1630, 236) # --- Script Brain
time.sleep(0.5)
pydi.press('p') # --- Properties
time.sleep(0.5)
pydi.mouseDown(1886, 231) # --- Scroll down
pydi.moveTo(1886, 453) # --- Scroll down
pydi.mouseUp()
brainRandomizer = forgeObjectRandomizer()
You can use to move mouse
import win32api,win32con
def click():
win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(x), int(y), 15, 15)
for mouse click equivalent, but I only had problems with moving the mouse
This is kinda a blind shot since u said pyautogui was working fine before on halo 5...are you running the game as administrator? If so make sure that ur code does too, because i had same problem in a game named asda global
I'm somewhat new to both Python and Pygame, and trying to simply read MIDI controls live from a Windows computer. I've looked into Pygame and have set up what I think to be most of the right syntaxes, but the shell seems to crash or at least reset at the line pygame.midi.Input(input_id) every time.
import pygame.midi
import time
pygame.init()
pygame.midi.init()
print("The default input device number is " + str(pygame.midi.get_default_input_id()))
input_id = int(pygame.midi.get_default_input_id())
clock = pygame.time.Clock()
crashed = False
while (crashed == False):
print("input:")
pygame.midi.Input(input_id)
print(str(pygame.midi.Input.read(10)))
clock.tick(2)
I am also new to both Python and and Pygame.
I also tried for quite some time to get the midi input reading to work. In another forum I was pointed to an included sample file in the library. You can see the answer here.
Side note:
On my computer there are several Midi devices active. So it maybe would be a good idea to not rely on the default input id. You can get a list of your Midi devices like this:
import pygame
from pygame.locals import *
from pygame import midi
def printMIDIDeviceList():
for i in range(pygame.midi.get_count()):
print(pygame.midi.get_device_info(i), i)
TL;DR
I am fiddling with a Raspberry Pi 2 and a 2.8" TFT touch screen attached to the Pi's GPIO. The Pi is also connected to a HDMI monitor.
My issue is that my Python3 pygame script is not able to use the TFT screen, but always displays on my HDMI screen instead.
Some background
I've installed the latest vanilla Raspbian ready-to-use distro and followed the TFT screen installation steps, everything works well: the TFT can display the console and X without issue. The touchscreen is calibrated and moves the cursor correctly. I can also see a new framebuffer device as /dev/fb1.
I've tried the following to test this new device:
sudo fbi -T 2 -d /dev/fb1 -noverbose -a my_picture.jpg
=> This successfully displays the pic on the TFT screen
while true; do sudo cat /dev/urandom > /dev/fb1; sleep .01; done
=> This successfully displays statics on the TFT screen
However, when I run this Python3/pygame script, the result appears in the HDMI screen consistently and not on the TFT screen:
#!/usr/bin/python3
import os, pygame, time
def setSDLVariables():
print("Setting SDL variables...")
os.environ["SDL_FBDEV"] = "/dev/fb1"
os.environ["SDL_VIDEODRIVER"] = driver
print("...done")
def printSDLVariables():
print("Checking current env variables...")
print("SDL_VIDEODRIVER = {0}".format(os.getenv("SDL_VIDEODRIVER")))
print("SDL_FBDEV = {0}".format(os.getenv("SDL_FBDEV")))
def runHW5():
print("Running HW5...")
try:
pygame.init()
except pygame.error:
print("Driver '{0}' failed!".format(driver))
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
print("Detected screen size: {0}".format(size))
lcd = pygame.display.set_mode(size)
lcd.fill((10,50,100))
pygame.display.update()
time.sleep(sleepTime)
print("...done")
driver = 'fbcon'
sleepTime= 0.1
printSDLVariables()
setSDLVariables()
printSDLVariables()
runHW5()
The script above runs as follow:
pi#raspberrypi:~/Documents/Python_HW_GUI $ ./hw5-ThorPy-fb1.py
Checking current env variables...
SDL_VIDEODRIVER = None
SDL_FBDEV = None
Setting SDL variables...
...done
Checking current env variables...
SDL_VIDEODRIVER = fbcon
SDL_FBDEV = /dev/fb1
Running HW5...
Detected screen size: (1920, 1080)
...done
I have tried different drivers (fbcon, directfb, svgalib...) without success.
Any help or idea would be greatly appreciated, I've been through a lot of doc, manuals and samples and just ran out of leads :/ Furthermore, it appears that a lot of people have succeeded in getting Python3/pygame to output to their TFT screen via /dev/fb1.
I have been fiddling around that for far too many hours now, but at least I have found what I'd call a decent workaround, if not a solution.
TL;DR
I've kept using pygame for building my graphics/GUI, and switched to evdev for handling the TFT touch events. The reason for using evdev rather than pygame's built-in input management (or pymouse, or any other high level stuff) is explained in the next section.
In a nutshell, this program builds some graphics in memory (RAM, not graphic) using pygame, and pushes the built graphics as bytes into the TFT screen framebuffer directly. This bypasses any driver so it is virtually compatible with any screen accessible through a framebuffer, however it also bypasses any potential optimizations coming along what would be a good driver.
Here is a code sample that makes the magic happen:
#!/usr/bin/python3
##
# Prerequisites:
# A Touchscreen properly installed on your system:
# - a device to output to it, e.g. /dev/fb1
# - a device to get input from it, e.g. /dev/input/touchscreen
##
import pygame, time, evdev, select, math
# Very important: the exact pixel size of the TFT screen must be known so we can build graphics at this exact format
surfaceSize = (320, 240)
# Note that we don't instantiate any display!
pygame.init()
# The pygame surface we are going to draw onto.
# /!\ It must be the exact same size of the target display /!\
lcd = pygame.Surface(surfaceSize)
# This is the important bit
def refresh():
# We open the TFT screen's framebuffer as a binary file. Note that we will write bytes into it, hence the "wb" operator
f = open("/dev/fb1","wb")
# According to the TFT screen specs, it supports only 16bits pixels depth
# Pygame surfaces use 24bits pixels depth by default, but the surface itself provides a very handy method to convert it.
# once converted, we write the full byte buffer of the pygame surface into the TFT screen framebuffer like we would in a plain file:
f.write(lcd.convert(16,0).get_buffer())
# We can then close our access to the framebuffer
f.close()
time.sleep(0.1)
# Now we've got a function that can get the bytes from a pygame surface to the TFT framebuffer,
# we can use the usual pygame primitives to draw on our surface before calling the refresh function.
# Here we just blink the screen background in a few colors with the "Hello World!" text
pygame.font.init()
defaultFont = pygame.font.SysFont(None,30)
lcd.fill((255,0,0))
lcd.blit(defaultFont.render("Hello World!", False, (0, 0, 0)),(0, 0))
refresh()
lcd.fill((0, 255, 0))
lcd.blit(defaultFont.render("Hello World!", False, (0, 0, 0)),(0, 0))
refresh()
lcd.fill((0,0,255))
lcd.blit(defaultFont.render("Hello World!", False, (0, 0, 0)),(0, 0))
refresh()
lcd.fill((128, 128, 128))
lcd.blit(defaultFont.render("Hello World!", False, (0, 0, 0)),(0, 0))
refresh()
##
# Everything that follows is for handling the touchscreen touch events via evdev
##
# Used to map touch event from the screen hardware to the pygame surface pixels.
# (Those values have been found empirically, but I'm working on a simple interactive calibration tool
tftOrig = (3750, 180)
tftEnd = (150, 3750)
tftDelta = (tftEnd [0] - tftOrig [0], tftEnd [1] - tftOrig [1])
tftAbsDelta = (abs(tftEnd [0] - tftOrig [0]), abs(tftEnd [1] - tftOrig [1]))
# We use evdev to read events from our touchscreen
# (The device must exist and be properly installed for this to work)
touch = evdev.InputDevice('/dev/input/touchscreen')
# We make sure the events from the touchscreen will be handled only by this program
# (so the mouse pointer won't move on X when we touch the TFT screen)
touch.grab()
# Prints some info on how evdev sees our input device
print(touch)
# Even more info for curious people
#print(touch.capabilities())
# Here we convert the evdev "hardware" touch coordinates into pygame surface pixel coordinates
def getPixelsFromCoordinates(coords):
# TODO check divide by 0!
if tftDelta [0] < 0:
x = float(tftAbsDelta [0] - coords [0] + tftEnd [0]) / float(tftAbsDelta [0]) * float(surfaceSize [0])
else:
x = float(coords [0] - tftOrig [0]) / float(tftAbsDelta [0]) * float(surfaceSize [0])
if tftDelta [1] < 0:
y = float(tftAbsDelta [1] - coords [1] + tftEnd [1]) / float(tftAbsDelta [1]) * float(surfaceSize [1])
else:
y = float(coords [1] - tftOrig [1]) / float(tftAbsDelta [1]) * float(surfaceSize [1])
return (int(x), int(y))
# Was useful to see what pieces I would need from the evdev events
def printEvent(event):
print(evdev.categorize(event))
print("Value: {0}".format(event.value))
print("Type: {0}".format(event.type))
print("Code: {0}".format(event.code))
# This loop allows us to write red dots on the screen where we touch it
while True:
# TODO get the right ecodes instead of int
r,w,x = select.select([touch], [], [])
for event in touch.read():
if event.type == evdev.ecodes.EV_ABS:
if event.code == 1:
X = event.value
elif event.code == 0:
Y = event.value
elif event.type == evdev.ecodes.EV_KEY:
if event.code == 330 and event.value == 1:
printEvent(event)
p = getPixelsFromCoordinates((X, Y))
print("TFT: {0}:{1} | Pixels: {2}:{3}".format(X, Y, p [0], p [1]))
pygame.draw.circle(lcd, (255, 0, 0), p , 2, 2)
refresh()
exit()
More details
A quick recap on what I wanted to achieve: my goal is to display content onto a TFT display with the following constraints:
Be able to display another content on the HDMI display without interference (e.g. X on HDMI, the output of a graphical app on the TFT);
be able to use the touch capability of the TFT display for the benefit of the graphical app;
make sure the point above would not interfere with the mouse pointer on the HDMI display;
leverage Python and Pygame to keep it very easy to build whatever graphics/GUI I'd fancy;
keep a less-than-decent-but-sufficient-for-me framerate, e.g. 10 FPS.
Why not using pygame/SDL1.2.x as instructed in many forums and the adafruit TFT manual?
First, it doesn't work, at all. I have tried a gazillion versions of libsdl and its dependencies and they all failed consistently. I've tried forcing some libsdl versions downgrades, same with pygame version, just to try to get back to what the software was when my TFT screen was released (~2014). Then I aslo tried switching to C and handle SDL2 primitives directly.
Furthermore, SDL1.2 is getting old and I believe it is bad practice to build new code on top of old one. That said, I am still using pygame-1.9.4...
So why not SDL2? Well, they have stopped (or are about to stop) supporting framebuffers. I have not tried their alternative to framebuffers, EGL, as it got more complex the further I digged and it did not look too engaging (so old it felt like necro-browsing). Any fresh help or advice on that would be greatly appreciated BTW.
What about the touchscreen inputs?
All the high level solutions that work in a conventional context are embedding a display. I've tried pygame events, pymouse and a couple others that would not work in my case as I got rid of the notion of display on purpose. That's why I had to go back to a generic and low level solution, and the internet introduced my to evdev, see the commented code above for more details.
Any comment on the above would be greatly appreciated, these are my first step with Raspbian, Python and TFT screens, I reckon I most probably have missed some pretty obvious stuff along the way.
When I start my program that has to open pygame window, it opens it behind the current window. What have I to write to make a focus on pygame window and it won't be as background? This is for a game for mathematics revision so I need to use the python shell as well, in this example for (1+1).
This is the code I run. (just as an example)
import pygame
play = input("DO you want to play the game(y,n): ")
if play == "y":
pygame.init()
font = pygame.font.SysFont("None", 24, bold = False, italic = False )
Screen = pygame.display.set_mode((800, 600))
black = (0,0,0)
Screen.fill(black)
pygame.display.set_caption("Snake Game")
pygame.draw.rect(Screen, (218,123,255), ((0,0),(600,20)),0)
pygame.draw.rect(Screen, (0,197,243), ((0,20),(800, 580)), 16)
pygame.display.update()
#Play game for some time
Add = input("1 + 1 = ")
#Continue game on the pygame window
The only way to have the system position the pygame window in front of all the other ones is to not use the Python Shell.
Aside from doing that I do not think that there is any way to change how the system positions the window in pygame. I haven't found anything that changes whether it is positioned behind or in front of the current window. Correct me if I am wrong but I would guess that is the OS's job.
Thus, you must do everything in Pygame or else you will have the user switching between windows.
To fix this problem:
Remove all uses of the input function and use of the Python shell.
Implement a way to ask the mathematics questions in Pygame using font rendering
Implement a way to let the user type in an answer and enter it in. This can be done using font rendering and input handling.
I hope this answer helped you and if you have any further questions please feel free to post a comment below!
I'm on Windows, where the curses module is not native, so I am using the Windows curses module for python 3.2, found here.
My goal is to resize the terminal, which is currently at a small 25 lines x 80 columns size. First I tried the curses.resizeterm(lines, cols) command, which is apparently not found in the windows curses module (and hasattr(curses, 'resizeterm') returned false). So I look at the alternative module unicurses, which is also for windows, but that doesn't even have a resize command.
So I do more reading and learn about the environment variables 'LINES' and 'COLS' which, when set by os.environ, should resize the terminal. And they do, kind of. The terminal itself gets resized, but the Windows program displaying the terminal is still the same size as before, 25 x 80. I have confirmed that the two variables have indeed been changed, writing a little thing to display them in the top left corner. In addition, the box() function does draw a border around the screen as if it the variables were changed.
So, can anyone explain either 1) how to resize the "Windows window" to match the terminal or 2) how to get resizeterm() to work on my python installation? The relevant code of my program and a picture of how it looks are attached below.
import random, sys, math, curses, os
from curses import *
curses.use_env(True)
os.environ['LINES'] = "80"
os.environ['COLS'] = "60"
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
curses.start_color()
stdscr.keypad(1)
curses.curs_set(0)
LINES, COLS = stdscr.getmaxyx()
This code might help:
import curses
screen = curses.initscr()
# Check if screen was re-sized (True or False)
resize = curses.is_term_resized(y, x)
# Action in loop if resize is True:
if resize is True:
y, x = screen.getmaxyx()
screen.clear()
curses.resizeterm(y, x)
screen.refresh()