Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 51 additions & 12 deletions .github/workflows/static-analysis-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
python-version: "3.11"

- name: Install dependencies
run: |
Expand All @@ -35,6 +35,9 @@ jobs:
- name: Format with black
run: tox -e black

- name: Check Qt Enums
run: tox -e qt-enum


test:
# We want to run on external PRs, but not on our own internal PRs as they'll
Expand All @@ -46,38 +49,74 @@ jobs:

strategy:
matrix:
os: ['ubuntu-latest', 'windows-latest']
python: ['3.8', '3.9', '3.10', '3.11']
# Works around the depreciation of python 3.7 for ubuntu
# https://github.com/actions/setup-python/issues/544
include:
- os: 'ubuntu-22.04'
python: '3.7'
os: ['ubuntu-22.04', 'windows-latest']
test_env: [
'py39-PyQt5',
'py39-PySide5',
'py310-PyQt5',
'py310-PySide5',
'py311-PyQt5',
'py311-PyQt6.7',
'py311-PyQt6.9',
'py311-PySide6.7',
'py311-PySide6.9',
]

runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Python
# Pulled from https://github.com/mottosso/Qt.py/blob/master/.github/actions/setup-tox/action.yml
- name: Install EGL mesa
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update -y -qq
sudo apt-get install -y -qq libegl1-mesa libegl1-mesa-dev libgl1-mesa-glx libgl1-mesa-dev

- name: Install GUI libs
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get install -y -qq libxcb-xinerama0
sudo apt-get install -y -qq libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xfixes0 libxcb-cursor0

# Note: The last python to get setup becomes the default for future python calls
- name: Setup Python 3.9
uses: actions/setup-python@v5
with:
python-version: '3.9'

- name: Setup Python 3.11
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Setup Python 3.10
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
python-version: '3.10'

- name: Install dependencies
shell: bash
run: |
python --version
python -m pip install --upgrade pip
python -m pip install tox

- name: Run Tox
# Note: `--skip-missing-interpreters` prevents false success if python
# version is not installed when testing.
run: |
tox -e begin,py
which tox
tox --skip-missing-interpreters false -e begin,${{ matrix.test_env }}

- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.os }}-${{ matrix.python }}
name: coverage-${{ matrix.os }}-${{ matrix.test_env }}
path: .coverage/*
include-hidden-files: true
retention-days: 1
Expand Down
2 changes: 1 addition & 1 deletion hab_gui/actions/edit_custom_variables_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, settings, parent=None):

def edit_custom_variables(self):
dlg = CustomVariableEditor.create_dialog(self.settings, parent=self.parent())
dlg.exec_()
utils.exec_obj(dlg)

# Ensure the hab_gui respects any changes the user may have made
self.settings.root_widget.refresh_cache()
12 changes: 3 additions & 9 deletions hab_gui/actions/verbosity_action.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from Qt import QtCore, QtWidgets
from Qt import QtWidgets


class VerbosityAction(QtWidgets.QAction):
Expand Down Expand Up @@ -48,10 +48,7 @@ def __init__(self, settings, parent=None):
action = menu.addAction(key)
action.setData(value)
action.setCheckable(True)
if verbosity == value:
action.setChecked(QtCore.Qt.Checked)
else:
action.setChecked(QtCore.Qt.Unchecked)
action.setChecked(verbosity == value)
self.setMenu(menu)

def load_config(self):
Expand All @@ -71,7 +68,4 @@ def refresh(self):
verbosity = self.settings.verbosity
for action in self.menu().actions():
value = action.data()
if verbosity == value:
action.setChecked(QtCore.Qt.Checked)
else:
action.setChecked(QtCore.Qt.Unchecked)
action.setChecked(verbosity == value)
2 changes: 1 addition & 1 deletion hab_gui/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def launch(settings, verbosity, uri, alias, args):
if splash:
splash.finish(window)

app.exec_()
utils.exec_obj(app)


@gui.command()
Expand Down
10 changes: 5 additions & 5 deletions hab_gui/dialogs/error_message_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ def __init__(self, etype, value, tb, parent=None):
self.stack_limit = -5

self.setWindowTitle("Exception")
self.setTextFormat(QtCore.Qt.RichText)
self.setStandardButtons(QtWidgets.QMessageBox.Ok)
self.setTextFormat(QtCore.Qt.TextFormat.RichText)
self.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok)
# Using detailedText seems to disable the close button, enable it
self.setEscapeButton(QtWidgets.QMessageBox.Ok)
self.setEscapeButton(QtWidgets.QMessageBox.StandardButton.Ok)

# Create a button allowing the user to copy the non-highlighted text
copy_btn = self.addButton("Copy", QtWidgets.QMessageBox.ActionRole)
copy_btn = self.addButton("Copy", QtWidgets.QMessageBox.ButtonRole.ActionRole)
copy_btn.setToolTip("Copy the full traceback for error reporting.")
# Disconnect the QMessageBox signals that would cause the box to close
# when this button is pressed and add our own signal connection
copy_btn.disconnect()
copy_btn.released.connect(self.copy_traceback)

self.setDefaultButton(QtWidgets.QMessageBox.Ok)
self.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Ok)
self.refresh()

@QtCore.Slot()
Expand Down
8 changes: 5 additions & 3 deletions hab_gui/dialogs/uri_picker_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ def init_gui(self):
)

self.uiButtonsBOX = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Cancel, self
QtWidgets.QDialogButtonBox.StandardButton.Cancel, self
)
self.uiButtonsBOX.addButton(
"Launch", QtWidgets.QDialogButtonBox.ButtonRole.AcceptRole
)
self.uiButtonsBOX.addButton("Launch", QtWidgets.QDialogButtonBox.AcceptRole)
self.uiButtonsBOX.accepted.connect(self.accept)
self.uiButtonsBOX.rejected.connect(self.reject)

Expand Down Expand Up @@ -151,7 +153,7 @@ def should_show(cls, settings, alias=None):
# Note: Not using `keyboardModifiers` because it is not updated when
# calling this from the cli module.
modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier:
if modifiers == QtCore.Qt.KeyboardModifier.ShiftModifier:
return True

# always_ask is checked for this alias
Expand Down
3 changes: 2 additions & 1 deletion hab_gui/entry_points/message_box.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

from .. import utils
from .logging_exception import LoggingExceptionInit

logger = logging.getLogger(__name__)
Expand All @@ -17,4 +18,4 @@ def excepthook(self, cls, exception, tb):
from ..dialogs.error_message_box import ErrorMessageBox

box = ErrorMessageBox(cls, exception, tb, parent=None)
box.exec_()
utils.exec_obj(box)
13 changes: 12 additions & 1 deletion hab_gui/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


@contextmanager
def cursor_override(cursor=QtCore.Qt.BusyCursor):
def cursor_override(cursor=QtCore.Qt.CursorShape.BusyCursor):
"""Change the application cursor to wait while running the context/decorator.
Ensures that the cursor is restored even if an exception is raised.
"""
Expand Down Expand Up @@ -220,3 +220,14 @@ def block_signals(objs):
finally:
for o, b in blocked:
o.blockSignals(b)


def exec_obj(obj, *args, **kwargs):
"""Work around the removal of `exec_` from Qt6(especially PyQt6).

This calls the `obj.exec` method if it exists, and falls back to `obj.exec_`
otherwise.
"""
if hasattr(obj, "exec"):
return obj.exec(*args, **kwargs)
return obj.exec_(*args, **kwargs)
3 changes: 2 additions & 1 deletion hab_gui/widgets/alias_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def __init__(self, cfg, alias_name, parent=None):

self.alias_dict = self.cfg.aliases
qsize_policy = QtWidgets.QSizePolicy
size_policy = qsize_policy(qsize_policy.Minimum, qsize_policy.Preferred)
policy = qsize_policy.Policy
size_policy = qsize_policy(policy.Minimum, policy.Preferred)
self.setSizePolicy(size_policy)
self.clicked.connect(self._button_action)
self.refresh()
Expand Down
2 changes: 1 addition & 1 deletion hab_gui/widgets/alias_icon_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class AliasIconButton(AliasButton):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextBesideIcon)

def refresh(self):
alias = self.alias_dict[self.alias_name]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def editing_finished(self, top_left, bottom_right, roles):
if self._is_refreshing:
return

if QtCore.Qt.EditRole in roles:
if QtCore.Qt.ItemDataRole.EditRole in roles:
item = self.uiVariableTREE.itemFromIndex(top_left)
column = top_left.column()
if column == 0:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def __init__(self, parent, parser):

# Add a child item that shows the filename. It should not be editable.
self.filename_item = QtWidgets.QTreeWidgetItem(self)
self.filename_item.setFlags(QtCore.Qt.NoItemFlags)
self.filename_item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags)

self.refresh()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def __init__(self, parent, variable_name):
super().__init__(parent)
self.parser = parent.parser
self._variable_name = variable_name
self.setFlags(self.flags() | QtCore.Qt.ItemIsEditable)
self.setFlags(self.flags() | QtCore.Qt.ItemFlag.ItemIsEditable)
self.refresh()

def remove_variable(self):
Expand Down
2 changes: 1 addition & 1 deletion hab_gui/widgets/menu_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, settings, parent=None):

self.setText("Menu")
self.setIcon(utils.Paths.icon("menu.svg"))
self.setPopupMode(self.InstantPopup)
self.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup)
self.refresh()

@property
Expand Down
11 changes: 7 additions & 4 deletions hab_gui/widgets/name_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def names(self):
ret = {}
for index in range(self.name_tree.topLevelItemCount()):
item = self.name_tree.topLevelItem(index)
checked = item.checkState(0) == QtCore.Qt.Checked
checked = item.checkState(0) == QtCore.Qt.CheckState.Checked
ret[item.text(0)] = [item.text(1), checked]
return ret

Expand All @@ -61,7 +61,7 @@ def set_names(self, names, uri=None):
# Build the `default_selection` set for the current URI
if len(settings) > 1 and settings[1]:
self.default_selection.add(name)
item.setCheckState(0, QtCore.Qt.Unchecked)
item.setCheckState(0, QtCore.Qt.CheckState.Unchecked)

self.name_tree.resizeColumnToContents(0)
user_selection = self.user_selection(uri)
Expand All @@ -84,7 +84,7 @@ def selected(self):
ret = set()
for index in range(self.name_tree.topLevelItemCount()):
item = self.name_tree.topLevelItem(index)
if item.checkState(0) == QtCore.Qt.Checked:
if item.checkState(0) == QtCore.Qt.CheckState.Checked:
ret.add(item.text(0))
return ret

Expand All @@ -95,7 +95,10 @@ def set_selected(self, selected):
item = self.name_tree.topLevelItem(index)
name = item.text(0)
item.setCheckState(
0, QtCore.Qt.Checked if name in selected else QtCore.Qt.Unchecked
0,
QtCore.Qt.CheckState.Checked
if name in selected
else QtCore.Qt.CheckState.Unchecked,
)

def sizeHint(self): # noqa: N802
Expand Down
2 changes: 1 addition & 1 deletion hab_gui/widgets/pinned_uris_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self, settings, uri_widget, parent=None):
self.setToolTip("Select and manage quick access to commonly used URI's.")
self.setText(self._text_main)
self.setIcon(utils.Paths.icon("pin-outline.svg"))
self.setPopupMode(self.InstantPopup)
self.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup)
self.refresh()

def add_uri(self, uri):
Expand Down
10 changes: 7 additions & 3 deletions hab_gui/windows/alias_launch_window.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import math
from functools import partial

import hab
Expand Down Expand Up @@ -62,7 +63,7 @@ def __init__(
refresh_time = refresh_time[0]
if refresh_time:
self.refresh_timer.timeout.connect(partial(self.refresh_cache, False))
refresh_time = utils.interval(refresh_time)
refresh_time = math.ceil(utils.interval(refresh_time))
logger.debug(f"Setting auto-refresh interval to {refresh_time} seconds")
self.refresh_timer.start(refresh_time * 1000)

Expand All @@ -86,7 +87,10 @@ def apply_layout(self):
self.layout.addWidget(self.footer_widget, 2, 0, 1, -1)
else:
self.spacer_item = QtWidgets.QSpacerItem(
0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding
0,
0,
QtWidgets.QSizePolicy.Policy.Minimum,
QtWidgets.QSizePolicy.Policy.Expanding,
)
self.layout.addItem(self.spacer_item, self.layout.rowCount(), 0, 1, -1)

Expand Down Expand Up @@ -227,4 +231,4 @@ def main():
app = QtWidgets.QApplication([])
window = AliasLaunchWindow(hab.Resolver(target="hab-gui"), verbosity=1)
window.show()
app.exec_()
utils.exec_obj(app)
Loading
Loading