Skip to content

Commit

Permalink
Add QLineEdit with QSortFilterProxyModel for QTableView
Browse files Browse the repository at this point in the history
Ref #272
  • Loading branch information
algorys committed Mar 13, 2018
1 parent 8602479 commit e347a41
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 65 deletions.
93 changes: 70 additions & 23 deletions alignak_app/qobjects/alignak/problems.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,18 @@
Problems
++++++++
Problems manage creation of QWidgets to display problems found in Alignak backend:
* **Hosts**: ``DOWN``, ``UNREACHABLE``
* **Services**: ``WARNING``, ``CRITICAL``, ``UNKNOWN``
"""

from logging import getLogger

from PyQt5.Qt import QWidget, QIcon, QVBoxLayout, QPushButton, Qt, QLabel, QHBoxLayout
from PyQt5.Qt import QWidget, QIcon, QVBoxLayout, QPushButton, Qt, QLabel, QHBoxLayout, QLineEdit
from PyQt5.Qt import QStandardItemModel, QSortFilterProxyModel

from alignak_app.backend.datamanager import data_manager
from alignak_app.utils.config import settings

from alignak_app.qobjects.common.actions import ActionsQWidget
from alignak_app.qobjects.alignak.problems_table import ProblemsQTableWidget
from alignak_app.qobjects.alignak.problems_table import ProblemsQTableView

logger = getLogger(__name__)

Expand All @@ -51,12 +48,14 @@ def __init__(self, parent=None):
super(ProblemsQWidget, self).__init__(parent)
self.setWindowIcon(QIcon(settings.get_image('icon')))
# Fields
self.problem_table = ProblemsQTableWidget()
self.problem_table = ProblemsQTableView()
self.problems_title = QLabel()
self.problems_model = QStandardItemModel()
self.actions_widget = ActionsQWidget()
self.host_btn = QPushButton()
self.spy_btn = QPushButton()
self.spy_widget = None
self.line_search = QLineEdit()

def initialize(self, spy_widget):
"""
Expand All @@ -73,8 +72,9 @@ def initialize(self, spy_widget):

problem_layout.addWidget(self.get_problems_widget_title())

self.problem_table.initialize()
self.problem_table.currentItemChanged.connect(self.update_action_buttons)
problem_layout.addWidget(self.get_search_widget())

# self.problem_table.initialize()
problem_layout.addWidget(self.problem_table)

self.update_problems_data()
Expand All @@ -85,9 +85,14 @@ def update_action_buttons(self):
"""

if self.problem_table.currentItem():
# Get item
item = self.problem_table.currentItem().item
# Get QStandardItem
standard_item = self.problems_model.item(
self.problem_table.selectionModel().currentIndex().row(),
self.problem_table.selectionModel().currentIndex().column()
)

if standard_item:
item = standard_item.item

# If the elements had been ack or downtimed, they would not be present
self.actions_widget.acknowledge_btn.setEnabled(True)
Expand All @@ -103,6 +108,32 @@ def update_action_buttons(self):
)
self.host_btn.setEnabled(True)

def get_search_widget(self):
"""
Create and return the search QWidget
:return: search QWidget
:rtype: QWidget
"""

widget = QWidget()
layout = QHBoxLayout()
layout.setSpacing(0)
widget.setLayout(layout)

# Search label
search_lbl = QLabel(_('Search Problems'))
search_lbl.setObjectName('bordertitle')
search_lbl.setFixedHeight(25)
search_lbl.setToolTip(_('Search Problems'))
layout.addWidget(search_lbl)

# QLineEdit
self.line_search.setFixedHeight(search_lbl.height())
layout.addWidget(self.line_search)

return widget

def get_problems_widget_title(self):
"""
Return QWidget title with number of problems and refresh QPushButton
Expand Down Expand Up @@ -169,12 +200,18 @@ def add_spied_host(self):
"""

if self.problem_table.currentItem():
item = self.problem_table.currentItem().item
# Get QStandardItem
standard_item = self.problems_model.item(
self.problem_table.selectionModel().currentIndex().row(),
self.problem_table.selectionModel().currentIndex().column()
)

if standard_item:
item = standard_item.item
if 'service' in item.item_type:
item_id = self.problem_table.currentItem().item.data['host']
item_id = item.data['host']
else:
item_id = self.problem_table.currentItem().item.item_id
item_id = item.item_id

self.spy_widget.spy_list_widget.add_spy_host(item_id)
self.spy_widget.update_parent_spytab()
Expand All @@ -188,6 +225,7 @@ def update_problems_data(self):
"""

problems_data = data_manager.get_problems()

if self.parent():
self.parent().parent().setTabText(
1, _("Problems (%d)") % len(problems_data['problems'])
Expand All @@ -200,10 +238,19 @@ def update_problems_data(self):
problems_data['services_nb']
)
)
self.problem_table.setRowCount(len(problems_data['problems']))

