240 lines
9.3 KiB
Python
240 lines
9.3 KiB
Python
#!/usr/bin/env python3
|
|
#
|
|
# Qtvcp widget
|
|
# Copyright (c) 2017 Chris Morley
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
###############################################################################
|
|
|
|
from qtpy.QtWidgets import QComboBox, QTreeView, QFrame
|
|
from qtpy.QtGui import QStandardItemModel, QStandardItem
|
|
from qtpy.QtCore import Qt, QEvent, QModelIndex, Signal
|
|
import hal
|
|
from qtvcp.widgets.widget_baseclass import _HalWidgetBase
|
|
|
|
from qtvcp import logger
|
|
|
|
# Instantiate the libraries with global reference
|
|
# LOG is for running code logging
|
|
LOG = logger.getLogger(__name__)
|
|
|
|
# Force the log level for this module
|
|
# LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
|
|
class TreeComboBox(QComboBox):
|
|
selectionUpdated = Signal('PyQt_PyObject')
|
|
objectSelected = Signal('PyQt_PyObject')
|
|
NAME = 1
|
|
SELECTABLE = 2
|
|
OBJECT = 3
|
|
|
|
def __init__(self, parent=None):
|
|
super(TreeComboBox, self).__init__(parent)
|
|
|
|
tree_view = QTreeView(self)
|
|
tree_view.setHeaderHidden( False )
|
|
tree_view.setFrameShape(QFrame.NoFrame)
|
|
tree_view.setEditTriggers(tree_view.NoEditTriggers)
|
|
tree_view.setAlternatingRowColors(True)
|
|
tree_view.setSelectionBehavior(tree_view.SelectItems)
|
|
tree_view.setWordWrap(True)
|
|
tree_view.setAllColumnsShowFocus(True)
|
|
tree_view.setFixedWidth(300)
|
|
self.setView(tree_view)
|
|
|
|
#self.view().viewport().installEventFilter(self)
|
|
self.currentIndexChanged.connect(self.selected)
|
|
|
|
def showPopup(self):
|
|
self.setRootModelIndex(QModelIndex())
|
|
super(TreeComboBox, self).showPopup()
|
|
|
|
# set combox view the same as tree view ?
|
|
def hidePopup(self):
|
|
self.setRootModelIndex(self.view().currentIndex().parent())
|
|
self.setCurrentIndex(self.view().currentIndex().row())
|
|
super(TreeComboBox, self).hidePopup()
|
|
|
|
def eventFilter(self, object, event):
|
|
if event.type() == QEvent.MouseButtonPress and object is self.view().viewport():
|
|
index = self.view().indexAt(event.pos())
|
|
# print index.parent(),index.row(),index.column(),index.data(),index.data(Qt.UserRole + 1)
|
|
# print self.view().isExpanded(self.view().currentIndex())
|
|
# if self.itemAt(event.pos()) is None
|
|
self.__skip_next_hide = not self.view().visualRect(index).contains(event.pos())
|
|
return False
|
|
|
|
def addItems(self, parent, elements):
|
|
for text, value, children in elements:
|
|
item = QStandardItem(text)
|
|
# don't let title lines be selectable
|
|
if not value[1]:
|
|
item.setFlags(item.flags() & -(1<<1))
|
|
item.setData(value[0], role=Qt.ToolTipRole)
|
|
# store the HAL name and selectability in Qt user roles
|
|
item.setData(value[0], role=Qt.UserRole + self.NAME)
|
|
# selectability in Qt user roles
|
|
item.setData(value[1], role=Qt.UserRole + self.SELECTABLE)
|
|
# all info of object
|
|
item.setData(value[2], role=Qt.UserRole + self.OBJECT)
|
|
parent.appendRow(item)
|
|
|
|
# next level
|
|
if children:
|
|
self.addItems(item, children)
|
|
|
|
# emit the (by default) signal name
|
|
def getSelectionData(self, index,userIndex=1):
|
|
choice = self.itemData(self.currentIndex(), Qt.UserRole + userIndex)
|
|
return choice
|
|
|
|
def selected(self,index):
|
|
if self.getSelectionData(index) is None:
|
|
#print ('should reset selection or expand node')
|
|
self.selectionUpdated.emit(None)
|
|
self.objectSelected.emit({})
|
|
self.selectionUpdated.emit(self.getSelectionData(index))
|
|
self.objectSelected.emit(self.getSelectionData(index, self.OBJECT))
|
|
|
|
################################################################
|
|
class HALSelectionBox(TreeComboBox, _HalWidgetBase):
|
|
HAL_BIT = hal.HAL_BIT
|
|
HAL_FLOAT = hal.HAL_FLOAT
|
|
HAL_S32 = hal.HAL_S32
|
|
HAL_U32 = hal.HAL_U32
|
|
|
|
HAL_IN = hal.HAL_IN
|
|
HAL_OUT = hal.HAL_OUT
|
|
HAL_IO = hal.HAL_IO
|
|
HAL_RO = hal.HAL_RO
|
|
HAL_RW = hal.HAL_RW
|
|
|
|
PINS = 1
|
|
SIGS = SIGNALS = 2
|
|
PARAMS = PARAMETERS = 4
|
|
|
|
def __init__(self, parent=None):
|
|
super(HALSelectionBox, self).__init__(parent)
|
|
self.PINTYPE = [self.HAL_BIT,self.HAL_FLOAT,self.HAL_S32,self.HAL_U32]
|
|
self.PINDIRECTION = [self.HAL_IN, self.HAL_OUT]
|
|
self.SIGTYPE = [self.HAL_BIT,self.HAL_FLOAT,self.HAL_S32,self.HAL_U32]
|
|
self.SIGDRIVEN = [True, False]
|
|
self.PARAMTYPE = [self.HAL_RO, self.HAL_RW]
|
|
self.SHOWTYPES = [self.PINS]
|
|
|
|
def _hal_init(self):
|
|
model = QStandardItemModel()
|
|
model.setHorizontalHeaderLabels(['Connect to:'])
|
|
node_pin = []
|
|
node_pinin = []
|
|
node_pinout = []
|
|
node_pininout = []
|
|
node_sig = []
|
|
node_param = []
|
|
parent_node = []
|
|
if self.PINS in self.SHOWTYPES:
|
|
# append((label -> 'Name',
|
|
# user data -> [HAL name -> None, selectable -> False],
|
|
# next level -> next_node_list))
|
|
parent_node.append(('Pins', [None, False, {}], node_pin))
|
|
node_pin.append(('IN', [None, False, {}], node_pinin))
|
|
node_pin.append(('OUT', [None, False, {}], node_pinout))
|
|
node_pin.append(('IO', [None, False, {}], node_pininout))
|
|
for i in hal.get_info_pins():
|
|
i['OBJECT'] = 'pin'
|
|
if i['TYPE'] in self.PINTYPE:
|
|
if i['DIRECTION'] in self.PINDIRECTION:
|
|
if i['DIRECTION'] == self.HAL_IN:
|
|
node_pinin.append((i['NAME'], [i['NAME'], True,i], []))
|
|
elif i['DIRECTION'] == self.HAL_OUT:
|
|
node_pinout.append((i['NAME'], [i['NAME'], True,i], []))
|
|
else:
|
|
node_pininout.append((i['NAME'], [i['NAME'], True,i], []))
|
|
|
|
if self.SIGS in self.SHOWTYPES:
|
|
node_sigdriven = []
|
|
node_sigundriven = []
|
|
parent_node.append(('Signals', [None, None, {}], node_sig))
|
|
node_sig.append(('Driven', [None, False, {}], node_sigdriven))
|
|
node_sig.append(('Undriven', [None, False, {}], node_sigundriven))
|
|
|
|
for i in hal.get_info_signals():
|
|
i['OBJECT'] = 'signal'
|
|
if i['TYPE'] in self.SIGTYPE:
|
|
if not bool(i['DRIVER']is None) and bool(True) == self.SIGDRIVEN[0]:
|
|
node_sigdriven.append((i['NAME'], [i['NAME'], True,i], []))
|
|
elif bool(i['DRIVER']is None) and bool(True) == self.SIGDRIVEN[1]:
|
|
node_sigundriven.append((i['NAME'], [i['NAME'], True,i], []))
|
|
|
|
if self.PARAMS in self.SHOWTYPES:
|
|
parent_node.append(('Parameters', [None, None, {}], node_param))
|
|
for i in hal.get_info_params():
|
|
i['OBJECT'] = 'parameter'
|
|
if i['TYPE'] in self.PARAMTYPE:
|
|
node_sig.append((i['NAME'], [i['NAME'], True,i], []))
|
|
|
|
self.addItems(model, parent_node)
|
|
self.setModel(model)
|
|
self.view().setMinimumHeight(300)
|
|
self.view().adjustSize()
|
|
|
|
def setShowTypes(self, types):
|
|
''' Sets the pin type: HAL_BIT,HAL_FLOAT,HAL_S32,HAL_U32
|
|
that will be shown in the combbox.
|
|
widget defaults to all pin types shown
|
|
ie. combobox.setShowTypes([combobox.HAL_BIT])'''
|
|
self.SHOWTYPES = types
|
|
|
|
def setPinTypes(self, \
|
|
types = [HAL_BIT,HAL_FLOAT,HAL_S32,HAL_U32], \
|
|
direction = [HAL_IN, HAL_OUT]):
|
|
''' Sets the pin type: HAL_BIT,HAL_FLOAT,HAL_S32,HAL_U32
|
|
and direction: HAL_IN, HAL_OUT
|
|
that will be shown in combobox.
|
|
widget defaults to all pins, all directions.
|
|
ie. combobox.setPinTypes([combobox.HAL_BIT], direction = [HAL_IN])'''
|
|
self.PINTYPE = types
|
|
self.PINDIRECTION = direction
|
|
|
|
def setSignalTypes(self, \
|
|
types = [HAL_BIT,HAL_FLOAT,HAL_S32,HAL_U32], \
|
|
driven = [True, False]):
|
|
''' Sets the pin type: HAL_BIT,HAL_FLOAT,HAL_S32,HAL_U32
|
|
and what type of signals (driven by a pin, not or both).
|
|
True being driven and False being un-driven signals
|
|
combobox.setSignalTypes([combobox.HAL_BIT], direction = [True,False])'''
|
|
self.SIGTYPE = types
|
|
self.SIGDRIVEN = driven
|
|
|
|
|
|
##############################
|
|
# required class boiler code #
|
|
##############################
|
|
|
|
def __getitem__(self, item):
|
|
return getattr(self, item)
|
|
def __setitem__(self, item, value):
|
|
return setattr(self, item, value)
|
|
|
|
# for testing without editor:
|
|
def main():
|
|
import sys
|
|
from qtpy.QtWidgets import QApplication
|
|
|
|
app = QApplication(sys.argv)
|
|
x = hal.component('_X_')
|
|
widget = HALSelectionBox()
|
|
widget._hal_init()
|
|
widget.show()
|
|
sys.exit(app.exec())
|
|
if __name__ == "__main__":
|
|
main()
|