I'm attempting to use the SetLayeredWindowAttributes function to change the windows transparency color. I made a structure using the ctypes module. I'm pretty sure I have to use the COLORREF RGB macro to get this to work properly.
How do I use macros on a structure made using ctypes?
What I have going.
import Tkinter as tk
import win32gui
import win32con
class ColorRef (ctypes.Structure) :
_fields_ = [("byRed", ctypes.c_byte),
("byGreen", ctypes.c_byte),
("byBlue", ctypes.c_byte)]
# makes a Tkinter window
root = tk.Tk()
# a handle to that window
handle = int(root.wm_frame(), 0)
# a COLORRED struct
colorref = ColorRef(1, 1, 1)
# attempting to change the transparency color
win32gui.SetLayeredWindowAttributes(handle, colorref, 0, win32con.LWA_COLORKEY)
root.mainloop()
Three things:
C preprocessor macros don't exist outside C code. They are textually expanded before the actual compilation takes place.
COLORREF is a typedef to DWORD, not a structure.
All RGB macro does is some bitshifting to get 0x00bbggrr value.
So the code would look like this:
def RGB(r, g, b):
r = r & 0xFF
g = g & 0xFF
b = b & 0xFF
return (b << 16) | (g << 8) | r
colour = RGB(1, 1, 1)
win32gui.SetLayeredWindowAttributes(handle, colour, 0, win32con.LWA_COLORKEY)
Related
Im having problems with blitting images to rect objects in pygame. i have a background image blitted to my main pygame window, and also an image blitted to a rect object on the screen which moves. the problem i am having is the rect object is overlapping my background image when its moving around. i was looking to only be able to see the green helicopter shape and not the black outline around it. sorry if i havent explained this very well. will try to include all files im using.
Thanks for any help
import pygame as pg
import random as r
import time
pg.init()
MAX_X = 1190
MAX_Y = 590
MIN_X = 10
MIN_Y = 10
SIZE = 100
SPEED = 1
COLOR = (0,255,0)
move_amount = 0
wn = pg.display.set_mode((1200, 600))
BG_IMG = pg.image.load('bg.png').convert()
BG_IMG = pg.transform.scale(BG_IMG, (1200, 600))
class Wall (pg.Rect):
def __init__(self, posX, posY):
self.xcor = posX
self.ycor = posY
self.rect = None
class Heli (pg.Rect):
def __init__(self, posX, posY):
self.image = pg.image.load('art.png').convert()
self.rect = self.image.get_rect()
self.xcor = posX
self.ycor = posY
# top and bottom constant walls
TOP = pg.Rect(MIN_X, MIN_Y, MAX_X, 3)
BOTTOM = pg.Rect(MIN_X, MAX_Y, MAX_X, 3)
heli = Heli(MIN_X, MAX_Y //2)
# keep moving walls in a list
moving_walls = [Wall(MAX_X, r.randint((MIN_Y + 10), (MAX_Y - 10)))]
# main loop
while True:
# fill screen
wn.fill('black')
# editing objects to move
# blitting must happen before everything else
pg.draw.rect(wn,COLOR, heli.rect)
wn.blit(BG_IMG, (0,0))
wn.blit(heli.image, heli.rect)
heli.rect.y += move_amount
heli.rect.y += 1
# use a variable to control how much movement is happening
# movement happens continuosly
# if key down it oves if key up it doesnt
for wall in moving_walls :
wall.rect = pg.Rect(wall.xcor, wall.ycor, 3, SIZE)
pg.draw.rect(wn, COLOR, wall.rect)
wall.xcor -= SPEED
if wall.xcor < MIN_X + 10:
wall.xcor = MAX_X
wall.ycor = r.randint((MIN_Y), (MAX_Y - SIZE))
# drawing all objects back to the screen
pg.draw.rect(wn, COLOR, TOP)
pg.draw.rect(wn, COLOR, BOTTOM)
# update window
pg.display.update()
# event handling
for ev in pg.event.get():
if ev.type == pg.KEYDOWN:
if ev.key == pg.K_UP:
move_amount = -3
if ev.type == pg.KEYUP:
move_amount = 0
if ev.type == pg.QUIT:
pg.quit()
time.sleep(0.01)
You discard the transparency information of the image. You have to use convert_alpha instead of convert:
self.image = pg.image.load('art.png').convert()
self.image = pg.image.load('art.png').convert_alpha()
The pygame documentation notes that:
The returned Surface will contain the same color format, colorkey and alpha transparency as the file it came from. You will often want to call convert() with no arguments, to create a copy that will draw more quickly on the screen.
For alpha transparency, like in .png images, use the convert_alpha() method after loading so that the image has per pixel transparency.
See also How can I make an Image with a transparent Backround in Pygame?
I noticed when I try to run BitBlt, the resulting data buffer is unexpected in two ways:
It is flipped along the y axis (the origin seems to be bottom left instead of top left)
In each RGBA grouping, the R and B values seem to be switched.
For the first issue, I noticed it when testing with my command prompt; if my command prompt was in the upper left portion of the screen, it would only say it was black when my cursor was in the lower left portion. I had to fix the inversion of the y axis by changing int offset = (y * monitor_width + x) * 4; to int offset = ((monitor_height - 1 - y) * monitor_width + x) * 4; this fixed the pixel location issue because it was showing black where I expected black.
However, the colors were still strong. I tested by trying to get the color of known pixels. I noticed every blue pixel had a very high R value and every red pixel had a very high blue value. That's when I compared with an existing tool I had and found out that the red and blue values seem to be switched in every pixel. At first I thought it was backwards or a byte alignment issue, but I also verified in a clustering of pixels that aren't uniform to make sure it's picking the right position of pixel, and it did perfectly well, just with the colors switched.
Full simplified code below (originally my tool was getting my cursor position and printing the pixel color via hotkey press; this is a simplified version that gets one specific point).
BYTE* my_pixel_data;
HDC hScreenDC = GetDC(GetDesktopWindow());
int BitsPerPixel = GetDeviceCaps(hScreenDC, BITSPIXEL);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int monitor_width = GetSystemMetrics(SM_CXSCREEN);
int monitor_height = GetSystemMetrics(SM_CYSCREEN);
std::cout << std::format("monitor width height: {}, {}\n", monitor_width, monitor_height);
BITMAPINFO info;
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
info.bmiHeader.biWidth = monitor_width; // client_width;
info.bmiHeader.biHeight = monitor_height; // client_height;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = BitsPerPixel;
info.bmiHeader.biCompression = BI_RGB;
HBITMAP hbitmap = CreateDIBSection(hMemoryDC, &info, DIB_RGB_COLORS, (void**)&my_pixel_data, 0, 0);
SelectObject(hMemoryDC, hbitmap);
BitBlt(hMemoryDC, 0, 0, monitor_width, monitor_height, hScreenDC, 0, 0, SRCCOPY);
int x = 12, y = 12;
int offset = ((monitor_height - 1 - y) * monitor_width + x) * 4;
std::cout << std::format("debug: ({}, {}): ({}, {}, {})\n", x, y, (int)my_pixel_data[offset], (int)my_pixel_data[offset + 1], (int)my_pixel_data[offset + 2], (int)my_pixel_data[offset + 3]);
system("pause");
The output of this will be debug: (12, 12): (199, 76, 133) even though another program has verified the colors are actually (133, 76, 199).
I can easily fix this in my code by flipping the y axis and switching each R and B value and the program will work perfectly well. However, I am just baffled by how this happened and whether there's a more elegant fix.
I can answer the RGB (and it looks like Hans answered the inverted Y axis in a comment). Remember that RGB is stored 0xAARRGGBB, so in that 32 bit value BB is byte 0, GG is byte 1, and RR is byte 2 (alpha is byte 3 if you use it), so when you index in at +0, +1 and +2 you're actually getting the values correctly. When we say RGB we're saying the colors in opposite order of how they're stored in memory.
Given a toy struct with a default constructor like this one:
struct RGB {
unsigned char r, g, b;
RGB()
:r(0), g(0), b(0) {}
};
How do I initialise one to a specific colour, assuming I don't have access to the source code to add my own constructor.
I don't understand fully why these don't work:
// OK, I can sort-of accept this one
RGB red = {255, 0, 0};
// Not shorthand for green.r=0, green.g=255, green.b=0;?
RGB green = {.r = 0, .g = 255, .b = 0};
// I seem to be missing a constructor that accepts a list?
RGB blue{0, 0, 255};
Is there any other C++11 way to shorten the good old-fashioned:
RGB yellow;
yellow.r = 255;
yellow.g = 255;
yellow.b = 0;
Furthermore, how could I minimally modify the struct declaration to support any of the above, as well as having a default initialisation method.
If you have no possibility to add default arguments to the struct's constructor, how about a helper function:
RGB makeRGB(unsigned char r, unsigned char g, unsigned char b)
{
RGB result;
result.r = r;
result.g = g;
result.b = b;
return result;
}
Which can be used like so:
RGB red = makeRGB(255, 0, 0);
Return value optimization will take care of the temporary and provide a no-overhead solution unless you are using a terrible compiler.
The ideal solution would be modifying the default constructor to take optional arguments:
struct RGB {
unsigned char r, g, b;
explicit RGB(unsigned char r, unsigned char g, unsigned char b)
:r(r), g(g), b(b) {}
RGB() : RGB(0, 0, 0) {}
};
Which can be used like you would expect:
RGB red(255, 0, 0);
RGB green{0, 255, 0};
RGB blue;
blue.b = 255;
Live demo here.
Given
struct color
{
color(std::initializer_list<float> list) = delete;
float r, g, b;
};
color c = {1,2,3} and color c {1,2,3} are the same.
syntax like color c = {.r = 0, .g = 255, .b = 0}; is specific to C programming language, not C++.
color c = {1,2,3}; is perfectly fine in C++11. It is called aggregate initialization. And it does even override explicit constructor from initializer_list. Link describes how to explicitly delete it.
Constructor from initializer_list can be explicitly called by color c({1,2,3})
I have to create a square around my pointer on the canvas. And I want that square to follow my pointer as I move it around.
from tkinter import *
root = Tk()
f = Frame(root)
f.pack()
c = Canvas(f,bg = "black")
while root:
x = c.winfo_pointerx()
y = c.winfo_pointery()
c.create_rectangle(x,y,(x+10),(y+10),fill = "red")
root.mainloop()
root.mainloop()
Now when I run this the rectangle doesn't load.
Your method won't work, because once you call mainloop, it waits for the window to close, so it will never get past the first iteration of the loop. And if you remove the mainloop from the loop, it will never reach the mainloop after the (infinite) loop.
The proper way to do this is to use callback events. Also, you should move the rectangle, instead of creating a bunch of new ones. Try something like this:
def callback(event):
x, y = event.x, event.y
c.coords(rect, x - 10, y - 10, x + 10, y + 10)
root = Tk()
c = Canvas(root)
rect = c.create_rectangle(0, 0, 0, 0)
c.bind('<Motion>', callback)
c.pack()
root.mainloop()
Is there a simple complete code example using any gui toolkit (that will work in both Linux and Windows) of opening multiple opengl windows simultaneously? And how to handle their events and such separately of course. I tried it naively and it crashes.
I received a full working source code example from someone outside of stackoverflow. I'm pasting it here for all to benefit.
module Main where
import Graphics.UI.GLUT
import System.Exit (exitWith, ExitCode(ExitSuccess))
reshape :: ReshapeCallback
reshape size = do
viewport $= (Position 0 0, size)
matrixMode $= Projection
loadIdentity
frustum (-1) 1 (-1) 1 1.5 20
matrixMode $= Modelview 0
keyboard :: KeyboardMouseCallback
keyboard (Char '\27') Down _ _ = exitWith ExitSuccess
keyboard _ _ _ _ = return ()
renderCube :: Color3 GLfloat -> IO ()
renderCube c = do
clear [ ColorBuffer ]
let color3f = color :: Color3 GLfloat -> IO ()
scalef = scale :: GLfloat -> GLfloat -> GLfloat -> IO ()
color3f c
loadIdentity
lookAt (Vertex3 0 0 5) (Vertex3 0 0 0) (Vector3 0 1 0)
scalef 1 2 1
renderObject Wireframe (Cube 1)
flush
displayR :: DisplayCallback
displayR = renderCube (Color3 1 0 0)
displayB :: DisplayCallback
displayB = renderCube (Color3 0 0 1)
createWindowWithDisplayFunc :: String -> Position -> DisplayCallback -> IO Window
createWindowWithDisplayFunc name pos display = do
win <- createWindow name
windowPosition $= pos
clearColor $= Color4 0 0 0 0
shadeModel $= Flat
displayCallback $= display
reshapeCallback $= Just reshape
keyboardMouseCallback $= Just keyboard
return win
main = do
getArgsAndInitialize
initialDisplayMode $= [ SingleBuffered, RGBMode ]
initialWindowSize $= Size 100 100
initialWindowPosition $= Position 100 100
createWindowWithDisplayFunc "R" (Position 10 10) displayR
createWindowWithDisplayFunc "B" (Position 110 10) displayB
mainLoop
GLUT, of course.
The GLUT homepage states
The toolkit supports:
- Multiple windows for OpenGL rendering
- Callback driven event processing
- Sophisticated input devices
- An 'idle' routine and timers
- A simple, cascading pop-up menu facility
- Utility routines to generate various solid and wire frame objects
- Support for bitmap and stroke fonts
- Miscellaneous window management functions
Hence you can use GLUT for managing multiple windows (I had used once). Here is a tutorial for what you need.
I've also found this article which you may look a little, since it's Haskell specific.
OpenGL support in wxWidgets uses the WxGLCanvas class, which is in wxcore as GLCanvas. Unfortunately, it doesn't seem to exist in the wx package. You can probably implement your own control for GLCanvas without too much difficulty, using the other controls in the wx package and C++ usage examples as a reference.