171 строка
5.2 KiB
Python
171 строка
5.2 KiB
Python
from PySide6.QtCore import QEvent, Qt, Signal
|
|
from PySide6.QtGui import QBrush, QPen
|
|
from PySide6.QtWidgets import (
|
|
QDialog,
|
|
QGraphicsRectItem,
|
|
QGraphicsScene,
|
|
QGraphicsView,
|
|
QMessageBox,
|
|
QToolTip,
|
|
)
|
|
|
|
|
|
class BuildItem(QGraphicsRectItem):
|
|
WIDTH = 30
|
|
|
|
def __init__(self, future_build_info, x=0, y=0, selectable=True):
|
|
QGraphicsRectItem.__init__(self, x, y, self.WIDTH, self.WIDTH)
|
|
self.future_build_info = future_build_info
|
|
if selectable:
|
|
self.setFlags(BuildItem.ItemIsSelectable | BuildItem.ItemIsFocusable)
|
|
else:
|
|
self.setFlags(BuildItem.ItemIsFocusable)
|
|
|
|
def __str__(self):
|
|
return "Build %s" % self.future_build_info.data
|
|
|
|
|
|
class SkipChooserScene(QGraphicsScene):
|
|
COLUMNS = 7
|
|
SPACE = 10
|
|
|
|
def __init__(self, build_range):
|
|
QGraphicsScene.__init__(self)
|
|
self.from_range(build_range)
|
|
|
|
def from_range(self, build_range):
|
|
self.build_range = build_range
|
|
mid = build_range.mid_point()
|
|
bounds = (0, len(build_range) - 1)
|
|
row = -1
|
|
for i, future in enumerate(build_range.future_build_infos):
|
|
column = i % self.COLUMNS
|
|
if column == 0:
|
|
row += 1
|
|
item = BuildItem(
|
|
future,
|
|
column * BuildItem.WIDTH + self.SPACE * column,
|
|
row * BuildItem.WIDTH + self.SPACE * row,
|
|
selectable=i not in bounds,
|
|
)
|
|
if i == mid:
|
|
item.setBrush(QBrush(Qt.blue))
|
|
self.mid_build = item
|
|
elif i in bounds:
|
|
item.setBrush(QBrush(Qt.lightGray))
|
|
|
|
pen = QPen(self.palette().windowText().color())
|
|
item.setPen(pen)
|
|
self.addItem(item)
|
|
|
|
def _update_palette(self):
|
|
for item in self.items():
|
|
pen = QPen(self.palette().windowText().color())
|
|
item.setPen(pen)
|
|
|
|
def event(self, event: QEvent) -> bool:
|
|
if event.type() == QEvent.PaletteChange:
|
|
self._update_palette()
|
|
return True
|
|
return super().event(event)
|
|
|
|
|
|
class SkipChooserView(QGraphicsView):
|
|
build_choosen = Signal()
|
|
|
|
def __init__(self, parent=None):
|
|
QGraphicsView.__init__(self, parent)
|
|
self.setMouseTracking(True)
|
|
|
|
def setScene(self, scene):
|
|
QGraphicsView.setScene(self, scene)
|
|
self.centerOn(scene.mid_build)
|
|
scene.mid_build.setSelected(True)
|
|
|
|
def mousePressEvent(self, evt):
|
|
item = self.itemAt(evt.pos())
|
|
# do nothing if we don't click on an item
|
|
if not item:
|
|
return
|
|
# do nothing if we click on a bound
|
|
if not (item.flags() & QGraphicsRectItem.ItemIsSelectable):
|
|
return
|
|
# only one item can be selected at a time, so deselect if any
|
|
for item in self.scene().selectedItems():
|
|
item.setSelected(False)
|
|
QGraphicsView.mousePressEvent(self, evt)
|
|
|
|
def mouseMoveEvent(self, evt):
|
|
QGraphicsView.mouseMoveEvent(self, evt)
|
|
# implement a real time tooltip
|
|
item = self.itemAt(evt.pos())
|
|
if item:
|
|
QToolTip.showText(evt.globalPos(), str(item))
|
|
|
|
def mouseDoubleClickEvent(self, evt):
|
|
item = self.itemAt(evt.pos())
|
|
# do nothing if we click on a bound
|
|
if item and item.flags() & QGraphicsRectItem.ItemIsSelectable:
|
|
self.build_choosen.emit()
|
|
|
|
|
|
class SkipDialog(QDialog):
|
|
def __init__(self, build_range, parent=None):
|
|
QDialog.__init__(self, parent)
|
|
assert len(build_range) > 3
|
|
from mozregui.ui.skip_dialog import Ui_SkipDialog
|
|
|
|
self.ui = Ui_SkipDialog()
|
|
self.ui.setupUi(self)
|
|
self.scene = SkipChooserScene(build_range)
|
|
self.scene.selectionChanged.connect(self.on_selection_changed)
|
|
self.ui.gview.setScene(self.scene)
|
|
self.ui.gview.build_choosen.connect(self.accept)
|
|
|
|
def on_selection_changed(self):
|
|
items = self.scene.selectedItems()
|
|
if items:
|
|
self.ui.lbl_status.setText("Selected build to test, %s" % items[0])
|
|
|
|
def build_index(self, item):
|
|
return self.scene.build_range.future_build_infos.index(item.future_build_info)
|
|
|
|
def choose_next_build(self):
|
|
if self.exec() == SkipDialog.Accepted:
|
|
items = self.scene.selectedItems()
|
|
assert len(items) == 1
|
|
return self.build_index(items[0])
|
|
|
|
def closeEvent(self, evt):
|
|
if (
|
|
QMessageBox.warning(
|
|
self,
|
|
"Stop the bisection ?",
|
|
"Closing this dialog will end the bisection. Are you sure"
|
|
" you want to end the bisection now ?",
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
QMessageBox.No,
|
|
)
|
|
== QMessageBox.Yes
|
|
):
|
|
evt.accept()
|
|
else:
|
|
evt.ignore()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
from mozregression.build_range import BuildRange, FutureBuildInfo
|
|
|
|
class FInfo(FutureBuildInfo):
|
|
def _fetch(self):
|
|
return self.data
|
|
|
|
build_range = BuildRange(None, [FInfo(None, i) for i in range(420)])
|
|
|
|
from PySide6.QtWidgets import QApplication, QMainWindow
|
|
|
|
app = QApplication([])
|
|
win = QMainWindow()
|
|
|
|
dlg = SkipDialog(build_range)
|