Skip to content

Commit

Permalink
Impl layout (#68)
Browse files Browse the repository at this point in the history
* Disable existing main window

* Refactored main_window2

* Catch errors from the toolbar

* Exposed some attributes

* Updated home page

Modified home page to work with the new window system

* Moved the toolbar class

This is specific to the main window so it should be here

* Reformatted

* Refactored the SVG icons to take full paths

The svg icons were limited to only tablericons.
They now need the full path to the file

* Added a name to the home page

This should probably return a translation key

* Refactored the toolbar and main window classes

Refactored the toolbar to allow changing the name, icon and other attributes after creation.
Added support for sticky buttons.
Added support to click the button in code.

* Added the 3D view to the tab engine

* Improved stderr output

* Added another exception logging class

* Improved error handling in the renderer

* Modified home plugin slightly

* Added a thread_manager plugin

If a thread is not stopped before the application exits it will crash the program.
Calling quit and then wait for each thread is the general suggestion but this means the program needs to wait for each thread to finish before quitting the next thread.
This plugin automatically quits all threads and then waits for all threads which means that all the threads can finish in parallel.

* level_geometry now uses thread plugin

* Fixed circular reference bug

* Fixed camera signals

If the camera position or rotation were set to all zeros the signals were not emitted.
The internal storage is now None to start so that any initial value will emit the signals.
The properties default to zeros if they have not been set yet.

* Removed context resetting

The create and destroy vbo methods used to revert the context but this caused problems if the context had been destroyed.

* Added missing context reset

* Fixed context issues

Set the camera location after the opengl context has been initialised so that chunks are only created after the context exists.

* Reformatted

* Removed frame from main window

* Fixed circular reference

* Simplified widget removal

All that is required here is deleteLater

* Delete the sub window when it is closed

The window is kept alive somewhere so we need to explicitly kill it.

* Fixed some OpenGL issues with the window system

The OpenGL destruction was not being run which caused Qt to do a very slow teardown.
OpenGL destruction is now run when the widget is hidden because this is the only way I have found to do it when the widget may be destroyed before the containing window.
This also means that we must do OpenGL initialisation in the showEvent to set it back up after it gets hidden.
Fixed a number of crashes in a number of different situations.

* Added a QOpenGLContext C++ extension

The QOpenGLContext implementation does not allow access to the context after the destructor has started.
This means that the context cannot be made current from within aboutToBeDestroyed rendering it worthless.
This adds a Cython wrapper for the C++ context instance so that it can be made current after destruction has started.

* Refactored the renderer to fix some issues

* Refactored renderer to not need Cython class

I worked out how to make the renderer work without the Cython class.
This is preferable because I do not need to work out how to compile the code for each platform.

* Hide the widget before destroying it

This is required to force the hideEvent so that QOpenGLWidgets can destroy their context data.

* Fixed warning when closing window

The only the data actually needs to be destroyed

* Cleaned up imports

* Remove pyside6 from setup.py

This is no longer needed
  • Loading branch information
gentlegiantJGC authored May 7, 2024
1 parent c787e3a commit a7eeea0
Show file tree
Hide file tree
Showing 53 changed files with 1,345 additions and 543 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import List, Tuple
from setuptools import setup, find_packages
from setuptools import setup
from wheel.bdist_wheel import bdist_wheel
from Cython.Build import cythonize
import glob
Expand Down
5 changes: 3 additions & 2 deletions src/amulet_editor/application/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ def __init__(self, logger: Callable[[str], None]) -> None:
super().__init__(log_file) # type: ignore
self._logger = logger

def write(self, msg: str) -> int:
if msg != "\n":
def write(self, msg) -> int:
msg = msg.rstrip()
if msg:
self._logger(msg)
return len(msg)
return 0
Expand Down
12 changes: 8 additions & 4 deletions src/amulet_editor/models/widgets/_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,21 @@ def paintEvent(self, event: QPaintEvent):
class AIconButton(QPushButton):
"""A QPushButton containing a stylable icon."""

def __init__(self, icon_name: str = "question-mark.svg", parent: QWidget = None):
def __init__(
self,
icon_path: str = build.get_resource("icons/tabler/question-mark.svg"),
parent: QWidget = None,
):
super().__init__(parent)
self.setProperty("hover", "false")
self._layout = QVBoxLayout(self)
self._layout.setContentsMargins(0, 0, 0, 0)
self._layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._icon = AStylableSvgWidget(build.get_resource(f"icons/tabler/{icon_name}"))
self._icon = AStylableSvgWidget(icon_path)
self._layout.addWidget(self._icon)

def setIcon(self, icon_name: Optional[str] = None):
self._icon.load(build.get_resource(f"icons/tabler/{icon_name}"))
def setIcon(self, icon_path: Optional[str] = None):
self._icon.load(icon_path)

def setIconSize(self, size: QSize):
self._icon.setFixedSize(size)
Expand Down
129 changes: 0 additions & 129 deletions src/amulet_editor/models/widgets/_toolbar.py

This file was deleted.

14 changes: 12 additions & 2 deletions src/amulet_editor/models/widgets/traceback_dialog/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
from ._api import display_exception, display_exception_blocking, DisplayException
from ._api import (
display_exception,
display_exception_blocking,
DisplayException,
CatchException,
)

__all__ = ["display_exception", "display_exception_blocking", "DisplayException"]
__all__ = [
"display_exception",
"display_exception_blocking",
"DisplayException",
"CatchException",
]
22 changes: 22 additions & 0 deletions src/amulet_editor/models/widgets/traceback_dialog/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from amulet_editor.application._invoke import invoke
from ._cls import _AmuletTracebackDialog

main_logger = logging.getLogger()


def display_exception_blocking(title: str = "", error: str = "", traceback: str = ""):
"""
Expand Down Expand Up @@ -59,3 +61,23 @@ def __exit__(self, exc_type, exc_val, exc_tb):
)
return self._suppress
return False


class CatchException:
"""
A context manager class to suppress an exception and display the traceback dialog.
It will also log the exception to the logging module.
"""

def __enter__(self):
pass

def __exit__(self, _, exc_val, exc_tb):
if isinstance(exc_val, Exception):
main_logger.exception(exc_val)
display_exception(
title="Exception Dialog",
error=str(exc_val),
traceback="".join(tb.format_tb(exc_tb)),
)
return True
26 changes: 21 additions & 5 deletions src/amulet_editor/plugins/amulet_team_3d_viewer/_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,40 @@

from PySide6.QtCore import QLocale, QCoreApplication

from amulet_editor.data.project import get_level
from amulet_editor.models.localisation import ATranslator
from amulet_editor.models.plugin import PluginV1

import amulet_team_locale
import amulet_team_main_window2
import tablericons

import amulet_team_3d_viewer
from ._view_3d import View3D


# Qt only weekly references this. We must hold a strong reference to stop it getting garbage collected
_translator: Optional[ATranslator] = None
view_3d_button: Optional[amulet_team_main_window2.ButtonProxy] = None


def _set_view_3d_layout():
amulet_team_main_window2.get_main_window().set_layout(View3D)


def load_plugin():
global _translator
_translator = ATranslator()
_locale_changed()
QCoreApplication.installTranslator(_translator)
amulet_team_locale.locale_changed.connect(_locale_changed)
global _translator, view_3d_button
if get_level() is not None:
_translator = ATranslator()
_locale_changed()
QCoreApplication.installTranslator(_translator)
amulet_team_locale.locale_changed.connect(_locale_changed)
amulet_team_main_window2.register_widget(View3D)

view_3d_button = amulet_team_main_window2.add_toolbar_button(sticky=True)
view_3d_button.set_icon(tablericons.three_d_cube_sphere)
view_3d_button.set_name("3D Editor")
view_3d_button.set_callback(_set_view_3d_layout)


def _locale_changed():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class Camera(QObject):
# Private variables
_bounds: Bounds
# Extrinsic attrs
_location: Location
_rotation: Rotation
_location: Optional[Location]
_rotation: Optional[Rotation]
# Matrix
_intrinsic_matrix: QMatrix4x4
_extrinsic_matrix: Optional[QMatrix4x4]
Expand All @@ -65,8 +65,8 @@ def __init__(self, parent: QObject = None):
1_000_000_000,
1_000_000_000,
)
self._location = Location(0.0, 0.0, 0.0)
self._rotation = Rotation(0.0, 0.0)
self._location = None
self._rotation = None

self._intrinsic_matrix = QMatrix4x4()
self._extrinsic_matrix = None
Expand All @@ -81,7 +81,7 @@ def _clamp_location(self, location: Location) -> Location:
@property
def location(self) -> Location:
"""The location of the camera. (x, y, z)"""
return self._location
return self._location or Location(0.0, 0.0, 0.0)

@location.setter
def location(self, location: Location):
Expand Down Expand Up @@ -110,7 +110,7 @@ def rotation(self) -> Rotation:
"""The rotation of the camera. (azimuth/yaw, elevation/pitch).
This should behave the same as how Minecraft handles it.
"""
return self._rotation
return self._rotation or Rotation(0.0, 0.0)

@rotation.setter
def rotation(self, rotation: Rotation):
Expand Down Expand Up @@ -192,8 +192,8 @@ def extrinsic_matrix(self) -> QMatrix4x4:
"""The matrix storing all extrinsic parameters (location/rotation)"""
if self._extrinsic_matrix is None:
self._extrinsic_matrix = QMatrix4x4()
location = self._location
rotation = self._rotation
location = self.location
rotation = self.rotation
self._extrinsic_matrix.rotate(rotation.elevation, 1, 0, 0)
self._extrinsic_matrix.rotate(rotation.azimuth, 0, 1, 0)
self._extrinsic_matrix.translate(-location.x, -location.y, -location.z)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from threading import Lock
from enum import IntEnum
import time
from weakref import WeakMethod
from inspect import ismethod

from PySide6.QtCore import QObject, QEvent, Slot, Signal, QTimer, Qt
from PySide6.QtGui import QMouseEvent, QKeyEvent, QScrollEvent, QMoveEvent
Expand All @@ -25,7 +27,7 @@ class KeySrc(IntEnum):
]
ModifierT = frozenset[KeyT]
Number = Union[float, int]
ReceiverT = Union[Slot, Signal, Callable[[], None]]
ReceiverT = Union[Slot, Signal, Callable[[], None], WeakMethod]


class TimerData(QObject):
Expand Down Expand Up @@ -230,7 +232,11 @@ def connect_repeating(
storage.timers[interval] = TimerData(interval)
timer_data = storage.timers[interval]
timer_data.delta_timeout.connect(receiver)
timer_data.receivers.add(receiver)
if ismethod(receiver):
# If this class strongly stores references to methods they can't get garbage collected
timer_data.receivers.add(WeakMethod(receiver))
else:
timer_data.receivers.add(receiver)

def disconnect_repeating(
self,
Expand All @@ -253,7 +259,10 @@ def disconnect_repeating(
timer_data = storage.timers.get(interval)
if timer_data is not None:
timer_data.delta_timeout.disconnect(receiver)
timer_data.receivers.remove(receiver)
if ismethod(receiver):
timer_data.receivers.remove(WeakMethod(receiver))
else:
timer_data.receivers.remove(receiver)
self._clean_storage(key, modifiers)


Expand Down
Loading

0 comments on commit a7eeea0

Please sign in to comment.