gui/mozregui/mainwindow.py (97 lines of code) (raw):
from tempfile import mkdtemp
import mozfile
from PySide6.QtCore import QEvent, QSettings, Slot
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QMainWindow, QMessageBox
from mozregression import __version__ as mozregression_version
from mozregression.telemetry import initialize_telemetry
from mozregui.bisection import BisectRunner
from mozregui.global_prefs import change_prefs_dialog, get_prefs
from mozregui.report_delegate import ReportItemDelegate
from mozregui.single_runner import SingleBuildRunner
from mozregui.ui.mainwindow import Ui_MainWindow
from mozregui.utils import is_dark_mode_enabled
from mozregui.wizard import BisectionWizard, SingleRunWizard
ABOUT_TEXT = """\
<p><strong>mozregression-gui</strong> is a desktop interface for
<strong>mozregression</strong>, a regression range finder for Mozilla
nightly and integration builds.</p>
<p><a href="http://mozilla.github.io/mozregression/">\
http://mozilla.github.io/mozregression/</a></p>
<p><b>Using mozregression version: %s</b></p>
<p>mozregression logo by <a href="https://mozillians.org/en-US/u/victoria/">Victoria Wang</a></p>
<p>All application icons are made by <a href="http://www.freepik.com"
title="Freepik">Freepik</a>
from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a>
and licensed under <a href="http://creativecommons.org/licenses/by/3.0/"
title="Creative Commons BY 3.0">CC BY 3.0</a></p>
""" % (
mozregression_version
)
class MainWindow(QMainWindow):
INSTANCE = None
def __init__(self):
QMainWindow.__init__(self)
MainWindow.INSTANCE = self
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self._update_palette()
self.bisect_runner = BisectRunner(self)
self.single_runner = SingleBuildRunner(self)
self.current_runner = None
self.bisect_runner.worker_created.connect(self.ui.report_view.model().attach_bisector)
self.single_runner.worker_created.connect(self.ui.report_view.model().attach_single_runner)
self.ui.report_view.model().need_evaluate_editor.connect(
self.bisect_runner.open_evaluate_editor
)
self.ui.report_view.step_report_changed.connect(self.ui.build_info_browser.update_content)
self.report_delegate = ReportItemDelegate()
self.report_delegate.got_verdict.connect(self.bisect_runner.set_verdict)
self.ui.report_view.setItemDelegateForColumn(0, self.report_delegate)
for runner in (self.bisect_runner, self.single_runner):
runner.running_state_changed.connect(self.ui.actionStart_a_new_bisection.setDisabled)
runner.running_state_changed.connect(self.ui.actionStop_the_bisection.setEnabled)
runner.running_state_changed.connect(self.ui.actionRun_a_single_build.setDisabled)
self.persist = mkdtemp()
self.read_settings()
# get weird behaviour if we enable multiprocessing on pyinstaller builds of the GUI
initialize_telemetry(get_prefs()["enable_telemetry"], allow_multiprocessing=False)
# Make sure the toolbar and logviews are visible (in case
# the user manually turned them off in a previous release
# where this was possible)
self.ui.toolBar.setVisible(True)
self.ui.log_view.setVisible(True)
self.ui.logDockWidget.setVisible(True)
@Slot()
def clear(self):
mozfile.remove(self.persist)
def read_settings(self):
settings = QSettings()
self.restoreGeometry(settings.value("mainWin/geometry"))
self.restoreState(settings.value("mainWin/windowState"))
def closeEvent(self, evt):
settings = QSettings()
settings.setValue("mainWin/geometry", self.saveGeometry())
settings.setValue("mainWin/windowState", self.saveState())
QMainWindow.closeEvent(self, evt)
def _start_runner(self, wizard_class, runner):
wizard = wizard_class(self)
if wizard.exec() == wizard_class.Accepted:
self.current_runner = runner
# clear the report model
self.ui.report_view.model().clear()
# clear the build info main panel
self.ui.build_info_browser.clear()
runner.start(*wizard.options())
def _update_palette(self):
if is_dark_mode_enabled():
self.ui.actionRun_a_single_build.setIcon(QIcon(":/s_white.png"))
self.ui.actionStart_a_new_bisection.setIcon(QIcon(":/cutting_white.png"))
self.ui.actionStop_the_bisection.setIcon(QIcon(":/stop_white.png"))
else:
self.ui.actionRun_a_single_build.setIcon(QIcon(":/s.png"))
self.ui.actionStart_a_new_bisection.setIcon(QIcon(":/cutting.png"))
self.ui.actionStop_the_bisection.setIcon(QIcon(":/stop.png"))
@Slot()
def start_bisection_wizard(self):
self._start_runner(BisectionWizard, self.bisect_runner)
@Slot()
def stop_bisection(self):
# stop the bisection or the single runner without blocking
self.ui.report_view.model().attach_bisector(None)
self.current_runner.stop(False)
@Slot()
def single_run(self):
self._start_runner(SingleRunWizard, self.single_runner)
@Slot()
def show_about(self):
QMessageBox.about(self, "About", ABOUT_TEXT)
@Slot()
def edit_global_prefs(self):
change_prefs_dialog(self)
def event(self, event: QEvent) -> bool:
if event.type() == QEvent.PaletteChange:
self._update_palette()
return True
return super().event(event)