Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/kernel #17

Merged
merged 37 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7f6b24f
:tada: Add IPython Execution
AlexandreSajus Oct 8, 2021
103e3cb
:memo: Add Ipython to requirements
MathisFederico Oct 10, 2021
acc0e7f
:tada: Add IPython Execution
AlexandreSajus Oct 8, 2021
a020380
:memo: Add Ipython to requirements
MathisFederico Oct 10, 2021
65d732d
Merge branch 'feature/IPythonExecution' of github.com:MathisFederico/…
MathisFederico Oct 10, 2021
ad33fab
:wrench: Refactor pyeditor focusOutEvent
MathisFederico Oct 10, 2021
ea04fc9
:sparkles: new docstrings and tests
AlexandreSajus Oct 14, 2021
014cccf
:wrench: some of the refactors requested
AlexandreSajus Oct 20, 2021
9e4d582
:hammer: Changes requested last commit
AlexandreSajus Oct 29, 2021
d524fa7
:wrench: removed a print
AlexandreSajus Oct 30, 2021
a79e1ed
:tada: execution of code through a kernel and image support
AlexandreSajus Oct 31, 2021
5b1ab38
:tada: support for variable output (ex: Tensorflow progress bars)
AlexandreSajus Nov 1, 2021
0755cc0
:wrench: removed unnecessary prints
AlexandreSajus Nov 2, 2021
a9b8dd1
:wrench: specified args and return types
AlexandreSajus Nov 2, 2021
18e8dc7
Merge branch 'master' of https://github.com/MathisFederico/OpenCodeBl…
AlexandreSajus Nov 3, 2021
a00c37e
:wrench: added requirements
AlexandreSajus Nov 3, 2021
b89f994
:tada: Fully functional notebook
AlexandreSajus Nov 4, 2021
fda4dec
:wrench: Comments
AlexandreSajus Nov 4, 2021
e45c029
Merge remote-tracking branch 'origin' into feature/kernel
MathisFederico Nov 6, 2021
e001977
:memo: Update requirements with versions
MathisFederico Nov 6, 2021
b947f24
:beetle: Fix crash on open if json is modified
MathisFederico Nov 13, 2021
c106af3
The cursor changes to resize mode when hovering over the resizing are…
vanyle Nov 13, 2021
4560323
Add ability to resize the source_editor and the output display part o…
vanyle Nov 13, 2021
2e7461a
Removed trailing whitespace
vanyle Nov 13, 2021
51c3561
:wrench: Rewrote part of block.py to remove repetitions related to ho…
vanyle Nov 14, 2021
0c2b5bf
:wrench: Added type annotation to event
vanyle Nov 14, 2021
b5d2bc8
:memo: Remove an incorrect comment
vanyle Nov 14, 2021
5e5ea8d
:sparkles: Add autopep8 as a dependency to make to easier to fix most…
vanyle Nov 14, 2021
c9ed4da
:beetle: Fix a bug where the cursor would remain in resize mode when …
vanyle Nov 14, 2021
c48caa3
:wrench: Replace %string by f-string
MathisFederico Nov 14, 2021
d49aeef
:sparkles: Fix Redefining built-in type
MathisFederico Nov 14, 2021
5b60941
:sparkles: Fix pylint issues
MathisFederico Nov 14, 2021
73cffaf
:wrench: Refactor Kernel
MathisFederico Nov 14, 2021
a4b9acd
:beetle: Fix Kernel.execute
MathisFederico Nov 14, 2021
f3b115f
:tada: Merge feature/output_resizing (#29)
MathisFederico Nov 14, 2021
0f367f4
:beetle: Fix kernel update output
MathisFederico Nov 15, 2021
5d2ee76
:twisted_rightwards_arrows: Merge 'master' into feature/kernel
MathisFederico Nov 17, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ Before doing your **pull request**, check using `pylint` and `pytest` that there
pylint .\opencodeblocks\
```

Some `pylint` issues can be fixed automatically using `autopep8`, with the following command:

```bash
autopep8 --in-place --recursive --aggressive opencodeblocks
```

```bash
pytest --cov=opencodeblocks --cov-report=html tests/unit
```
Expand Down
4 changes: 0 additions & 4 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
import sys

from qtpy.QtWidgets import QApplication

from opencodeblocks.graphics.blocks.codeblock import OCBCodeBlock, OCBBlock
from opencodeblocks.graphics.edge import OCBEdge
from opencodeblocks.graphics.socket import OCBSocket
from opencodeblocks.graphics.window import OCBWindow

sys.path.insert(0, os.path.join( os.path.dirname(__file__), "..", ".." ))
Expand Down
51 changes: 42 additions & 9 deletions opencodeblocks/graphics/blocks/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from PyQt5.QtCore import QPointF, QRectF, Qt
from PyQt5.QtGui import QBrush, QPen, QColor, QFont, QPainter, QPainterPath
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsSceneMouseEvent, QGraphicsTextItem, \
QStyleOptionGraphicsItem, QWidget, QApplication
QStyleOptionGraphicsItem, QWidget, QApplication, QGraphicsSceneHoverEvent

from opencodeblocks.core.serializable import Serializable
from opencodeblocks.graphics.socket import OCBSocket
Expand Down Expand Up @@ -72,7 +72,10 @@ def __init__(self, block_type:str='base', source:str='', position:tuple=(0, 0),
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)
self.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)

self.setAcceptHoverEvents(True)

self.resizing = False
self.resizing_hover = False # Is the mouse hovering over the resizing area ?
self.moved = False
self.metadata = {
'title_metadata': {
Expand Down Expand Up @@ -130,8 +133,8 @@ def paint(self, painter: QPainter,

def _is_in_resize_area(self, pos:QPointF):
""" Return True if the given position is in the block resize_area. """
return self.width - pos.x() < 2 * self.edge_size \
and self.height - pos.y() < 2 * self.edge_size
return self.width - self.edge_size*2 < pos.x() \
and self.height - self.edge_size*2 < pos.y()

def get_socket_pos(self, socket:OCBSocket) -> Tuple[float]:
""" Get a socket position to place them on the block sides. """
Expand Down Expand Up @@ -172,21 +175,51 @@ def remove_socket(self, socket:OCBSocket):
socket.remove()
self.update_sockets()

def hoverMoveEvent(self, event:QGraphicsSceneHoverEvent):
""" Triggered when hovering over a block """
pos = event.pos()
if self._is_in_resize_area(pos):
if not self.resizing_hover:
self._start_hovering()
elif self.resizing_hover:
self._stop_hovering()
return super().hoverMoveEvent(event)

def _start_hovering(self):
self.resizing_hover = True
QApplication.setOverrideCursor(Qt.CursorShape.SizeFDiagCursor)

def _stop_hovering(self):
self.resizing_hover = False
QApplication.restoreOverrideCursor()

def _start_resize(self,pos:QPointF):
self.resizing = True
self.resize_start = pos
QApplication.setOverrideCursor(Qt.CursorShape.SizeFDiagCursor)

def _stop_resize(self):
self.resizing = False
QApplication.setOverrideCursor(Qt.CursorShape.SizeFDiagCursor)

def hoverLeaveEvent(self, event:QGraphicsSceneHoverEvent):
""" Triggered when the mouse stops hovering over a block """
if self.resizing_hover:
self._stop_hovering()
return super().hoverLeaveEvent(event)

def mousePressEvent(self, event:QGraphicsSceneMouseEvent):
""" OCBBlock reaction to a mousePressEvent. """
pos = event.pos()
if self._is_in_resize_area(pos) and event.buttons() == Qt.MouseButton.LeftButton:
self.resize_start = pos
self.resizing = True
QApplication.setOverrideCursor(Qt.CursorShape.SizeFDiagCursor)
if self.resizing_hover and event.buttons() == Qt.MouseButton.LeftButton:
self._start_resize(pos)
super().mousePressEvent(event)

def mouseReleaseEvent(self, event:QGraphicsSceneMouseEvent):
""" OCBBlock reaction to a mouseReleaseEvent. """
if self.resizing:
self.scene().history.checkpoint("Resized block", set_modified=True)
self.resizing = False
QApplication.restoreOverrideCursor()
self._stop_resize()
if self.moved:
self.moved = False
self.scene().history.checkpoint("Moved block", set_modified=True)
Expand Down
172 changes: 163 additions & 9 deletions opencodeblocks/graphics/blocks/codeblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,61 @@

""" Module for the base OCB Code Block. """

from PyQt5.QtWidgets import QGraphicsProxyWidget
from typing import Optional

from PyQt5.QtCore import Qt, QByteArray, QPointF
from PyQt5.QtGui import QPainter, QPainterPath, QPixmap
from PyQt5.QtWidgets import QStyleOptionGraphicsItem, QWidget, QGraphicsProxyWidget, QLabel, \
QGraphicsSceneMouseEvent, QApplication

from opencodeblocks.graphics.blocks.block import OCBBlock
from opencodeblocks.graphics.pyeditor import PythonEditor

class OCBCodeBlock(OCBBlock):

""" Code Block. """
"""
Code Block

Features an area to edit code as well as a panel to display the output.

The following is always true:
output_panel_height + source_panel_height + edge_size*2 + title_height == height

"""

def __init__(self, **kwargs):
super().__init__(block_type='code', **kwargs)

self.output_panel_height = self.height / 3
self._min_output_panel_height = 20
self._min_source_editor_height = 20

self.source_editor = self.init_source_editor()
self.display = self.init_display()
self.stdout = ""
self.image = ""

self.resizing_source_code = False

self.update_all() # Set the geometry of display and source_editor

def init_source_editor(self):
""" Initialize the python source code editor. """
source_editor_graphics = QGraphicsProxyWidget(self)
source_editor = PythonEditor(self)
source_editor.setGeometry(
int(self.edge_size),
int(self.edge_size + self.title_height),
int(self.width - 2*self.edge_size),
int(self.height - self.title_height - 2*self.edge_size)
)
source_editor_graphics.setWidget(source_editor)
source_editor_graphics.setZValue(-1)
return source_editor_graphics

@property
def _editor_widget_height(self):
return self.height - self.title_height - 2*self.edge_size \
- self.output_panel_height

@_editor_widget_height.setter
def _editor_widget_height(self, value: int):
self.output_panel_height = self.height - value - self.title_height - 2*self.edge_size

def update_all(self):
""" Update the code block parts. """
if hasattr(self, 'source_editor'):
Expand All @@ -38,17 +66,143 @@ def update_all(self):
int(self.edge_size),
int(self.edge_size + self.title_height),
int(self._width - 2*self.edge_size),
int(self.height - self.title_height - 2*self.edge_size)
int(self._editor_widget_height)
)
display_widget = self.display.widget()
display_widget.setGeometry(
int(self.edge_size),
int(self.height - self.output_panel_height - self.edge_size),
int(self.width - 2*self.edge_size),
int(self.output_panel_height)
)
super().update_all()

@property
def source(self) -> str:
""" Source code. """
return self._source

@source.setter
def source(self, value:str):
self._source = value
if hasattr(self, 'source_editor'):
editor_widget = self.source_editor.widget()
editor_widget.setText(self._source)

@property
def stdout(self) -> str:
""" Code output. Be careful, this also includes stderr """
return self._stdout
@stdout.setter
def stdout(self, value:str):
self._stdout = value
if hasattr(self, 'source_editor'):
# If there is a text output, erase the image output and display the text output
self.image = ""
editor_widget = self.display.widget()
editor_widget.setText(self._stdout)

@property
def image(self) -> str:
""" Code output. """
return self._image

@image.setter
def image(self, value:str):
self._image = value
if hasattr(self, 'source_editor') and self.image != "":
# If there is an image output, erase the text output and display the image output
editor_widget = self.display.widget()
editor_widget.setText("")
qlabel = editor_widget
ba = QByteArray.fromBase64(str.encode(self.image))
pixmap = QPixmap()
pixmap.loadFromData(ba)
qlabel.setPixmap(pixmap)

@source.setter
def source(self, value:str):
self._source = value
if hasattr(self, 'source_editor'):
editor_widget = self.source_editor.widget()
editor_widget.setText(self._source)

def paint(self, painter: QPainter,
option: QStyleOptionGraphicsItem, #pylint:disable=unused-argument
widget: Optional[QWidget]=None): #pylint:disable=unused-argument
""" Paint the code output panel """
super().paint(painter, option, widget)
path_title = QPainterPath()
path_title.setFillRule(Qt.FillRule.WindingFill)
path_title.addRoundedRect(0, 0, self.width, self.height,
self.edge_size, self.edge_size)
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(self._brush_background)
painter.drawPath(path_title.simplified())

def _is_in_resize_source_code_area(self, pos:QPointF):
"""
Return True if the given position is in the area
used to resize the source code widget
"""
source_editor_start = self.height - self.output_panel_height - self.edge_size

return self.width - self.edge_size/2 < pos.x() and \
source_editor_start - self.edge_size < pos.y() < source_editor_start + self.edge_size


def _is_in_resize_area(self, pos:QPointF):
""" Return True if the given position is in the block resize_area. """

# This block features 2 resizing areas with 2 different behaviors
is_in_bottom_left = super()._is_in_resize_area(pos)
return is_in_bottom_left or self._is_in_resize_source_code_area(pos)

def _start_resize(self,pos:QPointF):
self.resizing = True
self.resize_start = pos
if self._is_in_resize_source_code_area(pos):
self.resizing_source_code = True
QApplication.setOverrideCursor(Qt.CursorShape.SizeFDiagCursor)

def _stop_resize(self):
self.resizing = False
self.resizing_source_code = False
QApplication.restoreOverrideCursor()

def mouseMoveEvent(self, event:QGraphicsSceneMouseEvent):
"""
We override the default resizing behavior as the code part and the display part of the block
block can be resized independently.
"""
if self.resizing:
delta = event.pos() - self.resize_start
self.width = max(self.width + delta.x(), self._min_width)

height_delta = max(delta.y(),
# List of all the quantities that must remain negative.
# Mainly: min_height - height must be negative for all elements
self._min_output_panel_height - self.output_panel_height,
self._min_height - self.height,
self._min_source_editor_height - self._editor_widget_height
)

self.height += height_delta
if not self.resizing_source_code:
self.output_panel_height += height_delta

self.resize_start = event.pos()
self.title_graphics.setTextWidth(self.width - 2 * self.edge_size)
self.update()

self.moved = True
super().mouseMoveEvent(event)

def init_display(self):
""" Initialize the output display widget: QLabel """
display_graphics = QGraphicsProxyWidget(self)
display = QLabel()
display.setText("")
display_graphics.setWidget(display)
display_graphics.setZValue(-1)
return display_graphics
14 changes: 9 additions & 5 deletions opencodeblocks/graphics/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,12 @@ def deserialize(self, data: OrderedDict, hashmap: dict = None, restore_id=True):
if restore_id:
self.id = data['id']
self.path_type = data['path_type']
self.source_socket = hashmap[data['source']['socket']]
self.source_socket.add_edge(self)
self.destination_socket = hashmap[data['destination']['socket']]
self.destination_socket.add_edge(self)
self.update_path()
try:
self.source_socket = hashmap[data['source']['socket']]
self.source_socket.add_edge(self)

self.destination_socket = hashmap[data['destination']['socket']]
self.destination_socket.add_edge(self)
self.update_path()
except KeyError:
self.remove()
Loading