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

GlobalEval #28

Merged
merged 28 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1e7a136
Initialize NARSWindow.
bowen-xu Sep 16, 2023
2c221b8
Merge branch 'dev' into gui
bowen-xu Sep 16, 2023
b6f5350
modify
bowen-xu Sep 16, 2023
eb7e870
gui and reasnoner can communicate with each other now.
bowen-xu Sep 17, 2023
2afa600
start point
bowen-xu Sep 17, 2023
7cda44f
Merge branch 'gui' into GlobalEval
bowen-xu Sep 17, 2023
6c9aec6
fix bug
bowen-xu Sep 17, 2023
2896bb4
Merge branch 'gui' into GlobalEval
bowen-xu Sep 17, 2023
aacdb53
can do evaluation of satisfaction now.
bowen-xu Sep 18, 2023
2ea3c3a
Merge branch 'bug/processing-same-task-multiple-times' into GlobalEval
bowen-xu Sep 18, 2023
9ba5055
Merge branch 'dev' into GlobalEval
bowen-xu Sep 19, 2023
89d6385
modify
bowen-xu Sep 20, 2023
689ce83
avoid printing empty line
bowen-xu Sep 20, 2023
e4dc845
Merge branch 'gui' into GlobalEval
bowen-xu Sep 20, 2023
bcf0590
add plots to gui
bowen-xu Sep 21, 2023
6428fcf
display busyness
bowen-xu Sep 21, 2023
097386d
Merge branch 'dev' into GlobalEval
bowen-xu Sep 24, 2023
46a25ea
modify
bowen-xu Oct 17, 2023
b4bb221
Merge branch 'dev' into GlobalEval
bowen-xu Oct 17, 2023
bdda4f2
Merge branch 'dev' into GlobalEval
bowen-xu Oct 22, 2023
efb4055
Merge branch 'dev' into GlobalEval
bowen-xu Nov 8, 2023
6c1c951
plot all the four evaluators
bowen-xu Nov 8, 2023
bd84a0b
implemented
bowen-xu Nov 9, 2023
704a9b9
modify
bowen-xu Nov 9, 2023
1ec9a74
fix requirement
bowen-xu Nov 9, 2023
b57d487
Merge branch 'dev' into GlobalEval
bowen-xu Dec 6, 2023
49e4b83
Merge branch 'dev' into GlobalEval
bowen-xu Dec 7, 2023
390d51d
call `update_wellbeing()` in `_accept_goal()`
bowen-xu Dec 7, 2023
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: 3 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"name": "Python: pynars.GUI",
"type": "python",
"request": "launch",
"module": "enter-your-module-name",
"module": "pynars.GUI",
"justMyCode": true
},
{
Expand Down Expand Up @@ -268,7 +268,7 @@
"module": "Narsese.Parser._test"
},
{
"name": "Python: 当前文件",
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
Expand Down
113 changes: 113 additions & 0 deletions pynars/GUI/Backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from copy import deepcopy
from typing import Tuple, Union
from pathlib import Path
from multiprocessing import Process
import random
from pynars.NARS import Reasoner as Reasoner
from pynars.utils.Print import print_out, PrintType
from pynars.Narsese import Task
from typing import List
from pynars.utils.tools import rand_seed
import asyncio
from as_rpc import AioRpcServer, AioRpcClient, rpc
from functools import partial



def run_line(nars: Reasoner, line: str):
''''''

ret = []
satisfaction = []
busyness = []
alertness = []
wellness = []

def handle_line(tasks_line: Tuple[List[Task], Task, Task, List[Task], Task, Tuple[Task, Task]]):
tasks_derived, judgement_revised, goal_revised, answers_question, answers_quest, (
task_operation_return, task_executed) = tasks_line

for task in tasks_derived:
ret.append(repr(task))

if judgement_revised is not None:
ret.append(repr(judgement_revised))
if goal_revised is not None:
ret.append(repr(goal_revised))
if answers_question is not None:
for answer in answers_question:
ret.append(repr(answer))
if answers_quest is not None:
for answer in answers_quest:
ret.append(repr(answer))
if task_executed is not None:
ret.append(f'{task_executed.term.repr()} = {str(task_operation_return) if task_operation_return is not None else None}')

line = line.strip(' \n')
if line.startswith("'"):
return None
elif line.isdigit():
n_cycle = int(line)
for _ in range(n_cycle):
tasks_all = nars.cycle()
satisfaction.append(nars.global_eval.S)
busyness.append(nars.global_eval.B)
alertness.append(nars.global_eval.A)
wellness.append(nars.global_eval.W)
handle_line(tasks_all)
else:
line = line.rstrip(' \n')
if len(line) == 0:
return ret
try:
success, task, _ = nars.input_narsese(line, go_cycle=False)
if success:
ret.append(repr(task))
else:
ret.append(f':Invalid input! Failed to parse: {line}')

tasks_all = nars.cycle()
satisfaction.append(nars.global_eval.S)
busyness.append(nars.global_eval.B)
alertness.append(nars.global_eval.A)
wellness.append(nars.global_eval.W)
handle_line(tasks_all)
except Exception as e:
ret.append(f':Unknown error: {line}. \n{e}')
return ret, (satisfaction, busyness, alertness, wellness)


def handle_lines(nars: Reasoner, lines: str):
ret = []
satisfaction = []
busyness = []
alertness = []
wellness = []
for line in lines.split('\n'):
if len(line) == 0:
continue

ret_line, (satisfaction_line, busynesse_line, alertness_line, wellbeing_line) = run_line(nars, line)
ret.extend(ret_line)
satisfaction.extend(satisfaction_line)
busyness.extend(busynesse_line)
alertness.extend(alertness_line)
wellness.extend(wellbeing_line)
return '\n'.join(ret), (satisfaction, busyness, alertness, wellness)


def run_nars(capacity_mem=1000, capacity_buff=1000):
seed = 137
rand_seed(seed)
nars = Reasoner(capacity_mem, capacity_buff)
print_out(PrintType.COMMENT, 'Running with GUI.', comment_title='NARS')
server = AioRpcServer(buff=6553500)
rpc(server, "run_line")(run_line)

def _handle_lines(content):
return handle_lines(nars, content)
rpc(server, "handle_lines")(_handle_lines)
server.init()
loop = asyncio.get_event_loop()
# loop.run_until_complete(run_nars())
loop.run_forever()
238 changes: 238 additions & 0 deletions pynars/GUI/MainWindow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import sys
from PySide6 import QtWidgets
# from PySide6
from PySide6.QtWidgets import QMainWindow, QLabel, QPushButton, QApplication
from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QStackedLayout
from PySide6.QtWidgets import QFrame, QTextEdit, QToolBar, QPushButton, QSlider, QSplitter, QDockWidget
from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QScreen, QAction, QIcon, QColor, QFont, QKeyEvent, QFontDatabase
import qtawesome as qta
from .utils import change_stylesheet
from .Widgets.Button import Button
from .Widgets.Slider import Slider
from .Widgets.Plot import Plot
from as_rpc import AioRpcClient

# create the application and the main window


class NARSWindow(QMainWindow):
client: AioRpcClient = None

def __init__(self):
super().__init__()
self.init_layout()

def init_layout(self):
'''
initialize the layout
'''
self.setGeometry(0, 0, 1000, 618)
self._center_window()
self.setWindowTitle('Open-NARS v4.0.0 (PyNARS)')

central_widget = QWidget(self)
self.setCentralWidget(central_widget)

# create left area
left_widget = QFrame(self)
left_widget.setFixedWidth(200)
left_widget.setContentsMargins(0, 0, 0, 0)
self.left_layout = QVBoxLayout(left_widget) # vertical layout
self.left_layout.setContentsMargins(0, 0, 0, 0)
self.left_layout.setSpacing(0)

# create right-top and right-bottom areas
right_top_widget = QFrame(self)
right_top_widget.setContentsMargins(0, 0, 0, 0)
self.right_top_layout = QVBoxLayout(
right_top_widget) # vertical layout
self.right_top_layout.setContentsMargins(0, 0, 0, 0)
self.right_top_layout.setSpacing(0)

right_bottom_widget = QFrame(self)
right_bottom_widget.setContentsMargins(0, 0, 0, 0)
self.right_bottom_layout = QVBoxLayout(
right_bottom_widget) # vertical layout
self.right_bottom_layout.setContentsMargins(0, 0, 0, 0)
self.right_bottom_layout.setSpacing(0)

# create a splitter to adjust the right two areas
right_splitter = QSplitter(self)
right_splitter.setOrientation(Qt.Vertical) # vertical layout

# add the widgets into the splitter
right_splitter.addWidget(right_top_widget)
right_splitter.addWidget(right_bottom_widget)
right_splitter.setSizes([500, 120])

# create main layout, and add the left widget and the right splitter into it
main_layout = QHBoxLayout(central_widget) # 水平布局
main_layout.addWidget(left_widget)
main_layout.addWidget(right_splitter) # 添加可调整大小的右侧区域

self.init_output_toolbar()
self.init_output_textbox()
self.init_input_textbox()

self.left_layout.addStretch(1)
self.init_plot()

self.slider_fontsize.value_changed_connect(
lambda value: (change_stylesheet(self.text_output, f"font-size: {value+6}px;"), change_stylesheet(self.text_input, f"font-size: {value+6}px;")))

self.button_clear.clicked.connect(
lambda *args: self.text_output.clear())

def init_output_toolbar(self):
''''''
# tools bar
toolbar = QWidget(self)
toolbar.setFixedHeight(35)
toolbar.setContentsMargins(0, 0, 0, 0)
toolbar_layout = QHBoxLayout(toolbar)
toolbar_layout.setContentsMargins(3, 0, 3, 0)

self.right_top_layout.addWidget(toolbar)

# create buttons
# run "qta-browser" to browse all the icons
icon_clear = qta.icon('ph.file', color=QColor('white'))
button_clear = Button(icon_clear)
toolbar_layout.addWidget(button_clear)
self.button_clear = button_clear

icon_save = qta.icon('ph.floppy-disk', color=QColor('white'))
button_save = Button(icon_save)
toolbar_layout.addWidget(button_save)
self.button_save = button_save

slider_fontsize = Slider(Qt.Horizontal, 6, 40, 12, " Font size: ")
slider_fontsize.setFixedWidth(100)
toolbar_layout.addWidget(slider_fontsize)

# set stretch and spacing
toolbar_layout.addStretch(1)
toolbar_layout.setSpacing(3)

self.slider_fontsize = slider_fontsize

def init_output_textbox(self):
''''''
# textbox of output
text_output = QTextEdit(self)
self.right_top_layout.addWidget(text_output)
text_output.setReadOnly(True)
# text = '\n'.join([f"This is line {i}" for i in range(50)])
# text_output.setPlainText(text)
text_output.setStyleSheet(
"font-family: Consolas, Monaco, Courier New; color: white;")

def output_clicked(text_output: QTextEdit):
print("line:", text_output.textCursor().blockNumber())
text_output.mouseDoubleClickEvent = lambda x: output_clicked(
text_output)
self.text_output = text_output

def init_input_textbox(self):
''''''
text_input = QTextEdit(self)
self.right_bottom_layout.addWidget(text_input)
text_input.setReadOnly(False)
text_input.setStyleSheet(
"font-family: Consolas, Monaco, Courier New; color: white;")
text_input.installEventFilter(self)
self.text_input = text_input

def init_plot(self):
'''
Plots for global evaluations
'''
dock = QDockWidget()
widget = QWidget()
widget.setContentsMargins(0,0,0,0)
layout = QVBoxLayout()
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
widget.setLayout(layout)
dock.setWidget(widget)

self.plot_satisfaction = Plot()
self.plot_satisfaction.setTitle("satisfaction")
self.plot_satisfaction.setYRange(0.0, 1.0)
self.plot_satisfaction.setFixedHeight(100)
layout.addWidget(self.plot_satisfaction)

self.plot_busyness = Plot()
self.plot_busyness.setTitle("busyness")
self.plot_busyness.setYRange(0.0, 1.0)
self.plot_busyness.setFixedHeight(100)
layout.addWidget(self.plot_busyness)

self.plot_alertness = Plot()
self.plot_alertness.setTitle("alertness")
self.plot_alertness.setYRange(0.0, 1.0)
self.plot_alertness.setFixedHeight(100)
layout.addWidget(self.plot_alertness)

self.plot_wellbeing = Plot()
self.plot_wellbeing.setTitle("well-being")
self.plot_wellbeing.setYRange(0.0, 1.0)
self.plot_wellbeing.setFixedHeight(100)
layout.addWidget(self.plot_wellbeing)

self.left_layout.addWidget(dock)


def eventFilter(self, obj, event):
'''
For the input textbox, when pressing Enter, the content should be input to NARS reasoner; but when pressing shift+Enter, just start a new line.
'''
if obj == self.text_input and event.type() == QKeyEvent.KeyPress:
if event.key() == Qt.Key_Return:
if event.modifiers() == Qt.ShiftModifier: # pressing Shift+Enter
return super().eventFilter(obj, event)
else: # pressing Enter
self.input_narsese()
return True
return super().eventFilter(obj, event)

def set_client(self, client: AioRpcClient):
self.client = client

def input_narsese(self):
content: str = self.text_input.toPlainText()
self.text_input.clear()
if self.client is not None:
self.client.call_func("handle_lines", self.print_out, content)
else:
print(content)

def print_out(self, content):
''''''
# print(content)
out, err = content
if err is not None:
print(err)
self.text_output.append(':Error')
else:
text, (satisfaction, busyness, alertness, wellbeing) = out
# print(satisfactions)
if len(satisfaction) > 0:
self.plot_satisfaction.update_values(satisfaction)
self.plot_busyness.update_values(busyness)
self.plot_alertness.update_values(alertness)
self.plot_wellbeing.update_values(wellbeing)
if len(text) > 0:
self.text_output.append(text)
# self.text_output.append('\n')

def _center_window(self):
'''
Move the window to the center of the screen
'''
center = QScreen.availableGeometry(
QApplication.primaryScreen()).center()
geo = self.frameGeometry()
geo.moveCenter(center)
self.move(geo.topLeft())
9 changes: 9 additions & 0 deletions pynars/GUI/Widgets/Button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from PySide6.QtWidgets import QPushButton


class Button(QPushButton):
def __init__(self, icon, text="", parent=None):
super().__init__(icon, text, parent)
self.setFixedSize(30, 30)
self.setIcon(icon)
self.setFlat(True)
Loading
Loading