pax_global_header 0000666 0000000 0000000 00000000064 14566137314 0014524 g ustar 00root root 0000000 0000000 52 comment=ed7ac31923d2cd77508e9a20749177833efea1c7
enamlx-0.6.4/ 0000775 0000000 0000000 00000000000 14566137314 0013017 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/.gitignore 0000664 0000000 0000000 00000000147 14566137314 0015011 0 ustar 00root root 0000000 0000000 *.pyc
*.enamlc
__pycache__/
__enamlcache__/
build/
dist/
.*/
*.egg-info
*.dist-info
*.kdev4
*.kate-swp
enamlx-0.6.4/.isort.cfg 0000664 0000000 0000000 00000000032 14566137314 0014711 0 ustar 00root root 0000000 0000000 [settings]
profile=black
enamlx-0.6.4/.project 0000664 0000000 0000000 00000000550 14566137314 0014466 0 ustar 00root root 0000000 0000000
enamlx
org.python.pydev.PyDevBuilder
org.python.pydev.pythonNature
enamlx-0.6.4/.pydevproject 0000664 0000000 0000000 00000000661 14566137314 0015541 0 ustar 00root root 0000000 0000000
python 2.7
Default
/${PROJECT_DIR_NAME}
enamlx-0.6.4/LICENSE 0000664 0000000 0000000 00000002065 14566137314 0014027 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) 2015 frmdstryr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
enamlx-0.6.4/README.md 0000664 0000000 0000000 00000002351 14566137314 0014277 0 ustar 00root root 0000000 0000000 # enamlx
Additional Qt Widgets for Enaml, mainly used for the Tree and Table widgets.
Supports 3.5+ Qt5 and Qt6.
## Install
Now on [pypi](https://pypi.org/project/enamlx/).
```bash
pip install enamlx
```
#### Widgets ####
1. TableView
2. TreeView
3. DoubleSpinBox
4. GraphicsView
5. PyQtGraph Plot
6. KeyEvent
#### Examples ####
__TableView__
Table view using enaml syntax. See example for usage.

1. Text/Icons/Checkboxes
2. Delegate widgets (any widget can be child of a cell)
3. Right click menus per item
4. Tested and working with 1 million+ rows.
__DoubleSpinBox__
SpinBox that works with float values
__PlotItem__
Plot widgets using PyQtGraph

__GraphicsView__
A "canvas" Widget for drawing with Qt's GraphicsView.
# Usage
```python
import enamlx
enamlx.install()
# Then use like any enaml widget
from enamlx.widgets.api import TreeView # etc..
```
enamlx-0.6.4/enamlx/ 0000775 0000000 0000000 00000000000 14566137314 0014303 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/__init__.py 0000664 0000000 0000000 00000000246 14566137314 0016416 0 ustar 00root root 0000000 0000000 def install():
"""Install the toolkit factory widgets for the widgets provided
by this library.
"""
from enamlx.qt import qt_factories # noqa: F401
enamlx-0.6.4/enamlx/core/ 0000775 0000000 0000000 00000000000 14566137314 0015233 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/core/__init__.py 0000664 0000000 0000000 00000000000 14566137314 0017332 0 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/core/block.py 0000664 0000000 0000000 00000003222 14566137314 0016676 0 ustar 00root root 0000000 0000000 """
Created on Apr 15, 2017
@author: jrm
"""
from atom.api import ForwardInstance
from enaml.core.declarative import Declarative, d_
class Block(Declarative):
"""An object which dynamically insert's its children into another block's parent object.
The 'Block' object is used to cleanly and easily insert it's children
into the children of another object. The 'Object' instance assigned to the
'object' property of the 'Block' will be parented with the parent of
the 'Include'. Creating an 'Include' with no parent is a programming
error.
"""
#: The Block to which this blocks children should be inserted into
block = d_(ForwardInstance(lambda: Block))
def initialize(self):
"""A reimplemented initializer.
This method will add the include objects to the parent of the
include and ensure that they are initialized.
"""
super(Block, self).initialize()
if self.block:
self.block.parent.insert_children(self.block, self.children)
def _observe_block(self, change):
"""A change handler for the 'objects' list of the Include.
If the object is initialized objects which are removed will be
unparented and objects which are added will be reparented. Old
objects will be destroyed if the 'destroy_old' flag is True.
"""
if self.is_initialized:
if change["type"] == "update":
old_block = change["oldvalue"]
old_block.parent.remove_children(old_block, self.children)
new_block = change["value"]
new_block.parent.insert_children(new_block, self.children)
enamlx-0.6.4/enamlx/core/looper.py 0000664 0000000 0000000 00000022706 14566137314 0017114 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Created on Aug 29, 2015
Looper that only shows visible elements
@author: jrm
"""
from atom.api import ContainerList, Instance, List, observe
from enaml.core.declarative import d_
from enaml.core.looper import Looper, new_scope, recursive_expand, sortedmap
from enamlx.widgets.abstract_item_view import AbstractItemView
class ListLooper(Looper):
"""A looper handles ContainerList updates"""
items = ContainerList()
_expanded = List()
def _observe_iterable(self, change):
if not self.is_initialized:
return
elif change["type"] == "update":
self.refresh_items()
elif change["type"] == "container":
if change["operation"] == "append":
self.append_items([change["item"]])
elif change["operation"] == "extend":
self.append_items(change["items"])
elif change["operation"] == "insert":
self.insert_item(change["index"], change["item"])
elif change["operation"] == "remove":
self.remove_items([change["item"]])
elif change["operation"] == "pop":
self.pop_item(change["index"])
elif change["operation"] in ["reverse", "sort"]:
self.refresh_items()
def remove_items(self, old_items):
for item in old_items:
index = self.items.index(item)
self.pop_item(index)
def pop_item(self, index):
for iteration in self.items.pop(index):
for old in iteration:
if not old.is_destroyed:
old.destroy()
def insert_item(self, loop_index, loop_item):
iteration = []
self._iter_data[loop_item] = iteration
for nodes, key, f_locals in self.pattern_nodes:
with new_scope(key, f_locals) as f_locals:
f_locals["loop_index"] = loop_index
f_locals["loop_item"] = loop_item
for node in nodes:
child = node(None)
if isinstance(child, list):
iteration.extend(child)
else:
iteration.append(child)
expanded = []
recursive_expand(sum([iteration], []), expanded)
# Where do I insert it!
self.parent.insert_children(self, expanded)
self.items.insert(loop_index, iteration)
def append_item(self, item):
self.insert_item(len(self.items), item)
def refresh_items(self):
"""Refresh the items of the pattern.
This method destroys the old items and creates and initializes
the new items.
"""
old_items = self.items[:] # if self._dirty else []
old_iter_data = self._iter_data # if self._dirty else {}
iterable = self.iterable
pattern_nodes = self.pattern_nodes
new_iter_data = sortedmap()
new_items = []
if iterable is not None and len(pattern_nodes) > 0:
for loop_index, loop_item in enumerate(iterable):
iteration = old_iter_data.get(loop_item)
if iteration is not None:
new_iter_data[loop_item] = iteration
new_items.append(iteration)
old_items.remove(iteration)
continue
iteration = []
new_iter_data[loop_item] = iteration
new_items.append(iteration)
for nodes, key, f_locals in pattern_nodes:
with new_scope(key, f_locals) as f_locals:
f_locals["loop_index"] = loop_index
f_locals["loop_item"] = loop_item
for node in nodes:
child = node(None)
if isinstance(child, list):
iteration.extend(child)
else:
iteration.append(child)
for iteration in old_items:
for old in iteration:
if not old.is_destroyed:
old.destroy()
if len(new_items) > 0:
expanded = []
recursive_expand(sum(new_items, []), expanded)
self._expanded = expanded
self.parent.insert_children(self, expanded)
self.items = new_items
self._iter_data = new_iter_data
class ItemViewLooper(Looper):
"""A looper that only creates the objects
in the visible window.
"""
item_view = d_(Instance(AbstractItemView))
# --------------------------------------------------------------------------
# Observers
# --------------------------------------------------------------------------
@observe("item_view.iterable")
def _observe_iterable(self, change):
super(ItemViewLooper, self)._observe_iterable(change)
def _observe_item_view(self, change):
"""A private observer for the `window_size` attribute.
If the iterable changes while the looper is active, the loop
items will be refreshed.
"""
self.iterable = self.item_view.iterable
@observe(
"item_view.iterable_index",
"item_view.iterable_fetch_size",
"item_view.iterable_prefetch",
)
def _refresh_window(self, change):
if change["type"] == "update" and self.is_initialized:
self.refresh_items()
@observe("item_view.visible_rect")
def _prefetch_items(self, change):
"""When the current_row in the model changes (whether from scrolling) or
set by the application. Make sure the results are loaded!
"""
if self.is_initialized:
view = self.item_view
upper_limit = (
view.iterable_index + view.iterable_fetch_size - view.iterable_prefetch
)
lower_limit = max(0, view.iterable_index + view.iterable_prefetch)
offset = int(view.iterable_fetch_size / 2.0)
upper_visible_row = view.visible_rect[2]
lower_visible_row = view.visible_rect[0]
print("Visible rect = %s" % view.visible_rect)
if upper_visible_row >= upper_limit:
next_index = max(0, upper_visible_row - offset) # Center on current row
# Going up works...
if next_index > view.iterable_index:
print("Auto prefetch upper limit %s!" % upper_limit)
view.iterable_index = next_index
# view.model().reset()
# But doewn doesnt?
elif view.iterable_index > 0 and lower_visible_row < lower_limit:
next_index = max(0, lower_visible_row - offset) # Center on current row
# Going down works
if next_index < view.iterable_index:
print(
"Auto prefetch lower limit=%s, iterable=%s, setting next=%s!"
% (lower_limit, view.iterable_index, next_index)
)
view.iterable_index = next_index
# view.model().reset()
@property
def windowed_iterable(self):
"""That returns only the window"""
# Seek to offset
effective_offset = max(0, self.item_view.iterable_index)
for i, item in enumerate(self.iterable):
if i < effective_offset:
continue
elif i >= (effective_offset + self.item_view.iterable_fetch_size):
return
yield item
def refresh_items(self):
"""Refresh the items of the pattern.
This method destroys the old items and creates and initializes
the new items.
"""
old_items = self.items[:] # if self._dirty else []
old_iter_data = self._iter_data # if self._dirty else {}
iterable = self.windowed_iterable
pattern_nodes = self.pattern_nodes
new_iter_data = sortedmap()
new_items = []
if iterable is not None and len(pattern_nodes) > 0:
for loop_index, loop_item in enumerate(iterable):
iteration = old_iter_data.get(loop_item)
if iteration is not None:
new_iter_data[loop_item] = iteration
new_items.append(iteration)
old_items.remove(iteration)
continue
iteration = []
new_iter_data[loop_item] = iteration
new_items.append(iteration)
for nodes, key, f_locals in pattern_nodes:
with new_scope(key, f_locals) as f_locals:
f_locals["loop_index"] = loop_index
f_locals["loop_item"] = loop_item
for node in nodes:
child = node(None)
if isinstance(child, list):
iteration.extend(child)
else:
iteration.append(child)
# Add to old items list
# self.old_items.extend(old_items)
# if self._dirty:
for iteration in old_items:
for old in iteration:
if not old.is_destroyed:
old.destroy()
if len(new_items) > 0:
expanded = []
recursive_expand(sum(new_items, []), expanded)
self.parent.insert_children(self, expanded)
self.items = new_items # if self._dirty else new_items+old_items
self._iter_data = new_iter_data
enamlx-0.6.4/enamlx/qt/ 0000775 0000000 0000000 00000000000 14566137314 0014727 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/qt/__init__.py 0000664 0000000 0000000 00000000000 14566137314 0017026 0 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/qt/qt_abstract_item.py 0000664 0000000 0000000 00000005107 14566137314 0020631 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 24, 2015
"""
from atom.api import Bool, ForwardInstance, Instance
from enaml.core.pattern import Pattern
from enaml.qt.qt_control import QtControl
from enaml.qt.qt_menu import QtMenu
from enaml.qt.qt_widget import QtWidget
from qtpy.QtCore import QModelIndex, Qt
from qtpy.QtWidgets import QHeaderView
from enamlx.widgets.abstract_item import (
ProxyAbstractWidgetItem,
ProxyAbstractWidgetItemGroup,
)
TEXT_H_ALIGNMENTS = {
"left": Qt.AlignLeft,
"right": Qt.AlignRight,
"center": Qt.AlignHCenter,
"justify": Qt.AlignJustify,
}
TEXT_V_ALIGNMENTS = {
"top": Qt.AlignTop,
"bottom": Qt.AlignBottom,
"center": Qt.AlignVCenter,
}
RESIZE_MODES = {
"interactive": QHeaderView.Interactive,
"fixed": QHeaderView.Fixed,
"stretch": QHeaderView.Stretch,
"resize_to_contents": QHeaderView.ResizeToContents,
"custom": QHeaderView.Custom,
}
class AbstractQtWidgetItemGroup(QtControl, ProxyAbstractWidgetItemGroup):
"""Base class for Table and Tree Views"""
#: Context menu for this group
menu = Instance(QtMenu)
def init_layout(self):
for child in self.children():
if isinstance(child, QtMenu):
self.menu = child
def refresh_style_sheet(self):
pass # Takes a lot of time
def _abstract_item_view():
from .qt_abstract_item_view import QtAbstractItemView
return QtAbstractItemView
class AbstractQtWidgetItem(AbstractQtWidgetItemGroup, ProxyAbstractWidgetItem):
#:
is_destroyed = Bool()
#: Index within the view
index = Instance(QModelIndex)
#: Delegate widget to display when editing the cell
#: if the widget is editable
delegate = Instance(QtWidget)
#: Reference to view
view = ForwardInstance(_abstract_item_view)
def create_widget(self):
# View items have no widget!
for child in self.children():
if isinstance(child, (Pattern, QtWidget)):
self.delegate = child
def init_widget(self):
pass
def init_layout(self):
super(AbstractQtWidgetItem, self).init_layout()
self._update_index()
def _update_index(self):
"""Update where this item is within the model"""
raise NotImplementedError
def destroy(self):
"""Set the flag so we know when this item is destroyed"""
self.is_destroyed = True
super(AbstractQtWidgetItem, self).destroy()
enamlx-0.6.4/enamlx/qt/qt_abstract_item_view.py 0000664 0000000 0000000 00000036004 14566137314 0021663 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 20, 2015
"""
from atom.api import Instance, Int
from enaml.application import timed_call
from enaml.qt.q_resource_helpers import get_cached_qcolor, get_cached_qicon
from enaml.qt.qt_control import QtControl
from qtpy.QtCore import QAbstractItemModel, QItemSelectionModel, Qt
from qtpy.QtWidgets import QAbstractItemView
from enamlx.qt.qt_abstract_item import TEXT_H_ALIGNMENTS, TEXT_V_ALIGNMENTS
from enamlx.widgets.abstract_item_view import ProxyAbstractItemView
SELECTION_MODES = {
"extended": QAbstractItemView.ExtendedSelection,
"single": QAbstractItemView.SingleSelection,
"contiguous": QAbstractItemView.ContiguousSelection,
"multi": QAbstractItemView.MultiSelection,
"none": QAbstractItemView.NoSelection,
}
SELECTION_BEHAVIORS = {
"items": QAbstractItemView.SelectItems,
"rows": QAbstractItemView.SelectRows,
"columns": QAbstractItemView.SelectColumns,
}
class QAbstractAtomItemModel(object):
"""A mixin for an ItemModel"""
declaration = None
def setDeclaration(self, declaration):
"""Set the declaration this model will use for rendering
the the headers.
"""
assert isinstance(declaration.proxy, ProxyAbstractItemView), (
"The model declaration must be a QtAbstractItemView subclass. "
"Got {}".format(declaration)
)
self.declaration = declaration
def data(self, index, role):
"""Retrieve the data for the item at the given index"""
item = self.itemAt(index)
if not item:
return None
d = item.declaration
if role == Qt.DisplayRole:
return d.text
elif role == Qt.ToolTipRole:
return d.tool_tip
elif role == Qt.CheckStateRole and d.checkable:
return d.checked and Qt.Checked or Qt.Unchecked
elif role == Qt.DecorationRole and d.icon:
return get_cached_qicon(d.icon)
elif role == Qt.EditRole and d.editable:
return d.text
elif role == Qt.StatusTipRole:
return d.status_tip
elif role == Qt.TextAlignmentRole:
h, v = d.text_alignment
return TEXT_H_ALIGNMENTS[h] | TEXT_V_ALIGNMENTS[v]
elif role == Qt.ForegroundRole and d.foreground:
return get_cached_qcolor(d.foreground)
elif role == Qt.BackgroundRole and d.background:
return get_cached_qcolor(d.background)
# elif role == Qt.SizeHintRole and d.minimum_size:
# return d.minimum_size
return None
def itemAt(self, index):
"""Get the item at the given model index.
Returns
-------
item:
"""
raise NotImplementedError
def flags(self, index):
item = self.itemAt(index)
if not item:
return Qt.NoItemFlags
d = item.declaration
flags = Qt.ItemIsEnabled
if d.editable:
flags |= Qt.ItemIsEditable
if d.checkable:
flags |= Qt.ItemIsUserCheckable
if d.selectable:
flags |= Qt.ItemIsSelectable
return flags
def setData(self, index, value, role=Qt.EditRole):
"""Set the data for the item at the given index to the given value."""
item = self.itemAt(index)
if not item:
return False
d = item.declaration
if role == Qt.CheckStateRole:
checked = Qt.CheckState(value) == Qt.CheckState.Checked
if checked != d.checked:
d.checked = checked
d.toggled(checked)
return True
elif role == Qt.EditRole:
if value != d.text:
d.text = value
return True
return super(QAbstractAtomItemModel, self).setData(index, value, role)
def headerData(self, index, orientation, role):
"""QHeaderView respects the following item data roles:
TextAlignmentRole,
DisplayRole,
FontRole,
DecorationRole,
ForegroundRole,
BackgroundRole.
"""
d = self.declaration
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
try:
return d.horizontal_headers[index] if d.horizontal_headers else index
except IndexError:
return index
elif orientation == Qt.Vertical and role == Qt.DisplayRole:
try:
return d.vertical_headers[index] if d.vertical_headers else index
except IndexError:
return index
return None
def clear(self):
self.beginResetModel()
d = self.declaration
if d.items:
d.items = []
self.endResetModel()
class QtAbstractItemView(QtControl, ProxyAbstractItemView):
#: Reference to the view
widget = Instance(QAbstractItemView)
#: View model
model = Instance(QAbstractItemModel)
#: Hold reference to selection model to PySide segfault
selection_model = Instance(QItemSelectionModel)
#: Refreshing the view on every update makes it really slow
#: So if we defer refreshing until everything is added it's fast :)
_pending_timeout = Int(100)
_pending_view_refreshes = Int(0)
_pending_row_refreshes = Int(0)
_pending_column_refreshes = Int(0)
def init_widget(self):
super(QtAbstractItemView, self).init_widget()
self.init_model()
d = self.declaration
#: Enable context menus
self.widget.setContextMenuPolicy(Qt.CustomContextMenu)
self.set_selection_mode(d.selection_mode)
self.set_selection_behavior(d.selection_behavior)
self.set_alternating_row_colors(d.alternating_row_colors)
self.set_word_wrap(d.word_wrap)
self.set_resize_mode(d.resize_mode)
self.set_show_vertical_header(d.show_vertical_header)
self.set_show_horizontal_header(d.show_horizontal_header)
self.set_horizontal_stretch(d.horizontal_stretch)
self.set_vertical_stretch(d.vertical_stretch)
if d.cell_padding:
self.set_cell_padding(d.cell_padding)
if d.vertical_minimum_section_size:
self.set_vertical_minimum_section_size(d.vertical_minimum_section_size)
if d.vertical_sizes:
self.set_vertical_sizes(d.vertical_sizes)
if d.horizontal_minimum_section_size:
self.set_horizontal_minimum_section_size(d.horizontal_minimum_section_size)
if d.horizontal_sizes:
self.set_horizontal_sizes(d.horizontal_sizes)
self.init_signals()
def init_model(self):
raise NotImplementedError
def init_signals(self):
"""Connect signals"""
self.widget.activated.connect(self.on_item_activated)
self.widget.clicked.connect(self.on_item_clicked)
self.widget.doubleClicked.connect(self.on_item_double_clicked)
self.widget.entered.connect(self.on_item_entered)
self.widget.pressed.connect(self.on_item_pressed)
self.widget.customContextMenuRequested.connect(
self.on_custom_context_menu_requested
)
self.selection_model = self.widget.selectionModel()
self.selection_model.selectionChanged.connect(self.on_selection_changed)
self.widget.horizontalScrollBar().valueChanged.connect(
self.on_horizontal_scrollbar_moved
)
self.widget.verticalScrollBar().valueChanged.connect(
self.on_vertical_scrollbar_moved
)
def item_at(self, index):
return self.model.itemAt(index)
def destroy(self):
"""Make sure all the table widgets are destroyed first."""
self.model.clear()
super(QtAbstractItemView, self).destroy()
# -------------------------------------------------------------------------
# Widget Setters
# -------------------------------------------------------------------------
def set_selection_mode(self, mode):
self.widget.setSelectionMode(SELECTION_MODES[mode])
def set_selection_behavior(self, behavior):
self.widget.setSelectionBehavior(SELECTION_BEHAVIORS[behavior])
def set_scroll_to_bottom(self, enabled):
if enabled:
self.widget.scrollToBottom()
def set_alternating_row_colors(self, enabled):
self.widget.setAlternatingRowColors(enabled)
def set_sortable(self, sortable):
self.widget.setSortingEnabled(sortable)
def set_word_wrap(self, wrap):
self.widget.setWordWrap(wrap)
def set_auto_resize_columns(self, enabled):
if enabled:
self.widget.resizeColumnsToContents()
def set_visible_row(self, row):
self.widget.verticalScrollBar().setValue(row)
def set_visible_column(self, column):
self.widget.horizontalScrollBar().setValue(column)
def set_model(self, model):
if isinstance(model, QAbstractAtomItemModel):
model.setDeclaration(self.declaration)
self.widget.setModel(model)
self.model = self.widget.model()
def set_items(self, items):
"""Defer until later so the view is only updated after all items
are added.
"""
self.model.beginResetModel()
self._pending_view_refreshes += 1
timed_call(self._pending_timeout, self._refresh_layout)
self.model.endResetModel()
def set_selection(self, items):
pass
def set_horizontal_sizes(self, sizes):
header = self.widget.horizontalHeader()
for i, s in enumerate(sizes):
if s is not None and s >= 0:
header.resizeSection(i, s)
def set_vertical_sizes(self, sizes):
header = self.widget.verticalHeader()
for i, s in enumerate(sizes):
if s is not None and s >= 0:
header.resizeSection(i, s)
# -------------------------------------------------------------------------
# Widget Events
# -------------------------------------------------------------------------
def on_horizontal_scrollbar_moved(self, value):
"""When the scrollbar moves, queue a refresh of the visible
columns. This makes it only update the view when needed
making scrolling much smoother.
"""
self._pending_column_refreshes += 1
timed_call(0, self._refresh_visible_column, value)
def on_vertical_scrollbar_moved(self, value):
"""When the scrollbar moves, queue a refresh of the visible
rows. This makes it only update the view when needed
making scrolling much smoother.
"""
self._pending_row_refreshes += 1
timed_call(0, self._refresh_visible_row, value)
def on_item_activated(self, index):
item = self.item_at(index)
if not item:
return
parent = item.parent()
if parent != self:
parent.declaration.activated()
item.declaration.activated()
def on_item_clicked(self, index):
item = self.item_at(index)
if not item:
return
parent = item.parent()
if parent != self:
parent.declaration.clicked()
item.declaration.clicked()
def on_item_double_clicked(self, index):
item = self.item_at(index)
if not item:
return
parent = item.parent()
if parent != self:
parent.declaration.double_clicked()
item.declaration.double_clicked()
def on_item_pressed(self, index):
item = self.item_at(index)
if not item:
return
parent = item.parent()
if parent != self:
parent.declaration.pressed()
item.declaration.pressed()
def on_item_entered(self, index):
item = self.item_at(index)
if not item:
return
parent = item.parent()
if parent != self:
parent.declaration.entered()
item.declaration.entered()
def on_selection_changed(self, selected, deselected):
selection = self.declaration.selection[:]
for index in selected.indexes():
item = self.item_at(index)
if not item:
continue
d = item.declaration
selection.append(d)
if not d.selected:
d.selected = True
d.selection_changed(d.selected)
for index in deselected.indexes():
item = self.item_at(index)
if not item:
continue
d = item.declaration
try:
selection.remove(d)
except ValueError:
pass
if d.selected:
d.selected = False
d.selection_changed(d.selected)
self.declaration.selection = selection
def on_custom_context_menu_requested(self, pos):
item = self.item_at(self.widget.indexAt(pos))
if not item:
return
if item.menu:
item.menu.popup()
return
parent = item.parent()
if parent and hasattr(parent, "menu") and parent.menu:
parent.menu.popup()
def on_layout_refreshed(self):
pass
# -------------------------------------------------------------------------
# View refresh handlers
# -------------------------------------------------------------------------
def _refresh_layout(self):
"""This queues and batches model changes so that the layout is only
refreshed after the `_pending_timeout` expires. This prevents
the UI from refreshing when inserting or removing a large number of
items until the operation is complete.
"""
self._pending_view_refreshes -= 1
if self._pending_view_refreshes == 0:
try:
self.model.layoutChanged.emit()
self.on_layout_refreshed()
except RuntimeError:
# View can be destroyed before we get here
return
self._refresh_sizes()
def _refresh_sizes(self):
"""Refresh column sizes when the data changes."""
pass
# header = self.widget.horizontalHeader()
#
# for i,item in enumerate(self.items[0]):
# header.setResizeMode(i,RESIZE_MODES[item.declaration.resize_mode])
# if item.declaration.width:
# header.resizeSection(i,item.declaration.width)
def _refresh_visible_row(self, value):
"""Subclasses must implement this method to refresh the content in the
row with the content at the given index.
Parameters
----------
value: int
Index of the row that needs to be refreshed
"""
raise NotImplementedError
def _refresh_visible_column(self, value):
"""Subclasses must implement this method to refresh the content in the
column with the content at the given index.
Parameters
----------
value: int
Index of the column that needs to be refreshed
"""
raise NotImplementedError
enamlx-0.6.4/enamlx/qt/qt_double_spin_box.py 0000664 0000000 0000000 00000002546 14566137314 0021167 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 29, 2015
"""
from atom.api import Typed
from enaml.qt.qt_spin_box import QtSpinBox
from qtpy.QtWidgets import QDoubleSpinBox
from enamlx.widgets.double_spin_box import ProxyDoubleSpinBox
class QtDoubleSpinBox(QtSpinBox, ProxyDoubleSpinBox):
"""A Qt implementation of an Enaml SpinBox."""
#: A reference to the widget created by the proxy.
widget = Typed(QDoubleSpinBox)
# -------------------------------------------------------------------------
# Initialization API
# -------------------------------------------------------------------------
def create_widget(self):
"""Create the underlying QDoubleSpinBox widget."""
widget = QDoubleSpinBox(self.parent_widget())
widget.setKeyboardTracking(False)
self.widget = widget
def init_widget(self):
self.set_decimals(self.declaration.decimals)
super(QtDoubleSpinBox, self).init_widget()
# -------------------------------------------------------------------------
# ProxyDoubleSpinBox API
# -------------------------------------------------------------------------
def set_decimals(self, prec):
self.widget.setDecimals(prec)
enamlx-0.6.4/enamlx/qt/qt_factories.py 0000664 0000000 0000000 00000013174 14566137314 0017772 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 23, 2015
"""
from enaml.qt import qt_factories
def double_spin_box_factory():
from .qt_double_spin_box import QtDoubleSpinBox
return QtDoubleSpinBox
def graphics_view_factory():
from .qt_graphics_view import QtGraphicsView
return QtGraphicsView
def graphics_item_factory():
from .qt_graphics_view import QtGraphicsItem
return QtGraphicsItem
def graphics_item_group_factory():
from .qt_graphics_view import QtGraphicsItemGroup
return QtGraphicsItemGroup
def graphics_ellipse_item_factory():
from .qt_graphics_view import QtGraphicsEllipseItem
return QtGraphicsEllipseItem
def graphics_image_item_factory():
from .qt_graphics_view import QtGraphicsImageItem
return QtGraphicsImageItem
def graphics_line_item_factory():
from .qt_graphics_view import QtGraphicsLineItem
return QtGraphicsLineItem
def graphics_path_item_factory():
from .qt_graphics_view import QtGraphicsPathItem
return QtGraphicsPathItem
def graphics_polygon_item_factory():
from .qt_graphics_view import QtGraphicsPolygonItem
return QtGraphicsPolygonItem
def graphics_rect_item_factory():
from .qt_graphics_view import QtGraphicsRectItem
return QtGraphicsRectItem
def graphics_text_item_factory():
from .qt_graphics_view import QtGraphicsTextItem
return QtGraphicsTextItem
def graphics_widget_factory():
from .qt_graphics_view import QtGraphicsWidget
return QtGraphicsWidget
def key_event_factory():
from .qt_key_event import QtKeyEvent
return QtKeyEvent
def plot_area_factory():
from .qt_plot_area import QtPlotArea
return QtPlotArea
def plot_item_2d_factory():
from .qt_plot_area import QtPlotItem2D
return QtPlotItem2D
def plot_item_3d_factory():
from .qt_plot_area import QtPlotItem3D
return QtPlotItem3D
def plot_item_array_factory():
from .qt_plot_area import QtPlotItemArray
return QtPlotItemArray
def plot_item_array_3d_factory():
from .qt_plot_area import QtPlotItemArray3D
return QtPlotItemArray3D
def plot_item_list_factory():
from .qt_plot_area import QtPlotItemList
return QtPlotItemList
def plot_item_dict_factory():
from .qt_plot_area import QtPlotItemDict
return QtPlotItemDict
def table_view_factory():
from .qt_table_view import QtTableView
return QtTableView
def table_view_item_factory():
from .qt_table_view import QtTableViewItem
return QtTableViewItem
def table_view_row_factory():
from .qt_table_view import QtTableViewRow
return QtTableViewRow
def table_view_col_factory():
from .qt_table_view import QtTableViewColumn
return QtTableViewColumn
def table_widget_factory():
from .qt_table_widget import QtTableWidget
return QtTableWidget
def table_widget_item_factory():
from .qt_table_widget import QtTableWidgetItem
return QtTableWidgetItem
def table_widget_row_factory():
from .qt_table_widget import QtTableWidgetRow
return QtTableWidgetRow
def table_widget_col_factory():
from .qt_table_widget import QtTableWidgetColumn
return QtTableWidgetColumn
def tree_view_factory():
from .qt_tree_view import QtTreeView
return QtTreeView
def tree_view_item_factory():
from .qt_tree_view import QtTreeViewItem
return QtTreeViewItem
def tree_view_col_factory():
from .qt_tree_view import QtTreeViewColumn
return QtTreeViewColumn
def tree_widget_factory():
from .qt_tree_widget import QtTreeWidget
return QtTreeWidget
def tree_widget_item_factory():
from .qt_tree_widget import QtTreeWidgetItem
return QtTreeWidgetItem
def tree_widget_col_factory():
from .qt_tree_widget import QtTreeWidgetColumn
return QtTreeWidgetColumn
# Inject the factory
qt_factories.QT_FACTORIES.update(
{
"DoubleSpinBox": double_spin_box_factory,
"GraphicsView": graphics_view_factory,
"GraphicsItem": graphics_item_factory,
"GraphicsItemGroup": graphics_item_group_factory,
"GraphicsEllipseItem": graphics_ellipse_item_factory,
"GraphicsLineItem": graphics_line_item_factory,
"GraphicsPathItem": graphics_path_item_factory,
"GraphicsPolygonItem": graphics_polygon_item_factory,
"GraphicsRectItem": graphics_rect_item_factory,
"GraphicsTextItem": graphics_text_item_factory,
"GraphicsImageItem": graphics_image_item_factory,
"GraphicsWidget": graphics_widget_factory,
"KeyEvent": key_event_factory,
"PlotArea": plot_area_factory,
"PlotItem2D": plot_item_2d_factory,
"PlotItem3D": plot_item_3d_factory,
"PlotItemArray": plot_item_array_factory,
"PlotItemArray3D": plot_item_array_3d_factory,
"PlotItemList": plot_item_list_factory,
"PlotItemDict": plot_item_dict_factory,
"TableView": table_view_factory,
"TableViewItem": table_view_item_factory,
"TableViewRow": table_view_row_factory,
"TableViewColumn": table_view_col_factory,
"TableWidget": table_widget_factory,
"TableWidgetItem": table_widget_item_factory,
"TableWidgetRow": table_widget_row_factory,
"TableWidgetColumn": table_widget_col_factory,
"TreeView": tree_view_factory,
"TreeViewItem": tree_view_item_factory,
"TreeViewColumn": tree_view_col_factory,
"TreeWidget": tree_widget_factory,
"TreeWidgetItem": tree_widget_item_factory,
"TreeWidgetColumn": tree_widget_col_factory,
}
)
enamlx-0.6.4/enamlx/qt/qt_graphics_view.py 0000664 0000000 0000000 00000112626 14566137314 0020647 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2018, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Sept 4, 2018
"""
import warnings
from atom.api import Atom, Coerced, Int, Typed, atomref
from enaml.drag_drop import DropAction
from enaml.qt.q_resource_helpers import (
get_cached_qcolor,
get_cached_qfont,
get_cached_qimage,
)
from enaml.qt.qt_control import QtControl
from enaml.qt.qt_drag_drop import QtDropEvent
from enaml.qt.qt_toolkit_object import QtToolkitObject
from enaml.qt.qt_widget import QtWidget, focus_registry
from enaml.qt.QtCore import QPoint, QPointF, QRectF, Qt
from enaml.qt.QtGui import (
QBrush,
QColor,
QCursor,
QDrag,
QPainter,
QPen,
QPixmap,
QPolygonF,
)
from enaml.qt.QtWidgets import (
QFrame,
QGraphicsEllipseItem,
QGraphicsItem,
QGraphicsItemGroup,
QGraphicsLineItem,
QGraphicsObject,
QGraphicsPathItem,
QGraphicsPixmapItem,
QGraphicsPolygonItem,
QGraphicsProxyWidget,
QGraphicsRectItem,
QGraphicsScene,
QGraphicsSimpleTextItem,
QGraphicsView,
QWidgetAction,
)
from enaml.widgets.widget import Feature
from enamlx.widgets.graphics_view import (
GraphicFeature,
Point,
ProxyAbstractGraphicsShapeItem,
ProxyGraphicsEllipseItem,
ProxyGraphicsImageItem,
ProxyGraphicsItem,
ProxyGraphicsItemGroup,
ProxyGraphicsLineItem,
ProxyGraphicsPathItem,
ProxyGraphicsPolygonItem,
ProxyGraphicsRectItem,
ProxyGraphicsTextItem,
ProxyGraphicsView,
ProxyGraphicsWidget,
)
PEN_STYLES = {
"none": Qt.NoPen,
"solid": Qt.SolidLine,
"dash": Qt.DashLine,
"dot": Qt.DotLine,
"dash_dot": Qt.DashDotLine,
"dash_dot_dot": Qt.DashDotDotLine,
"custom": Qt.CustomDashLine,
}
CAP_STYLES = {
"square": Qt.SquareCap,
"flat": Qt.FlatCap,
"round": Qt.RoundCap,
}
JOIN_STYLES = {
"bevel": Qt.BevelJoin,
"miter": Qt.MiterJoin,
"round": Qt.RoundJoin,
}
BRUSH_STYLES = {
"solid": Qt.SolidPattern,
"dense1": Qt.Dense1Pattern,
"dense2": Qt.Dense2Pattern,
"dense3": Qt.Dense3Pattern,
"dense4": Qt.Dense4Pattern,
"dense5": Qt.Dense5Pattern,
"dense6": Qt.Dense6Pattern,
"dense7": Qt.Dense7Pattern,
"horizontal": Qt.HorPattern,
"vertical": Qt.VerPattern,
"cross": Qt.CrossPattern,
"bdiag": Qt.BDiagPattern,
"fdiag": Qt.FDiagPattern,
"diag": Qt.DiagCrossPattern,
"linear": Qt.LinearGradientPattern,
"radial": Qt.RadialGradientPattern,
"conical": Qt.ConicalGradientPattern,
"texture": Qt.TexturePattern,
"none": Qt.NoBrush,
}
DRAG_MODES = {
"none": QGraphicsView.NoDrag,
"scroll": QGraphicsView.ScrollHandDrag,
"selection": QGraphicsView.RubberBandDrag,
}
# --------------------------------------------------------------------------
# Qt Resource Helpers
# --------------------------------------------------------------------------
def QPen_from_Pen(pen):
qpen = QPen()
if pen.color:
qpen.setColor(get_cached_qcolor(pen.color))
qpen.setWidth(int(pen.width))
qpen.setStyle(PEN_STYLES.get(pen.line_style))
qpen.setCapStyle(CAP_STYLES.get(pen.cap_style))
qpen.setJoinStyle(JOIN_STYLES.get(pen.join_style))
if pen.line_style == "custom":
qpen.setDashPattern(*pen.dash_pattern)
return qpen
def QBrush_from_Brush(brush):
qbrush = QBrush()
if brush.color:
qbrush.setColor(get_cached_qcolor(brush.color))
if brush.image:
qbrush.setTextureImage(get_cached_qimage(brush.image))
qbrush.setStyle(Qt.TexturePattern)
else:
qbrush.setStyle(BRUSH_STYLES.get(brush.style))
return qbrush
def get_cached_qpen(pen):
qpen = pen._tkdata
if not isinstance(qpen, QPen):
qpen = pen._tkdata = QPen_from_Pen(pen)
return qpen
def get_cached_qbrush(brush):
qbrush = brush._tkdata
if not isinstance(qbrush, QBrush):
qbrush = brush._tkdata = QBrush_from_Brush(brush)
return qbrush
# --------------------------------------------------------------------------
# Mixin classes
# --------------------------------------------------------------------------
class FeatureMixin(Atom):
"""A mixin that provides focus and mouse features."""
#: A private copy of the declaration features. This ensures that
#: feature cleanup will proceed correctly in the event that user
#: code modifies the declaration features value at runtime.
_features = Coerced(Feature, (0,))
_extra_features = Coerced(GraphicFeature, (0,))
#: Internal storage for the shared widget action.
_widget_action = Typed(QWidgetAction)
#: Internal storage for the drag origin position.
_drag_origin = Typed(QPointF)
# --------------------------------------------------------------------------
# Private API
# --------------------------------------------------------------------------
def _setup_features(self):
"""Setup the advanced widget feature handlers."""
features = self._features = self.declaration.features
if not features:
return
if features & Feature.FocusTraversal:
self.hook_focus_traversal()
if features & Feature.FocusEvents:
self.hook_focus_events()
if features & Feature.DragEnabled:
self.hook_drag()
if features & Feature.DropEnabled:
self.hook_drop()
features = self._extra_features
if features & GraphicFeature.WheelEvent:
self.hook_wheel()
if features & GraphicFeature.DrawEvent:
self.hook_draw()
def _teardown_features(self):
"""Teardowns the advanced widget feature handlers."""
features = self._features
if not features:
return
if features & Feature.FocusTraversal:
self.unhook_focus_traversal()
if features & Feature.FocusEvents:
self.unhook_focus_events()
if features & Feature.DragEnabled:
self.unhook_drag()
if features & Feature.DropEnabled:
self.unhook_drop()
features = self._extra_features
if features & GraphicFeature.WheelEvent:
self.unhook_wheel()
if features & GraphicFeature.DrawEvent:
self.unhook_draw()
# --------------------------------------------------------------------------
# Protected API
# --------------------------------------------------------------------------
def tab_focus_request(self, reason):
"""Handle a custom tab focus request.
This method is called when focus is being set on the proxy
as a result of a user-implemented focus traversal handler.
This can be reimplemented by subclasses as needed.
Parameters
----------
reason : Qt.FocusReason
The reason value for the focus request.
Returns
-------
result : bool
True if focus was set, False otherwise.
"""
widget = self.focus_target()
if not widget.focusPolicy & Qt.TabFocus:
return False
if not widget.isEnabled():
return False
if not widget.isVisibleTo(widget.window()):
return False
widget.setFocus(reason)
return False
def focus_target(self):
"""Return the current focus target for a focus request.
This can be reimplemented by subclasses as needed. The default
implementation of this method returns the current proxy widget.
"""
return self.widget
def hook_focus_traversal(self):
"""Install the hooks for focus traversal.
This method may be overridden by subclasses as needed.
"""
self.widget.focusNextPrevChild = self.focusNextPrevChild
def unhook_focus_traversal(self):
"""Remove the hooks for the next/prev child focusing.
This method may be overridden by subclasses as needed.
"""
del self.widget.focusNextPrevChild
def hook_focus_events(self):
"""Install the hooks for focus events.
This method may be overridden by subclasses as needed.
"""
widget = self.widget
widget.focusInEvent = self.focusInEvent
widget.focusOutEvent = self.focusOutEvent
def unhook_focus_events(self):
"""Remove the hooks for the focus events.
This method may be overridden by subclasses as needed.
"""
widget = self.widget
del widget.focusInEvent
del widget.focusOutEvent
def focusNextPrevChild(self, next_child):
"""The default 'focusNextPrevChild' implementation."""
fd = focus_registry.focused_declaration()
if next_child:
child = self.declaration.next_focus_child(fd)
reason = Qt.TabFocusReason
else:
child = self.declaration.previous_focus_child(fd)
reason = Qt.BacktabFocusReason
if child is not None and child.proxy_is_active:
return child.proxy.tab_focus_request(reason)
widget = self.widget
return type(widget).focusNextPrevChild(widget, next_child)
def focusInEvent(self, event):
"""The default 'focusInEvent' implementation."""
widget = self.widget
type(widget).focusInEvent(widget, event)
self.declaration.focus_gained()
def focusOutEvent(self, event):
"""The default 'focusOutEvent' implementation."""
widget = self.widget
type(widget).focusOutEvent(widget, event)
self.declaration.focus_lost()
def hook_drag(self):
"""Install the hooks for drag operations."""
widget = self.widget
widget.mousePressEvent = self.mousePressEvent
widget.mouseMoveEvent = self.mouseMoveEvent
widget.mouseReleaseEvent = self.mouseReleaseEvent
def unhook_drag(self):
"""Remove the hooks for drag operations."""
widget = self.widget
del widget.mousePressEvent
del widget.mouseMoveEvent
del widget.mouseReleaseEvent
def mousePressEvent(self, event):
"""Handle the mouse press event for a drag operation."""
if event.button() == Qt.LeftButton:
self._drag_origin = event.pos()
widget = self.widget
type(widget).mousePressEvent(widget, event)
def mouseMoveEvent(self, event):
"""Handle the mouse move event for a drag operation."""
# if event.buttons() & Qt.LeftButton and self._drag_origin is not None:
# dist = (event.pos() - self._drag_origin).manhattanLength()
# if dist >= QApplication.startDragDistance():
# self.do_drag(event.widget())
# self._drag_origin = None
# return # Don't returns
widget = self.widget
type(widget).mouseMoveEvent(widget, event)
def mouseReleaseEvent(self, event):
"""Handle the mouse release event for the drag operation."""
if event.button() == Qt.LeftButton:
self._drag_origin = None
widget = self.widget
type(widget).mouseReleaseEvent(widget, event)
def hook_drop(self):
"""Install hooks for drop operations."""
widget = self.widget
widget.setAcceptDrops(True)
widget.dragEnterEvent = self.dragEnterEvent
widget.dragMoveEvent = self.dragMoveEvent
widget.dragLeaveEvent = self.dragLeaveEvent
widget.dropEvent = self.dropEvent
def unhook_drop(self):
"""Remove hooks for drop operations."""
widget = self.widget
widget.setAcceptDrops(False)
del widget.dragEnterEvent
del widget.dragMoveEvent
del widget.dragLeaveEvent
del widget.dropEvent
def do_drag(self, widget):
"""Perform the drag operation for the widget.
Parameters
----------
widget: QWidget
A reference to the viewport widget.
"""
drag_data = self.declaration.drag_start()
if drag_data is None:
return
# widget = self.widget
qdrag = QDrag(widget)
qdrag.setMimeData(drag_data.mime_data.q_data())
if drag_data.image is not None:
qimg = get_cached_qimage(drag_data.image)
qdrag.setPixmap(QPixmap.fromImage(qimg))
# else:
# if __version_info__ < (5, ):
# qdrag.setPixmap(QPixmap.grabWidget(self.widget))
# else:
# qdrag.setPixmap(widget.grab())
if drag_data.hotspot:
qdrag.setHotSpot(QPoint(*drag_data.hotspot))
else:
cursor_position = widget.mapFromGlobal(QCursor.pos())
qdrag.setHotSpot(cursor_position)
default = Qt.DropAction(drag_data.default_drop_action)
supported = Qt.DropActions(drag_data.supported_actions)
qresult = qdrag.exec_(supported, default)
self.declaration.drag_end(drag_data, DropAction(int(qresult)))
def dragEnterEvent(self, event):
"""Handle the drag enter event for the widget."""
self.declaration.drag_enter(QtDropEvent(event))
def dragMoveEvent(self, event):
"""Handle the drag move event for the widget."""
self.declaration.drag_move(QtDropEvent(event))
def dragLeaveEvent(self, event):
"""Handle the drag leave event for the widget."""
self.declaration.drag_leave()
def dropEvent(self, event):
"""Handle the drop event for the widget."""
self.declaration.drop(QtDropEvent(event))
def hook_wheel(self):
"""Install the hooks for wheel events."""
widget = self.widget
widget.wheelEvent = self.wheelEvent
def unhook_wheel(self):
"""Removes the hooks for wheel events."""
widget = self.widget
del widget.wheelEvent
def wheelEvent(self, event):
"""Handle the mouse wheel event for the widget."""
self.declaration.wheel_event(event)
def hook_draw(self):
"""Remove the hooks for the draw (paint) event.
This method may be overridden by subclasses as needed.
"""
widget = self.widget
widget.paint = self.draw
def unhook_draw(self):
"""Remove the hooks for the draw (paint) event.
This method may be overridden by subclasses as needed.
"""
widget = self.widget
del widget.paint
def draw(self, painter, options, widget):
"""Handle the draw event for the widget."""
self.declaration.draw(painter, options, widget)
# --------------------------------------------------------------------------
# Framework API
# --------------------------------------------------------------------------
def get_action(self, create=False):
"""Get the shared widget action for this widget.
This API is used to support widgets in tool bars and menus.
Parameters
----------
create : bool, optional
Whether to create the action if it doesn't already exist.
The default is False.
Returns
-------
result : QWidgetAction or None
The cached widget action or None, depending on arguments.
"""
action = self._widget_action
if action is None and create:
action = self._widget_action = QWidgetAction(None)
action.setDefaultWidget(self.widget)
return action
# --------------------------------------------------------------------------
# Toolkit implementations
# --------------------------------------------------------------------------
class QtGraphicsView(QtControl, ProxyGraphicsView):
#: Internal widget
widget = Typed(QGraphicsView)
#: Internal scene
scene = Typed(QGraphicsScene)
#: View Range
view_range = Typed(QRectF, (0, 0, 1, 1))
#: Custom features
_extra_features = Coerced(GraphicFeature, (0,))
#: Cyclic notification guard. This a bitfield of multiple guards.
_guards = Int(0)
def create_widget(self):
self.scene = QGraphicsScene()
self.widget = QGraphicsView(self.scene, self.parent_widget())
def init_widget(self):
d = self.declaration
self._extra_features = d.extra_features
super(QtGraphicsView, self).init_widget()
widget = self.widget
widget.setCacheMode(QGraphicsView.CacheBackground)
widget.setFocusPolicy(Qt.StrongFocus)
widget.setFrameShape(QFrame.NoFrame)
widget.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
widget.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
widget.setResizeAnchor(QGraphicsView.AnchorViewCenter)
widget.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
widget.setMouseTracking(True)
self.set_drag_mode(d.drag_mode)
self.set_renderer(d.renderer)
self.set_antialiasing(d.antialiasing)
self.scene.selectionChanged.connect(self.on_selection_changed)
def init_layout(self):
super(QtGraphicsView, self).init_layout()
scene = self.scene
for item in self.scene_items():
scene.addItem(item)
self.set_view_range(self.view_range)
def child_added(self, child):
if isinstance(child, QtGraphicsItem):
self.scene.addItem(child.widget)
else:
super(QtGraphicsView, self).child_added(child)
def child_removed(self, child):
if isinstance(child, QtGraphicsItem):
self.scene.removeItem(child.widget)
else:
super(QtGraphicsView, self).child_removed(child)
def scene_items(self):
for w in self.children():
if isinstance(w, QtGraphicsItem):
yield w.widget
# --------------------------------------------------------------------------
# GraphicFeature API
# --------------------------------------------------------------------------
def _setup_features(self):
super(QtGraphicsView, self)._setup_features()
features = self._extra_features
if features & GraphicFeature.MouseEvent:
self.hook_drag()
if features & GraphicFeature.WheelEvent:
self.hook_wheel()
self.hook_resize()
def _teardown_features(self):
super(QtGraphicsView, self)._teardown_features()
features = self._extra_features
if features & GraphicFeature.MouseEvent:
self.unhook_drag()
if features & GraphicFeature.WheelEvent:
self.unhook_wheel()
self.unhook_resize()
def hook_wheel(self):
"""Install the hooks for wheel events."""
widget = self.widget
widget.wheelEvent = self.wheelEvent
def unhook_wheel(self):
"""Removes the hooks for wheel events."""
widget = self.widget
del widget.wheelEvent
def hook_draw(self):
"""Install the hooks for background draw events."""
widget = self.widget
widget.drawBackground = self.drawBackground
def unhook_draw(self):
"""Removes the hooks for background draw events."""
widget = self.widget
del widget.drawBackground
def hook_resize(self):
"""Install the hooks for resize events."""
widget = self.widget
widget.resizeEvent = self.resizeEvent
def unhook_resize(self):
"""Removes the hooks for resize events."""
widget = self.widget
del widget.resizeEvent
# --------------------------------------------------------------------------
# QGraphicView API
# --------------------------------------------------------------------------
def wheelEvent(self, event):
"""Handle the wheel event for the widget."""
self.declaration.wheel_event(event)
def drawBackground(self, painter, rect):
"""Handle the drawBackground request"""
self.declaration.draw_background(painter, rect)
def mousePressEvent(self, event):
"""Handle the mouse press event for a drag operation."""
self.declaration.mouse_press_event(event)
super(QtGraphicsView, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
"""Handle the mouse move event for a drag operation."""
self.declaration.mouse_move_event(event)
super(QtGraphicsView, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
"""Handle the mouse release event for the drag operation."""
self.declaration.mouse_release_event(event)
super(QtGraphicsView, self).mouseReleaseEvent(event)
def resizeEvent(self, event):
"""Resize the view range to match the widget size"""
d = self.declaration
widget = self.widget
if d.auto_range:
size = self.widget.size()
view_range = QRectF(0, 0, size.width(), size.height())
# view_range = self.scene.itemsBoundingRect()
else:
# Return the boundaries of the view in scene coordinates
r = QRectF(widget.rect())
view_range = widget.viewportTransform().inverted()[0].mapRect(r)
self.set_view_range(view_range)
# --------------------------------------------------------------------------
# ProxyGraphicsView API
# --------------------------------------------------------------------------
def set_view_range(self, view_range):
"""Set the visible scene rect to override the default behavior
of limiting panning and viewing to the graphics items view.
Based on Luke Campagnola's updateMatrix of pyqtgraph's GraphicsView
"""
d = self.declaration
self.view_range = view_range
widget = self.widget
widget.setSceneRect(view_range)
if d.auto_range:
widget.resetTransform()
else:
if d.lock_aspect_ratio:
flags = Qt.KeepAspectRatio
else:
flags = Qt.IgnoreAspectRatio
widget.fitInView(view_range, flags)
def set_drag_mode(self, mode):
self.widget.setDragMode(DRAG_MODES.get(mode))
def set_auto_range(self, enabled):
self.set_view_range(self.view_range)
def set_lock_aspect_ratio(self, locked):
self.set_view_range(self.view_range)
def set_antialiasing(self, enabled):
flags = self.widget.renderHints()
if enabled:
flags |= QPainter.Antialiasing
else:
flags &= ~QPainter.Antialiasing
self.widget.setRenderHints(flags)
def set_renderer(self, renderer):
"""Set the viewport widget."""
viewport = None
if renderer == "opengl":
from enaml.qt.QtWidgets import QOpenGLWidget
viewport = QOpenGLWidget()
elif renderer == "default":
try:
from enaml.qt.QtWidgets import QOpenGLWidget
viewport = QOpenGLWidget()
except ImportError as e:
warnings.warn("QOpenGLWidget could not be imported: {}".format(e))
self.widget.setViewport(viewport)
def set_selected_items(self, items):
if self._guards & 0x01:
return
self.scene.clearSelection()
for item in items:
item.selected = True
def on_selection_changed(self):
"""Callback invoked one the selection has changed."""
d = self.declaration
selection = self.scene.selectedItems()
self._guards |= 0x01
try:
d.selected_items = [
item.ref().declaration for item in selection if item.ref()
]
finally:
self._guards &= ~0x01
def set_background(self, background):
"""Set the background color of the widget."""
scene = self.scene
scene.setBackgroundBrush(QColor.fromRgba(background.argb))
def get_item_at(self, point):
item = self.scene.getItemAt(point.x, point.y)
if item and item.ref():
return item.ref().declaration
def get_items_at(self, point):
items = self.scene.items(point.x, point.y)
return [item.ref().declaration for item in items if item.ref()]
def get_items_in(self, top_left, bottom_right):
qrect = QRectF(
QPointF(top_left.x, top_left.y), QPointF(bottom_right.x, bottom_right.y)
)
items = self.scene.items(qrect)
return [item.ref().declaration for item in items if item.ref()]
def fit_in_view(self, item):
self.widget.fitInView(item.proxy.widget)
def center_on(self, item):
if isinstance(item, Point):
self.widget.centerOn(item.x, item.y)
else:
self.widget.centerOn(item.proxy.widget)
def reset_view(self):
self.widget.resetTransform()
def translate_view(self, x, y):
view_range = self.view_range.adjusted(x, y, x, y)
self.set_view_range(view_range)
return view_range
def scale_view(self, x, y):
"""Scale the zoom but keep in in the min and max zoom bounds."""
d = self.declaration
sx, sy = float(x), float(y)
if d.lock_aspect_ratio:
sy = sx
view_range = self.view_range
center = view_range.center()
w, h = view_range.width() / sx, view_range.height() / sy
cx, cy = center.x(), center.y()
x = cx - (cx - view_range.left()) / sx
y = cy - (cy - view_range.top()) / sy
view_range = QRectF(x, y, w, h)
self.set_view_range(view_range)
return view_range
def rotate_view(self, angle):
self.widget.rotate(angle)
def map_from_scene(self, point):
qpoint = self.widget.mapFromScene(point.x, point.y)
return Point(qpoint.x(), qpoint.y())
def map_to_scene(self, point):
qpoint = self.widget.mapToScene(point.x, point.y)
return Point(qpoint.x(), qpoint.y())
def pixel_density(self):
tr = self.widget.transform().inverted()[0]
p = tr.map(QPoint(1, 1)) - tr.map(QPoint(0, 0))
return Point(p.x(), p.y())
class QtGraphicsItem(QtToolkitObject, ProxyGraphicsItem, FeatureMixin):
"""QtGraphicsItem is essentially a copy of QtWidget except that
it uses `self.widget`, `create_widget` and `init_widget` instead of
widget.
"""
#: Internal item
widget = Typed(QGraphicsObject)
#: Cyclic notification guard. This a bitfield of multiple guards.
#: 0x01 is position
_guards = Int(0)
# --------------------------------------------------------------------------
# Initialization API
# --------------------------------------------------------------------------
def create_widget(self):
self.widget = QGraphicsObject(self.parent_widget())
def init_widget(self):
widget = self.widget
# Save a reference so we can retrieve the QtGraphicsItem
# from the QGraphicsItem
widget.ref = atomref(self)
focus_registry.register(widget, self)
d = self.declaration
self._extra_features = d.extra_features
self._setup_features()
if d.selectable:
self.set_selectable(d.selectable)
if d.movable:
self.set_movable(d.movable)
if d.tool_tip:
self.set_tool_tip(d.tool_tip)
if d.status_tip:
self.set_status_tip(d.status_tip)
if not d.enabled:
self.set_enabled(d.enabled)
if not d.visible:
self.set_visible(d.visible)
if d.opacity != 1:
self.set_opacity(d.opacity)
if d.rotation:
self.set_rotation(d.rotation)
if d.scale != 1:
self.set_scale(d.scale)
self.set_position(d.position)
self.hook_item_change()
def init_layout(self):
pass
# --------------------------------------------------------------------------
# ProxyToolkitObject API
# --------------------------------------------------------------------------
def destroy(self):
"""Destroy the underlying QWidget object."""
self._teardown_features()
focus_registry.unregister(self.widget)
widget = self.widget
if widget is not None:
del self.widget
super(QtGraphicsItem, self).destroy()
# If a QWidgetAction was created for this widget, then it has
# taken ownership of the widget and the widget will be deleted
# when the QWidgetAction is garbage collected. This means the
# superclass destroy() method must run before the reference to
# the QWidgetAction is dropped.
del self._widget_action
def parent_widget(self):
"""Reimplemented to only return GraphicsItems"""
parent = self.parent()
if parent is not None and isinstance(parent, QtGraphicsItem):
return parent.widget
def _teardown_features(self):
super(QtGraphicsItem, self)._teardown_features()
self.unhook_item_change()
def hook_item_change(self):
widget = self.widget
widget.itemChange = self.itemChange
def unhook_item_change(self):
del self.widget.itemChange
# --------------------------------------------------------------------------
# Signals
# --------------------------------------------------------------------------
def itemChange(self, change, value):
widget = self.widget
if change == QGraphicsItem.ItemPositionHasChanged:
pos = widget.pos()
pos = Point(pos.x(), pos.y(), widget.zValue())
self._guards |= 0x01
try:
self.declaration.position = pos
finally:
self._guards &= ~0x01
elif change == QGraphicsItem.ItemSelectedChange:
self.declaration.selected = widget.isSelected()
return type(widget).itemChange(widget, change, value)
# --------------------------------------------------------------------------
# ProxyGraphicsItem API
# --------------------------------------------------------------------------
def set_visible(self, visible):
self.widget.setVisible(visible)
def set_enabled(self, enabled):
self.widget.setEnabled(enabled)
def set_selectable(self, enabled):
self.widget.setFlag(QGraphicsItem.ItemIsSelectable, enabled)
def set_movable(self, enabled):
self.widget.setFlag(QGraphicsItem.ItemIsMovable, enabled)
def set_x(self, x):
if self._guards & 0x01:
return
self.widget.setX(x)
def set_y(self, y):
if self._guards & 0x01:
return
self.widget.setY(y)
def set_z(self, z):
if self._guards & 0x01:
return
self.widget.setZValue(z)
def set_position(self, position):
if self._guards & 0x01:
return
pos = self.declaration.position
w = self.widget
w.setPos(pos.x, pos.y)
w.setZValue(pos.z)
def set_rotation(self, rotation):
self.widget.setRotation(rotation)
def set_scale(self, scale):
self.widget.setScale(scale)
def set_opacity(self, opacity):
self.widget.setOpacity(opacity)
def set_selected(self, selected):
self.widget.setSelected(selected)
def set_tool_tip(self, tool_tip):
self.widget.setToolTip(tool_tip)
def set_status_tip(self, status_tip):
self.widget.setToolTip(status_tip)
class QtGraphicsItemGroup(QtGraphicsItem, ProxyGraphicsItemGroup):
#: Internal widget
widget = Typed(QGraphicsItemGroup)
def create_widget(self):
self.widget = QGraphicsItemGroup(self.parent_widget())
def init_layout(self):
super(QtGraphicsItemGroup, self).init_layout()
widget = self.widget
for item in self.child_widgets():
widget.addToGroup(item)
def child_added(self, child):
super(QtGraphicsItemGroup, self).child_added(child)
if isinstance(child, QtGraphicsItem):
self.widget.addToGroup(child.widget)
def child_removed(self, child):
super(QtGraphicsItemGroup, self).child_removed(child)
if isinstance(child, QtGraphicsItem):
self.widget.removeFromGroup(child.widget)
class QtGraphicsWidget(QtGraphicsItem, ProxyGraphicsWidget):
#: Internal widget
widget = Typed(QGraphicsProxyWidget)
def create_widget(self):
"""Deferred to the layout pass"""
pass
def init_widget(self):
pass
def init_layout(self):
"""Create the widget in the layout pass after the child widget has
been created and intialized. We do this so the child widget does not
attempt to use this proxy widget as its parent and because
repositioning must be done after the widget is set.
"""
self.widget = QGraphicsProxyWidget(self.parent_widget())
widget = self.widget
for item in self.child_widgets():
widget.setWidget(item)
break
super(QtGraphicsWidget, self).init_widget()
super(QtGraphicsWidget, self).init_layout()
def child_added(self, child):
super(QtGraphicsItemGroup, self).child_added(child)
if isinstance(child, QtWidget):
self.widget.setWidget(child.widget)
class QtAbstractGraphicsShapeItem(QtGraphicsItem, ProxyAbstractGraphicsShapeItem):
def init_widget(self):
super(QtAbstractGraphicsShapeItem, self).init_widget()
d = self.declaration
if d.pen:
self.set_pen(d.pen)
if d.brush:
self.set_brush(d.brush)
def set_pen(self, pen):
if pen:
self.widget.setPen(get_cached_qpen(pen))
else:
self.widget.setPen(QPen())
def set_brush(self, brush):
if brush:
self.widget.setBrush(get_cached_qbrush(brush))
else:
self.widget.setBrush(QBrush())
class QtGraphicsLineItem(QtAbstractGraphicsShapeItem, ProxyGraphicsLineItem):
#: Internal widget
widget = Typed(QGraphicsLineItem)
def create_widget(self):
self.widget = QGraphicsLineItem(self.parent_widget())
def set_position(self, position):
self.set_point(self.declaration.point)
def set_point(self, point):
pos = self.declaration.position
self.widget.setLine(pos.x, pos.y, *point[:2])
class QtGraphicsEllipseItem(QtAbstractGraphicsShapeItem, ProxyGraphicsEllipseItem):
#: Internal widget
widget = Typed(QGraphicsEllipseItem)
def create_widget(self):
self.widget = QGraphicsEllipseItem(self.parent_widget())
def init_widget(self):
super(QtGraphicsEllipseItem, self).init_widget()
d = self.declaration
self.set_start_angle(d.start_angle)
self.set_span_angle(d.span_angle)
def set_position(self, position):
self.update_rect()
def set_width(self, width):
self.update_rect()
def set_height(self, height):
self.update_rect()
def update_rect(self):
d = self.declaration
pos = d.position
self.widget.setRect(pos.x, pos.y, d.width, d.height)
def set_span_angle(self, angle):
self.widget.setSpanAngle(int(angle * 16))
def set_start_angle(self, angle):
self.widget.setStartAngle(int(angle * 16))
class QtGraphicsRectItem(QtAbstractGraphicsShapeItem, ProxyGraphicsRectItem):
#: Internal widget
widget = Typed(QGraphicsRectItem)
def create_widget(self):
self.widget = QGraphicsRectItem(self.parent_widget())
def set_position(self, position):
self.update_rect()
def set_width(self, width):
self.update_rect()
def set_height(self, height):
self.update_rect()
def update_rect(self):
d = self.declaration
pos = d.position
self.widget.setRect(pos.x, pos.y, d.width, d.height)
class QtGraphicsTextItem(QtAbstractGraphicsShapeItem, ProxyGraphicsTextItem):
#: Internal widget
widget = Typed(QGraphicsSimpleTextItem)
def create_widget(self):
self.widget = QGraphicsSimpleTextItem(self.parent_widget())
def init_widget(self):
super(QtGraphicsTextItem, self).init_widget()
d = self.declaration
if d.text:
self.set_text(d.text)
if d.font:
self.set_font(d.font)
def set_text(self, text):
self.widget.setText(text)
def set_font(self, font):
self.widget.setFont(get_cached_qfont(font))
class QtGraphicsPolygonItem(QtAbstractGraphicsShapeItem, ProxyGraphicsPolygonItem):
#: Internal widget
widget = Typed(QGraphicsPolygonItem)
def create_widget(self):
self.widget = QGraphicsPolygonItem(self.parent_widget())
def init_widget(self):
super(QtGraphicsPolygonItem, self).init_widget()
d = self.declaration
self.set_points(d.points)
def set_points(self, points):
polygon = QPolygonF([QPointF(*p[:2]) for p in points])
self.widget.setPolygon(polygon)
class QtGraphicsPathItem(QtAbstractGraphicsShapeItem, ProxyGraphicsPathItem):
#: Internal widget
widget = Typed(QGraphicsPathItem)
def create_widget(self):
self.widget = QGraphicsPathItem(self.parent_widget())
def init_widget(self):
super(QtGraphicsPathItem, self).init_widget()
d = self.declaration
if d.path:
self.set_path(d.path)
def set_path(self, path):
#: TODO: Convert path
self.widget.setPath(path)
class QtGraphicsImageItem(QtGraphicsItem, ProxyGraphicsImageItem):
#: Internal widget
widget = Typed(QGraphicsPixmapItem)
def create_widget(self):
self.widget = QGraphicsPixmapItem(self.parent_widget())
def init_widget(self):
super(QtGraphicsImageItem, self).init_widget()
d = self.declaration
if d.image:
self.set_image(d.image)
def set_image(self, image):
self.widget.setPixmap(QPixmap.fromImage(get_cached_qimage(image)))
enamlx-0.6.4/enamlx/qt/qt_key_event.py 0000664 0000000 0000000 00000011011 14566137314 0017770 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015-2022, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 29, 2015
"""
from atom.api import Callable, Dict, Instance
from enaml.qt.qt_control import QtControl
from qtpy import QtCore
from enamlx.widgets.key_event import ProxyKeyEvent
Qt = QtCore.Qt
MODIFIERS = {
"": Qt.NoModifier,
"shift": Qt.ShiftModifier,
"ctrl": Qt.ControlModifier,
"alt": Qt.AltModifier,
"meta": Qt.MetaModifier,
"keypad": Qt.KeypadModifier,
"group": Qt.GroupSwitchModifier,
}
KEYS = {
k.split("Key_")[-1].lower(): getattr(Qt, k)
for k in Qt.__dict__
if k.startswith("Key_")
}
class QtKeyEvent(QtControl, ProxyKeyEvent):
# Reference to the original handler
_keyPressEvent = Callable()
# Reference to the original handler
_keyReleaseEvent = Callable()
#: Widget that this key press handler is overriding
widget = Instance(QtCore.QObject)
#: Key codes to match
codes = Dict()
def create_widget(self):
"""The KeyEvent uses the parent_widget as it's widget"""
self.widget = self.parent_widget()
def init_widget(self):
"""The KeyEvent uses the parent_widget as it's widget"""
super(QtKeyEvent, self).init_widget()
d = self.declaration
widget = self.widget
self._keyPressEvent = widget.keyPressEvent
self._keyReleaseEvent = widget.keyReleaseEvent
self.set_enabled(d.enabled)
self.set_keys(d.keys)
# -------------------------------------------------------------------------
# ProxyKeyEvent API
# -------------------------------------------------------------------------
def set_enabled(self, enabled):
widget = self.widget
if enabled:
widget.keyPressEvent = lambda event: self.on_key_press(event)
widget.keyReleaseEvent = lambda event: self.on_key_release(event)
else:
# Restore original
widget.keyPressEvent = self._keyPressEvent
widget.keyReleaseEvent = self._keyReleaseEvent
def set_keys(self, keys):
"""Parse all the key codes and save them"""
codes = {}
for key in keys:
parts = [k.strip().lower() for k in key.split("+")]
code = KEYS.get(parts[-1])
modifier = Qt.KeyboardModifier(0)
if code is None:
raise KeyError("Invalid key code '{}'".format(key))
if len(parts) > 1:
for mod in parts[:-1]:
mod_code = MODIFIERS.get(mod)
if mod_code is None:
raise KeyError("Invalid key modifier '{}'".format(mod_code))
modifier |= mod_code
if code not in codes:
codes[code] = []
codes[code].append(modifier)
self.codes = codes
# -------------------------------------------------------------------------
# QWidget Keys API
# -------------------------------------------------------------------------
def is_matching_key(self, code, mods):
codes = self.codes.get(code, None)
if codes is None:
return False
return mods in codes
def on_key_press(self, event):
d = self.declaration
try:
code = event.key()
mods = event.modifiers()
is_repeat = event.isAutoRepeat()
if not self.codes or self.is_matching_key(code, mods):
if not d.repeats and is_repeat:
return
d.pressed(
{
"code": code,
"modifiers": mods,
"key": event.text(),
"repeated": is_repeat,
}
)
finally:
self._keyPressEvent(event)
def on_key_release(self, event):
d = self.declaration
try:
code = event.key()
mods = event.modifiers()
is_repeat = event.isAutoRepeat()
if not self.codes or self.is_matching_key(code, mods):
if not d.repeats and is_repeat:
return
d.released(
{
"code": code,
"key": event.text(),
"modifiers": mods,
"repeated": is_repeat,
}
)
finally:
self._keyReleaseEvent(event)
enamlx-0.6.4/enamlx/qt/qt_plot_area.py 0000664 0000000 0000000 00000036632 14566137314 0017765 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 31, 2015
"""
import types
import pyqtgraph as pg
from atom.api import Bool, ForwardInstance, Instance, Int, Typed
from enaml.application import timed_call
from enaml.qt.q_resource_helpers import get_cached_qcolor
from enaml.qt.qt_control import QtControl
from pyqtgraph.widgets.GraphicsLayoutWidget import GraphicsLayoutWidget
from enamlx.widgets.plot_area import ProxyPlotArea
def gl_view_widget():
from pyqtgraph.opengl import GLViewWidget
return GLViewWidget
class QtPlotArea(QtControl, ProxyPlotArea):
"""PyQtGraph Plot Widget"""
__weakref__ = None
widget = Typed(GraphicsLayoutWidget)
def create_widget(self):
self.widget = GraphicsLayoutWidget(self.parent_widget())
def init_layout(self):
for child in self.children():
self.child_added(child)
def child_added(self, child):
# TODO support layouts
if isinstance(child, AbstractQtPlotItem):
d = child.declaration
kwargs = dict(row=d.row, col=d.column) if d.row or d.column else {}
self.widget.addItem(child.widget, **kwargs)
def child_removed(self, child):
if isinstance(child, AbstractQtPlotItem):
self.widget.removeItem(child.widget)
class AbstractQtPlotItem(QtControl):
#: So we can receive signals
__weakref__ = None
#: Plot item or parent plot item if nested
widget = Instance(pg.PlotItem)
#: Actual plot
plot = Instance(pg.GraphicsObject)
#: Root or nested graph
is_root = Bool()
#: View box
viewbox = Instance(pg.ViewBox)
#: Axis item
axis = Instance(pg.AxisItem)
_pending_refreshes = Int(0)
def create_widget(self):
if isinstance(self.parent(), AbstractQtPlotItem):
self.widget = self.parent_widget()
self.is_root = False
else:
self.is_root = True
self.widget = pg.PlotItem()
def init_widget(self):
# super(AbstractQtPlotItem, self).init_widget()
d = self.declaration
if self.is_root:
if d.grid:
self.set_grid(d.grid)
if d.title:
self.set_title(d.title)
if d.label_left:
self.set_label_left(d.label_left)
if d.label_right:
self.set_label_right(d.label_right)
if d.label_top:
self.set_label_top(d.label_top)
if d.label_bottom:
self.set_label_bottom(d.label_bottom)
if d.show_legend:
self.set_show_legend(d.show_legend)
if d.multi_axis:
self.set_multi_axis(d.multi_axis)
self.set_antialias(d.antialias)
self.set_aspect_locked(d.aspect_locked)
if d.background:
self.set_background(d.background)
if d.axis_left_ticks:
self.set_axis_left_ticks(d.axis_left_ticks)
if d.axis_bottom_ticks:
self.set_axis_bottom_ticks(d.axis_bottom_ticks)
self._refresh_plot()
self.set_auto_range(d.auto_range)
self.init_signals()
def init_signals(self):
self.widget.sigRangeChanged.connect(self.on_range_changed)
self.widget.vb.sigResized.connect(self.on_resized)
def _format_data(self):
raise NotImplementedError
def _format_style(self):
data = {}
d = self.declaration
if d.line_pen:
data["pen"] = d.line_pen
if d.shadow_pen:
data["shadowPen"] = d.shadow_pen
if d.fill_level:
data["fillLevel"] = d.fill_level
if d.fill_brush:
data["fillBrush"] = d.fill_brush
if d.step_mode:
data["stepMode"] = d.step_mode
# if d.background:
# data['background'] = d.background
if d.symbol:
data["symbol"] = d.symbol
if d.symbol_pen:
data["symbolPen"] = d.symbol_pen
if d.symbol_brush:
data["symbolBrush"] = d.symbol_brush
if d.symbol_size:
data["symbolSize"] = d.symbol_size
if d.antialias:
data["antialias"] = d.antialias
return data
def _refresh_plot(self):
"""Defer drawing until all changes are done so we don't draw
during initialization or when many values change at once.
"""
self._pending_refreshes += 1
refresh_time = self.declaration.refresh_time
timed_call(refresh_time, self._redraw_plot)
def _redraw_plot(self):
self._pending_refreshes -= 1
if self._pending_refreshes != 0:
return # Another change occurred
if self.plot:
self.plot.clear()
if self.viewbox:
self.viewbox.close()
d = self.declaration
data = self._format_data()
style = self._format_style()
if not self.is_root and d.parent.multi_axis:
self._refresh_multi_axis()
self.plot = self.viewbox.addItem(pg.PlotDataItem(*data, **style))
else:
self.plot = self.widget.plot(*data, **style)
def _refresh_multi_axis(self):
"""If linked axis' are used, setup and link them"""
d = self.declaration
#: Create a separate viewbox
self.viewbox = pg.ViewBox()
#: If this is the first nested plot, use the parent right axis
_plots = [
c for c in self.parent().children() if isinstance(c, AbstractQtPlotItem)
]
i = _plots.index(self)
if i == 0:
self.axis = self.widget.getAxis("right")
self.widget.showAxis("right")
else:
self.axis = pg.AxisItem("right")
self.axis.setZValue(-10000)
#: Add new axis to scene
self.widget.layout.addItem(self.axis, 2, i + 2)
#: Link x axis to the parent axis
self.viewbox.setXLink(self.widget.vb)
#: Link y axis to the view
self.axis.linkToView(self.viewbox)
#: Set axis label
self.axis.setLabel(d.label_right)
#: Add Viewbox to parent scene
self.parent().parent_widget().scene().addItem(self.viewbox)
def set_row(self, row):
self._refresh_plot()
def set_column(self, column):
self._refresh_plot()
def set_aspect_locked(self, locked):
return
# self.widget.setAspectLocked(locked)
def set_background(self, background):
color = get_cached_qcolor(background) if background else None
if isinstance(self.parent(), AbstractQtPlotItem):
self.parent().parent_widget().setBackground(color)
else:
self.parent_widget().setBackground(color)
def set_line_pen(self, pen):
self.widget.setPen(pen)
def set_shadow_pen(self, pen):
self.widget.setShadowPen(pen)
def set_axis_left_ticks(self, callback):
self.set_axis_ticks("left", callback)
def set_axis_bottom_ticks(self, callback):
self.set_axis_ticks("bottom", callback)
def set_axis_ticks(self, axis_name, callback):
axis = self.widget.getAxis(axis_name)
# Save ref
if not hasattr(axis, "_tickStrings"):
axis._tickStrings = axis.tickStrings
axis.tickStrings = (
types.MethodType(callback, axis, axis.__class__)
if callback
else axis._tickStrings
)
def set_grid(self, grid):
self.widget.showGrid(grid[0], grid[1], self.declaration.grid_alpha)
def set_grid_alpha(self, alpha):
self.set_grid(self.declaration.grid)
def set_show_legend(self, show):
if show:
if not self.widget.legend:
self.widget.addLegend()
else:
self.widget.legend.hide()
def set_title(self, title):
self.widget.setTitle(title)
def set_label_top(self, text):
self.widget.setLabels("top", text)
def set_label_left(self, text):
self.widget.setLabel("left", text)
def set_label_right(self, text):
if not self.is_root and self.declaration.parent.multi_axis:
return # don't override multi axis label
self.widget.setLabel("right", text)
def set_label_bottom(self, text):
self.widget.setLabel("bottom", text)
def set_auto_downsampling(self, enabled):
self.widget.setDownsampling(auto=enabled)
def set_antialias(self, enabled):
self._refresh_plot()
def set_auto_range(self, auto_range):
d = self.declaration
if not isinstance(auto_range, tuple):
auto_range = (auto_range, auto_range)
self.declaration.auto_range = auto_range
if not auto_range[0]:
self.set_range_x(d.range_x)
if not auto_range[1]:
self.set_range_y(d.range_y)
#
# Setters that require a full refresh
#
def set_symbol(self, arg):
self._refresh_plot()
def set_symbol_size(self, arg):
self._refresh_plot()
def set_symbol_brush(self, arg):
self._refresh_plot()
def set_fill_brush(self, arg):
self._refresh_plot()
def set_fill_level(self, arg):
self._refresh_plot()
def set_multi_axis(self, arg):
self._refresh_plot()
def set_log_mode(self, arg):
self._refresh_plot()
def set_clip_to_view(self, arg):
self._refresh_plot()
def set_step_mode(self, arg):
self._refresh_plot()
def set_range_x(self, val):
"""Set visible range of x data.
Note: Padding must be 0 or it will create an infinite loop
"""
d = self.declaration
if d.auto_range[0]:
return
self.widget.setXRange(*val, padding=0)
def set_range_y(self, val):
"""Set visible range of y data.
Note: Padding must be 0 or it will create an infinite loop
"""
d = self.declaration
if d.auto_range[1]:
return
self.widget.setYRange(*val, padding=0)
#
# Widget events
#
def on_range_changed(self, vb, rect):
d = self.declaration
d.range_x = rect[0]
d.range_y = rect[1]
def on_scale_changed(self, view_scale):
pass
def on_resized(self):
"""Update linked views"""
d = self.declaration
if not self.is_root and d.parent.multi_axis:
if self.viewbox:
self.viewbox.setGeometry(self.widget.vb.sceneBoundingRect())
self.viewbox.linkedViewChanged(self.widget.vb, self.viewbox.XAxis)
# #
# # Child events
# #
#
# def child_added(self, child):
# # TODO support layouts
# if isinstance(child,AbstractQtPlotItem):
# self.addItem(child.widget)
#
# def child_removed(self, child):
# if isinstance(child,AbstractQtPlotItem):
# self.removeItem(child.widget)
class QtPlotItem2D(AbstractQtPlotItem):
def set_x(self, x):
# Only refresh when they are equal
# because one can be set at a time
if len(self.declaration.y) == len(x):
self._refresh_plot()
def set_y(self, y):
# Only refresh when they are equal
# because one can be set at a time
if len(self.declaration.x) == len(y):
self._refresh_plot()
def _format_data(self):
data = [self.declaration.y]
if self.declaration.x is not None:
data.insert(0, self.declaration.x)
return data
class QtPlotItemDict(QtPlotItem2D):
def set_data(self, data):
self._refresh_plot()
def _format_data(self):
return self.declaration.data
class QtPlotItemList(QtPlotItemDict):
pass
class QtPlotItemArray(QtPlotItem2D):
pass
class QtPlotItem3D(QtPlotItem2D):
"""Use forward instance to not cause import issues if
not installed."""
widget = ForwardInstance(gl_view_widget)
def create_widget(self):
from pyqtgraph.opengl import GLViewWidget
if isinstance(self.parent(), AbstractQtPlotItem):
self.widget = self.parent_widget()
else:
self.widget = GLViewWidget(parent=self.parent_widget())
self.widget.opts["distance"] = 40
self.widget.raise_()
def init_signals(self):
pass
def _create_grid(self):
from pyqtgraph.opengl import GLGridItem
gx = GLGridItem()
gx.rotate(90, 0, 1, 0)
gx.translate(-10, 0, 0)
self.widget.addItem(gx)
gy = GLGridItem()
gy.rotate(90, 1, 0, 0)
gy.translate(0, -10, 0)
self.widget.addItem(gy)
gz = GLGridItem()
gz.translate(0, 0, -10)
self.widget.addItem(gz)
def set_z(self, z):
self._refresh_plot()
def _refresh_plot(self):
import numpy as np
# import pyqtgraph as pg
from pyqtgraph import opengl as gl
self._create_grid()
pts = np.vstack(
[self.declaration.x, self.declaration.y, self.declaration.z]
).transpose()
plt = gl.GLLinePlotItem(
pos=pts
) # , color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True)
self.widget.addItem(plt)
def set_grid(self, grid):
pass
class QtPlotItemArray3D(QtPlotItem3D):
def _refresh_plot(self):
import numpy as np
import pyqtgraph as pg
from pyqtgraph import opengl as gl
self._create_grid()
n = 51
x = self.declaration.x
y = self.declaration.y
for i in range(n):
yi = np.array([y[i]] * 100)
d = (x**2 + yi**2) ** 0.5
z = 10 * np.cos(d) / (d + 1)
pts = np.vstack([x, yi, z]).transpose()
plt = gl.GLLinePlotItem(
pos=pts,
color=pg.glColor((i, n * 1.3)),
width=(i + 1) / 10.0,
antialias=True,
)
self.widget.addItem(plt)
# def set_data(self,data):
# self.widget.plotItem.clear()
# if self._views:
# for view in self._views:
# view.clear()
#
# views = []
# i = 0
# if self.declaration.multi_axis:
# for i,plot in enumerate(data):
# if i>3:
# break
# if 'pen' not in plot:
# plot['pen'] = self._colors[i]
# if i>0:
# view = ViewBox()
# views.append(view)
# self.widget.plotItem.scene().addItem(view)
# if i==1:
# axis = self.widget.plotItem.getAxis('right')
# elif i>1:
# axis = AxisItem('right')
# axis.setZValue(-10000)
# self.widget.plotItem.layout.addItem(axis,2,3)
# axis.linkToView(view)
# view.setXLink(self.widget.plotItem)
# view.addItem(PlotCurveItem(**plot))
# else: #view.setYLink(self.widget.plotItem)
# self.widget.plot(**plot)
# if i>0:
# def syncViews():
# for v in views:
# v.setGeometry(self.widget.plotItem.vb.sceneBoundingRect())
# v.linkedViewChanged(self.widget.plotItem.vb,v.XAxis)
# syncViews()
# self.widget.plotItem.vb.sigResized.connect(syncViews)
# self._views = views
enamlx-0.6.4/enamlx/qt/qt_table_view.py 0000664 0000000 0000000 00000015264 14566137314 0020136 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 28, 2015
"""
from atom.api import Int, Typed
from enaml.application import timed_call
from qtpy.QtCore import QAbstractTableModel
from qtpy.QtWidgets import QTableView
from enamlx.qt.qt_abstract_item import (
RESIZE_MODES,
AbstractQtWidgetItem,
AbstractQtWidgetItemGroup,
)
from enamlx.qt.qt_abstract_item_view import QAbstractAtomItemModel, QtAbstractItemView
from enamlx.widgets.table_view import (
ProxyTableView,
ProxyTableViewColumn,
ProxyTableViewItem,
ProxyTableViewRow,
)
class QAtomTableModel(QAbstractAtomItemModel, QAbstractTableModel):
"""Model that pulls it's data from the TableViewItems
declaration.
"""
def rowCount(self, parent=None):
d = self.declaration
return len(d.vertical_headers if d.vertical_headers else d.items)
def columnCount(self, parent=None):
d = self.declaration
return len(d.horizontal_headers if d.horizontal_headers else d.items)
def itemAt(self, index):
if not index.isValid():
return None
d = self.declaration
try:
d.current_row = index.row()
d.current_column = index.column()
r = d.current_row - d.visible_row
c = d.current_column - d.visible_column
return d._items[r]._items[c].proxy
except IndexError:
return None
class QtTableView(QtAbstractItemView, ProxyTableView):
#: Proxy widget
widget = Typed(QTableView)
def create_widget(self):
self.widget = QTableView(self.parent_widget())
def init_widget(self):
super(QtTableView, self).init_widget()
d = self.declaration
self.set_show_grid(d.show_grid)
def init_model(self):
self.set_model(QAtomTableModel(parent=self.widget))
# -------------------------------------------------------------------------
# Widget settters
# -------------------------------------------------------------------------
def set_show_grid(self, show):
self.widget.setShowGrid(show)
def set_cell_padding(self, padding):
self.widget.setStyleSheet("QTableView::item { padding: %ipx }" % padding)
def set_vertical_minimum_section_size(self, size):
self.widget.verticalHeader().setMinimumSectionSize(size)
def set_horizontal_minimum_section_size(self, size):
self.widget.horizontalHeader().setMinimumSectionSize(size)
def set_horizontal_stretch(self, stretch):
self.widget.horizontalHeader().setStretchLastSection(stretch)
def set_vertical_stretch(self, stretch):
self.widget.verticalHeader().setStretchLastSection(stretch)
def set_resize_mode(self, mode):
header = self.widget.horizontalHeader()
# Custom is obsolete, use fixed instead.
mode = "fixed" if mode == "custom" else mode
header.setSectionResizeMode(RESIZE_MODES[mode])
def set_show_horizontal_header(self, show):
header = self.widget.horizontalHeader()
header.show() if show else header.hide()
def set_show_vertical_header(self, show):
header = self.widget.verticalHeader()
header.show() if show else header.hide()
# -------------------------------------------------------------------------
# View refresh handlers
# -------------------------------------------------------------------------
def _refresh_visible_column(self, value):
self._pending_column_refreshes -= 1
if self._pending_column_refreshes == 0:
d = self.declaration
cols = self.model.columnCount() - d.visible_columns
d.visible_column = max(0, min(value, cols))
def _refresh_visible_row(self, value):
self._pending_row_refreshes -= 1
if self._pending_row_refreshes == 0 and (self.declaration is not None):
d = self.declaration
rows = self.model.rowCount() - d.visible_rows
d.visible_row = max(0, min(value, rows))
def _refresh_visible_rows(self):
return
top = self.widget.rowAt(self.widget.rect().top())
bottom = self.widget.rowAt(self.widget.rect().bottom())
self.declaration.visible_rows = max(1, (bottom - top)) * 2 # 2x for safety
def _refresh_visible_columns(self):
return
left = self.widget.rowAt(self.widget.rect().left())
right = self.widget.rowAt(self.widget.rect().right())
self.declaration.visible_columns = max(1, (right - left)) * 2
class AbstractQtTableViewItemGroup(AbstractQtWidgetItemGroup):
def create_widget(self):
pass
@property
def widget(self):
return self.parent_widget()
class QtTableViewItem(AbstractQtWidgetItem, ProxyTableViewItem):
#: Pending refreshes when loading widgets
_refresh_count = Int(0)
#: Time to wait before loading widget in ms
_loading_interval = Int(100)
def _default_view(self):
return self.parent().parent()
def set_row(self, row):
self._update_index()
def set_column(self, column):
self._update_index()
def _update_index(self):
"""Update the reference to the index within the table"""
d = self.declaration
self.index = self.view.model.index(d.row, d.column)
if self.delegate:
self._refresh_count += 1
timed_call(self._loading_interval, self._update_delegate)
def _update_delegate(self):
"""Update the delegate cell widget. This is deferred so it
does not get called until the user is done scrolling.
"""
self._refresh_count -= 1
if self._refresh_count != 0:
return
try:
delegate = self.delegate
if not self.is_visible():
return
# The table destroys when it goes out of view
# so we always have to make a new one
delegate.create_widget()
delegate.init_widget()
# Set the index widget
self.view.widget.setIndexWidget(self.index, delegate.widget)
except RuntimeError:
pass # Since this is deferred, the table could be deleted already
def is_visible(self):
"""Check if this index is currently visible"""
return True
def data_changed(self, change):
"""Notify the model that data has changed in this cell!"""
index = self.index
if index:
self.view.model.dataChanged.emit(index, index)
class QtTableViewRow(AbstractQtTableViewItemGroup, ProxyTableViewRow):
pass
class QtTableViewColumn(AbstractQtTableViewItemGroup, ProxyTableViewColumn):
pass
enamlx-0.6.4/enamlx/qt/qt_tree_view.py 0000664 0000000 0000000 00000017710 14566137314 0020004 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 28, 2015
"""
from atom.api import Instance, Int, Typed
from enaml.application import timed_call
from enaml.core.pattern import Pattern
from enaml.qt.qt_widget import QtWidget
from qtpy.QtCore import QAbstractItemModel, QModelIndex
from qtpy.QtWidgets import QTreeView
from enamlx.qt.qt_abstract_item import RESIZE_MODES, AbstractQtWidgetItem
from enamlx.qt.qt_abstract_item_view import QAbstractAtomItemModel, QtAbstractItemView
from enamlx.widgets.tree_view import (
ProxyTreeView,
ProxyTreeViewColumn,
ProxyTreeViewItem,
)
class QAtomTreeModel(QAbstractAtomItemModel, QAbstractItemModel):
def rowCount(self, parent):
d = self.declaration
if d.vertical_headers:
return len(d.vertical_headers)
elif parent.isValid():
item = parent.internalPointer()
d = item.declaration
return len(d.items) if d and not d.is_destroyed else 0
def columnCount(self, parent):
d = self.declaration
if d.horizontal_headers:
return len(d.horizontal_headers)
elif parent.isValid():
item = parent.internalPointer()
d = item.declaration
return len(d._columns) if d and not d.is_destroyed else 0
def index(self, row, column, parent):
"""The index should point to the corresponding QtControl in the
enaml object hierarchy.
"""
item = parent.internalPointer()
#: If the parent is None
d = self.declaration if item is None else item.declaration
if row < len(d._items):
proxy = d._items[row].proxy
assert isinstance(proxy, QtTreeViewItem), "Invalid item {}".format(proxy)
else:
proxy = d.proxy
return self.createIndex(row, column, proxy)
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer()
if not isinstance(item, QtTreeViewItem) or item.is_destroyed:
return QModelIndex()
parent = item.parent()
if not isinstance(parent, QtTreeViewItem) or parent.is_destroyed:
return QModelIndex()
d = parent.declaration
return self.createIndex(d.row, 0, parent)
def itemAt(self, index=None):
if not index or not index.isValid():
return
item = index.internalPointer()
assert isinstance(
item, QtTreeViewItem
), "Invalid index: {} at ({},{}) {}".format(
index, index.row(), index.column(), item
)
d = item.declaration
try:
c = index.column() # - d.visible_column
return d._columns[c].proxy
except IndexError:
return
class QtTreeView(QtAbstractItemView, ProxyTreeView):
#: Tree widget
widget = Typed(QTreeView)
#: Root index
index = Instance(QModelIndex, ())
def create_widget(self):
self.widget = QTreeView(self.parent_widget())
def init_widget(self):
super(QtTreeView, self).init_widget()
d = self.declaration
self.set_show_root(d.show_root)
def init_model(self):
self.set_model(QAtomTreeModel(parent=self.widget))
# -------------------------------------------------------------------------
# Widget Setters
# -------------------------------------------------------------------------
def set_show_root(self, show):
self.widget.setRootIsDecorated(show)
def set_cell_padding(self, padding):
self.widget.setStyleSheet("QTreeView::item { padding: %ipx }" % padding)
def set_horizontal_minimum_section_size(self, size):
self.widget.header().setMinimumSectionSize(size)
def set_horizontal_stretch(self, stretch):
self.widget.header().setStretchLastSection(stretch)
def set_horizontal_headers(self, headers):
self.widget.header().model().layoutChanged.emit()
def set_resize_mode(self, mode):
self.widget.header().setSectionResizeMode(RESIZE_MODES[mode])
def set_show_horizontal_header(self, show):
header = self.widget.header()
header.show() if show else header.hide()
# -------------------------------------------------------------------------
# View refresh handlers
# -------------------------------------------------------------------------
def _refresh_visible_column(self, value):
self._pending_column_refreshes -= 1
if self._pending_column_refreshes == 0:
d = self.declaration
# TODO: What about parents???
try:
cols = self.model.columnCount(self.index) - d.visible_columns
d.visible_column = max(0, min(value, cols))
except RuntimeError:
#: Since refreshing is deferred several ms later
pass
def _refresh_visible_row(self, value):
self._pending_row_refreshes -= 1
if self._pending_row_refreshes == 0:
d = self.declaration
try:
rows = self.model.rowCount(self.index) - d.visible_rows
d.visible_row = max(0, min(value, rows))
except RuntimeError:
pass
class AbstractQtTreeViewItem(AbstractQtWidgetItem):
"""Base TreeViewItem class"""
#: Pending refreshes when loading widgets
_refresh_count = Int(0)
#: Time to wait before loading widget
_loading_interval = Int(100)
def create_widget(self):
if self.declaration:
for child in self.children():
if isinstance(child, (Pattern, QtWidget)):
self.delegate = child
def set_row(self, row):
self._update_index()
def set_column(self, column):
self._update_index()
def _default_index(self):
d = self.declaration
return self.view.model.index(d.row, d.column, self.parent().index)
def _update_index(self):
self.index = self._default_index()
if self.delegate:
self._refresh_count += 1
timed_call(self._loading_interval, self._update_delegate)
def _update_delegate(self):
"""Update the delegate cell widget. This is deferred so it
does not get called until the user is done scrolling.
"""
self._refresh_count -= 1
if self._refresh_count != 0:
return
try:
delegate = self.delegate
if not self._is_visible():
return
# The table destroys when it goes out of view
# so we always have to make a new one
delegate.create_widget()
delegate.init_widget()
# Set the index widget
self.view.widget.setIndexWidget(self.index, delegate.widget)
except RuntimeError:
# Since this is deferred, the table could be deleted already
# and a RuntimeError is possible
pass
def _is_visible(self):
return self.index.isValid()
def data_changed(self, change):
"""Notify the model that data has changed in this cell!"""
self.view.model.dataChanged.emit(self.index, self.index)
class QtTreeViewItem(AbstractQtTreeViewItem, ProxyTreeViewItem):
def _default_view(self):
"""If this is the root item, return the parent
which must be a TreeView, otherwise return the
parent Item's view.
"""
parent = self.parent()
if isinstance(parent, QtTreeView):
return parent
return parent.view
class QtTreeViewColumn(AbstractQtTreeViewItem, ProxyTreeViewColumn):
def _default_view(self):
"""Since the TreeViewColumn must be a child of a TreeViewItem,
simply return the Item's view.
"""
return self.parent().view
def _default_index(self):
d = self.declaration
return self.view.model.index(d.row, d.column, self.parent().index)
enamlx-0.6.4/enamlx/widgets/ 0000775 0000000 0000000 00000000000 14566137314 0015751 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/widgets/__init__.py 0000664 0000000 0000000 00000000000 14566137314 0020050 0 ustar 00root root 0000000 0000000 enamlx-0.6.4/enamlx/widgets/abstract_item.py 0000664 0000000 0000000 00000010334 14566137314 0021145 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 24, 2015
"""
from atom.api import (
Bool,
Coerced,
Enum,
Event,
ForwardInstance,
Int,
Property,
Str,
Typed,
observe,
)
from enaml.core.declarative import d_
from enaml.icon import Icon
from enaml.layout.geometry import Size
from enaml.widgets.control import Control, ProxyControl
class ProxyAbstractWidgetItemGroup(ProxyControl):
#: Reference to the declaration
declaration = ForwardInstance(lambda: AbstractWidgetItemGroup)
def set_selectable(self, selectable):
pass
class ProxyAbstractWidgetItem(ProxyControl):
#: Reference to the declaration
declaration = ForwardInstance(lambda: AbstractWidgetItem)
def set_row(self, row):
pass
def set_column(self, column):
pass
def set_text(self, text):
pass
def set_text_alignment(self, text_alignment):
pass
def set_icon(self, icon):
pass
def set_icon_size(self, size):
pass
def set_editable(self, editable):
pass
def set_checkable(self, checkable):
pass
class AbstractWidgetItemGroup(Control):
#: Triggered when clicked
clicked = d_(Event(), writable=False)
#: Triggered when double clicked
double_clicked = d_(Event(), writable=False)
#: Triggered when the row, column, or item is entered
entered = d_(Event(), writable=False)
#: Triggered when the row, column, or item is pressed
pressed = d_(Event(), writable=False)
#: Triggered when the row, column, or item's selection changes
selection_changed = d_(Event(bool), writable=False)
def _get_items(self):
return [c for c in self.children if isinstance(c, AbstractWidgetItem)]
#: Internal item reference
_items = Property(lambda self: self._get_items(), cached=True)
def child_added(self, child):
"""Reset the item cache when a child is added"""
super(AbstractWidgetItemGroup, self).child_added(child)
self.get_member("_items").reset(self)
def child_removed(self, child):
"""Reset the item cache when a child is removed"""
super(AbstractWidgetItemGroup, self).child_removed(child)
self.get_member("_items").reset(self)
class AbstractWidgetItem(AbstractWidgetItemGroup):
"""Item to be shared between table views and tree views"""
#: Model index or row within the view
row = d_(Int(), writable=False)
#: Column within the view
column = d_(Int(), writable=False)
#: Text to display within the cell
text = d_(Str())
#: Text alignment within the cell
text_alignment = d_(
Enum(
*[
(h, v)
for h in ("left", "right", "center", "justify")
for v in ("center", "top", "bottom")
]
)
)
#: Icon to display in the cell
icon = d_(Typed(Icon))
#: The size to use for the icon. The default is an invalid size
#: and indicates that an appropriate default should be used.
icon_size = d_(Coerced(Size, (-1, -1)))
#: Whether the item or group can be selected
selectable = d_(Bool(True))
#: Selection state of the item or group
selected = d_(Bool())
#: Whether the item or group can be checked
checkable = d_(Bool())
#: Checked state of the item or group
checked = d_(Bool())
#: Whether the item or group can be edited
editable = d_(Bool())
#: Triggered when the item's contents change
changed = d_(Event(), writable=False)
#: Triggered when the checkbox state changes
toggled = d_(Event(bool), writable=False)
@observe(
"row",
"column",
"text",
"text_alignment",
"icon",
"icon_size",
"selectable",
"selected",
"checkable",
"checked",
"editable",
)
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
if change["name"] in ["row", "column"]:
super(AbstractWidgetItem, self)._update_proxy(change)
else:
self.proxy.data_changed(change)
enamlx-0.6.4/enamlx/widgets/abstract_item_view.py 0000664 0000000 0000000 00000012760 14566137314 0022204 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 23, 2015
"""
from atom.api import (
Bool,
ContainerList,
Enum,
ForwardInstance,
Int,
Property,
observe,
set_default,
)
from enaml.core.declarative import d_
from enaml.widgets.control import Control, ProxyControl
from .abstract_item import AbstractWidgetItemGroup
class ProxyAbstractItemView(ProxyControl):
#: Reference to the declaration
declaration = ForwardInstance(lambda: AbstractItemView)
def set_items(self, items):
pass
def set_selection_mode(self, mode):
pass
def set_selection_behavior(self, behavior):
pass
def set_selection(self, items):
pass
def set_scroll_to_bottom(self, scroll_to_bottom):
pass
def set_alternating_row_colors(self, alternate):
pass
def set_cell_padding(self, padding):
pass
def set_auto_resize(self, enabled):
pass
def set_resize_mode(self, mode):
pass
def set_word_wrap(self, enabled):
pass
def set_show_vertical_header(self, visible):
pass
def set_vertical_headers(self, headers):
pass
def set_vertical_stretch(self, stretch):
pass
def set_vertical_sizes(self, sizes):
pass
def set_vertical_minimum_section_size(self, size):
pass
def set_show_horizontal_header(self, visible):
pass
def set_horizontal_headers(self, headers):
pass
def set_horizontal_sizes(self, sizes):
pass
def set_horizontal_stretch(self, stretch):
pass
def set_horizontal_minimum_section_size(self, size):
pass
def set_sortable(self, sortable):
pass
def set_visible_row(self, row):
pass
def set_visible_column(self, column):
pass
class AbstractItemView(Control):
#: Table should expand by default
hug_width = set_default("ignore")
#: Table should expand by default
hug_height = set_default("ignore")
#: The items to display in the view
items = d_(ContainerList(default=[]))
#: Selection mode of the view
selection_mode = d_(Enum("extended", "none", "multi", "single", "contiguous"))
#: Selection behavior of the view
selection_behavior = d_(Enum("items", "rows", "columns"))
#: Selection
selection = d_(ContainerList(default=[]))
#: Automatically scroll to bottm when new items are added
scroll_to_bottom = d_(Bool(False))
#: Set alternating row colors
alternating_row_colors = d_(Bool(False))
#: Cell padding
cell_padding = d_(Int(0))
#: Automatically resize columns to fit contents
auto_resize = d_(Bool(True))
#: Resize mode of columns and rows
resize_mode = d_(
Enum("interactive", "fixed", "stretch", "resize_to_contents", "custom")
)
#: Word wrap
word_wrap = d_(Bool(False))
#: Show vertical header bar
show_vertical_header = d_(Bool(True))
#: Row headers
vertical_headers = d_(ContainerList())
#: Stretch last row
vertical_stretch = d_(Bool(False))
#: Sizes for vertical headers (list of int)
vertical_sizes = d_(ContainerList())
#: Minimum row size
vertical_minimum_section_size = d_(Int(0))
#: Show horizontal hearder bar
show_horizontal_header = d_(Bool(True))
#: Column headers
horizontal_headers = d_(ContainerList())
#: Stretch last column
horizontal_stretch = d_(Bool(False))
#: Minimum column size
horizontal_minimum_section_size = d_(Int(0))
#: Sizes for horizontal headers (list of int)
horizontal_sizes = d_(ContainerList())
#: Table is sortable
sortable = d_(Bool(True))
#: Current row index
current_row = d_(Int(0))
#: Current column index
current_column = d_(Int(0))
#: First visible row
visible_row = d_(Int(0))
#: Number of rows visible
visible_rows = d_(Int(100))
#: First visible column
visible_column = d_(Int(0))
#: Number of columns visible
visible_columns = d_(Int(1))
def _get_items(self):
return [c for c in self.children if isinstance(c, AbstractWidgetItemGroup)]
#: Cached property listing the row or columns of the table
_items = Property(_get_items, cached=True)
@observe(
"items",
"scroll_to_bottom",
"alternating_row_colors",
"selection_mode",
"selection_behavior",
"selection",
"cell_padding",
"auto_resize",
"resize_mode",
"word_wrap",
"show_horizontal_header",
"horizontal_headers",
"horizontal_sizes",
"horizontal_stretch",
"show_vertical_header",
"vertical_headers",
"vertical_stretch",
"vertical_sizes",
"visible_row",
"visible_column",
)
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
# The superclass handler implementation is sufficient.
super(AbstractItemView, self)._update_proxy(change)
def child_added(self, child):
"""Reset the item cache when a child is added"""
super(AbstractItemView, self).child_added(child)
self.get_member("_items").reset(self)
def child_removed(self, child):
"""Reset the item cache when a child is removed"""
super(AbstractItemView, self).child_removed(child)
self.get_member("_items").reset(self)
enamlx-0.6.4/enamlx/widgets/api.py 0000664 0000000 0000000 00000001776 14566137314 0017107 0 ustar 00root root 0000000 0000000 """
Copyright (c) 2015-2018, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Jun 3, 2015
"""
from .double_spin_box import DoubleSpinBox # noqa: F401
from .graphics_view import ( # noqa: F401
Brush,
GraphicsEllipseItem,
GraphicsImageItem,
GraphicsItem,
GraphicsItemGroup,
GraphicsLineItem,
GraphicsPathItem,
GraphicsPolygonItem,
GraphicsRectItem,
GraphicsTextItem,
GraphicsView,
GraphicsWidget,
Pen,
Point,
Rect,
)
from .key_event import KeyEvent # noqa: F401
from .plot_area import ( # noqa: F401
PlotArea,
PlotItem2D,
PlotItem3D,
PlotItemArray,
PlotItemArray3D,
PlotItemDict,
PlotItemList,
)
from .table_view import ( # noqa: F401
TableView,
TableViewColumn,
TableViewItem,
TableViewRow,
)
from .tree_view import TreeView, TreeViewColumn, TreeViewItem # noqa: F401
enamlx-0.6.4/enamlx/widgets/double_spin_box.py 0000664 0000000 0000000 00000002445 14566137314 0021503 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015-2018, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 29, 2015
"""
from atom.api import Float, ForwardTyped, Int, observe
from enaml.core.declarative import d_
from enaml.widgets.spin_box import ProxySpinBox, SpinBox
class ProxyDoubleSpinBox(ProxySpinBox):
declaration = ForwardTyped(lambda: DoubleSpinBox)
def set_decimals(self, prec):
raise NotImplementedError()
class DoubleSpinBox(SpinBox):
"""A spin box widget which manipulates float values."""
#: The number of decmial places to be shown. Defaults to 2.
decimals = d_(Int(2))
#: The minimum value for the spin box. Defaults to 0.
minimum = d_(Float(0, strict=False))
#: The maximum value for the spin box. Defaults to 100.
maximum = d_(Float(100, strict=False))
#: The maximum value for the spin box. Defaults to 100.
single_step = d_(Float(1.0, strict=False))
#: The position value of the spin box. The value will be clipped to
#: always fall between the minimum and maximum.
value = d_(Float(0, strict=False))
@observe("decimals")
def _update_proxy(self, change):
super(DoubleSpinBox, self)._update_proxy(change)
enamlx-0.6.4/enamlx/widgets/graphics_view.py 0000664 0000000 0000000 00000071461 14566137314 0021166 0 ustar 00root root 0000000 0000000 """
Copyright (c) 2018, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Sept 5, 2018
"""
import sys
from enum import IntFlag
from atom.api import (
Atom,
Bool,
Coerced,
Enum,
Event,
Float,
ForwardTyped,
Instance,
List,
Str,
Typed,
Value,
observe,
set_default,
)
from enaml.colors import ColorMember
from enaml.core.declarative import d_, d_func
from enaml.fonts import FontMember
from enaml.image import Image
from enaml.layout.constrainable import ConstrainableMixin
from enaml.widgets.control import Control, ProxyControl
from enaml.widgets.toolkit_object import ProxyToolkitObject, ToolkitObject
from enaml.widgets.widget import Feature
NUMERIC = (
(int, float, long) if sys.version_info.major < 3 else (int, float) # noqa: F821
)
class GraphicFeature(IntFlag):
#: Enables support for mouse events.
MouseEvent = 0x08
#: Enables support for wheel events.
WheelEvent = 0x16
#: Enables support for draw or paint events.
DrawEvent = 0x32
#: Enables support for backgound draw events.
BackgroundDrawEvent = 0x64
class Point(Atom):
#: x position
x = d_(Float(0, strict=False))
#: y position
y = d_(Float(0, strict=False))
#: z position
z = d_(Float(0, strict=False))
def __init__(self, x=0, y=0, z=0):
super(Point, self).__init__(x=x, y=y, z=z)
def __iter__(self):
yield self.x
yield self.y
yield self.z
def __len__(self):
return 3
def __eq__(self, other):
pos = (self.x, self.y, self.z)
if isinstance(other, Point):
return pos == (other.x, other.y, other.z)
return pos == other
def __add__(self, other):
return Point(
self.x + other[0],
self.y + other[1],
self.z + other[2] if len(other) > 2 else self.z,
)
__radd__ = __add__
def __sub__(self, other):
return Point(
self.x - other[0],
self.y - other[1],
self.z - other[2] if len(other) > 2 else self.z,
)
def __rsub__(self, other):
return Point(
other[0] - self.x,
other[1] - self.y,
other[2] - self.z if len(other) > 2 else self.z,
)
def __mul__(self, other):
if isinstance(other, NUMERIC):
return Point(self.x * other, self.y * other, self.z * other)
return Point(
other[0] * self.x,
other[1] * self.y,
other[2] * self.z if len(other) > 2 else self.z,
)
__rmul__ = __mul__
def __div__(self, other):
if isinstance(other, NUMERIC):
return Point(self.x / other, self.y / other, self.z / other)
return Point(
self.x / other[0],
self.y / other[1],
self.z / other[2] if len(other) > 2 else self.z,
)
def __rdiv__(self, other):
if isinstance(other, NUMERIC):
return Point(other / self.x, other / self.y, other / self.z)
return Point(
other[0] / self.x,
other[1] / self.y,
other[2] / self.z if len(other) > 2 else self.z,
)
def __neg__(self):
return Point(-self.x, -self.y, -self.z)
def __hash__(self):
return id(self)
def __getitem__(self, key):
return (self.x, self.y, self.z)[key]
def __repr__(self):
return "Point(%d, %d, %d)" % (self.x, self.y, self.z)
class Rect(Atom):
x = d_(Float(0, strict=False))
y = d_(Float(0, strict=False))
width = d_(Float(0, strict=False))
height = d_(Float(0, strict=False))
def coerce_point(p):
return p if isinstance(p, Point) else Point(*p)
class PointMember(Coerced):
def __init__(self, args=None, kwargs=None, factory=None, coercer=coerce_point):
super(PointMember, self).__init__(
Point, args, kwargs, factory=factory, coercer=coercer
)
class Pen(Atom):
#: Color
color = ColorMember()
#: Width
width = Float(1.0, strict=False)
#: Line Style
line_style = Enum(
"solid", "dash", "dot", "dash_dot", "dash_dot_dot", "custom", "none"
)
#: Cap Style
cap_style = Enum("square", "flat", "round")
#: Join Style
join_style = Enum("bevel", "miter", "round")
#: Dash pattern used when line_style is 'custom'
dash_pattern = List(Float(strict=False))
#: Internal data
_tkdata = Value()
class Brush(Atom):
"""Defines the fill pattern"""
#: Color
color = ColorMember()
#: Image
image = Instance(Image)
#: Style
style = Enum(
"solid",
"dense1",
"dense2",
"dense3",
"dense4",
"dense5",
"dense6",
"dense7",
"horizontal",
"vertical",
"cross",
"diag",
"bdiag",
"fdiag",
"linear",
"radial",
"conical",
"texture",
"none",
)
#: Internal data
_tkdata = Value()
class ProxyGraphicsView(ProxyControl):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsView)
def set_auto_range(self, enabled):
raise NotImplementedError
def set_antialiasing(self, enabled):
raise NotImplementedError
def set_drag_mode(self, mode):
raise NotImplementedError
def set_renderer(self, renderer):
raise NotImplementedError
def get_item_at(self, point):
raise NotImplementedError
def set_lock_aspect_ratio(self, locked):
raise NotImplementedError
def set_selected_items(self, items):
raise NotImplementedError
def fit_in_view(self, item):
raise NotImplementedError
def center_on(self, item):
raise NotImplementedError
def reset_view(self):
raise NotImplementedError
def translate_view(self, x, y):
raise NotImplementedError
def scale_view(self, x, y):
raise NotImplementedError
def rotate_view(self, angle):
raise NotImplementedError
def map_from_scene(self, point):
raise NotImplementedError
def map_to_scene(self, point):
raise NotImplementedError
def pixel_density(self):
raise NotImplementedError
class ProxyGraphicsItem(ProxyToolkitObject):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsItem)
def set_x(self, x):
raise NotImplementedError
def set_y(self, y):
raise NotImplementedError
def set_z(self, z):
raise NotImplementedError
def set_position(self, position):
raise NotImplementedError
def set_rotation(self, rotation):
raise NotImplementedError
def set_scale(self, scale):
raise NotImplementedError
def set_opacity(self, opacity):
raise NotImplementedError
def set_selected(self, selected):
raise NotImplementedError
def set_enabled(self, enabled):
raise NotImplementedError
def set_selectable(self, enabled):
raise NotImplementedError
def set_movable(self, enabled):
raise NotImplementedError
def set_visible(self, visible):
raise NotImplementedError
def set_tool_tip(self, tool_tip):
raise NotImplementedError
def set_status_tip(self, status_tip):
raise NotImplementedError
def request_update(self):
raise NotImplementedError
def ensure_visible(self):
raise NotImplementedError
def ensure_hidden(self):
raise NotImplementedError
def set_focus(self):
raise NotImplementedError
def clear_focus(self):
raise NotImplementedError
def has_focus(self):
raise NotImplementedError
class ProxyGraphicsItemGroup(ProxyGraphicsItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsItemGroup)
class ProxyGraphicsWidget(ProxyGraphicsItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsWidget)
class ProxyAbstractGraphicsShapeItem(ProxyGraphicsItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: AbstractGraphicsShapeItem)
def set_pen(self, pen):
raise NotImplementedError
def set_brush(self, brush):
raise NotImplementedError
class ProxyGraphicsRectItem(ProxyAbstractGraphicsShapeItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsRectItem)
def set_width(self, width):
raise NotImplementedError
def set_height(self, height):
raise NotImplementedError
class ProxyGraphicsEllipseItem(ProxyAbstractGraphicsShapeItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsEllipseItem)
def set_width(self, width):
raise NotImplementedError
def set_height(self, height):
raise NotImplementedError
def set_span_angle(self, angle):
raise NotImplementedError
def set_start_angle(self, angle):
raise NotImplementedError
class ProxyGraphicsLineItem(ProxyAbstractGraphicsShapeItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsLineItem)
def set_point(self, point):
raise NotImplementedError
class ProxyGraphicsTextItem(ProxyAbstractGraphicsShapeItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsTextItem)
def set_text(self, text):
raise NotImplementedError
def set_font(self, font):
raise NotImplementedError
class ProxyGraphicsPolygonItem(ProxyAbstractGraphicsShapeItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsPolygonItem)
def set_points(self, points):
raise NotImplementedError
class ProxyGraphicsPathItem(ProxyAbstractGraphicsShapeItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsPathItem)
def set_path(self, path):
raise NotImplementedError
class ProxyGraphicsImageItem(ProxyGraphicsItem):
#: Reference to the declaration
declaration = ForwardTyped(lambda: GraphicsImageItem)
def set_image(self, image):
raise NotImplementedError
class GraphicsItem(ToolkitObject, ConstrainableMixin):
#: Proxy reference
proxy = Typed(ProxyGraphicsItem)
# --------------------------------------------------------------------------
# Item orentation
# --------------------------------------------------------------------------
#: Position
position = d_(PointMember())
#: Item rotation
rotation = d_(Float(strict=False))
#: Item scale
scale = d_(Float(1.0, strict=False))
# --------------------------------------------------------------------------
# Item display
# --------------------------------------------------------------------------
#: Item opacity
opacity = d_(Float(1.0, strict=False))
# Item selected
selected = d_(Bool())
# Item is enabled
enabled = d_(Bool(True))
# Item is visible
visible = d_(Bool(True))
#: Tool tip
tool_tip = d_(Str())
#: Status tip
status_tip = d_(Str())
# --------------------------------------------------------------------------
# Item interaction
# --------------------------------------------------------------------------
#: Set the extra features to enable for this widget. This value must
#: be provided when the widget is instantiated. Runtime changes to
#: this value are ignored.
features = d_(Coerced(Feature, (0,)))
extra_features = d_(Coerced(GraphicFeature, (0,)))
#: Update
request_update = d_(Event())
#: Set whether this item can be selected.
selectable = d_(Bool())
#: Set whether this item can be moved.
movable = d_(Bool())
@observe(
"position",
"position.x",
"position.y",
"position.z",
"scale",
"rotation",
"opacity",
"selected",
"enabled",
"visible",
"tool_tip",
"status_tip",
"request_update",
"selectable",
"movable",
)
def _update_proxy(self, change):
super(GraphicsItem, self)._update_proxy(change)
# --------------------------------------------------------------------------
# Widget API
# --------------------------------------------------------------------------
def show(self):
"""Ensure the widget is shown.
Calling this method will also set the widget visibility to True.
"""
self.visible = True
if self.proxy_is_active:
self.proxy.ensure_visible()
def hide(self):
"""Ensure the widget is hidden.
Calling this method will also set the widget visibility to False.
"""
self.visible = False
if self.proxy_is_active:
self.proxy.ensure_hidden()
def set_focus(self):
"""Set the keyboard input focus to this widget.
FOR ADVANCED USE CASES ONLY: DO NOT ABUSE THIS!
"""
if self.proxy_is_active:
self.proxy.set_focus()
def clear_focus(self):
"""Clear the keyboard input focus from this widget.
FOR ADVANCED USE CASES ONLY: DO NOT ABUSE THIS!
"""
if self.proxy_is_active:
self.proxy.clear_focus()
def has_focus(self):
"""Test whether this widget has input focus.
FOR ADVANCED USE CASES ONLY: DO NOT ABUSE THIS!
Returns
-------
result : bool
True if this widget has input focus, False otherwise.
"""
if self.proxy_is_active:
return self.proxy.has_focus()
return False
@d_func
def focus_gained(self):
"""A method invoked when the widget gains input focus.
** The FocusEvents feature must be enabled for the widget in
order for this method to be called. **
"""
pass
@d_func
def focus_lost(self):
"""A method invoked when the widget loses input focus.
** The FocusEvents feature must be enabled for the widget in
order for this method to be called. **
"""
pass
@d_func
def drag_start(self):
"""A method called at the start of a drag-drop operation.
This method is called when the user starts a drag operation
by dragging the widget with the left mouse button. It returns
the drag data for the drag operation.
** The DragEnabled feature must be enabled for the widget in
order for this method to be called. **
Returns
-------
result : DragData
An Enaml DragData object which holds the drag data. If
this is not provided, no drag operation will occur.
"""
return None
@d_func
def drag_end(self, drag_data, result):
"""A method called at the end of a drag-drop operation.
This method is called after the user has completed the drop
operation by releasing the left mouse button. It is passed
the original drag data object along with the resulting drop
action of the operation.
** The DragEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
data : DragData
The drag data created by the `drag_start` method.
result : DropAction
The requested drop action when the drop completed.
"""
pass
@d_func
def drag_enter(self, event):
"""A method invoked when a drag operation enters the widget.
The widget should inspect the mime data of the event and
accept the event if it can handle the drop action. The event
must be accepted in order to receive further drag-drop events.
** The DropEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : DropEvent
The event representing the drag-drop operation.
"""
pass
@d_func
def drag_move(self, event):
"""A method invoked when a drag operation moves in the widget.
This method will not normally be implemented, but it can be
useful for supporting advanced drag-drop interactions.
** The DropEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : DropEvent
The event representing the drag-drop operation.
"""
pass
@d_func
def drag_leave(self):
"""A method invoked when a drag operation leaves the widget.
** The DropEnabled feature must be enabled for the widget in
order for this method to be called. **
"""
pass
@d_func
def drop(self, event):
"""A method invoked when the user drops the data on the widget.
The widget should either accept the proposed action, or set
the drop action to an appropriate action before accepting the
event, or set the drop action to DropAction.Ignore and then
ignore the event.
** The DropEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : DropEvent
The event representing the drag-drop operation.
"""
pass
@d_func
def mouse_press_event(self, event):
"""A method invoked when a mouse press event occurs in the widget.
** The MouseEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : MouseEvent
The event representing the press operation.
"""
pass
@d_func
def mouse_move_event(self, event):
"""A method invoked when a mouse move event occurs in the widget.
** The MouseEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : MouseEvent
The event representing the press operation.
"""
pass
@d_func
def mouse_release_event(self, event):
"""A method invoked when a mouse release event occurs in the widget.
** The MouseEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : MouseEvent
The event representing the press operation.
"""
pass
@d_func
def wheel_event(self, event):
"""A method invoked when a wheel event occurs in the widget.
This method will not normally be implemented, but it can be
useful for supporting zooming and other scolling interactions.
** The WheelEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : WheelEvent
The event representing the wheel operation.
"""
pass
@d_func
def draw(self, painter, options, widget):
"""A method invoked when this widget needs to be drawn.
This method will not normally be implemented, but it can be
useful for creating custom graphics.
** The DrawEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
painter: Object
The toolkit dependent painter object.
options: Object
The toolkit dependent options object.
widget: Widget
The underlying widget.
"""
pass
class GraphicsView(Control):
#: Proxy reference
proxy = Typed(ProxyGraphicsView)
#: An graphicsview widget expands freely in height and width by default.
hug_width = set_default("ignore")
hug_height = set_default("ignore")
#: Select backend for rendering. OpenGL is used by default if available.
renderer = d_(Enum("default", "opengl", "qwidget"))
#: Antialiasing is enabled by default. If performance is an issue, disable
#: this.
antialiasing = d_(Bool(True))
#: Items currently selected
selected_items = d_(List(GraphicsItem))
#: Defines the behavior when dragging. By default nothing is done. Pan
#: will pan the scene around and selection will draw a box to select items.
drag_mode = d_(Enum("none", "scroll", "selection"))
#: Range of allowed zoom factors. This is used to prevent scaling way out
#: or way in by accident.
min_zoom = d_(Float(0.007, strict=False))
max_zoom = d_(Float(100.0, strict=False))
#: Automatically resize view to fit the scene contents
auto_range = d_(Bool(False)) # TODO: Broken
#: Keep the aspect ratio locked when resizing the view range
lock_aspect_ratio = d_(Bool(True))
#: Set the extra features to enable for this widget. This value must
#: be provided when the widget is instantiated. Runtime changes to
#: this value are ignored.
extra_features = d_(Coerced(GraphicFeature, (0,)))
def _default_extra_features(self):
return GraphicFeature.WheelEvent | GraphicFeature.MouseEvent
@observe(
"selected_items",
"renderer",
"antialiasing",
"drag_mode",
"auto_range",
"lock_aspect_ratio",
)
def _update_proxy(self, change):
super(GraphicsView, self)._update_proxy(change)
# --------------------------------------------------------------------------
# Widget API
# --------------------------------------------------------------------------
@d_func
def wheel_event(self, event):
"""A method invoked when a wheel event occurs in the widget.
This method will not normally be implemented, but it can be
useful for supporting zooming and other scolling interactions.
** The WheelEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : WheelEvent
The event representing the wheel operation.
"""
pass
@d_func
def mouse_press_event(self, event):
"""A method invoked when a mouse press event occurs in the widget.
** The MouseEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : MouseEvent
The event representing the press operation.
"""
pass
@d_func
def mouse_move_event(self, event):
"""A method invoked when a mouse move event occurs in the widget.
** The MouseEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : MouseEvent
The event representing the press operation.
"""
pass
@d_func
def mouse_release_event(self, event):
"""A method invoked when a mouse release event occurs in the widget.
** The MouseEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
event : MouseEvent
The event representing the press operation.
"""
pass
@d_func
def draw_background(self, painter, rect):
"""A method invoked when a background draw is requested.
This method will not normally be implemented, but it can be
useful for implementing custom backgrounds. This drawing is cached.
** The DrawBackgroundEnabled feature must be enabled for the widget in
order for this method to be called. **
Parameters
----------
painter : Painter
A the toolkit dependent handle drawing.
rect : Rect
A rect showing the area of interest.
"""
pass
# --------------------------------------------------------------------------
# Graphics Scene API
# --------------------------------------------------------------------------
def get_item_at(self, *args, **kwargs):
"""Return the items at the given position"""
return self.proxy.get_item_at(coerce_point(*args, **kwargs))
def fit_in_view(self, item):
"""Fit this item into the view"""
self.proxy.fit_in_view(item)
def center_on(self, item):
"""Center on the given item or point."""
if not isinstance(item, GraphicsItem):
item = coerce_point(item)
self.proxy.center_on(item)
def translate_view(self, x=0, y=0):
"""Translate the view by the given x and y pixels."""
return self.proxy.translate_view(x, y)
def scale_view(self, x=1, y=1):
"""Scale the view by the given x and y factors."""
return self.proxy.scale_view(x, y)
def rotate_view(self, angle=0):
"""Roteate the view by the given x and y factors."""
self.proxy.rotate_view(angle)
def reset_view(self):
"""Reset all view transformations."""
self.proxy.reset_view()
def map_from_scene(self, point):
"""Returns the scene coordinate point mapped to viewport coordinates."""
return self.proxy.map_from_scene(point)
def map_to_scene(self, point):
"""Returns the viewport coordinate point mapped to scene coordinates."""
return self.proxy.map_to_scene(point)
def pixel_density(self):
"""Returns the size of a pixel in sceen coordinates."""
return self.proxy.pixel_density()
class GraphicsItemGroup(GraphicsItem):
#: Proxy reference
proxy = Typed(ProxyGraphicsItemGroup)
class AbstractGraphicsShapeItem(GraphicsItem):
"""A common base for all path items."""
#: Proxy reference
proxy = Typed(ProxyAbstractGraphicsShapeItem)
#: Set the pen or "line" style.
pen = d_(Instance(Pen))
#: Set the brush or "fill" style.
brush = d_(Instance(Brush))
@observe("pen", "brush")
def _update_proxy(self, change):
super(AbstractGraphicsShapeItem, self)._update_proxy(change)
class GraphicsRectItem(AbstractGraphicsShapeItem):
#: Proxy reference
proxy = Typed(ProxyGraphicsRectItem)
#: Width
width = d_(Float(10.0, strict=False))
#: Height
height = d_(Float(10.0, strict=False))
@observe("width", "height")
def _update_proxy(self, change):
super(GraphicsRectItem, self)._update_proxy(change)
class GraphicsEllipseItem(AbstractGraphicsShapeItem):
#: Proxy reference
proxy = Typed(ProxyGraphicsEllipseItem)
#: Width
width = d_(Float(10.0, strict=False))
#: Height
height = d_(Float(10.0, strict=False))
#: Sets the span angle for an ellipse segment to angle.
#: This is rounded to the nearest 16ths of a degree.
span_angle = d_(Float(360.0, strict=False))
#: Sets the start angle for an ellipse segment to angle.
#: This is rounded to the nearest 16ths of a degree.
start_angle = d_(Float(0.0, strict=False))
@observe("width", "height", "span_angle", "start_angle")
def _update_proxy(self, change):
super(GraphicsEllipseItem, self)._update_proxy(change)
class GraphicsTextItem(AbstractGraphicsShapeItem):
#: Proxy reference
proxy = Typed(ProxyGraphicsTextItem)
#: Text
text = d_(Str())
#: Font
font = d_(FontMember())
@observe("text", "font")
def _update_proxy(self, change):
super(GraphicsTextItem, self)._update_proxy(change)
class GraphicsLineItem(AbstractGraphicsShapeItem):
"""Creates a line from the position x,y to the given point"""
#: Proxy reference
proxy = Typed(ProxyGraphicsLineItem)
#: An x,y or x,y,z point
point = d_(PointMember())
@observe("point")
def _update_proxy(self, change):
super(GraphicsLineItem, self)._update_proxy(change)
class GraphicsPolygonItem(AbstractGraphicsShapeItem):
"""Creates a line from the position x,y to the given point"""
#: Proxy reference
proxy = Typed(ProxyGraphicsPolygonItem)
#: A list of (x,y) or (x,y,z) points
#: TODO: Support np array
points = d_(List(PointMember()))
@observe("points")
def _update_proxy(self, change):
super(GraphicsPolygonItem, self)._update_proxy(change)
class GraphicsPathItem(AbstractGraphicsShapeItem):
#: Proxy reference
proxy = Typed(ProxyGraphicsPathItem)
#: Path. For now you must pass a QPainterPath until some "abstract"
#: path value and format is accepted.
path = d_(Value())
@observe("path")
def _update_proxy(self, change):
super(GraphicsPathItem, self)._update_proxy(change)
class GraphicsImageItem(GraphicsItem):
#: Proxy reference
proxy = Typed(ProxyGraphicsImageItem)
#: Image
image = d_(Instance(Image))
@observe("image")
def _update_proxy(self, change):
super(GraphicsImageItem, self)._update_proxy(change)
class GraphicsWidget(GraphicsItem):
"""Use this to embed a widget within a graphics scene"""
#: Proxy reference
proxy = Typed(ProxyGraphicsWidget)
enamlx-0.6.4/enamlx/widgets/key_event.py 0000664 0000000 0000000 00000002650 14566137314 0020317 0 ustar 00root root 0000000 0000000 """
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Aug 29, 2015
"""
import sys
from atom.api import Bool, Event, ForwardTyped, List, Typed, observe
from enaml.core.declarative import d_
from enaml.widgets.control import Control, ProxyControl
if sys.version_info.major < 3:
str = basestring # noqa: F821
class ProxyKeyEvent(ProxyControl):
#: Reference to the declaration
declaration = ForwardTyped(lambda: KeyEvent)
def set_enabled(self, enabled):
raise NotImplementedError
def set_keys(self, keys):
raise NotImplementedError
class KeyEvent(Control):
#: Proxy reference
proxy = Typed(ProxyKeyEvent)
#: List of keys that or codes to filter
#: Can be a key letter or code and including modifiers
#: Ex. Ctrl + r, up, esc, etc..
#: If empty will fire for any key combination
keys = d_(List(str))
#: Listen for events
enabled = d_(Bool(True))
#: Fire multiple times when the key is held
repeats = d_(Bool(True))
#: Pressed event
pressed = d_(Event(dict), writable=False)
#: Released event
released = d_(Event(dict), writable=False)
@observe("enabled", "keys")
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
super(KeyEvent, self)._update_proxy(change)
enamlx-0.6.4/enamlx/widgets/plot_area.py 0000664 0000000 0000000 00000014355 14566137314 0020301 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
"""
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Jun 11, 2015
"""
import sys
from atom.api import (
Bool,
Callable,
ContainerList,
Dict,
Enum,
Float,
FloatRange,
ForwardInstance,
ForwardTyped,
Instance,
Int,
Str,
Tuple,
Typed,
Value,
observe,
)
from atom.atom import set_default
from enaml.core.declarative import d_
from enaml.widgets.api import Container
from enaml.widgets.control import Control, ProxyControl
if sys.version_info.major < 3:
str = basestring # noqa: F821
def numpy_ndarray():
import numpy
return numpy.ndarray
class ProxyPlotArea(ProxyControl):
declaration = ForwardTyped(lambda: PlotArea)
class PlotArea(Container):
hug_width = set_default("ignore")
hug_height = set_default("ignore")
proxy = Typed(ProxyPlotArea)
setup = d_(Callable(lambda graph: None))
PEN_ARGTYPES = (tuple, list, str, dict)
BRUSH_ARGTYPES = (tuple, list, str, dict, int, float)
class PlotItem(Control):
#: Title of data series
title = d_(Str())
#: Name
name = d_(Str())
#: Row in plot area
row = d_(Int(0))
#: Column in plot area
column = d_(Int(0))
#: Pen type to use for line
line_pen = d_(Instance(PEN_ARGTYPES))
#: Pen type to use for shadow
shadow_pen = d_(Instance(PEN_ARGTYPES))
#: Fill level
fill_level = d_(Float(strict=False))
# ‘c’ one of: r, g, b, c, m, y, k, w
# R, G, B, [A] integers 0-255
# (R, G, B, [A]) tuple of integers 0-255
# float greyscale, 0.0-1.0
# int see intColor()
# (int, hues) see intColor()
# “RGB” hexadecimal strings; may begin with ‘#’
# “RGBA”
# “RRGGBB”
# “RRGGBBAA”
#: Brush fill type
fill_brush = d_(Instance(BRUSH_ARGTYPES))
#: Symbol to use for points
symbol = d_(Enum(None, "o", "s", "t", "d", "+"))
#: Symbol sizes for points
symbol_size = d_(Float(10, strict=False))
#: Symbol pen to use
symbol_pen = d_(Instance(PEN_ARGTYPES))
#: Symbol brush
symbol_brush = d_(Instance(BRUSH_ARGTYPES))
#: Show legend
show_legend = d_(Bool(False))
label_left = d_(Str())
label_right = d_(Str())
label_top = d_(Str())
label_bottom = d_(Str())
# H, V
grid = d_(Tuple(bool, default=(False, False)))
grid_alpha = d_(FloatRange(low=0.0, high=1.0, value=0.5))
#: Display a separate axis for each nested plot
multi_axis = d_(Bool(True))
axis_left_ticks = d_(Callable())
axis_bottom_ticks = d_(Callable())
#: Display the axis on log scale
log_mode = d_(Tuple(bool, default=(False, False))) # x,y
#: Enable antialiasing
antialias = d_(Bool(False))
#: Set auto range for each axis
auto_range = d_(
Enum(True, False, (True, True), (True, False), (False, True), (False, False))
)
# x-range to use if auto_range is disabled
range_x = d_(ContainerList(default=[0, 100]))
#: y-range to use if auto_range is disabled
range_y = d_(ContainerList(default=[0, 100]))
#: Automatically downsaple
auto_downsample = d_(Bool(False))
#: Clip data points to view
clip_to_view = d_(Bool(False))
#: Step mode to use
step_mode = d_(Bool(False))
#: Keep aspect ratio locked when resizing
aspect_locked = d_(Bool(False))
#: Time between updates
refresh_time = d_(Int(100))
@observe(
"line_pen",
"symbol",
"symbol_size",
"symbol_pen",
"symbol_brush",
"fill_brush",
"fill_level",
"multi_axis",
"title",
"label_left",
"label_right",
"label_top",
"label_bottom",
"grid",
"grid_alpha",
"log_mode",
"antialias",
"auto_range",
"auto_downsample",
"clip_to_view",
"step_mode",
"aspect_locked",
"axis_left_ticks",
"axis_bottom_ticks",
"show_legend",
"row",
"column",
)
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
# The superclass handler implementation is sufficient.
super(PlotItem, self)._update_proxy(change)
@observe("range_x", "range_y")
def _update_range(self, change):
"""Handle updates and changes"""
getattr(self.proxy, "set_%s" % change["name"])(change["value"])
class PlotItem2D(PlotItem):
#: x-axis values, as a list
x = d_(ContainerList())
#: y-axis values, as a list
y = d_(ContainerList())
@observe("x", "y")
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
# The superclass handler implementation is sufficient.
super(PlotItem2D, self)._update_proxy(change)
class PlotItem3D(PlotItem2D):
#: z-axis values, as a list
z = d_(ContainerList())
@observe("z")
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
# The superclass handler implementation is sufficient.
super(PlotItem3D, self)._update_proxy(change)
class PlotItemArray(PlotItem2D):
"""Numpy array item"""
#: x-axis values, as a numpy array
x = d_(ForwardInstance(numpy_ndarray))
#: y-axis values, as a numpy array
y = d_(ForwardInstance(numpy_ndarray))
class PlotItemArray3D(PlotItem3D):
"""Numpy array item"""
#: Plot type
type = Enum("line")
#: x-axis values, as a numpy array
x = d_(ForwardInstance(numpy_ndarray))
#: y-axis values, as a numpy array
y = d_(ForwardInstance(numpy_ndarray))
#: z-axis values, as a numpy array
z = d_(ForwardInstance(numpy_ndarray))
class AbstractDataPlotItem(PlotItem):
data = d_(Value())
@observe("data")
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
# The superclass handler implementation is sufficient.
super(AbstractDataPlotItem, self)._update_proxy(change)
class PlotItemList(AbstractDataPlotItem):
data = d_(ContainerList())
class PlotItemDict(AbstractDataPlotItem):
data = d_(Dict(default={"x": [], "y": []}))
enamlx-0.6.4/enamlx/widgets/table_view.py 0000664 0000000 0000000 00000005462 14566137314 0020453 0 ustar 00root root 0000000 0000000 """
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Jun 3, 2015
"""
from atom.api import Bool, ForwardTyped, Int, Typed, observe
from enaml.core.declarative import d_
from enamlx.widgets.abstract_item import (
AbstractWidgetItem,
AbstractWidgetItemGroup,
ProxyAbstractWidgetItem,
ProxyAbstractWidgetItemGroup,
)
from enamlx.widgets.abstract_item_view import AbstractItemView, ProxyAbstractItemView
class ProxyTableView(ProxyAbstractItemView):
declaration = ForwardTyped(lambda: TableView)
def get_row_count(self):
raise NotImplementedError
def get_column_count(self):
raise NotImplementedError
def set_show_grid(self, show):
pass
class ProxyTableViewRow(ProxyAbstractWidgetItemGroup):
declaration = ForwardTyped(lambda: TableViewRow)
def set_row(self, row):
raise NotImplementedError
class ProxyTableViewColumn(ProxyAbstractWidgetItemGroup):
declaration = ForwardTyped(lambda: TableViewColumn)
def set_column(self, column):
raise NotImplementedError
class ProxyTableViewItem(ProxyAbstractWidgetItem):
declaration = ForwardTyped(lambda: TableViewItem)
def data_changed(self, change):
raise NotImplementedError
class TableView(AbstractItemView):
#: Proxy reference
proxy = Typed(ProxyTableView)
#: Show grid of cells
show_grid = d_(Bool(True))
@observe("show_grid")
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
if change["name"] == "items":
self._update_visible_area()
super(TableView, self)._update_proxy(change)
def _update_visible_area(self):
self.visible_rows = min(100, len(self.items))
self.visible_columns = min(100, len(self.items))
class TableViewItem(AbstractWidgetItem):
"""The base class implementation is sufficient."""
#: Proxy reference
proxy = Typed(ProxyTableViewItem)
class TableViewRow(AbstractWidgetItemGroup):
"""Use this to build a table by defining the rows."""
#: Proxy reference
proxy = Typed(ProxyTableViewRow)
#: Row within the table
row = d_(Int())
@observe("row")
def _update_index(self, change):
for column, item in enumerate(self._items):
item.row = self.row
item.column = column
class TableViewColumn(AbstractWidgetItemGroup):
"""Use this to build a table by defining the columns."""
#: Proxy reference
proxy = Typed(ProxyTableViewColumn)
#: Column within the table
column = d_(Int())
@observe("column")
def _update_index(self, change):
for row, item in enumerate(self._item):
item.row = row
item.column = self.column
enamlx-0.6.4/enamlx/widgets/tree_view.py 0000664 0000000 0000000 00000006636 14566137314 0020327 0 ustar 00root root 0000000 0000000 """
Copyright (c) 2015, Jairus Martin.
Distributed under the terms of the MIT License.
The full license is in the file COPYING.txt, distributed with this software.
Created on Jun 3, 2015
"""
from atom.api import Bool, ContainerList, ForwardTyped, Int, Property, Typed, observe
from enaml.core.declarative import d_
from enamlx.widgets.abstract_item import (
AbstractWidgetItem,
ProxyAbstractWidgetItem,
ProxyAbstractWidgetItemGroup,
)
from enamlx.widgets.abstract_item_view import AbstractItemView, ProxyAbstractItemView
class ProxyTreeView(ProxyAbstractItemView):
declaration = ForwardTyped(lambda: TreeView)
class ProxyTreeViewColumn(ProxyAbstractWidgetItemGroup):
declaration = ForwardTyped(lambda: TreeViewColumn)
def set_column(self, column):
raise NotImplementedError
class ProxyTreeViewItem(ProxyAbstractWidgetItem):
declaration = ForwardTyped(lambda: TreeViewItem)
def refresh_model(self, schange):
raise NotImplementedError
class TreeView(AbstractItemView):
#: Proxy widget
proxy = Typed(ProxyTreeView)
#: Show root node
show_root = d_(Bool(True))
@observe("show_root")
def _update_proxy(self, change):
"""An observer which sends state change to the proxy."""
# The superclass handler implementation is sufficient.
super(TreeView, self)._update_proxy(change)
def child_added(self, child):
super(TreeView, self).child_added(child)
self._update_rows()
def child_removed(self, child):
super(TreeView, self).child_removed(child)
self._update_rows()
def _update_rows(self):
for r, item in enumerate(self._items):
item.row = r
class TreeViewItem(AbstractWidgetItem):
#: Proxy reference
proxy = Typed(ProxyTreeViewItem)
#: The child items
items = d_(ContainerList(default=[]))
#: First visible row
visible_row = d_(Int(0))
#: Number of rows visible
visible_rows = d_(Int(100))
#: First visible column
visible_column = d_(Int(0))
#: Number of columns visible
visible_columns = d_(Int(1))
def _get_items(self):
"""Items should be a list of child TreeViewItems excluding
columns.
"""
return [c for c in self.children if isinstance(c, TreeViewItem)]
def _get_columns(self):
"""List of child TreeViewColumns including
this item as the first column
"""
return [self] + [c for c in self.children if isinstance(c, TreeViewColumn)]
#: Columns
_columns = Property(lambda self: self._get_columns(), cached=True)
def child_added(self, child):
super(TreeViewItem, self).child_added(child)
self.get_member("_columns").reset(self)
self._update_rows()
def child_removed(self, child):
super(TreeViewItem, self).child_removed(child)
self.get_member("_columns").reset(self)
self._update_rows()
def _update_rows(self):
"""Update the row and column numbers of child items."""
for row, item in enumerate(self._items):
item.row = row # Row is the Parent item
item.column = 0
for column, item in enumerate(self._columns):
item.row = self.row # Row is the Parent item
item.column = column
class TreeViewColumn(AbstractWidgetItem):
"""Use this to build a table by defining the columns."""
#: Proxy reference
proxy = Typed(ProxyTreeViewColumn)
enamlx-0.6.4/examples/ 0000775 0000000 0000000 00000000000 14566137314 0014635 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/examples/double_spin_box.enaml 0000664 0000000 0000000 00000001023 14566137314 0021022 0 ustar 00root root 0000000 0000000 import enamlx
enamlx.install()
from enaml.widgets.api import Window, Form, Label, Field, SpinBox
from enaml.layout.api import hbox, vbox
from enamlx.widgets.api import DoubleSpinBox
enamldef Main(Window):
title = 'DoubleSpinBox Example'
Form:
Label: lbl:
text = 'Value'
DoubleSpinBox: sbox:
maximum = 100
minimum = 0
decimals = 9
value = 1/25.4
Field: fld:
text << u'Value: {}'.format(sbox.value)
read_only = True
enamlx-0.6.4/examples/graphics_view/ 0000775 0000000 0000000 00000000000 14566137314 0017467 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/examples/graphics_view/graphics_interact.enaml 0000664 0000000 0000000 00000006442 14566137314 0024204 0 ustar 00root root 0000000 0000000 import math
import enamlx
enamlx.install()
from enaml.core.api import Looper
from enamlx.widgets.api import (
GraphicsView, GraphicsItem, GraphicsTextItem, GraphicsRectItem,
GraphicsPolygonItem, GraphicsEllipseItem, GraphicsLineItem,
GraphicsPathItem, GraphicsItemGroup, Pen, Brush, Point
)
from enaml.widgets.api import (
MainWindow, Container, PushButton, CheckBox, RadioButton, SpinBox,
Menu, Action, MenuBar, Feature, ObjectCombo, Form, Label
)
from enaml.drag_drop import DragData, DropAction
from enaml.qt.QtGui import QPainterPath
from enaml.qt.QtCore import Qt
def create_drag_data(data):
drag = DragData()
drag.supported_actions = DropAction.Copy
drag.mime_data.set_data('text/plain', data)
return drag
enamldef Main(MainWindow): window:
attr blue_pen = Pen(color='blue')
attr red_pen = Pen(color='red')
attr green_brush = Brush(color='green')
MenuBar:
Menu:
title = "&File"
Action:
text = "Quit\tCtrl+Q"
triggered :: raise SystemExit(0)
Container:
Form:
Label:
text = "Drag mode:"
ObjectCombo:
items = list(canvas.get_member('drag_mode').items)
selected := canvas.drag_mode
GraphicsView: canvas:
attr size = (960, 960)
background = "#fff"
drag_mode = 'selection'
minimum_size = size
attr last_point = None
Menu:
context_menu = True
Action:
text = 'Reset'
triggered :: canvas.reset_view()
mouse_press_event => (evt):
self.last_point = evt.pos()
mouse_move_event => (evt):
if self.last_point is None:
self.last_point = evt.pos()
if self.selected_items or self.drag_mode == 'selection':
return
dp = evt.pos()-self.last_point
delta = Point(dp.x(), dp.y())
self.last_point = evt.pos()
if evt.buttons() in (Qt.MidButton, Qt.LeftButton):
px = self.pixel_density()
tr = -delta*px
self.translate_view(tr.x, tr.y)
wheel_event => (evt):
if hasattr(evt, 'delta'):
d = evt.delta()
else:
d = evt.angleDelta().y()
s = 1.001 ** d
self.scale_view(s, s)
selected_items ::
old = change['oldvalue']
new = change['value']
for item in old:
item.pen = blue_pen
for item in new:
item.pen = red_pen
Looper:
iterable = range(100)
GraphicsTextItem:
position = (0, loop_index*20)
pen = blue_pen
movable = True
selectable = True
text << "{}".format(position)
features = Feature.DragEnabled
drag_start => ():
return create_drag_data(b"Hello")
drag_end => (drag_data, result):
print(drag_data, result)
enamlx-0.6.4/examples/graphics_view/graphics_items.enaml 0000664 0000000 0000000 00000010035 14566137314 0023505 0 ustar 00root root 0000000 0000000 import math
import enamlx
enamlx.install()
from enaml.core.api import Looper
from enamlx.widgets.api import (
GraphicsView, GraphicsItem, GraphicsTextItem, GraphicsRectItem,
GraphicsPolygonItem, GraphicsEllipseItem, GraphicsLineItem,
GraphicsPathItem, GraphicsItemGroup, GraphicsWidget, Pen, Brush
)
from enaml.widgets.api import (
MainWindow, Container, PushButton, CheckBox, RadioButton, SpinBox,
Menu, Action, MenuBar
)
from enaml.qt.QtGui import QPainterPath
from enaml.application import timed_call
from random import randint
enamldef Main(MainWindow): window:
attr blue_pen = Pen(color='blue')
attr red_pen = Pen(color='red')
attr green_brush = Brush(color='green')
initial_size = canvas.size
MenuBar:
Menu:
title = "&File"
Action:
text = "Quit\tCtrl+Q"
triggered :: raise SystemExit(0)
Container:
padding = 0
GraphicsView: canvas:
attr size = (960, 960)
background = "#fff"
Looper:
iterable = range(500)
GraphicsTextItem:
position = (0, loop_index*20)
rotation = loop_index%30
pen = blue_pen if loop_index & 1 else red_pen
text << "{}".format(position)
GraphicsTextItem:
attr delay = 100+10*loop_index
attr fade_dir = -0.1
activated :: timed_call(delay, fade_in_and_out)
position = (300, loop_index*20, 0)
text << "{} {} {}".format(position.x, position.y, position.z)
func fade_in_and_out():
if self.opacity <= 0 or self.opacity >= 1:
self.fade_dir *= -1
self.opacity -= self.fade_dir
timed_call(delay, fade_in_and_out)
GraphicsEllipseItem:
attr delay = randint(500, 1000)
activated :: timed_call(delay, move_around)
func move_around():
self.position += (randint(-10, 10), randint(-10, 10))
timed_call(delay, move_around)
position = (randint(0, 240),
randint(500, 640), 0)
opacity = 0.3
pen = blue_pen if loop_index & 1 else red_pen
width = 20
height = 30
GraphicsRectItem:
brush = green_brush
position = (randint(0, 640),
randint(0, 640), 0)
opacity = 0.3
width = 10
GraphicsWidget:
position = (500, 100)
rotation = 90.0
PushButton:
text = "Click me!"
clicked :: parent.position += (0, 10)
# Grid
GraphicsItemGroup:
opacity = 0.05
Looper:
iterable = range(100)
GraphicsLineItem:
position = (10*loop_index, 0)
point = (10*loop_index, canvas.size[1], 0)
GraphicsLineItem:
position = (0, 10*loop_index)
point = (canvas.size[0], 10*loop_index, 0)
GraphicsPolygonItem:
scale = 3
points = [(i, 100+10*math.sin(i)) for i in range(100)]
GraphicsPathItem:
pen = Pen(color="purple", width=2)
scale = 3
movable = True
path << redraw(self.position)
func redraw(position):
x, y, z = position
p = QPainterPath()
p.moveTo(x, y)
p.lineTo(x+10, y)
w, h = 10, 10
p.addEllipse(x+10, y-w/2, w, h)
p.lineTo(x+10+w+10, y)
return p
enamlx-0.6.4/examples/graphics_view/main.py 0000664 0000000 0000000 00000000434 14566137314 0020766 0 ustar 00root root 0000000 0000000 import enaml
from enaml.qt.qt_application import QtApplication
import enamlx
if __name__ == "__main__":
enamlx.install(allow_def=True)
with enaml.imports():
from graphics_items import Main
app = QtApplication()
view = Main()
view.show()
app.start()
enamlx-0.6.4/examples/images/ 0000775 0000000 0000000 00000000000 14566137314 0016102 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/examples/images/icons/ 0000775 0000000 0000000 00000000000 14566137314 0017215 5 ustar 00root root 0000000 0000000 enamlx-0.6.4/examples/images/icons/README.md 0000664 0000000 0000000 00000000141 14566137314 0020470 0 ustar 00root root 0000000 0000000 Icons from [http://www.famfamfam.com/lab/icons/silk/](http://www.famfamfam.com/lab/icons/silk/)
enamlx-0.6.4/examples/images/icons/bug.png 0000664 0000000 0000000 00000001406 14566137314 0020501 0 ustar 00root root 0000000 0000000 PNG
IHDR a gAMA 7 tEXtSoftware Adobe ImageReadyqe<