def test_get(self): from app import get_application app = get_application() app.init_ui() self.assertIsNotNone(app) from controllers import get grid = get('main_grid')
def test_evaluator_errors(self): from app import get_application get_application().init_ui() from controllers import BlankClip, get, Text, TextClip from model import GridModel grid_model = GridModel(key='test_grid') grid_model.save() grid = get('test_grid') self.assertIsNotNone(grid) grid.eval_declarations() for y in range(2): for x in range(5): text = Text.create('') # This is a hack to avoid some UI stuff during tests. clip = TextClip.create(grid.model.key, text.model.key, x, y) grid.coordinates_to_clip[(x, y)] = clip #grid.add_text(text, x, y) grid.eval_declarations() # Declare an attribute on all Datums cmd = '#*foo:22' star_declaration = grid.coordinates_to_clip[(1, 0)] star_declaration.datum.model.data = cmd star_declaration.datum.model.save() grid.eval_declarations() self.assertEqual({'foo': '22'}, grid.grid_declarations) self.assertEqual({}, grid.declarations_by_clip) # Test error handling def _check_subcommands(cmd, clip): for i in range(len(cmd)): subcmd = cmd[:i+1] clip.datum.model.data = subcmd clip.datum.model.save() #log.info('---check subcommand {0}'.format(subcmd)) grid.eval_declarations() clip = grid.coordinates_to_clip[(4, 0)] cmd = '#*bar:22' _check_subcommands(cmd, clip) cmd = '#*bar:22\n#bar+bar' _check_subcommands(cmd, clip) self.assertEqual({'foo': '22', 'bar': '22'}, grid.grid_declarations) self.assertEqual({}, grid.declarations_by_clip)
def test_evaluator(self): from app import get_application get_application().init_ui() from controllers import BlankClip, get, Text, TextClip from model import GridModel grid_model = GridModel(key='test_grid') grid_model.save() grid = get('test_grid') self.assertIsNotNone(grid) grid.eval_declarations() for y in range(2): for x in range(5): text = Text.create('') # This is a hack to avoid some UI stuff during tests. clip = TextClip.create(grid.model.key, text.model.key, x, y) grid.coordinates_to_clip[(x, y)] = clip # grid.add_text(text, x, y) grid.eval_declarations() cmd1 = '#*foo:22\n#*bar:foo+&foo' cmd2 = '#foo\n#bar\n#foo:42' cursor_clip = grid.get_cursor_clip() self.assertIsInstance(cursor_clip, BlankClip) secondary_cursor_clip = grid.get_secondary_cursor_clip() self.assertIsInstance(secondary_cursor_clip, BlankClip) cmd1_x = 2 cmd1_y = 0 cmd2_x = 4 cmd2_y = 1 grid.main_cursor.model.x = cmd2_x grid.main_cursor.model.y = cmd2_y grid.main_cursor.model.save() grid.secondary_cursor.model.x = cmd1_x grid.secondary_cursor.model.y = cmd1_y grid.secondary_cursor.model.save() cmd1_clip = grid.coordinates_to_clip[(cmd1_x, cmd1_y)] cmd1_clip.datum.model.data = cmd1 cmd1_clip.datum.model.save() cmd2_clip = grid.coordinates_to_clip[(cmd2_x, cmd2_y)] cmd2_clip.datum.model.data = cmd2 cmd2_clip.datum.model.save() self.assertIs(cmd2_clip, grid.get_cursor_clip()) self.assertIs(cmd1_clip, grid.get_secondary_cursor_clip()) grid.eval_declarations() self.assertEqual({'foo': '22', 'bar': 'foo+&foo'}, grid.grid_declarations) self.assertEqual({cmd2_clip.datum.model.name: {'foo': '42'}}, grid.declarations_by_clip) # This simulates what the inspector does, which is request a parse # of the text of a datum into 'text' and various code snippets, # and then evaluating the code one-by-one. from parse import get_text_and_commands tac = get_text_and_commands(cmd2) self.assertEqual( [('evaluation', 'foo'), ('evaluation', 'bar'), ('declaration', ('foo', '42'))], tac) from parse import evaluate result = evaluate(tac[0][1], grid, clip=cmd2_clip) self.assertEqual([('value', 42)], result) result = evaluate(tac[1][1], grid, clip=cmd2_clip) self.assertEqual([('value', 64)], result)
def refresh(self): grid = self.inspector.grid selection_clip = grid.get_cursor_clip() self.clip = selection_clip marker_clip = grid.get_secondary_cursor_clip() value = '' # Set the score inputs, or disable them. if not (selection_clip and marker_clip): # Clear and disable the inputs. self.number_text_edit.setEnabled(False) self.number_text_edit.setText('') self.other_number_text_edit.setEnabled(False) self.other_number_text_edit.setText('') else: # Lookup values and set them in enabled inputs selection_key = selection_clip.datum.model.key marker_key = marker_clip.datum.model.key relationships = grid.model.relationships s_g_m = relationships.get(selection_key, {}).get(marker_key, '') m_g_s = relationships.get(marker_key, {}).get(selection_key, '') self.number_text_edit.setEnabled(True) self.number_text_edit.setText(s_g_m) self.other_number_text_edit.setEnabled(True) self.other_number_text_edit.setText(m_g_s) if selection_clip: pos = selection_clip.model.edit_cursor_position # Note that doing this changes the cursor, and triggers # the cursor changing thing... we need to disconnect and then # connect to eliminate this bounce, for now a hack. self.text_edit.setText(selection_clip.datum.model.data) #self.eq_text_edit.setText(selection_clip.datum.model.data) cursor = self.text_edit.textCursor() cursor.setPosition(pos) self.text_edit.setTextCursor(cursor) whole_text = selection_clip.datum.model.data self.eq_text_edit.clear() cursor = self.eq_text_edit.textCursor() for kind, text in get_text_and_commands(whole_text): if kind == 'text': cursor.insertText(text) elif kind == 'equation': pixmap = render_equation_to_pixmap(text) width = 200 #self.text_edit.width() pixmap = pixmap.scaled(width, width, Qt.KeepAspectRatio) image = QImage(pixmap) cursor.insertImage(image) else: self.text_edit.setText('') self.eq_text_edit.setText('') if marker_clip: pos = marker_clip.model.edit_cursor_position self.marker_edit.setText(marker_clip.datum.model.data) cursor = self.marker_edit.textCursor() cursor.setPosition(pos) self.marker_edit.setTextCursor(cursor) else: self.marker_edit.setText('') self.focus_label.setText('focus: {0}'.format(self.last_focus_name)) m = self.inspector.grid.model self.scroll_x_label.setText('scroll: {0}, {1}'.format( m.x_offset, m.y_offset)) # These coordinates are screen relative m = self.inspector.grid.main_cursor.model self.label.setText( '<span style="color:#ff0000;">selection</span>: {}, {}'.format( m.x + self.inspector.grid.model.x_offset, m.y + self.inspector.grid.model.y_offset)) # These coordinates are already absolute m = self.inspector.grid.secondary_cursor.model self.marker_label.setText( '<span style="color:{};">home row</span>: {}, {}'.format( GRID_BACKGROUND_HOMEROW, m.x, m.y)) # Set the hierarchy labels. parent_texts = [] selection_parent_name = '<none>' if selection_clip: parent_key = selection_clip.datum.model.parent if parent_key: selection_parent_name = get(parent_key).model.name if parent_key and marker_clip: if marker_clip.datum.model.key == parent_key: parent_texts.append('▲ parent of ▼') self.selection_parent_label.setText( 'parent: {0}'.format(selection_parent_name)) marker_parent_name = '<none>' if marker_clip: parent_key = marker_clip.datum.model.parent if parent_key: marker_parent_name = get(parent_key).model.name if parent_key and selection_clip: if selection_clip.datum.model.key == parent_key: parent_texts.append('▼ parent of ▲') self.marker_parent_label.setText( 'parent: {0}'.format(marker_parent_name)) if parent_texts: self.parent_label.setText('hierarchy: ' + ', '.join(parent_texts)) else: self.parent_label.setText('hierarchy: ▲ unrelated ▼')
def refresh(self): self.clip = self.grid.get_clip_at(self.screen_x, self.screen_y) if self.clip: name = self.clip.datum.model.name data = self.clip.datum.model.data else: name = '' data = '' offset_y = self.screen_y + self.grid.model.y_offset is_home_row = offset_y == 0 if data or name: if is_home_row: self.background = CLIP_BACKGROUND_HOMEROW else: self.background = CLIP_BACKGROUND else: if is_home_row: self.background = GRID_BACKGROUND_HOMEROW else: self.background = GRID_BACKGROUND self.style_sheet.set('background', self.background) main_cursor = self.grid.main_cursor secondary_cursor = self.grid.secondary_cursor # Screen coordinates m_x = main_cursor.model.x m_y = main_cursor.model.y # Absolute coordinates s_x = secondary_cursor.model.x - self.grid.model.x_offset s_y = secondary_cursor.model.y - self.grid.model.y_offset main_on = m_x == self.screen_x and m_y == self.screen_y secondary_on = s_x == self.screen_x and s_y == self.screen_y # Taking this out for a moment -- REMOVE TO PUT 2nd CURSOR BACK secondary_on = False if main_on and secondary_on: self.style_sheet.set('border', '2px dashed purple') elif main_on: self.style_sheet.set('border', '2px dashed red') elif secondary_on: self.style_sheet.set('border', '2px dashed blue') else: self.style_sheet.set('border', '2px solid {0}'.format(self.background)) self.setStyleSheet(self.style_sheet.render()) if data != self.text_edit.toPlainText(): self.text_edit.setText(data) if name != self.title_label.text(): self.title_label.setText(name) if not self.clip: self.left_score.setStyleSheet("background: rgba(0,0,0,0%)") self.left_score.display('') self.right_score.setStyleSheet("background: rgba(0,0,0,0%)") self.right_score.display('') return from controllers import _m, _term_to_index, _embedding, _lt from torch.autograd import Variable clip_datum_key = self.clip.model.datum_key clip_datum_text = get(clip_datum_key).model.data clip_term_id = _term_to_index[clip_datum_text] # Left side is distance from this to selection selection_score = None selection_clip = self.grid.get_cursor_clip() if selection_clip: selection_datum_key = selection_clip.model.datum_key selection_datum_text = get(selection_datum_key).model.data selection_term_id = _term_to_index[selection_datum_text] s_e = Variable(_lt[selection_term_id].expand_as(_embedding), volatile=True) _dists = _m.dist()(s_e, _embedding).data.cpu().numpy().flatten() selection_score = round(float(_dists[clip_term_id]), 5) if selection_score is None: left_score_text = '' else: left_score_text = str(selection_score) if selection_score is None: self.left_score.setStyleSheet("background: rgba(0,0,0,0%)") else: self.left_score.setStyleSheet("background: 'red'") self.left_score.display(left_score_text) # Right side is distance from this to home home_score = None # remember this is a clip.view, not a clip... we need to lookup in # absolute. absolute_x = self.screen_x + self.grid.model.x_offset coords = (absolute_x, 0) home_clip = self.grid.coordinates_to_clip[coords] if home_clip: home_datum_key = home_clip.model.datum_key home_datum_text = get(home_datum_key).model.data home_term_id = _term_to_index[home_datum_text] s_e = Variable(_lt[home_term_id].expand_as(_embedding), volatile=True) _dists = _m.dist()(s_e, _embedding).data.cpu().numpy().flatten() home_score = round(float(_dists[clip_term_id]), 5) if home_score is None: right_score_text = '' else: right_score_text = str(home_score) if home_score is None: self.right_score.setStyleSheet("background: rgba(0,0,0,0%)") else: self.right_score.setStyleSheet( "background: '{}'".format(GRID_BACKGROUND_HOMEROW)) self.right_score.display(right_score_text)
def setup(self): log.info('MainWindow setup') from app import get_application # Menu setup exit_action = QAction('&Exit', self) exit_action.setShortcut('Ctrl+Q') exit_action.setStatusTip('Quit Wren') exit_action.triggered.connect( QCoreApplication.instance().quit) #qApp.quit) import_action = QAction('&Import', self) import_action.setShortcut('Ctrl+I') import_action.setStatusTip('Import notes') import_action.triggered.connect(self.show_import_dialog) # Position, resize and decorate the window self.center() # Set to a large size in the desktop. log.info("getting window geometry...") geo = get_application().app.primaryScreen().size() #geo = QDesktopWidget().availableGeometry() log.info("... got {0} x {1}".format(geo.width(), geo.height())) x = geo.width() * 0.07 width = geo.width() - (2 * x) y = geo.height() * 0.04 + 50 height = geo.height() - (2 * y) log.info("setGeometry({0}, {1}, {2}, {3})".format(x, y, width, height)) self.setGeometry(x, y, width, height) import wren import math grid_width = max(1, math.floor(width * .72 / wren.CLIP_WIDTH)) grid_height = max(1, math.floor(height / wren.CLIP_HEIGHT)) if wren.GRID_WIDTH is not None: grid_width = wren.GRID_WIDTH if wren.GRID_HEIGHT is not None: grid_height = wren.GRID_HEIGHT log.info('gridwidth {0}, gridheight {1}'.format( grid_width, grid_height)) # This is accessed by other widgets when they need a progress bar. # see import mammals, and make_ranked_clips self.progress = QProgressDialog(self) self.progress.setModal(True) self.progress.setRange(0, 220) self.progress.setMinimumDuration(250) #self.progress.canceled.connect(self.thread.requestInterruption) self.progress.reset() self.setWindowTitle('Wren') log.info("creating Grid and Inspector") self.h_box = QHBoxLayout() self.grid = get(get_application().app_model.current_grid, width=grid_width, height=grid_height, status_bar=self.statusBar()) import controllers self.inspector = controllers.Inspector(None) controllers.get_controller_id_map().set('main_inspector', self.inspector) self.inspector.setup(self.grid) self.h_box.addWidget(self.grid.view) self.h_box.addWidget(self.inspector.view) self.grid.view.clip_changed.connect(self.inspector.view.refresh) self.grid.view.cursor_changed.connect(self.inspector.view.refresh) self.grid.view.secondary_cursor_changed.connect( self.inspector.view.refresh) w = QWidget(self) w.setLayout(self.h_box) self.setCentralWidget(w) delete_column_action = QAction('Delete Column', self) delete_column_action.setShortcut('Ctrl+W') delete_column_action.setStatusTip('Delete Column') delete_column_action.triggered.connect(self.grid.delete_cursor_column) make_ranked_clips_action = QAction('Ranked Column', self) make_ranked_clips_action.setShortcut('Ctrl+Return') make_ranked_clips_action.setStatusTip( 'Insert new column sorted by rank relative to selection') make_ranked_clips_action.triggered.connect( self.grid.make_ranked_clips_1) find_clip_action = QAction('Find', self) find_clip_action.setShortcut('Ctrl+S') # F is navigational atm. find_clip_action.setStatusTip('Find Clip closest matching string') find_clip_action.triggered.connect(self.grid.do_find) delete_clip_action = QAction('Delete Clip', self) delete_clip_action.setShortcut('Ctrl+Backspace') delete_clip_action.setStatusTip('Delete Clip (but not its Datum)') delete_clip_action.triggered.connect(self.grid.delete_clip) copy_clip_action = QAction('Copy Clip', self) copy_clip_action.setShortcut('Ctrl+C') copy_clip_action.setStatusTip("Copy current Clip's Datum to clipboard") copy_clip_action.triggered.connect(self.grid.copy_clip) cut_clip_action = QAction('Cut Clip', self) cut_clip_action.setShortcut('Ctrl+X') cut_clip_action.setStatusTip("Cut current Clip's Datum to clipboard") cut_clip_action.triggered.connect(self.grid.cut_clip) paste_clip_action = QAction('Paste Clip', self) paste_clip_action.setShortcut('Ctrl+V') paste_clip_action.setStatusTip("Make new Clip from clipboard Datum") paste_clip_action.triggered.connect(self.grid.paste_clip) archive_datum_action = QAction('Archive Datum', self) archive_datum_action.setShortcut('Ctrl+Delete') archive_datum_action.setStatusTip('Remove Datum from Grid') archive_datum_action.triggered.connect(self.grid.archive_datum) refresh_column_action = QAction('Refresh Column', self) refresh_column_action.setShortcut('Ctrl+R') refresh_column_action.setStatusTip('Re-sort the current column') refresh_column_action.triggered.connect( self.grid.refresh_selected_column) scroll_cursor_right_action = QAction('Scroll Cursor Right', self) scroll_cursor_right_action.setShortcut('Ctrl+G') scroll_cursor_right_action.setStatusTip( "Scroll such that Selection's current Clip is center right") scroll_cursor_right_action.triggered.connect( self.grid.scroll_cursor_right) scroll_cursor_center_action = QAction('Scroll Cursor Center', self) scroll_cursor_center_action.setShortcut('Ctrl+F') scroll_cursor_center_action.setStatusTip( "Scroll such that Selection's current Clip is center center") scroll_cursor_center_action.triggered.connect( self.grid.scroll_cursor_center) scroll_cursor_left_action = QAction('Scroll Cursor Left', self) scroll_cursor_left_action.setShortcut('Ctrl+D') scroll_cursor_left_action.setStatusTip( "Scroll such that Selection's current Clip is center left") scroll_cursor_left_action.triggered.connect( self.grid.scroll_cursor_left) scroll_column_action = QAction('Scroll Column', self) scroll_column_action.setShortcut('Ctrl+L') scroll_column_action.setStatusTip( "Scroll to Column top, home or bottom (cyclic)") scroll_column_action.triggered.connect(self.grid.do_column_scroll) cycle_parentage_action = QAction('Cycle Parentage', self) cycle_parentage_action.setShortcut('Ctrl+P') cycle_parentage_action.setStatusTip( "Cycle through parentage choices between Selection and Marker") cycle_parentage_action.triggered.connect(self.grid.do_cycle_parentage) import_algebra_action = QAction('Import Tensor Algebra Notes', self) import_algebra_action.setShortcut('Ctrl+A') import_algebra_action.setStatusTip("Import documents/tensors.txt") import_algebra_action.triggered.connect( self.grid.view.import_algebra_notes) import_mammals_action = QAction('Import Mammals Closure', self) import_mammals_action.setShortcut('Ctrl+M') import_mammals_action.setStatusTip("Import mammals.pth") import_mammals_action.triggered.connect( self.grid.view.import_mammals_closure) menubar = self.menuBar() menubar.setNativeMenuBar(False) # For in-window menu on a Mac file_menu = menubar.addMenu('&File') file_menu.addAction(import_action) file_menu.addAction(exit_action) grid_menu = menubar.addMenu('&Grid') grid_menu.addAction(import_mammals_action) grid_menu.addAction(import_algebra_action) grid_menu.addAction(delete_column_action) grid_menu.addAction(make_ranked_clips_action) grid_menu.addAction(find_clip_action) grid_menu.addAction(delete_clip_action) grid_menu.addAction(copy_clip_action) grid_menu.addAction(cut_clip_action) grid_menu.addAction(paste_clip_action) grid_menu.addAction(archive_datum_action) grid_menu.addAction(refresh_column_action) grid_menu.addAction(scroll_cursor_right_action) grid_menu.addAction(scroll_cursor_center_action) grid_menu.addAction(scroll_cursor_left_action) grid_menu.addAction(scroll_column_action) grid_menu.addAction(cycle_parentage_action) # Statusbar setup self.statusBar().showMessage('Ready') # Toolbar setup self.toolbar = self.addToolBar('Exit') self.toolbar.addAction(exit_action) self.toolbar.addAction(import_action) self.toolbar.addAction(import_algebra_action) self.toolbar.addAction(import_mammals_action) self.toolbar.addAction(delete_column_action) self.toolbar.addAction(make_ranked_clips_action) self.toolbar.addAction(delete_clip_action) self.toolbar.addAction(copy_clip_action) self.toolbar.addAction(cut_clip_action) self.toolbar.addAction(paste_clip_action) self.toolbar.addAction(archive_datum_action) self.toolbar.addAction(refresh_column_action) self.toolbar.addAction(scroll_cursor_right_action) self.toolbar.addAction(scroll_cursor_center_action) self.toolbar.addAction(scroll_cursor_left_action) self.toolbar.addAction(scroll_column_action) self.toolbar.addAction(cycle_parentage_action) self.show()