2024-01-10 14:48:52

Hello. We trying to integrate WXPython widgets onto a pygame window. Despite many resources that claim this is not doable on the internet, we didn't give up, and managed to get it working. The reason we want this is because I want to add advanced text editing commands like selecting, cutting, copying etc., to our game zero hour assault. Since writeing these from scratch is time-consuming, we decided to use native windows controls. However, this required to open a different window for the text control, but we want it to be in the same window. That's why we integrated WXPython widgets to my pygame window. My question is, my code works, but when the textbox appears in the window, nvda for some reason reads the pygame's window title and then reads the label of the textbox, like so:
Zero hour assault 1.0. Enter your username edit multiline
But since the window title doesn't change, it shouldn't read it, it should only say
Enter your username edit multiline
Similary, when I close the textbox, it again reads just the game window title, even if it doesn't change, like this.
Zero hour assault 1.0.
Normaly, it shouldn't say anything.
In summarily, when I open the textbox, it should only say the textbox title, and when I close it, it shouldn't say anything. How can I fix this issue? here's my code.
import wx
import ctypes
import pygame
class CustomTextInputDialog:
    def _init_(self, title):
        window_handle = int(pygame.display.get_wm_info()["window"])
        self.frame = wx.Frame(None) # since wxpython widgets require a parent as a wxpython object, I created this frame, It is actualy not shown on the screen.
        self.label = wx.StaticText(self.frame, label=title)
        self.text_ctrl = wx.TextCtrl(self.frame, style=wx.TE_MULTILINE, size=(1920,1080)) # I did the input fullscreen because nvda was reading 5-6 chars in each line if the line is longer when I press up and down arrow.
        self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
        ctypes.windll.user32.SetParent(self.text_ctrl.GetHandle(), window_handle)
        ctypes.windll.user32.SetParent(self.label.GetHandle(), window_handle)
    def on_key_down(self, event):
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_RETURN:
            if event.ShiftDown(): # I want new line with shift enter not enter, so that's why I wrote this. Enter submits input.
                current_pos = self.text_ctrl.GetInsertionPoint()
                text = self.text_ctrl.GetValue()
                new_text = text[:current_pos] + '\n' + text[current_pos:]
                self.text_ctrl.SetValue(new_text)
                self.text_ctrl.SetInsertionPoint(current_pos + 1)
            else:
                self.on_ok(None)
        elif keycode == wx.WXK_ESCAPE:
            self.on_cancel(None)
        else:
            event.Skip()

    def on_ok(self, event):
        self.entered_text = self.text_ctrl.GetValue()
        self.frame.Destroy()
        app.ExitMainLoop()

    def on_cancel(self, event):
        self.frame.Destroy()
        app.ExitMainLoop()
        self.entered_text = ""

app=wx.App(False)
def get_input(title):
    dialog = CustomTextInputDialog(title)
    timer = wx.Timer()



    def on_timer(event):
        dialog.text_ctrl.SetFocus() # I did this because when I alt  tab out of the window and reactivate the window, the focus wasn't getting to the text box, so this fixes it.
    timer.Bind(wx.EVT_TIMER, on_timer)
    timer.Start(1)

    app.MainLoop()
    return dialog.entered_text
There's also another issue I forgot to mention, when I alt tab out of the window and then reactivate it, the focus is in the input box, but nvda doesn't read anything at all. When I type, it reads the letters, but for example when I move with left and right arrows or select text, it doesn't read anything, but it actualy selects text, I tested. The way to fix this is to press the applications key to open the context menu and press escape to close it, and nvda works normaly. Or another way is to open nvda menu with nvda+n, and press escape. And for some reason this doesn't happen if i minimize the window with windows+m first, it only happens if i dirrectly use alt tab without minimizing it.
How can I fix these issues?

Everything about people can change, including their appearance, but the character inside a person never changes.
Regards...

Bilal

2024-01-10 18:29:00

