pnb 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pnb/__init__.py ADDED
@@ -0,0 +1,469 @@
1
+ #!/usr/bin/env python
2
+
3
+ import sys
4
+ import os
5
+
6
+ try:
7
+ import wx
8
+ except ImportError: # wxPython is an optional dependency, see README
9
+ sys.exit('pnb needs wxPython 4.2 or newer. Install it with\n'
10
+ ' pip install "pnb[wx]"\n'
11
+ 'or use the package of your Linux distribution '
12
+ '(python3-wxgtk4.0, python3-wxpython4).')
13
+
14
+ import pynotebook
15
+ from pynotebook import graphics
16
+ from pynotebook.nbtexels import NotFound
17
+ from pynotebook.nbview import TextModel, NBView
18
+ from pynotebook.inspector import TextCellInspector
19
+
20
+
21
+ wildcard = "Pynotebook files (*.pnb)|*.pnb|" \
22
+ "Python source (*.py)|*.py|" \
23
+ "All files (*.*)|*.*"
24
+
25
+ def action2cmd(label, action):
26
+ # Create a menu command from an editor action
27
+ def method(self, event, action=action):
28
+ self.shell.handle_action(action)
29
+ method.__doc__ = label
30
+ return method
31
+
32
+
33
+ def method2cmd(label, name):
34
+ # Create a menu command from a method of NBView
35
+ def method(self, event, name=name):
36
+ fun = getattr(self.shell, name)
37
+ fun()
38
+ method.__doc__ = label
39
+ return method
40
+
41
+
42
+
43
+
44
+
45
+ class MainWindow(wx.Frame):
46
+ set_width = ['Set width', 'increase_maxw', 'decrease_maxw', 'set_w1', 'set_w2', 'set_w3']
47
+ ctxt_entries = ['inspector', 'open', 'save', 'save_as', 'close']
48
+ file_entries = ['new', 'open', 'save', 'save_as', 'close']
49
+ edit_entries = ['copy', 'paste', 'cut', 'undo', 'redo', 'indent', 'dedent']
50
+ cell_entries = ['inspector', 'insert_textcell', 'insert_pycell',
51
+ 'remove_output', 'split_cell', set_width]
52
+ interpreter_entries = ['execute', 'execute_all', 'complete', 'help', 'reset']
53
+ debug_entries = ['rebuild_view', 'clean_model', 'dump_texels',
54
+ 'dump_styles', 'dump_boxes', 'profile_insert']
55
+ updaters = ()
56
+ def __init__(self, filename=None):
57
+ displayw, displayh = wx.GetDisplaySize()
58
+ wx.Frame.__init__(self, None,
59
+ size=(min(680, displayw), min(800, displayh)))
60
+ icon = logo.GetIcon()
61
+ self.SetIcon(icon)
62
+ panel = wx.Panel(self, -1)
63
+ shell = NBView(panel, -1, filename=filename, maxw=50*12)
64
+ box = wx.BoxSizer(wx.VERTICAL)
65
+ box.Add(shell, 1, wx.ALL|wx.GROW, 1)
66
+ panel.SetSizer(box)
67
+ panel.SetAutoLayout(True)
68
+ shell.Bind(wx.EVT_RIGHT_DOWN, self.right_click)
69
+ shell.SetFocus()
70
+ self.shell = shell
71
+ self.filename = filename
72
+ self.SetMenuBar(self.make_menubar())
73
+ sb = wx.StatusBar(self)
74
+ self.SetStatusBar(sb)
75
+ self.Bind(wx.EVT_IDLE, self.update)
76
+ self.SetName("pnb_python_notebook")
77
+
78
+
79
+ def make_menubar(self):
80
+ menubar = wx.MenuBar()
81
+ updaters = []
82
+ accel = []
83
+
84
+ def extract_accel(s):
85
+ # Extract accelerator information from menu
86
+ # labels. Currently only extracts simple accelerators of
87
+ # the form 'Ctrl+A'. Many accelerators are not parsed
88
+ # correctly, such as 'Shift-Return'. This is ok for now,
89
+ # as these are already handled in nbview. Extracting
90
+ # accelerators became necessary with wxpython 3.0 on. I
91
+ # think it is a bug.
92
+ if not '\t' in s: return
93
+ a, b = s.split("\t", 1)
94
+ if not '-' in b: return
95
+ modifier, key = b.split('-', 1)
96
+ m = dict(Ctrl=wx.ACCEL_CTRL, Alt=wx.ACCEL_ALT)
97
+ if not modifier in m: return
98
+ return m[modifier], ord(key)
99
+
100
+ def mk_menu(entries, self=self, updaters=updaters, accel=accel):
101
+ menu = wx.Menu()
102
+ for entry in entries:
103
+ if entry is None:
104
+ menu.AppendSeperator()
105
+ elif type(entry) is list:
106
+ submenu = mk_menu(entry[1:])
107
+ menu.AppendSubMenu(submenu, entry[0])
108
+ else:
109
+ fun = getattr(self, entry)
110
+ title = fun.__doc__
111
+ item = menu.Append(-1, title)
112
+ self.Bind(wx.EVT_MENU, fun, item)
113
+ shortcut = extract_accel(title)
114
+ if shortcut is not None:
115
+ accel.append(shortcut+(item.Id,))
116
+ if hasattr(self, 'can_'+entry):
117
+ fun = getattr(self, 'can_'+entry)
118
+ def update(fun=fun, item=item, menu=menu):
119
+ menu.Enable(item.Id, fun())
120
+ updaters.append(update)
121
+ return menu
122
+ menubar.Append(mk_menu(self.file_entries), '&File')
123
+ menubar.Append(mk_menu(self.edit_entries), '&Edit')
124
+ menubar.Append(mk_menu(self.cell_entries), '&Cell')
125
+ menubar.Append(mk_menu(self.interpreter_entries), '&Interpreter')
126
+ if debug:
127
+ menubar.Append(mk_menu(self.debug_entries), 'Debug')
128
+
129
+ self.SetAcceleratorTable(wx.AcceleratorTable(accel))
130
+ self.updaters = updaters
131
+ return menubar
132
+
133
+ def make_ctxtmenu(self):
134
+ menu = wx.Menu()
135
+ for entry in self.ctxt_entries:
136
+ fun = getattr(self, entry)
137
+ active = True
138
+ try:
139
+ statefun = getattr(self, 'can_'+entry)
140
+ active = statefun()
141
+ except AttributeError:
142
+ pass
143
+ title = fun.__doc__
144
+ item = menu.Append(-1, title)
145
+ menu.Enable(item.Id, active)
146
+ menu.Bind(wx.EVT_MENU, fun, item)
147
+ return menu
148
+
149
+ def right_click( self, event):
150
+ menu = self.make_ctxtmenu()
151
+ self.PopupMenu(menu, event.Position)
152
+ menu.Destroy() # destroy to avoid mem leak
153
+
154
+ def changed(self):
155
+ return self.shell.undocount()>0
156
+
157
+ def update(self, event):
158
+ # update filename in window title
159
+ if self.filename:
160
+ path, name = os.path.split(self.filename)
161
+ title = name
162
+ else:
163
+ title = '<unnamed>'
164
+ if self.changed():
165
+ title = title+' *'
166
+ self.SetTitle(title)
167
+
168
+ # update menus
169
+ for updater in self.updaters:
170
+ updater()
171
+
172
+ # update statusbar
173
+ i = self.shell.index
174
+ row, col, _ = self.shell.model.index2position(i)
175
+ try:
176
+ i, cell = self.shell.find_cell()
177
+ except NotFound:
178
+ self.StatusBar.SetStatusText('')
179
+ return
180
+ self.StatusBar.SetStatusText('Line: %i, Position: %i' % (row, col))
181
+
182
+ def new(self, event):
183
+ "&New Notebook\tCtrl-N"
184
+ win = MainWindow()
185
+ win.Show()
186
+
187
+ def open(self, event):
188
+ "&Open File ...\tCtrl-O"
189
+ dlg = wx.FileDialog(
190
+ self, message="Choose a file",
191
+ wildcard=wildcard,
192
+ style=wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_CHANGE_DIR
193
+ )
194
+ if dlg.ShowModal() == wx.ID_OK:
195
+ paths = dlg.GetPaths()
196
+ for path in paths:
197
+ win = MainWindow(path)
198
+ win.Show()
199
+ dlg.Destroy()
200
+
201
+ def save(self, event):
202
+ "&Save\tCtrl-S"
203
+ if self.filename is None:
204
+ self.save_as(event)
205
+ else:
206
+ self.shell.save(self.filename)
207
+ self.shell.clear_undo()
208
+
209
+ def save_as(self, event):
210
+ "Save &As ..."
211
+ dlg = wx.FileDialog(
212
+ self, message="Save File as ...",
213
+ defaultFile="", wildcard=wildcard,
214
+ style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT
215
+ )
216
+ if dlg.ShowModal() == wx.ID_OK:
217
+ path = dlg.GetPath()
218
+ self.shell.save(path)
219
+ self.filename = path
220
+ self.shell.clear_undo()
221
+
222
+ dlg.Destroy()
223
+
224
+ def close(self, event):
225
+ "&Close\tCtrl-W"
226
+ if self.changed():
227
+ dlg = wx.MessageDialog(
228
+ self, 'There are unsaved changes. Do you really want to close?',
229
+ 'Close window',
230
+ wx.YES_NO | wx.NO_DEFAULT | wx.CANCEL | wx.ICON_INFORMATION
231
+ )
232
+ result = dlg.ShowModal()
233
+ dlg.Destroy()
234
+ if result != wx.ID_YES:
235
+ return
236
+ self.Close(True)
237
+
238
+
239
+ def increase_maxw(self, event):
240
+ "Increase width\tCtrl-+"
241
+ self.shell.set_maxw(self.shell.maxw+20)
242
+
243
+ def decrease_maxw(self, event):
244
+ "Decrease width\tCtrl--"
245
+ self.shell.set_maxw(self.shell.maxw-20)
246
+
247
+ def can_decrease_maxw(self):
248
+ return self.shell.maxw>50
249
+
250
+ def set_w1(self, event):
251
+ "Set to 600 pixels"
252
+ self.shell.set_maxw(50*12)
253
+
254
+ def set_w2(self, event):
255
+ "Set to 720 pixels"
256
+ self.shell.set_maxw(60*12)
257
+
258
+ def set_w3(self, event):
259
+ "Set to 960 pixels"
260
+ self.shell.set_maxw(80*12)
261
+
262
+ cut = action2cmd("Cut\tCtrl-X", "cut")
263
+ copy = action2cmd("Copy\tCtrl-C", "copy")
264
+ paste = action2cmd("Paste\tCtrl-V", "paste")
265
+ indent = action2cmd("Indent\tCtrl-I", "indent")
266
+ dedent = action2cmd("Dedent\tCtrl-U", "dedent")
267
+ complete = action2cmd("Complete\tTab", "complete")
268
+ help = action2cmd("Help\tCtrl-H", "help")
269
+ execute = method2cmd("Execute Cell\tShift-Return", "execute")
270
+ execute_all = method2cmd("Execute all", "execute_all")
271
+ reset = method2cmd("Reset Interpreter", "reset_interpreter")
272
+ remove_output = method2cmd("Remove Output", "remove_output")
273
+ split_cell = method2cmd("Split Cell\tCtrl-B", "split_cell")
274
+ insert_textcell = method2cmd("Insert Text Cell\tCtrl-T", "insert_textcell")
275
+ insert_pycell = method2cmd("Insert Python Cell", "insert_pycell")
276
+ undo = method2cmd("Undo\tCtrl-Z", "undo")
277
+ redo = method2cmd("Redo\tCtrl-R", "redo")
278
+ dump_texels = action2cmd("Dump Texels\tESC", "dump_info")
279
+ dump_boxes = action2cmd("Dump all Boxes", "dump_boxes")
280
+
281
+ def dump_styles(self, event):
282
+ "Dump styles"
283
+ from pynotebook.textmodel.texeltree import length
284
+
285
+ def _dump_styles(texel, i=0):
286
+ if texel.is_single or texel.is_text:
287
+ print(i, ":", i+length(texel), id(texel.style), texel.__class__.__name__)
288
+ else:
289
+ for child in texel.childs:
290
+ _dump_styles(child, i)
291
+ i += length(child)
292
+ _dump_styles(self.shell.model.texel)
293
+
294
+ def between_cells(self):
295
+ return self.shell.between_cells()
296
+ can_insert_textcell = can_insert_pycell = between_cells
297
+
298
+ def can_undo(self):
299
+ return self.shell.undocount() > 0
300
+
301
+ def can_redo(self):
302
+ return self.shell.redocount() > 0
303
+
304
+ def inspector(self, event):
305
+ "Format ...\tCtrl-F"
306
+ inspector = TextCellInspector(self.shell, self.shell.Parent)
307
+ inspector.Show()
308
+ inspector.update()
309
+
310
+ def rebuild_view(self, event):
311
+ "Rebuild View"
312
+ self.shell.rebuild()
313
+
314
+ def clean_model(self, event):
315
+ "Clean Model"
316
+ # This is a bit hacky ...
317
+ from pynotebook import cerealizerformat
318
+ # 1. replace all styles to the canonical ones
319
+ model = self.shell.model
320
+ cerealizerformat._replace_styles(model.texel, {})
321
+ # 2. call set_properties (with no properties given) to merge
322
+ # character texels of equal style
323
+ self.shell.set_properties(0, len(model))
324
+
325
+ def profile_insert(self, event):
326
+ "Profile Insert"
327
+ from cProfile import runctx
328
+ runctx("self.shell.model.insert_text(self.shell.index, 'X')", globals(), locals())
329
+ self._set_logfont()
330
+
331
+ def _set_logfont(self):
332
+ # Change the font of the output window to make it more readable
333
+ app = wx.GetApp()
334
+ win = app.stdioWin
335
+ if hasattr(win, 'text'):
336
+ text = win.text
337
+ old_font = text.Font
338
+ new_font = wx.Font(old_font.GetPointSize(), wx.FONTFAMILY_MODERN,
339
+ old_font.GetStyle(), old_font.GetWeight())
340
+ text.SetFont(new_font)
341
+
342
+
343
+ debug = False
344
+ def main(argv=None):
345
+ if argv is None:
346
+ argv = sys.argv[1:]
347
+
348
+ global debug
349
+ redirect = console = False
350
+ filenames = []
351
+ for arg in argv:
352
+ if arg == '--redirect':
353
+ redirect = True
354
+ elif arg == '--debug':
355
+ debug = True
356
+ elif arg == '--console':
357
+ console = True
358
+ else:
359
+ filenames.append(arg)
360
+
361
+ if debug:
362
+ redirect = True
363
+
364
+ app = wx.App(redirect=redirect)
365
+
366
+ win = None
367
+ for name in filenames:
368
+ # Each file is opened in a seperate window.
369
+ win = MainWindow(name)
370
+ win.Show()
371
+ if win is None:
372
+ win = MainWindow()
373
+ win.Show()
374
+
375
+ if console:
376
+ from pynotebook.wxtextview import testing
377
+ testing.pyshell(namespace=dict(win=win))
378
+
379
+ app.MainLoop()
380
+
381
+ # Register classes to the fileformat
382
+ graphics.register_classes()
383
+
384
+
385
+ from wx.lib.embeddedimage import PyEmbeddedImage
386
+
387
+ logo = PyEmbeddedImage(
388
+ "iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABmJLR0QA/wD/AP+gvaeTAAAA"
389
+ "CXBIWXMAADddAAA3XQEZgEZdAAAAB3RJTUUH4QcbFAUil8zQBAAAABl0RVh0Q29tbWVudABD"
390
+ "cmVhdGVkIHdpdGggR0lNUFeBDhcAAA8USURBVHja7Z15UFVXnsc/59z73gNZRONCi0gSo9F2"
391
+ "yWBc2pgmaY2TpKykW5G4N0mPUWYmSbkhRBFRQRBcxhmrYtLpNHYEUQxIMmaW2DFttxF7bDOm"
392
+ "p9VEgyvuhiAqyHv3nvkDITEKvA0xer9VVmG9y49b3+/5bWd7Ag8wed57rF8aC8CCglPBCqI0"
393
+ "zTYeIZ4B0UdKGchdDqXMa0qpgyj136bhKlSm+UX6xIhLAPPzT5IxsatH9oS7DyblHmLZiz0A"
394
+ "SN10Nk5K7RUhZYwHJu5GOVCm+WfTNN/cVbI696O8DDNhxR9YO/sJ/wqQtO4wy+Ifuk7+uSJN"
395
+ "tz0PaFioh2Gaxn9VXDg1evT0frXvrd7Nmhk/8a8HJL5zMKhNaIftUmqDLL4b8QfD+Lyq6sJj"
396
+ "K37V68qi9ypYGNvOdwFSNpzCNA3N5gjcIKUWZ9HcNEzT+LSmquKZZS/1qHLnednUhzPXfkb6"
397
+ "hC5omi3eIt89SKk9FhgclvzPq/4gfBLguefSWJUQzbTsbQ7NZv+NRa0nKmiv3NelV1efBBic"
398
+ "MBeA8Pv7ZwkhLFI9KS2FCEXwTwCvrPyT9zngV2kf2qP6/eQwQkRatHqYkE3TmTb2PrtPOSCi"
399
+ "V/QAhGhr0emFF0hpe339sSE+CSA1vTsQYNHpHWx2x2M+CYAQ7QGbRaXXyaCbbwLUkW9lYG/5"
400
+ "p/m5seYEUBaNPiRiN/iTFk2t3DJYFFgC3NPQ78jYqVo/+Qhxe6oP/U4i3a5DsAM6hSgcumy1"
401
+ "8ssEKq+afH1VcOVayw4G/U4QSCmIaAdP9rLzUGc7SHmLCGk2ETVVIwWb+E4Vrdws6r61f7m6"
402
+ "ln3HavnTl05qDNEiA6JJgg1X7R4ltTXN17toCBkhpHxKCNnG05do10Yx4SfBBDokhgIMhZSS"
403
+ "I0fKqKysxOFw0Lt3b4QQHDx4kC1btlBWVkZAQABDhw4lLi4OXdcxDOMGuxcvXuD06dMAREZG"
404
+ "EhYWhlKNi2AYBocOHcLpdBISEkJUVBRDewYSEiD4YJ8Tw2yJXqERzN9QTsaECI8NLth45jXd"
405
+ "Zl8M7s0huQzF1BgH3ToGYJjfkqNpGiNHjmTbtm106tSJkydPMm3aNHJzc2+y0b59ewoLC3ny"
406
+ "yScbCBZCsHv3bmJiYnC5XEybNo01a9Yg5a3rDiklJSUljB49GoCUlBQWLVqEUgpNg3f/WEXZ"
407
+ "+brc4C4Mw7V2cVzHf/SqCvKU/Lm5XwKwZFz4v9ZcrRqilHmg2VirIDxU0a3TjeTXw+FwABAU"
408
+ "FMSUKVPIzc1l+PDhJCcnk5aWRnx8PMHBwXz99deMGDGCd955B03Troc1xdChQ4mJiQGgpKSE"
409
+ "ioqKRt+ltraWLVu2ANC2bVsmTZr0nQgn+GkPG64W8IAGAZZlZPhkKPvFnnWes+4AmZOjvjCd"
410
+ "ta8AV5oTIKqD1mxoPn78OJs3byYjI4Pi4mIyMjJYuHAhb775Jrt27SIqKgqA5ORk9u7dy3fX"
411
+ "L7KysgA4e/YsJSUlDQJ9HxUVFRQVFQEQExNDr169GrzJMBXdOtgwTP8roC9ftow5SUk4HI7A"
412
+ "ldnZj0spB3sz/2MqdVSZ5r8bJ4u+AVg8/kcfpxVe+B+haU821avbNeGGKxsMGzaMefPmYRgG"
413
+ "SikMw0DXdfr27UtWVhbx8fFcvHiR/Px8HnnkkYbfGzRoEAMHDmTPnj2kpaXx8ssv32Rf0zTy"
414
+ "8vKoqqpbxl20aNHNI1XXEJh+b530OUlJzJw9WxNCvKvreqwvrqSk/LMQYmTeb9+9NOmlKRiG"
415
+ "8029KQE8QEYjHmoYBrGxsSQlJXH8+HF27NjBpUuXCA0NbQhFmZmZjBw5klOnTrF582bGjh3b"
416
+ "kLCFECilSE9PB2DYsGFER0fflNBbtBPuFh7+mBDiGd+bFzEYGDLppSkAOK9V/8VfL/rEE080"
417
+ "SorNZmPQoLrdMl9++SXV1dXfeqZpMmDAAKKjowFYvHjxTcn3rbfe4ptvvgEgMzOzyUqppXKA"
418
+ "30pc8d0ArAy/2A0PD2/2me7duwNQWVmJy+W64bOwsDBiY+uc+8iRI3zyySc3VEM5OTkAREdH"
419
+ "07dvX8wWiPVNClBVVfWpUur3vjdU6jOlVOm76zfWxbeAoIF+iZO67tEztxrBcXFxtG3blsuX"
420
+ "L1NcXIzL5UJKydatWykvL0cIwZgxYxpC122bC1qZnc2suXNdK7Ozxwsp/14KMdALFxTAUdM0"
421
+ "i6WmXZoyeVydcc2e4I+XLC8vb/aZ+oYrICDgpkpHKUXPnj0ZMWIERUVFFBUVkZKSQmhoKMXF"
422
+ "xdTU1BAWFsbo0aMbcsJtE2DW3Ln1BUn1rDlzSoASXwymrvsbMJsFG0+PFJr2qH+mKhRlZWU8"
423
+ "+OCDjeaB0tJSAKKioggIuPUydnp6OkVFRZw8eZJt27YxfPhw3n//fQAGDx5Mnz59blvyvakP"
424
+ "mH1dCG+R/LuyuiQX34f5eSf6aLrj34A2/nrR+nr+Vh1saWkpJ06caCAyJCTkltVS7969efrp"
425
+ "pwFYuHAhW7Zs4fz5803av20CfB8pG8o9I+iXD9ZNRWw6M8seGPypEOJhf75ofn4+27ZtuyG8"
426
+ "SClxuVykpqZy+fJlHA4HY8eOxW6/9XYc0zQbys1Dhw4xa9asVik93ZqMS58QQUrBqaek1MaK"
427
+ "picCFUIKFBFCaiOEEH7fxtKlSxcAnn32WTIzMxkzZgx2u50DBw6QmJjIvn37ABg1ahTPP/98"
428
+ "o0QqpejRowfDhg1j586dXL16taHxup1x3+3ZUE23R0upTaeVERgYyNatWxk1ahSJiYkkJibe"
429
+ "9ExCQgJr1qxptoQMCQlh/PjxlJaWYhgG/fr149FHH72tpafbAgAu7gC4XC4efvhhdu/ezebN"
430
+ "mykqKuLYsWMEBgYyZMgQpk6dSv/+/RstQb/XpxAZGYndbqe6uprY2FhCQ0PvTA+40xAWFsb0"
431
+ "6dOZPn36TbHdXQJdLhcffvgh1dXVDaVna+IHJUB9NeMtNE2jvLycwsJCAB5//HH69+/fKsn3"
432
+ "zhBAQLWzZWOvUqqhSy4vL2fMmDEN6wJr1651c3oFnLUuENrdJYAUcPSCAaLl4q+u69zqfEN2"
433
+ "djYRERFujX4pBIfPudBa4JxEqwtwrkpy4GQ1vbu2wTBuFKJdu3a0a9eOjh07+pw7AgIC0HWd"
434
+ "yMhIXnjhBV599VW3Q4+pTD79yokm77YQBNh1wX987qJDSC0dQmzUr0yapsnq1avJzs5G13Wv"
435
+ "y0TTNNm/fz9CCIQQOByOW3bKTQ2SXYdqOFHh2XqwXwRQSl1WSp1o+g+L+o7aAYR5I2pVDazf"
436
+ "Wc2YgYqOoTZ0DQSKkJBQ6rkyvN6SoOjQoeP3RFFujHpwGrDnqyts/8JEky2zS6lRst7+q2Jq"
437
+ "P/Fr4NfuGHp9/fEomyNwuBRyhpCyf938nnvrDEJAZY3gNzuq6dquhvZBGnorHgNXCmqcivIK"
438
+ "g8oaiU1ruS1ijQrw+/x8jwxlTu52DPjtgoIzBUKTv9M021hP84HUJKcvwelKkzsBQkhsLTwQ"
439
+ "dIBVOTnMvN7eZ6an6ygFlNF/yRI3X1SQnJLiqvvZrFYGU5QwHxRSDvCiMr2njoToK7OzmZmY"
440
+ "yMrs7J5S014S0MkbQ6tXrCh3Gcbbs8d1OQ7UpBaeW6Uh38E64tT8gMvJzAx3OBx/BB7ysenZ"
441
+ "oZR6bsacOZeScg93bhN63+d4KejdALd3xtnt9qeAKN9jpogxTbMDwLIXHzoLqsYa483kvusj"
442
+ "t9pvFcR39rkpZbosit0Q4FJFxRal1Ge+GjOVyhNKlQO8nPlxgBB3/w1aPifhFVlZzE5ONoAh"
443
+ "/7J8+S+BHl6FH6X2z0hM3FD//x898OOhIEIsit1IwhmLFzM/NdUvBhfkHWXv6YMMvH9gvpTa"
444
+ "hHuZXLeTsD/In/XWX1n7F8WSSfczoGv/1+518j3KAbccyRvPehbLbHbbiS/K26duOrtatweu"
445
+ "tqj1cSpiybjOpBScGSzrdjo0U/jgEIIHEGKkELKLRasfBACQmva4JrWlWBc23f4QdL2xUhb5"
446
+ "rSiABUsASwALlgD3tADWXH5rlqG1NVc2SU3f16yKUrMLIXpLTf+FEPIR/Hgu4J4VYF7+CZZO"
447
+ "jDwKHHXT1gdA9oKCMz/TdFumkHKIRa8PIWjpRM/uak1aV3dCZsn48O0u57XRyjT3WvS64QH1"
448
+ "C/KrcnJsQogwvP1egPNFV4FLAOkTupxO3Xg2VZP2zVj3jjYJAbA8KyvYZrPlAL8AQvDsjiJB"
449
+ "3WU++5VSc2fMmbOj/oO09y7uF0L2vlfJdXs6Wtf1F4UQCUKIcCFEkBAi2IN/QUKIECHEECnE"
450
+ "0tU5OSGvrz8GgDKNj6wx3kwOmDx+vBBCdPePP4nuSBmQOTnqugDqqEVxMwKsLyhQwD5/GFNK"
451
+ "/c00zSsJWR/XC9LeotiNMvT86dPrOoSHRwgh0oQQuhfEA+wyTDNhVmLi1W/7A/mURXEzAixb"
452
+ "soTA4GD12syZGUCGrwZX5CxnduIc5uWf6Cuk1suiuBkBkhYs8JuxOW8fYPbUuqLHbg9MpW67"
453
+ "ugVvGjFvsPw6+ambzhUI60t/fJuKAJi/4VSAlDKouTUxhUAIYTcN1yDd5nhDSGtd2C8CSE2f"
454
+ "qEn5Os1uGBcgRGdNtwVblPpTACnDhNQesmhqvRxgfYHDDykJW7AEsASwYAlwVwlgbYvzAe58"
455
+ "CUfTApim06qEfIG66psAgouA0yLSS/qVOuaTAIZhHAask45ewnWtZqdPAhzZu/0zlKq0qPRm"
456
+ "9JvXlk6J2uO1AMnrysjLnug0DGeBRafnMF2ubICEFdt9q3Lil5TYHvjxT6uFEJpFq9ux/xvT"
457
+ "cPZb/ELnkz6FoBlvfMa6BT93Gs5r8RatHghgGqs/31Xs1tXDzXpAyoZyTOXSbLY2uVLTJ1v0"
458
+ "Nkv+9suVF59b/g8PX/GLAPVIfPtgYFBYh/8UUouxaG6sbTJKr1Zd+FnOS71qFr1fxcLnQ/wj"
459
+ "wOvrviIzvm7rUOqmc+s13TaOH+Cdoy1ZcZqG8UHlqcNxkf2GGIf+bxdrX3sMv3rAzLX/y6qE"
460
+ "vwNgwcYzP9c0/RWEfErcy4f4lEIpc6dpGG8sHtc5D2Ba1ibeSn7Bk+kK9xEXl0ZhYVpdbig4"
461
+ "FaQUXTWpjRVSPiOE7CukDLv7OTerlFL7lWl+ZBqu95RShzMmRlwGmJ93goxJnu0q/3/q6IVg"
462
+ "+Sj0hAAAAABJRU5ErkJggg==")
463
+
464
+
465
+ def test_00():
466
+ main(['--debug'])
467
+
468
+ if __name__ == '__main__':
469
+ main(sys.argv[1:])
pnb/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ from . import main
2
+ import sys
3
+
4
+ main(sys.argv[1:])
5
+
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: pnb
3
+ Version: 0.4.0
4
+ Summary: A beautiful and lightweight notebook interface for the python interpreter.
5
+ License-File: LICENSE
6
+ Author: C. Ecker
7
+ Author-email: textmodelview@gmail.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
16
+ Provides-Extra: wx
17
+ Requires-Dist: pynotebook (>=0.4.0,<0.5.0)
18
+ Requires-Dist: wxPython (>=4.2) ; extra == "wx"
19
+ Description-Content-Type: text/markdown
20
+
21
+
22
+ pnb
23
+ ====
24
+
25
+ ![Screenshot](https://codeberg.org/chrisecker/pynotebook/raw/branch/master/screenshots/ball.png)
26
+
27
+ [More screenshots](https://codeberg.org/chrisecker/pynotebook/src/branch/master/screenshots/)
28
+
29
+
30
+ Introduction
31
+ ------------
32
+
33
+ Pnb is an interactive environment for Python programming inspired by
34
+ the notebook concept of Mathematica and Jupyter. Notebooks offer a
35
+ nice way to develop and document Python code while organizing
36
+ ideas.
37
+
38
+ Unlike heavier applications like Jupyter or Spyder, pnb is
39
+ exceptionally lightweight and easy to install. It has only two
40
+ dependencies: wxPython and pynotebook, which itself only needs the
41
+ small pure-Python package cerealizer to read files of older versions.
42
+
43
+
44
+ Features
45
+ --------
46
+
47
+ - Code cells: run with Shift+Return, the output appears right below
48
+ - Syntax highlighting, completion (Tab) and help on the word at the
49
+ cursor (Ctrl-H)
50
+ - Text cells with formatted text for notes and documentation
51
+ - Plots via matplotlib and built-in vector graphics, right in the
52
+ document
53
+ - Safe, readable file format: notebooks are saved as JSON, and opening
54
+ a file never runs code
55
+ - BSD license
56
+
57
+
58
+ Quick start
59
+ -----------
60
+
61
+ Start `pnb`, type some Python code and press Shift+Return:
62
+
63
+ | Key | Action |
64
+ |---|---|
65
+ | Shift+Return | Execute the cell |
66
+ | Tab | Complete |
67
+ | Ctrl-H | Help on the word at the cursor |
68
+ | Ctrl-T | Insert a text cell |
69
+ | Ctrl-B | Split the cell at the cursor |
70
+ | Ctrl-I / Ctrl-U | Indent / dedent |
71
+
72
+ Examples: `demo/*.pnb` in the sources; the Linux packages install them
73
+ to `/usr/share/pnb/examples/`.
74
+
75
+
76
+ Installation
77
+ ------------
78
+
79
+ Linux (Debian, Ubuntu, Fedora, ...): download the `.deb` or `.rpm`
80
+ from the [Releases page](https://github.com/chrisecker/pynotebook/releases)
81
+ and install it, e.g.:
82
+
83
+ sudo apt install ./pnb_*_all.deb
84
+ sudo dnf install ./pnb-*.noarch.rpm
85
+
86
+ The packages use the wxPython of the distribution and add pnb to the
87
+ application menu.
88
+
89
+ With pip and the wxPython of your Linux distribution (4.2 or newer):
90
+
91
+ sudo apt install python3-wxgtk4.0 # or: sudo dnf install python3-wxpython4
92
+ python3 -m venv --system-site-packages ~/pnb-env
93
+ ~/pnb-env/bin/pip install pnb
94
+
95
+ With pip including wxPython (Windows, macOS; on Linux, pip has to build
96
+ wxPython from source):
97
+
98
+ pip install "pnb[wx]"
99
+
100
+ From the source folder:
101
+
102
+ pip install .
103
+
104
+ Desktop integration after a pip installation (pnb must be on the PATH):
105
+
106
+ mkdir -p ~/.local/share/applications ~/.local/share/icons/hicolor/96x96/apps
107
+ cp pnb.desktop ~/.local/share/applications/
108
+ cp pnb.png ~/.local/share/icons/hicolor/96x96/apps/
109
+
110
+
111
+ Run without installing
112
+ ----------------------
113
+
114
+ In this folder:
115
+
116
+ python runtests.py pnb
117
+
118
+
119
+ Links
120
+ -----
121
+ - [pynotebook module](https://pypi.org/project/pynotebook/)
122
+ - [source code](https://codeberg.org/chrisecker/pynotebook/src/branch/master/pnb)
123
+
@@ -0,0 +1,7 @@
1
+ pnb/__init__.py,sha256=sh-H_DFdBaDn2kTgkzXg9nmYm6qRrCglVDUZM9O0m4Q,18907
2
+ pnb/__main__.py,sha256=p21AEJ2qxs3EOirzql409Qv_W6xI1ftyMEPQSKR-0Mo,51
3
+ pnb-0.4.0.dist-info/METADATA,sha256=2WWJ9_9X4hRZdzj9ELOTgWG1ki29DP5qeIozzsimkJI,3568
4
+ pnb-0.4.0.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
5
+ pnb-0.4.0.dist-info/entry_points.txt,sha256=YE9toorwLeyvp14C6ovcOiCYwtQhwX_LMESMZ5TI-Y4,32
6
+ pnb-0.4.0.dist-info/licenses/LICENSE,sha256=xee71kXj-vLqCavUcdt-zm3DdwiW9ciOMAwvCPwexYg,1935
7
+ pnb-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.5.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ pnb=pnb:main
3
+
@@ -0,0 +1,38 @@
1
+ Copyright (c) 2013, 2014, 2015, 2016, 2017, 2018 C. Ecker
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ 3. Neither the name of the <organization> nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY C. Ecker ''AS IS'' AND ANY
16
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ -----------------------------------------------------------------------------
27
+
28
+ Pynb includes cerealizer which is released under the python
29
+ license. Copyright details can be found here:
30
+
31
+ http://www.lesfleursdunormal.fr/static/informatique/cerealizer/index_en.html
32
+
33
+
34
+ Pynb includes parts of pygui which is according to the readme free
35
+ software and can be used however you want it. Details can be found
36
+ here:
37
+
38
+ http://www.cosc.canterbury.ac.nz/greg.ewing/python_gui/