row = 0
for item in problems_data['problems']:
self.problem_table.setItem(row, 0, self.problem_table.get_tableitem(item))
self.problem_table.setItem(row, 1, self.problem_table.get_output_tableitem(item))
row += 1
self.problems_model.setRowCount(len(problems_data['problems']))
self.problems_model.setColumnCount(len(self.problem_table.headers_list))
self.problems_model.setSortRole(1)

for row, item in enumerate(problems_data['problems']):
self.problems_model.setItem(row, 0, self.problem_table.get_tableitem(item))
self.problems_model.setItem(row, 1, self.problem_table.get_output_tableitem(item))

proxy_filter = QSortFilterProxyModel()
proxy_filter.setFilterCaseSensitivity(Qt.CaseInsensitive)
proxy_filter.setSourceModel(self.problems_model)
self.line_search.textChanged.connect(proxy_filter.setFilterRegExp)

self.problem_table.initialize(proxy_filter)
self.problems_model.setHorizontalHeaderLabels(self.problem_table.headers_list)
self.problem_table.selectionModel().selectionChanged.connect(self.update_action_buttons)
36 changes: 18 additions & 18 deletions alignak_app/qobjects/alignak/problems_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@
"""
Problems
++++++++
Problems manage creation of QWidget to display problems found in Alignak backend:
Problems manage creation of QTableView to display problems found in Alignak backend:
* **Hosts**: ``DOWN``
* **Hosts**: ``DOWN``, ``UNREACHABLE``
* **Services**: ``WARNING``, ``CRITICAL``, ``UNKNOWN``
"""

from logging import getLogger

from PyQt5.Qt import QIcon, QTableWidget, QTableWidgetItem, Qt, QAbstractItemView, QSize
from PyQt5.Qt import QIcon, QStandardItem, Qt, QAbstractItemView, QSize, QTableView

from alignak_app.backend.datamanager import data_manager
from alignak_app.utils.config import settings
Expand All @@ -40,40 +40,40 @@
logger = getLogger(__name__)


class ProblemsQTableWidget(QTableWidget):
class ProblemsQTableView(QTableView):
"""
Class who create Problems QTableWidget to display each problem
Class who create Problems QTableView to display each problem
"""

def __init__(self, parent=None):
super(ProblemsQTableWidget, self).__init__(parent)
super(ProblemsQTableView, self).__init__(parent)
self.setWindowIcon(QIcon(settings.get_image('icon')))
# Fields
self.headers_list = [
_('Items in problem'), _('Output')
]

def initialize(self):
def initialize(self, proxy_filter):
"""
Initialize Problems QTableWidget cells, rows
:param proxy_filter: filter model with QStandardItemModel
:type proxy_filter: PyQt5.Qt.QSortFilterProxyModel
"""

self.setModel(proxy_filter)

self.setObjectName('problems')
self.verticalHeader().hide()
self.verticalHeader().setDefaultSectionSize(40)
self.setColumnCount(len(self.headers_list))
self.setColumnWidth(0, 500)
self.setColumnWidth(1, 300)
self.setSortingEnabled(True)
self.setIconSize(QSize(24, 24))
self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
self.setHorizontalHeaderLabels(self.headers_list)
self.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.horizontalHeader().setStretchLastSection(True)
self.horizontalHeader().setMinimumHeight(40)
self.horizontalHeader().setDefaultAlignment(Qt.AlignCenter)
self.setDragEnabled(True)

def get_tableitem(self, item):
"""
Expand All @@ -82,10 +82,10 @@ def get_tableitem(self, item):
:param item: host or service item
:type item: alignak_app.items.host.Host | alignak_app.items.service.Service
:return: table item with text
:rtype: QTableWidgetItem
:rtype: QStandardItem
"""

tableitem = AppQTableWidgetItem(self.get_item_text(item))
tableitem = AppQStandardItem(self.get_item_text(item))
tableitem.add_backend_item(item)