I think the issue may have to do with the fact that you are creating a frame for the text input, and NVDA is taking it in an unusual way since this is an unusual way to do something like this. What I would personally do is use a dialog instead of a frame. That would probably fix your problem, then set the parent of the dialog to the window handle, then set the text control to have the dialog as its parent. Then you could also have an ok and cancel button in the dialog and do something with it.

Blindgoofball
Founder and lead developer at Nibble Nerds
Where gamers are united!
https://nibblenerds.com

2024-01-11 15:29:47

That frame has nothing to do with it, It is not shown either (I didn't call frame.Show()). It is just there because wx python objects require a parent, so first I give the frame to parent, and then set the parent to pygame window using ctypes. Even If I make a dialog, since i will not be showing it, it would be the same.
Also, it re-reads the pygame window's title, not frames. Frame doesn't have a title.
Also, I don't want ok and cancel buttons, my current methods already work (enter to submit the input and esc to close it). Also, If I set the dialogs parent to pygame window, it will still be a seperate window, I want the widget to be directly in the pygame window. Because If I put it inside a dialog, it would read the dialogs title.
Do you have an idea about the alt tab issue? It may fix if I make it so that the window gets minimized when user presses alt tab, because normaly it doesn't I think. But I don't know how to make that.

blindgoofball wrote:

I think the issue may have to do with the fact that you are creating a frame for the text input, and NVDA is taking it in an unusual way since this is an unusual way to do something like this. What I would personally do is use a dialog instead of a frame. That would probably fix your problem, then set the parent of the dialog to the window handle, then set the text control to have the dialog as its parent. Then you could also have an ok and cancel button in the dialog and do something with it.

Everything about people can change, including their appearance, but the character inside a person never changes.
Regards...

Bilal

2024-01-11 17:17:52

Wx and pygame were not meant to work with each other, so issues like this are not surprising, and I don't think there is much you can do about it. I would recommend showing the dialog with the text control and ok and cancel buttons, then your enter and escape functions will automatically work. Putting a wx widget into a pygame window I don't think is possible.

Blindgoofball
Founder and lead developer at Nibble Nerds
Where gamers are united!
https://nibblenerds.com

2024-01-13 00:02:52 (edited by ambro86 2024-01-13 00:06:59)

@4 Exactly. In fact, from the tests carried out, a good use is precisely to use wx.TextEntryDialog and change the title of the window when it appears, so that NVDA does not read it. Once closed, restore the game title.
Here's an example:

import wx
import ctypes
import pygame
import sys

def get_input(title, game_title):
    dialog = wx.TextEntryDialog(None, title, caption=game_title, style=wx.OK | wx.CANCEL)
    result = dialog.ShowModal()

    if result == wx.ID_OK:
        entered_text = dialog.GetValue()
    else:
        entered_text = ""

    dialog.Destroy()
    return entered_text

def main():
    pygame.init()
    pygame.display.set_mode((800, 600))

    game_title = "My Game"
    pygame.display.set_caption(game_title)  

    clock = pygame.time.Clock()

    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_i:
                    input_text = get_input("Enter your text:", "")
                    print("Entered text:", input_text)

        pygame.display.flip()
        clock.tick(60)

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    app = wx.App(False)
    main()

2024-01-13 14:00:50

I mean your players won't care if your game title is read twice or not. This is a small problem that does not impact the user experience. Maybe other screenerading software will not have this problem, for instance.

If you want to contact me, do not use the forum PM. I respond once a year or two, when I need to write a PM myself. I apologize for the inconvenience.
Telegram: Nuno69a
E-Mail: nuno69a (at) gmail (dot) com

2024-02-11 14:46:38

Another thing. Use static text. And if you are going to show something over the prior main window, for example, setting up an account, hide the original so you don't get two windows. Make sure you use ShowModal and not Show for this process.

-----
YouTube
GitHub
Discord: @tunmi13#1880