table_widgets.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. #!/usr/bin/python2
  2. # -*- coding: utf8 -*-
  3. # pylint: skip-file
  4. #
  5. # The Qubes OS Project, http://www.qubes-os.org
  6. #
  7. # Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
  8. #
  9. # This program is free software; you can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License
  11. # as published by the Free Software Foundation; either version 2
  12. # of the License, or (at your option) any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Lesser General Public License along
  20. # with this program; if not, see <http://www.gnu.org/licenses/>.
  21. import os
  22. from PyQt4 import QtGui
  23. from PyQt4.QtCore import QSize, Qt
  24. from PyQt4.QtGui import QTableWidgetItem, QHBoxLayout, QIcon, QLabel, QWidget, \
  25. QSizePolicy, QSpacerItem, QFont, QColor, QProgressBar, QPainter, QPen
  26. import time
  27. from qubes.qubes import vm_files
  28. import main
  29. qubes_dom0_updates_stat_file = '/var/lib/qubes/updates/dom0-updates-available'
  30. power_order = Qt.DescendingOrder
  31. update_order = Qt.AscendingOrder
  32. row_height = 30
  33. class VmIconWidget (QWidget):
  34. def __init__(self, icon_path, enabled=True, size_multiplier=0.7,
  35. tooltip = None, parent=None, icon_sz = (32, 32)):
  36. super(VmIconWidget, self).__init__(parent)
  37. self.label_icon = QLabel()
  38. if icon_path[0] in ':/':
  39. icon = QIcon (icon_path)
  40. else:
  41. icon = QIcon.fromTheme(icon_path)
  42. icon_sz = QSize (row_height * size_multiplier, row_height * size_multiplier)
  43. icon_pixmap = icon.pixmap(icon_sz, QIcon.Disabled if not enabled else QIcon.Normal)
  44. self.label_icon.setPixmap (icon_pixmap)
  45. self.label_icon.setFixedSize (icon_sz)
  46. if tooltip != None:
  47. self.label_icon.setToolTip(tooltip)
  48. layout = QHBoxLayout()
  49. layout.addWidget(self.label_icon)
  50. layout.setContentsMargins(0,0,0,0)
  51. self.setLayout(layout)
  52. def setToolTip(self, tooltip):
  53. if tooltip is not None:
  54. self.label_icon.setToolTip(tooltip)
  55. else:
  56. self.label_icon.setToolTip('')
  57. class VmTypeWidget(VmIconWidget):
  58. class VmTypeItem(QTableWidgetItem):
  59. def __init__(self, value, vm):
  60. super(VmTypeWidget.VmTypeItem, self).__init__()
  61. self.value = value
  62. self.vm = vm
  63. def set_value(self, value):
  64. self.value = value
  65. def __lt__(self, other):
  66. if self.vm.qid == 0:
  67. return True
  68. elif other.vm.qid == 0:
  69. return False
  70. elif self.value == other.value:
  71. return self.vm.qid < other.vm.qid
  72. else:
  73. return self.value < other.value
  74. def __init__(self, vm, parent=None):
  75. (icon_path, tooltip) = self.get_vm_icon(vm)
  76. super (VmTypeWidget, self).__init__(icon_path, True, 0.8, tooltip, parent)
  77. self.vm = vm
  78. self.tableItem = self.VmTypeItem(self.value, vm)
  79. def get_vm_icon(self, vm):
  80. if vm.qid == 0:
  81. self.value = 0
  82. return (":/dom0.png", "Dom0")
  83. elif vm.is_netvm() and not vm.is_proxyvm():
  84. self.value = 1
  85. return (":/netvm.png", "NetVM")
  86. elif vm.is_proxyvm():
  87. self.value = 2
  88. return (":/proxyvm.png", "ProxyVM")
  89. elif vm.is_appvm() and vm.template is None:
  90. self.value = 4
  91. return (":/standalonevm.png", "StandaloneVM")
  92. elif vm.is_template():
  93. self.value = 3
  94. return (":/templatevm.png", "TemplateVM")
  95. elif vm.is_appvm() or vm.is_disposablevm():
  96. self.value = 5 + vm.label.index
  97. return (":/appvm.png", "AppVM")
  98. class VmLabelWidget(VmIconWidget):
  99. class VmLabelItem(QTableWidgetItem):
  100. def __init__(self, value, vm):
  101. super(VmLabelWidget.VmLabelItem, self).__init__()
  102. self.value = value
  103. self.vm = vm
  104. def set_value(self, value):
  105. self.value = value
  106. def __lt__(self, other):
  107. if self.vm.qid == 0:
  108. return True
  109. elif other.vm.qid == 0:
  110. return False
  111. elif self.value == other.value:
  112. return self.vm.qid < other.vm.qid
  113. else:
  114. return self.value < other.value
  115. def __init__(self, vm, parent=None):
  116. icon_path = self.get_vm_icon_path(vm)
  117. super (VmLabelWidget, self).__init__(icon_path, True, 0.8, None, parent)
  118. self.vm = vm
  119. self.tableItem = self.VmLabelItem(self.value, vm)
  120. def get_vm_icon_path(self, vm):
  121. if vm.qid == 0:
  122. self.value = 100
  123. return ":/off.png"
  124. else:
  125. self.value = vm.label.index
  126. return vm.label.icon
  127. class VmNameItem (QTableWidgetItem):
  128. def __init__(self, vm):
  129. super(VmNameItem, self).__init__()
  130. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  131. self.setText(vm.name)
  132. self.setTextAlignment(Qt.AlignVCenter)
  133. self.qid = vm.qid
  134. def __lt__(self, other):
  135. if self.qid == 0:
  136. return True
  137. elif other.qid == 0:
  138. return False
  139. return super(VmNameItem, self).__lt__(other)
  140. class VmStatusIcon(QLabel):
  141. def __init__(self, vm, parent=None):
  142. super (VmStatusIcon, self).__init__(parent)
  143. self.vm = vm
  144. self.set_on_icon()
  145. self.previous_power_state = vm.last_power_state
  146. def update(self):
  147. if self.previous_power_state != self.vm.last_power_state:
  148. self.set_on_icon()
  149. self.previous_power_state = self.vm.last_power_state
  150. def set_on_icon(self):
  151. if self.vm.last_power_state == "Running":
  152. icon = QIcon (":/on.png")
  153. elif self.vm.last_power_state in ["Paused", "Suspended"]:
  154. icon = QIcon (":/paused.png")
  155. elif self.vm.last_power_state in ["Transient", "Halting", "Dying"]:
  156. icon = QIcon (":/transient.png")
  157. else:
  158. icon = QIcon (":/off.png")
  159. icon_sz = QSize (row_height * 0.5, row_height *0.5)
  160. icon_pixmap = icon.pixmap(icon_sz)
  161. self.setPixmap (icon_pixmap)
  162. self.setFixedSize (icon_sz)
  163. class VmInfoWidget (QWidget):
  164. class VmInfoItem (QTableWidgetItem):
  165. def __init__(self, upd_info_item, vm):
  166. super(VmInfoWidget.VmInfoItem, self).__init__()
  167. self.upd_info_item = upd_info_item
  168. self.vm = vm
  169. def __lt__(self, other):
  170. if self.vm.qid == 0:
  171. return True
  172. elif other.vm.qid == 0:
  173. return False
  174. self_val = self.upd_info_item.value
  175. other_val = other.upd_info_item.value
  176. if self.tableWidget().horizontalHeader().sortIndicatorOrder() == update_order:
  177. # the result will be sorted by upd, sorting order: Ascending
  178. self_val += 1 if self.vm.is_running() else 0
  179. other_val += 1 if other.vm.is_running() else 0
  180. if self_val == other_val:
  181. return self.vm.qid < other.vm.qid
  182. else:
  183. return self_val > other_val
  184. elif self.tableWidget().horizontalHeader().sortIndicatorOrder() == power_order:
  185. #the result will be sorted by power state, sorting order: Descending
  186. self_val = -(self_val/10 + 10*(1 if self.vm.is_running() else 0))
  187. other_val = -(other_val/10 + 10*(1 if other.vm.is_running() else 0))
  188. if self_val == other_val:
  189. return self.vm.qid < other.vm.qid
  190. else:
  191. return self_val > other_val
  192. else:
  193. #it would be strange if this happened
  194. return
  195. def __init__(self, vm, parent = None):
  196. super (VmInfoWidget, self).__init__(parent)
  197. self.vm = vm
  198. layout = QHBoxLayout ()
  199. self.on_icon = VmStatusIcon(vm)
  200. self.upd_info = VmUpdateInfoWidget(vm, show_text=False)
  201. self.error_icon = VmIconWidget(":/warning.png")
  202. self.blk_icon = VmIconWidget(":/mount.png")
  203. self.rec_icon = VmIconWidget(":/mic.png")
  204. layout.addWidget(self.on_icon)
  205. layout.addWidget(self.upd_info)
  206. layout.addWidget(self.error_icon)
  207. layout.addItem(QSpacerItem(0, 10, QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding))
  208. layout.addWidget(self.blk_icon)
  209. layout.addWidget(self.rec_icon)
  210. layout.setContentsMargins(5,0,5,0)
  211. self.setLayout(layout)
  212. self.rec_icon.setVisible(False)
  213. self.blk_icon.setVisible(False)
  214. self.error_icon.setVisible(False)
  215. self.tableItem = self.VmInfoItem(self.upd_info.tableItem, vm)
  216. def update_vm_state(self, vm, blk_visible, rec_visible=None):
  217. self.on_icon.update()
  218. self.upd_info.update_outdated(vm)
  219. if blk_visible != None:
  220. self.blk_icon.setVisible(blk_visible)
  221. if rec_visible != None:
  222. self.rec_icon.setVisible(rec_visible)
  223. self.error_icon.setToolTip(vm.qubes_manager_state[main.QMVmState
  224. .ErrorMsg])
  225. self.error_icon.setVisible(vm.qubes_manager_state[main.QMVmState
  226. .ErrorMsg] is not None)
  227. class VmTemplateItem (QTableWidgetItem):
  228. def __init__(self, vm):
  229. super(VmTemplateItem, self).__init__()
  230. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  231. self.vm = vm
  232. if vm.template is not None:
  233. self.setText(vm.template.name)
  234. else:
  235. font = QFont()
  236. font.setStyle(QFont.StyleItalic)
  237. self.setFont(font)
  238. self.setTextColor(QColor("gray"))
  239. if vm.is_appvm(): # and vm.template is None
  240. self.setText("StandaloneVM")
  241. elif vm.is_template():
  242. self.setText("TemplateVM")
  243. elif vm.qid == 0:
  244. self.setText("AdminVM")
  245. elif vm.is_netvm():
  246. self.setText("NetVM")
  247. else:
  248. self.setText("---")
  249. self.setTextAlignment(Qt.AlignVCenter)
  250. def __lt__(self, other):
  251. if self.vm.qid == 0:
  252. return True
  253. elif other.vm.qid == 0:
  254. return False
  255. elif self.text() == other.text():
  256. return self.vm.qid < other.vm.qid
  257. else:
  258. return super(VmTemplateItem, self).__lt__(other)
  259. class VmNetvmItem (QTableWidgetItem):
  260. def __init__(self, vm):
  261. super(VmNetvmItem, self).__init__()
  262. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  263. self.vm = vm
  264. if vm.is_netvm() and not vm.is_proxyvm():
  265. self.setText("n/a")
  266. elif vm.netvm is not None:
  267. self.setText(vm.netvm.name)
  268. else:
  269. self.setText("---")
  270. self.setTextAlignment(Qt.AlignVCenter)
  271. def __lt__(self, other):
  272. if self.vm.qid == 0:
  273. return True
  274. elif other.vm.qid == 0:
  275. return False
  276. elif self.text() == other.text():
  277. return self.vm.qid < other.vm.qid
  278. else:
  279. return super(VmNetvmItem, self).__lt__(other)
  280. class VmInternalItem(QTableWidgetItem):
  281. def __init__(self, vm):
  282. super(VmInternalItem, self).__init__()
  283. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  284. self.vm = vm
  285. self.internal = self.vm.internal
  286. if self.internal:
  287. self.setText("Yes")
  288. else:
  289. self.setText("")
  290. def __lt__(self, other):
  291. if self.vm.qid == 0:
  292. return True
  293. elif other.vm.qid == 0:
  294. return False
  295. return super(VmInternalItem, self).__lt__(other)
  296. class VmUsageBarWidget (QWidget):
  297. class VmUsageBarItem (QTableWidgetItem):
  298. def __init__(self, value, vm):
  299. super(VmUsageBarWidget.VmUsageBarItem, self).__init__()
  300. self.value = value
  301. self.vm = vm
  302. def set_value(self, value):
  303. self.value = value
  304. def __lt__(self, other):
  305. if self.vm.qid == 0:
  306. return True
  307. elif other.vm.qid == 0:
  308. return False
  309. elif self.value == other.value:
  310. return self.vm.qid < other.vm.qid
  311. else:
  312. return int(self.value) < int(other.value)
  313. def __init__(self, min, max, format, update_func, vm, load, hue=210, parent = None):
  314. super (VmUsageBarWidget, self).__init__(parent)
  315. self.min = min
  316. self.max = max
  317. self.update_func = update_func
  318. self.value = min
  319. self.widget = QProgressBar()
  320. self.widget.setMinimum(min)
  321. self.widget.setMaximum(max)
  322. self.widget.setFormat(format)
  323. self.widget.setStyleSheet(
  324. "QProgressBar:horizontal{" +\
  325. "border: 1px solid hsv({0}, 100, 250);".format(hue) +\
  326. "border-radius: 4px;\
  327. background: transparent;\
  328. text-align: center;\
  329. }\
  330. QProgressBar::chunk:horizontal {\
  331. background: qlineargradient(x1: 1, y1: 0.5, x2: 1, y2: 0.5, " +\
  332. "stop: 0 hsv({0}, 170, 207),".format(hue) +
  333. " stop: 1 white); \
  334. }"
  335. )
  336. layout = QHBoxLayout()
  337. layout.addWidget(self.widget)
  338. self.setLayout(layout)
  339. self.tableItem = self.VmUsageBarItem(min, vm)
  340. self.update_load(vm, load)
  341. def update_load(self, vm, load):
  342. self.value = self.update_func(vm, load)
  343. self.widget.setValue(self.value)
  344. self.tableItem.set_value(self.value)
  345. class ChartWidget (QWidget):
  346. class ChartItem (QTableWidgetItem):
  347. def __init__(self, value, vm):
  348. super(ChartWidget.ChartItem, self).__init__()
  349. self.value = value
  350. self.vm = vm
  351. def set_value(self, value):
  352. self.value = value
  353. def __lt__(self, other):
  354. if self.vm.qid == 0:
  355. return True
  356. elif other.vm.qid == 0:
  357. return False
  358. elif self.value == other.value:
  359. return self.vm.qid < other.vm.qid
  360. else:
  361. return self.value < other.value
  362. def __init__(self, vm, update_func, hue, load = 0, parent = None):
  363. super (ChartWidget, self).__init__(parent)
  364. self.update_func = update_func
  365. self.hue = hue
  366. if hue < 0 or hue > 255:
  367. self.hue = 255
  368. self.load = load
  369. assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
  370. self.load_history = [self.load]
  371. self.tableItem = ChartWidget.ChartItem(self.load, vm)
  372. def update_load (self, vm, load):
  373. self.load = self.update_func(vm, load)
  374. assert self.load >= 0, "load = {0}".format(self.load)
  375. # assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
  376. if self.load > 100:
  377. # FIXME: This is an ugly workaround for cpu_load:/
  378. self.load = 100
  379. self.load_history.append (self.load)
  380. self.tableItem.set_value(self.load)
  381. self.repaint()
  382. def paintEvent (self, Event = None):
  383. p = QPainter (self)
  384. dx = 4
  385. W = self.width()
  386. H = self.height() - 5
  387. N = len(self.load_history)
  388. if N > W/dx:
  389. tail = N - W/dx
  390. N = W/dx
  391. self.load_history = self.load_history[tail:]
  392. assert len(self.load_history) == N
  393. for i in range (0, N-1):
  394. val = self.load_history[N- i - 1]
  395. sat = 70 + val*(255-70)/100
  396. color = QColor.fromHsv (self.hue, sat, 255)
  397. pen = QPen (color)
  398. pen.setWidth(dx-1)
  399. p.setPen(pen)
  400. if val > 0:
  401. p.drawLine (W - i*dx - dx, H , W - i*dx - dx, H - (H - 5) * val/100)
  402. class VmUpdateInfoWidget(QWidget):
  403. class VmUpdateInfoItem (QTableWidgetItem):
  404. def __init__(self, value, vm):
  405. super(VmUpdateInfoWidget.VmUpdateInfoItem, self).__init__()
  406. self.value = 0
  407. self.vm = vm
  408. self.set_value(value)
  409. def set_value(self, value):
  410. if value in ("outdated", "to-be-outdated"):
  411. self.value = 30
  412. elif value == "update":
  413. self.value = 20
  414. else:
  415. self.value = 0
  416. def __lt__(self, other):
  417. if self.vm.qid == 0:
  418. return True
  419. elif other.vm.qid == 0:
  420. return False
  421. elif self.value == other.value:
  422. return self.vm.qid < other.vm.qid
  423. else:
  424. return self.value < other.value
  425. def __init__(self, vm, show_text=True, parent = None):
  426. super (VmUpdateInfoWidget, self).__init__(parent)
  427. layout = QHBoxLayout ()
  428. self.show_text = show_text
  429. if self.show_text:
  430. self.label=QLabel("")
  431. layout.addWidget(self.label, alignment=Qt.AlignCenter)
  432. else:
  433. self.icon = QLabel("")
  434. layout.addWidget(self.icon, alignment=Qt.AlignCenter)
  435. self.setLayout(layout)
  436. self.previous_outdated_state = None
  437. self.previous_update_recommended = None
  438. self.value = None
  439. self.tableItem = VmUpdateInfoWidget.VmUpdateInfoItem(self.value, vm)
  440. def update_outdated(self, vm):
  441. if vm.type == "HVM":
  442. return
  443. if vm.is_outdated():
  444. outdated_state = "outdated"
  445. # During TemplateVM shutdown, there's an interval of a few seconds
  446. # during which vm.template.is_running() returns false but
  447. # vm.is_outdated() does not yet return true, so the icon disappears.
  448. # This looks goofy, but we've decided not to fix it at this time
  449. # (2015-02-09).
  450. elif vm.template and vm.template.is_running():
  451. outdated_state = "to-be-outdated"
  452. else:
  453. outdated_state = None
  454. if outdated_state != self.previous_outdated_state:
  455. self.update_status_widget(outdated_state)
  456. self.previous_outdated_state = outdated_state
  457. if not vm.is_updateable():
  458. return
  459. if vm.qid == 0:
  460. update_recommended = self.previous_update_recommended
  461. if os.path.exists(qubes_dom0_updates_stat_file):
  462. update_recommended = True
  463. else:
  464. update_recommended = False
  465. else:
  466. update_recommended = self.previous_update_recommended
  467. stat_file_path = vm.dir_path + '/' + vm_files["updates_stat_file"]
  468. if not os.path.exists(stat_file_path):
  469. update_recommended = False
  470. else:
  471. if (not hasattr(vm, "updates_stat_file_read_time")) or vm.updates_stat_file_read_time <= os.path.getmtime(stat_file_path):
  472. stat_file = open(stat_file_path, "r")
  473. updates = stat_file.read().strip()
  474. stat_file.close()
  475. if updates.isdigit():
  476. updates = int(updates)
  477. else:
  478. updates = 0
  479. if updates == 0:
  480. update_recommended = False
  481. else:
  482. update_recommended = True
  483. vm.updates_stat_file_read_time = time.time()
  484. if update_recommended and not self.previous_update_recommended:
  485. self.update_status_widget("update")
  486. elif self.previous_update_recommended and not update_recommended:
  487. self.update_status_widget(None)
  488. self.previous_update_recommended = update_recommended
  489. def update_status_widget(self, state):
  490. self.value = state
  491. self.tableItem.set_value(state)
  492. if state == "update":
  493. label_text = "<font color=\"#CCCC00\">Check updates</font>"
  494. icon_path = ":/update-recommended.png"
  495. tooltip_text = self.tr("Updates pending!")
  496. elif state == "outdated":
  497. label_text = "<font color=\"red\">VM outdated</font>"
  498. icon_path = ":/outdated.png"
  499. tooltip_text = self.tr(
  500. "The VM must be restarted for its filesystem to reflect the "
  501. "template's recent committed changes.")
  502. elif state == "to-be-outdated":
  503. label_text = "<font color=\"#800000\">TemplateVM running</font>"
  504. icon_path = ":/to-be-outdated.png"
  505. tooltip_text = self.tr(
  506. "The TemplateVM must be stopped before changes from its "
  507. "current session can be picked up by this VM.")
  508. elif state is None:
  509. label_text = ""
  510. icon_path = None
  511. tooltip_text = None
  512. if self.show_text:
  513. self.label.setText(label_text)
  514. else:
  515. self.layout().removeWidget(self.icon)
  516. self.icon.deleteLater()
  517. if icon_path is not None:
  518. self.icon = VmIconWidget(icon_path, True, 0.7)
  519. self.icon.setToolTip(tooltip_text)
  520. else:
  521. self.icon = QLabel(label_text)
  522. self.layout().addWidget(self.icon, alignment=Qt.AlignCenter)
  523. class VmSizeOnDiskItem (QTableWidgetItem):
  524. def __init__(self, vm):
  525. super(VmSizeOnDiskItem, self).__init__()
  526. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  527. self.vm = vm
  528. self.value = 0
  529. self.update()
  530. self.setTextAlignment(Qt.AlignVCenter)
  531. def update(self):
  532. if self.vm.qid == 0:
  533. self.setText("n/a")
  534. else:
  535. self.value = self.vm.get_disk_utilization()/(1024*1024)
  536. self.setText( str(self.value) + " MiB")
  537. def __lt__(self, other):
  538. if self.vm.qid == 0:
  539. return True
  540. elif other.vm.qid == 0:
  541. return False
  542. elif self.value == other.value:
  543. return self.vm.qid < other.vm.qid
  544. else:
  545. return self.value < other.value
  546. class VmIPItem(QTableWidgetItem):
  547. def __init__(self, vm):
  548. super(VmIPItem, self).__init__()
  549. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  550. self.vm = vm
  551. self.ip = self.vm.ip
  552. if self.ip:
  553. self.setText(self.ip)
  554. else:
  555. self.setText("n/a")
  556. def __lt__(self, other):
  557. if self.vm.qid == 0:
  558. return True
  559. elif other.vm.qid == 0:
  560. return False
  561. return super(VmIPItem, self).__lt__(other)
  562. class VmIncludeInBackupsItem(QTableWidgetItem):
  563. def __init__(self, vm):
  564. super(VmIncludeInBackupsItem, self).__init__()
  565. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  566. self.vm = vm
  567. if self.vm.include_in_backups:
  568. self.setText("Yes")
  569. else:
  570. self.setText("")
  571. def __lt__(self, other):
  572. if self.vm.qid == 0:
  573. return True
  574. elif other.vm.qid == 0:
  575. return False
  576. elif self.vm.include_in_backups == other.vm.include_in_backups:
  577. return self.vm.qid < other.vm.qid
  578. else:
  579. return self.vm.include_in_backups < other.vm.include_in_backups
  580. class VmLastBackupItem(QTableWidgetItem):
  581. def __init__(self, vm):
  582. super(VmLastBackupItem, self).__init__()
  583. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
  584. self.vm = vm
  585. if self.vm.backup_timestamp:
  586. self.setText(str(self.vm.backup_timestamp.date()))
  587. else:
  588. self.setText("")
  589. def __lt__(self, other):
  590. if self.vm.qid == 0:
  591. return True
  592. elif other.vm.qid == 0:
  593. return False
  594. elif self.vm.backup_timestamp == other.vm.backup_timestamp:
  595. return self.vm.qid < other.vm.qid
  596. elif not self.vm.backup_timestamp:
  597. return False
  598. elif not other.vm.backup_timestamp:
  599. return True
  600. else:
  601. return self.vm.backup_timestamp < other.vm.backup_timestamp