Skip to content

Commit

Permalink
Merge pull request #28 from bowen-xu/GlobalEval
Browse files Browse the repository at this point in the history
GlobalEval
  • Loading branch information
maxeeem authored Dec 26, 2023
2 parents f35bf37 + 390d51d commit ce41403
Show file tree
Hide file tree
Showing 13 changed files with 624 additions and 29 deletions.
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

0 comments on commit ce41403

Please sign in to comment.