# -*- coding: UTF-8 -*- import os from collections import OrderedDict from PyQt5.QtWidgets import (QDialog, QAbstractItemView, QVBoxLayout, QHBoxLayout, QLabel, QWidget, QTreeView, QLineEdit, QTableView, QCompleter) from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPixmap from PyQt5.QtCore import Qt, pyqtSlot, QModelIndex from .qt_models import get_simple_model from .forms import GridForm from .buttons import ActionButton __all__ = ['QuickDialog', 'SearchDialog', 'HelpDialog', 'FactoryIcons'] current_dir = os.path.dirname(__file__) _SIZE = (500, 200) class QuickDialog(QDialog): def __init__(self, parent, kind, string=None, data=None, widgets=None, icon=None, size=None, readonly=False): super(QuickDialog, self).__init__(parent) # Size arg is in deprecation if not size: size = _SIZE self.factory = None self.readonly = readonly self.parent = parent self.parent_model = None titles = { 'warning': self.tr('Warning...'), 'info': self.tr('Information...'), 'action': self.tr('Action...'), 'help': self.tr('Help...'), 'error': self.tr('Error...'), 'question': self.tr('Question...'), 'selection': self.tr('Selection...'), None: self.tr('Dialog...') } self.setWindowTitle(titles[kind]) self.setModal(True) self.setParent(parent) self.factory = FactoryIcons() self.default_widget_focus = None self.kind = kind self.widgets = widgets self.data = data string_widget = None data_widget = None _buttons = None row_stretch = 1 main_vbox = QVBoxLayout() self.sub_hbox = QHBoxLayout() # Add main message if string: # For simple dialog string_widget = QLabel(string) if kind == 'help': data_widget = widgets[0] elif kind == 'action': if widgets: data_widget = widgets[0] else: data_widget = GridForm(parent, OrderedDict(data)) elif kind == 'selection': self.name = data['name'] data_widget = self.set_selection(parent, data) elif widgets: data_widget = GridForm(parent, OrderedDict(widgets)) if string_widget: main_vbox.addWidget(string_widget, 0) if data_widget: if isinstance(data_widget, QWidget): row_stretch += 1 size = (size[0], size[1] + 200) self.sub_hbox.addWidget(data_widget, 0) else: self.sub_hbox.addLayout(data_widget, 0) self.ok_button = ActionButton('ok', self.dialog_accepted) self.ok_button.setFocus() self.ok_button.setDefault(True) self.cancel_button = ActionButton('cancel', self.dialog_rejected) _buttons = [] if kind in ('info', 'help', 'warning', 'question', 'error'): if kind in ('warning', 'question'): _buttons.append(self.cancel_button) _buttons.append(self.ok_button) elif kind in ('action', 'selection'): _buttons.extend([self.cancel_button, self.ok_button]) self.buttonbox = QHBoxLayout() for b in _buttons: self.buttonbox.addWidget(b, 1) main_vbox.addLayout(self.sub_hbox, 0) main_vbox.addLayout(self.buttonbox, 1) main_vbox.insertStretch(row_stretch, 0) self.setLayout(main_vbox) self.setMinimumSize(*size) if kind in ('info', 'error'): self.show() def exec_(self, args=None): res = None self.parent.releaseKeyboard() res = super(QuickDialog, self).exec() if self.kind == 'action': pass return res def show(self): super(QuickDialog, self).show() self.parent.releaseKeyboard() self.ok_button.setFocus() if self.default_widget_focus: self.default_widget_focus.setFocus() if hasattr(self.default_widget_focus, 'setText'): self.default_widget_focus.setText('') else: self.setFocus() def hide(self): super(QuickDialog, self).hide() self.parent.setFocus() def set_info(self, info): if hasattr(self, 'label_info'): self.label_info.setText(info) def set_widgets(self, widgets): if widgets: # Set default focus to first widget created self.default_widget_focus = widgets[0] def closeEvent(self, event): super(QuickDialog, self).closeEvent(event) def dialog_rejected(self): self.parent.setFocus() self.setResult(0) self.hide() def dialog_accepted(self): if self.kind in ('action', 'selection', 'warning', 'question'): self.setResult(1) self.done(1) self.hide() def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Escape: self.dialog_rejected() else: super(QuickDialog, self).keyPressEvent(event) def set_selection(self, obj, data): self.set_simple_model() setattr(obj, data['name'] + '_model', self.data_model) self.parent_model = data.get('parent_model') self.treeview = QTreeView() self.treeview.setRootIsDecorated(False) self.treeview.setColumnHidden(0, True) self.treeview.setItemsExpandable(False) self.treeview.setAlternatingRowColors(True) self.treeview.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeview.setModel(self.data_model) self.treeview.clicked.connect(self.field_selection_changed) self.treeview.activated.connect(self.field_selection_changed) self.update_values(self.data['values']) # By default first row must be selected item = self.data_model.item(0, 0) idx = self.data_model.indexFromItem(item) self.treeview.setCurrentIndex(idx) return self.treeview def update_values(self, values): self.data_model.removeRows(0, self.data_model.rowCount()) self._insert_items(self.data_model, values) self.treeview.resizeColumnToContents(0) def set_simple_model(self): self.data_model = QStandardItemModel(0, len(self.data['heads']), self) _horizontal = Qt.Horizontal for i, h in enumerate(self.data['heads'], 0): self.data_model.setHeaderData(i, _horizontal, h) def _insert_items(self, model, values): for value in values: row = [] for v in value: itemx = QStandardItem(v) itemx.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable) row.append(itemx) self.data_model.insertRow(0, row) self.data_model.sort(0, Qt.AscendingOrder) @pyqtSlot(QModelIndex) def field_selection_changed(self, qm_index): if not self.readonly: item_id = self.data_model.item(qm_index.row(), 0).text() item_name = self.data_model.item(qm_index.row(), 1).text() if self.parent_model is not None: self.parent_model[self.name] = item_id if hasattr(self.parent, 'field_' + self.name): field = getattr(self.parent, 'field_' + self.name) if hasattr(field, 'setText'): field.setText(item_name) else: setattr(self.parent, 'field_' + self.name + '_name', item_name) setattr(self.parent, 'field_' + self.name + '_id', int(item_id)) action = getattr(self.parent, 'action_' + self.name + '_selection_changed') action() self.dialog_accepted() class SearchDialog(QDialog): def __init__(self, parent, headers, values, on_activated, hide_headers=False, completion_column=None, title=None): super(SearchDialog, self).__init__(parent) self.parent = parent self.headers = headers self.values = values if not title: title = self.tr('Search Products...') self.setWindowTitle(title) self._product_line = QLineEdit() self.table_view = QTableView() button_cancel = ActionButton('cancel', self.on_reject) vbox = QVBoxLayout() hbox = QHBoxLayout() hbox.addWidget(button_cancel) vbox.addWidget(self._product_line) vbox.addLayout(hbox) self.setLayout(vbox) self.completer = QCompleter() self.treeview_search_product = QTreeView() if hide_headers: col_headers = self.treeview_search_product.header() col_headers.hide() self.completer.setPopup(self.treeview_search_product) self._product_line.setCompleter(self.completer) self.set_model() self.completer.activated.connect(self.on_accept) self.completer.setFilterMode(Qt.MatchStartsWith) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCompletionColumn(2) self.completer.activated.connect(on_activated) def set_model(self): headers_name = [h[1] for h in self.headers] self.model = get_simple_model(self.parent, self.values, headers_name) self.completer.setModel(self.model) def get_selected_index(self): model_index = self._get_model_index() idx = self.model.index(model_index.row(), 0) return idx.data() def get_selected_data(self): model_index = self._get_model_index() data = {} i = 0 for h, _ in self.headers: data[h] = self.model.index(model_index.row(), i).data() i += 1 return data def _get_model_index(self): item_view = self.completer.popup() index = item_view.currentIndex() proxy_model = self.completer.completionModel() model_index = proxy_model.mapToSource(index) return model_index def on_accept(self): self.accept() def on_reject(self): self.reject() class HelpDialog(QuickDialog): def __init__(self, parent): self.treeview = QTreeView() self.treeview.setRootIsDecorated(False) self.treeview.setAlternatingRowColors(True) self.treeview.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeview.setEditTriggers(QAbstractItemView.NoEditTriggers) super(HelpDialog, self).__init__(parent, 'help', widgets=[self.treeview], size=(400, 500)) self.set_info(self.tr('Keys Shortcuts...')) self.hide() def set_shortcuts(self, shortcuts): model = self._help_model(shortcuts) self.treeview.setModel(model) header = self.treeview.header() header.resizeSection(0, 250) def _help_model(self, shortcuts): model = QStandardItemModel(0, 2, self) model.setHeaderData(0, Qt.Horizontal, self.tr('Action')) model.setHeaderData(1, Qt.Horizontal, self.tr('Shortcut')) for short in shortcuts: model.insertRow(0) model.setData(model.index(0, 0), short[0]) model.setData(model.index(0, 1), short[1]) return model class FactoryIcons(object): def __init__(self): name_icons = ['print', 'warning', 'info', 'error', 'question'] self.icons = {} for name in name_icons: path_icon = os.path.join(current_dir, '..', 'share', 'icon-' + name + '.png') if not os.path.exists(path_icon): continue _qpixmap_icon = QPixmap() _qpixmap_icon.load(path_icon) _icon_label = QLabel() _icon_label.setAlignment(Qt.AlignCenter | Qt.AlignCenter) _icon_label.setPixmap(_qpixmap_icon.scaledToHeight(48)) self.icons[name] = _icon_label