-
-
Notifications
You must be signed in to change notification settings - Fork 145
Fixed bug: Flickering UI on canvas #127 #1150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,113 +1,148 @@ | ||
| from typing import Optional, Callable | ||
| import logging | ||
|
|
||
| import wx | ||
| from wx.glcanvas import GLCanvas, GLAttributes, GLContext, GLContextAttrs | ||
| from OpenGL.GL import ( | ||
| GL_DEPTH_TEST, | ||
| glEnable, | ||
| GL_CULL_FACE, | ||
| glDepthFunc, | ||
| GL_LEQUAL, | ||
| GL_BLEND, | ||
| glBlendFunc, | ||
| GL_SRC_ALPHA, | ||
| GL_ONE_MINUS_SRC_ALPHA, | ||
| glGetString, | ||
| GL_VERSION, | ||
| ) | ||
| from OpenGL.GL.ARB.explicit_attrib_location import glInitExplicitAttribLocationARB | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| """OpenGL workflow: | ||
| The initialisation function should be as minimal as possible. No OpenGL functions should be called here. The OpenGL state is not valid until the window is first shown. | ||
| You can implement functions that take a while in threads to not block the GUI but they must still not contain OpenGL functions. | ||
| Upon the window being shown the OpenGL context is activated and the state can be set in _init_opengl | ||
| Objects that need to bind textures or data should do so in the draw function so they can be sure the context is set. | ||
| """ | ||
|
|
||
|
|
||
| class BaseCanvas(GLCanvas): | ||
| _context: Optional[GLContext] | ||
|
|
||
| def __init__(self, parent: wx.Window): | ||
| """ | ||
| Construct the canvas. | ||
| No OpenGL interaction should be done here. | ||
| OpenGL initialisation should be done in _init_opengl which is run after the window is first shown. | ||
| """ | ||
| display_attributes = GLAttributes() | ||
| display_attributes.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth( | ||
| 24 | ||
| ).EndList() | ||
| super().__init__( | ||
| parent, | ||
| display_attributes, | ||
| size=parent.GetClientSize(), | ||
| style=wx.WANTS_CHARS, | ||
| ) | ||
|
|
||
| # Amulet-Team/Amulet-Map-Editor#84 | ||
| # Amulet-Team/Amulet-Map-Editor#597 | ||
| # Amulet-Team/Amulet-Map-Editor#856 | ||
| def gl3() -> Optional[GLContext]: | ||
| ctx_attrs = GLContextAttrs() | ||
| ctx_attrs.PlatformDefaults() | ||
| ctx_attrs.OGLVersion(3, 3) | ||
| ctx_attrs.CoreProfile() | ||
| ctx_attrs.EndList() | ||
| ctx = GLContext(self, ctxAttrs=ctx_attrs) | ||
| if ctx.IsOK(): | ||
| return ctx | ||
| return None | ||
|
|
||
| def gl2() -> Optional[GLContext]: | ||
| ctx_attrs = GLContextAttrs() | ||
| ctx_attrs.PlatformDefaults() | ||
| ctx_attrs.OGLVersion(2, 1) | ||
| ctx_attrs.CompatibilityProfile() | ||
| ctx_attrs.EndList() | ||
| ctx = GLContext(self, ctxAttrs=ctx_attrs) | ||
| if ctx.IsOK() and glInitExplicitAttribLocationARB(): | ||
| return ctx | ||
| return None | ||
|
|
||
| context_constructors: list[Callable[[], Optional[GLContext]]] = [gl3, gl2] | ||
| context = next((constructor() for constructor in context_constructors), None) | ||
| if context is None: | ||
| raise Exception(f"Failed setting up context") | ||
|
|
||
| self._context = context | ||
| self._init = False | ||
|
|
||
| self.Bind(wx.EVT_SHOW, self._on_show) | ||
|
|
||
| @property | ||
| def context(self) -> GLContext: | ||
| return self._context | ||
|
|
||
| @property | ||
| def context_identifier(self) -> str: | ||
| # if not self._init: | ||
| # raise Exception("Cannot access the context until the window has been shown.") | ||
| return str(id(self._context)) | ||
|
|
||
| def _on_show(self, evt: wx.ShowEvent): | ||
| if not self._init and evt.IsShown(): | ||
| self._init = True | ||
| self._init_opengl() | ||
|
|
||
| def _init_opengl(self): | ||
| """Set up the OpenGL state after the window is first shown.""" | ||
| self.SetCurrent(self._context) | ||
| gl_version = glGetString(GL_VERSION) | ||
| if isinstance(gl_version, bytes): | ||
| gl_version = gl_version.decode("utf-8") | ||
| log.info(f"OpenGL Version {gl_version}") | ||
| glEnable(GL_DEPTH_TEST) | ||
| glEnable(GL_CULL_FACE) | ||
| glDepthFunc(GL_LEQUAL) | ||
| glEnable(GL_BLEND) | ||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) | ||
| from typing import Optional, Callable | ||
| import logging | ||
|
|
||
| import sys | ||
| import wx | ||
| from wx.glcanvas import GLCanvas, GLAttributes, GLContext, GLContextAttrs | ||
| from OpenGL.GL import ( | ||
| GL_DEPTH_TEST, | ||
| glEnable, | ||
| GL_CULL_FACE, | ||
| glDepthFunc, | ||
| GL_LEQUAL, | ||
| GL_BLEND, | ||
| glBlendFunc, | ||
| GL_SRC_ALPHA, | ||
| GL_ONE_MINUS_SRC_ALPHA, | ||
| glGetString, | ||
| GL_VERSION, | ||
| ) | ||
| from OpenGL.GL.ARB.explicit_attrib_location import glInitExplicitAttribLocationARB | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| """OpenGL workflow: | ||
| The initialisation function should be as minimal as possible. No OpenGL functions should be called here. The OpenGL state is not valid until the window is first shown. | ||
| You can implement functions that take a while in threads to not block the GUI but they must still not contain OpenGL functions. | ||
| Upon the window being shown the OpenGL context is activated and the state can be set in _init_opengl | ||
| Objects that need to bind textures or data should do so in the draw function so they can be sure the context is set. | ||
| """ | ||
|
|
||
| if sys.platform == "linux": | ||
| Canvas_Type = wx.Window | ||
| else: | ||
| Canvas_Type = GLCanvas | ||
|
|
||
| class BaseCanvas(Canvas_Type): | ||
| _context: Optional[GLContext] | ||
|
|
||
| def __init__(self, parent: wx.Window): | ||
| """ | ||
| Construct the canvas. | ||
| No OpenGL interaction should be done here. | ||
| OpenGL initialisation should be done in _init_opengl which is run after the window is first shown. | ||
| """ | ||
| display_attributes = GLAttributes() | ||
| display_attributes.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth( | ||
| 24 | ||
| ).EndList() | ||
| if Canvas_Type == wx.Window: | ||
| super().__init__(parent) | ||
| # self.SetSizer(self._canvas_sizer) | ||
| self._opengl_canvas = GLCanvas( self, | ||
| display_attributes, | ||
| size=parent.GetClientSize(), | ||
| style=wx.WANTS_CHARS, | ||
| ) | ||
|
|
||
| def forward_event(event): | ||
| # Create a new mouse event and send it to the parent | ||
| new_event = wx.MouseEvent(event.GetEventType()) | ||
| new_event.SetPosition(event.GetPosition()) | ||
| wx.PostEvent(self, new_event) | ||
| event.Skip() # Continue processing normally | ||
| self._opengl_canvas.Bind(wx.EVT_RIGHT_DOWN, forward_event) | ||
| self._opengl_canvas.Bind(wx.EVT_RIGHT_UP, forward_event) | ||
| self._opengl_canvas.Bind(wx.EVT_LEFT_DOWN, forward_event) | ||
| self._opengl_canvas.Bind(wx.EVT_LEFT_UP, forward_event) | ||
| self._opengl_canvas.Bind(wx.EVT_MOTION, forward_event) | ||
| self._opengl_canvas.Bind(wx.EVT_MOUSEWHEEL, forward_event) | ||
| self._opengl_canvas.Bind(wx.EVT_SIZING, self.resize) | ||
| elif Canvas_Type == GLCanvas: | ||
| super().__init__( | ||
| parent, | ||
| display_attributes, | ||
| size=parent.GetClientSize(), | ||
| style=wx.WANTS_CHARS, | ||
| ) | ||
| self._opengl_canvas = self | ||
| else: | ||
| raise NotImplementedError | ||
|
|
||
| # Amulet-Team/Amulet-Map-Editor#84 | ||
| # Amulet-Team/Amulet-Map-Editor#597 | ||
| # Amulet-Team/Amulet-Map-Editor#856 | ||
| def gl3() -> Optional[GLContext]: | ||
| ctx_attrs = GLContextAttrs() | ||
| ctx_attrs.PlatformDefaults() | ||
| ctx_attrs.OGLVersion(3, 3) | ||
| ctx_attrs.CoreProfile() | ||
| ctx_attrs.EndList() | ||
| ctx = GLContext(self._opengl_canvas, ctxAttrs=ctx_attrs) | ||
| if ctx.IsOK(): | ||
| return ctx | ||
| return None | ||
|
|
||
| def gl2() -> Optional[GLContext]: | ||
| ctx_attrs = GLContextAttrs() | ||
| ctx_attrs.PlatformDefaults() | ||
| ctx_attrs.OGLVersion(2, 1) | ||
| ctx_attrs.CompatibilityProfile() | ||
| ctx_attrs.EndList() | ||
| ctx = GLContext(self._opengl_canvas, ctxAttrs=ctx_attrs) | ||
| if ctx.IsOK() and glInitExplicitAttribLocationARB(): | ||
| return ctx | ||
| return None | ||
|
|
||
| context_constructors: list[Callable[[], Optional[GLContext]]] = [gl3, gl2] | ||
| context = next((constructor() for constructor in context_constructors), None) | ||
| if context is None: | ||
| raise Exception(f"Failed setting up context") | ||
|
|
||
| self._context = context | ||
| self._init = False | ||
|
|
||
| self.Bind(wx.EVT_SHOW, self._on_show) | ||
|
|
||
| def resize(self, event): | ||
| self._opengl_canvas.SetCurrent(self._context) | ||
| glViewport(0, 0, event.GetSize().x, event.GetSize().y) | ||
|
|
||
| @property | ||
| def context(self) -> GLContext: | ||
| return self._context | ||
|
|
||
| @property | ||
| def context_identifier(self) -> str: | ||
| # if not self._init: | ||
| # raise Exception("Cannot access the context until the window has been shown.") | ||
| return str(id(self._context)) | ||
|
|
||
| def _on_show(self, evt: wx.ShowEvent): | ||
| if not self._init and evt.IsShown(): | ||
| self._init = True | ||
| self._init_opengl() | ||
|
|
||
| def _init_opengl(self): | ||
| """Set up the OpenGL state after the window is first shown.""" | ||
| self._opengl_canvas.SetCurrent(self._context) | ||
| gl_version = glGetString(GL_VERSION) | ||
| if isinstance(gl_version, bytes): | ||
| gl_version = gl_version.decode("utf-8") | ||
| log.info(f"OpenGL Version {gl_version}") | ||
| glEnable(GL_DEPTH_TEST) | ||
| glEnable(GL_CULL_FACE) | ||
| glDepthFunc(GL_LEQUAL) | ||
| glEnable(GL_BLEND) | ||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import logging | ||
| import warnings | ||
| import wx | ||
| import sys | ||
| from typing import Callable, TYPE_CHECKING, Any, Generator, Optional | ||
| from types import GeneratorType | ||
| from threading import RLock, Thread | ||
|
|
@@ -163,8 +164,18 @@ def __init__(self, parent: wx.Window, world: "BaseLevel"): | |
|
|
||
| def _init_opengl(self): | ||
| super()._init_opengl() | ||
| self._file_panel = FilePanel(self) | ||
| self._canvas_sizer.Add(self._file_panel, 0, wx.EXPAND, 0) | ||
| if sys.platform == "linux": | ||
| self.panel = wx.Panel(self, style=wx.STAY_ON_TOP) | ||
| self.button_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
| self.button_sizer.AddStretchSpacer(1) | ||
| self.button_sizer.Add(self.panel, 0, wx.EXPAND, 0) | ||
| self._file_panel = FilePanel(self, self.panel) | ||
| self.panel.SetSizer(self._file_panel) | ||
| self.panel.SetBackgroundColour((155, 178, 216, 255)) | ||
| self._canvas_sizer.Add(self.button_sizer, 0, wx.EXPAND, 0) | ||
| else: | ||
| self._file_panel = FilePanel(self, self) | ||
| self._canvas_sizer.Add(self._file_panel, 0, wx.EXPAND, 0) | ||
|
Comment on lines
+167
to
+178
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This issue exists on macos as well. Your check only applies the custom behaviour to linux.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer this as well in the long run, but this was a quick way to keep the same windows behavior while making the linux side usable again. The 2 switches on sys.platform I had to use were:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay I understand |
||
| self._tool_sizer = ToolManagerSizer(self) | ||
| self._canvas_sizer.Add(self._tool_sizer, 1, wx.EXPAND, 0) | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.