icon = QIcon(settings.get_image(
Expand All @@ -104,12 +104,12 @@ def get_output_tableitem(item):
:param item: host or service item
:type item: alignak_app.items.host.Host | alignak_app.items.service.Service
:return: table item with text
:rtype: QTableWidgetItem
:rtype: QStandardItem
"""

if not item.data['ls_output']:
item.data['ls_output'] = 'n\\a'
tableitem = AppQTableWidgetItem(item.data['ls_output'])
tableitem = AppQStandardItem(item.data['ls_output'])
tableitem.add_backend_item(item)

tableitem.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
Expand Down Expand Up @@ -142,13 +142,13 @@ def get_item_text(item):
return text


class AppQTableWidgetItem(QTableWidgetItem): # pylint: disable=too-few-public-methods
class AppQStandardItem(QStandardItem): # pylint: disable=too-few-public-methods
"""
Class who create QTableWidgetItem for App, with an item field
Class who create QStandardItem for App, with an item field, to allow actions later
"""

def __init__(self, parent=None):
super(AppQTableWidgetItem, self).__init__(parent)
super(AppQStandardItem, self).__init__(parent)
self.item = None

def add_backend_item(self, item):
Expand Down
5 changes: 4 additions & 1 deletion alignak_app/qobjects/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,10 @@ def define_hostname(self):

if isinstance(self.sender(), QPushButton):
# From Problems QWidget
item = self.problems_widget.problem_table.currentItem().item
item = self.problems_widget.problems_model.item(
self.problems_widget.problem_table.selectionModel().currentIndex().row(),
self.problems_widget.problem_table.selectionModel().currentIndex().column()
).item
if 'service' in item.item_type:
hostname = data_manager.get_item('host', item.data['host']).name
else:
Expand Down
23 changes: 10 additions & 13 deletions test/test_problems_table_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with (AlignakApp). If not, see <http://www.gnu.org/licenses/>.

import sys
import os

import unittest2

from PyQt5.Qt import QApplication, QWidget, QTableWidgetItem
from PyQt5.Qt import QSortFilterProxyModel

from alignak_app.items.host import Host
from alignak_app.items.service import Service
from alignak_app.backend.datamanager import data_manager

from alignak_app.qobjects.alignak.problems import ProblemsQWidget
from alignak_app.qobjects.alignak.problems_table import AppQTableWidgetItem, ProblemsQTableWidget
from alignak_app.qobjects.events.spy import SpyQWidget
from alignak_app.qobjects.alignak.problems_table import AppQStandardItem, ProblemsQTableView


class TestProblemsQTableWidget(unittest2.TestCase):
Expand Down Expand Up @@ -83,24 +77,27 @@ class TestProblemsQTableWidget(unittest2.TestCase):
def test_initialize_tables_problems(self):
"""Initialize ProblemsQTableWidget"""

under_test = ProblemsQTableWidget()
under_test = ProblemsQTableView()

proxy_model_test = QSortFilterProxyModel()
under_test.initialize(proxy_model_test)

self.assertEqual(
['Items in problem', 'Output'],
under_test.headers_list
)
self.assertEqual(0, under_test.columnCount())
self.assertEqual(0, under_test.model().columnCount())

def test_get_tableitem(self):
"""Get Problems Table Item"""

under_test = ProblemsQTableWidget()
under_test = ProblemsQTableView()
tableitem_test = under_test.get_tableitem(self.host_list[0])

self.assertIsInstance(tableitem_test, AppQTableWidgetItem)
self.assertIsInstance(tableitem_test, AppQStandardItem)
self.assertEqual('Host 0 is UNKNOWN', tableitem_test.text())

tableitem_test = under_test.get_tableitem(self.service_list[0])

self.assertIsInstance(tableitem_test, AppQTableWidgetItem)
self.assertIsInstance(tableitem_test, AppQStandardItem)
self.assertEqual('Service 0 is CRITICAL (Attached to Host 0)', tableitem_test.text())
23 changes: 13 additions & 10 deletions test/test_problems_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

import unittest2

from PyQt5.Qt import QApplication, QWidget
from PyQt5.Qt import QApplication, QWidget, QItemSelectionModel

from alignak_app.items.host import Host

from alignak_app.qobjects.alignak.problems import ProblemsQWidget
from alignak_app.qobjects.alignak.problems_table import AppQTableWidgetItem
from alignak_app.qobjects.alignak.problems_table import AppQStandardItem
from alignak_app.qobjects.events.spy import SpyQWidget


Expand Down Expand Up @@ -98,11 +98,14 @@ def test_add_spy_host(self):
spy_widget_test.initialize()
under_test.initialize(spy_widget_test)

# Set a current item
tableitem_test = AppQTableWidgetItem()
# Set a current QStandardItem
tableitem_test = AppQStandardItem()
tableitem_test.add_backend_item(self.host_list[0])
under_test.problem_table.setItem(0, 0, tableitem_test)
under_test.problem_table.setCurrentItem(tableitem_test)
under_test.problems_model.setItem(0, 0, tableitem_test)
# Make this QStandardItem as current index
index_test = under_test.problem_table.model().index(0, 0)
under_test.problem_table.selectionModel().select(index_test, QItemSelectionModel.Select)
under_test.problem_table.setCurrentIndex(index_test)

self.assertFalse(under_test.spy_widget.spy_list_widget.spied_hosts)

Expand All @@ -122,11 +125,11 @@ def test_update_problems_data(self):
spy_widget_test.initialize()
under_test.initialize(spy_widget_test)

host_tableitem_test = under_test.problem_table.takeItem(0, 0)
output_tableitem_test = under_test.problem_table.takeItem(0, 1)
host_tableitem_test = under_test.problems_model.item(0, 0)
output_tableitem_test = under_test.problems_model.item(0, 1)

under_test.update_problems_data()

# Assert Table items and QWidgets have changed
self.assertNotEqual(host_tableitem_test, under_test.problem_table.takeItem(0, 0))
self.assertNotEqual(output_tableitem_test, under_test.problem_table.takeItem(0, 1))
self.assertNotEqual(host_tableitem_test, under_test.problems_model.item(0, 0))
self.assertNotEqual(output_tableitem_test, under_test.problems_model.item(0, 1))

0 comments on commit e347a41

Please sign in to comment.