-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathAMaDiA_Widgets.py
485 lines (422 loc) · 23.7 KB
/
AMaDiA_Widgets.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# This Python file uses the following encoding: utf-8
# if__name__ == "__main__":
# pass
from AGeLib import *
import sys
sys.path.append('..')
from PyQt5 import QtWidgets,QtCore,QtGui,Qt#,QtQuick
#QtQuick.
#from PyQt5.QtQuick import Controls as QtControls
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as Canvas
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas #CLEANUP: Delete this line?
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib
import matplotlib.pyplot as plt
#matplotlib.use('Qt5Agg')
from mpl_toolkits.axes_grid1 import Divider, Size
from mpl_toolkits.axes_grid1.mpl_axes import Axes
import numpy as np
import scipy
import sympy
from sympy.parsing.sympy_parser import parse_expr
import re
import time
import warnings
from AMaDiA_Files import AMaDiA_Functions as AF
from AMaDiA_Files import AMaDiA_Classes as AC
from AMaDiA_Files import AMaDiA_ReplacementTables as ART
import importlib
def ReloadModules():
importlib.reload(AF)
importlib.reload(AC)
importlib.reload(ART)
# -----------------------------------------------------------------------------------------------------------------
#FEATURE: Add autocomplete for AMaDiA_TextEdit and AMaDiA_LineEdit. For Example:
# "sq" should suggest "√(*)" with * being the cursor position
# "int" should suggest "∫{(*)()}dx" or "∫{(*From*)(To)} f(x) dx" with "*From*" being selected or even fancier
class AMaDiA_TextEdit(TextEdit):
def __init__(self, parent=None):
super(AMaDiA_TextEdit, self).__init__(parent)
self.Highlighter = LineEditHighlighter(self.document(), self)
self.cursorPositionChanged.connect(self.CursorPositionChanged)
def CursorPositionChanged(self):
self.Highlighter.blockSignals(True) #block signals because rehighlight might trigger the change signal? but this blocking might also be unnecessary
self.Highlighter.rehighlight()
self.Highlighter.blockSignals(False)
cursor = self.textCursor()
curPos = cursor.position()
self.document().contentsChange.emit(curPos,0,0)
class AMaDiA_LineEdit(LineEdit):
def __init__(self, parent=None):
super(AMaDiA_LineEdit, self).__init__(parent)
self.Highlighter = LineEditHighlighter(self.document(), self)
self.cursorPositionChanged.connect(self.CursorPositionChanged)
def CursorPositionChanged(self):
cursor = self.textCursor()
curPos = cursor.position()
self.document().contentsChange.emit(curPos,0,0)
class LineEditHighlighter(QtGui.QSyntaxHighlighter): # TODO: performance, Fix FindPair
def __init__(self, document, Widget):
QtGui.QSyntaxHighlighter.__init__(self, document)
self.Widget = Widget
self.init_Styles()
try:
self.enabled = QtWidgets.QApplication.instance().optionWindow.cb_O_PairHighlighter.isChecked()
except common_exceptions:
self.enabled = True
QtWidgets.QApplication.instance().S_Highlighter.connect(self.ToggleActive)
# init the rules # Currently Unused...
rules = [(r'%s' % b, 0, self.STYLES['brace']) for b in self.braces]
self.rules = [(QtCore.QRegExp(pat), index, fmt) for (pat, index, fmt) in rules]
App().S_ColourChanged.connect(self.UpdateFormats)
def ToggleActive(self,Active):
self.enabled = Active
def UpdateFormats(self):
self.RedFormat.setForeground(App().PenColours["Red"].color())
self.GreenFormat.setForeground(App().PenColours["Green"].color())
self.BlueFormat.setForeground(App().PenColours["Blue"].color())
self.CyanFormat.setForeground(App().PenColours["Cyan"].color())
self.MagentaFormat.setForeground(App().PenColours["Magenta"].color())
def init_Styles(self):
# init Lists
self.braces = ['\{', '\}', '\(', '\)', '\[', '\]'] # pylint: disable=anomalous-backslash-in-string
# Init Formats
self.RedFormat = QtGui.QTextCharFormat()
self.RedFormat.setForeground(QtGui.QColor('red'))
self.GreenFormat = QtGui.QTextCharFormat()
self.GreenFormat.setForeground(QtGui.QColor('green'))
self.BlueFormat = QtGui.QTextCharFormat()
self.BlueFormat.setForeground(QtGui.QColor('blue'))
self.CyanFormat = QtGui.QTextCharFormat()
self.CyanFormat.setForeground(QtGui.QColor('cyan'))
self.MagentaFormat = QtGui.QTextCharFormat()
self.MagentaFormat.setForeground(QtGui.QColor('magenta'))
# Collect all Formats in a dictionary
self.STYLES = {'brace': self.RedFormat,'pair': self.RedFormat}
def highlightBlock(self, text):
if not self.enabled:
self.setCurrentBlockState(0)
return
cursor = self.Widget.textCursor()
if not cursor.block() == self.currentBlock():
#self.setCurrentBlockState(0)
return
curPos = cursor.positionInBlock()
pattern = ""
TheList = []
for i in ART.LIST_l_normal_pairs:
for j in i:
if not j[0] in TheList:
TheList.append(j[0])
if not j[1] in TheList:
TheList.append(j[1])
TheList.sort(key=len,reverse=True)
for i in TheList:
pattern += re.escape(i)
pattern += "|"
pattern += re.escape(i)
pattern += "|"
pattern = pattern[:-1]
braces_list = [[m.start(),m.end()] for m in re.finditer(pattern, text)]
braces_list.sort(key=AF.takeFirst,reverse=False)
for i in braces_list:
if curPos <= i[1] and curPos >= i[0]:
self.setFormat(i[0], i[1]-i[0], self.STYLES['pair'])
Element = text[i[0]:i[1]]
try:
Pair = AF.Counterpart(Element, ListOfLists=ART.LIST_l_normal_pairs, Both=True)
except Exception:
ExceptionOutput(sys.exc_info())#break
if Pair[0] == Element:
Pair = Pair.FirstResult
a,b = AF.FindPair(text,Pair,i[0])
self.setFormat(b, len(Pair[1]), self.STYLES['pair'])
else:
# IMPROVE: Opening pair finder
#---------method1----------
# FIXME: Does not work!!!!!!!!!!!!!! NEEDS FIX OF AF.FindPair ???
#k=0
#found = False
#while k < len(Pair):
# a,b = AF.FindPair(text,Pair.List[k],end=i[1])
# if b == i[0] and Pair.List[k][1] == Element:
# c,d = a, len(Pair.List[k][0])
# found = True
# k+=1
#if found:
# self.setFormat(c, d, self.STYLES['pair'])
#---------method2----------
found = False
for j in braces_list:
Element2 = text[j[0]:j[1]]
try:
Pair2 = AF.Counterpart(Element2, ListOfLists=ART.LIST_l_normal_pairs, Both=True)
except Exception:
ExceptionOutput(sys.exc_info())#break
else: #VALIDATE: This "else" was added to ensure that Pair2 exists but this code ran without problems before...
# (previously the following code was always executed)
# What did I think when I wrote this?
# Why did I use the ExceptionOutput? Why did I want to use "break" instead of "continue"?
k=0
while k < len(Pair2):
a,b = AF.FindPair(text,Pair2.List[k],j[0])
if b == i[0] and Pair2.List[k][1] == Element:
c,d = a, len(Pair2.List[k][0])
found = True
break
k+=1
if found:
self.setFormat(c, d, self.STYLES['pair'])
break
self.setCurrentBlockState(0)
# -----------------------------------------------------------------------------------------------------------------
class AMaDiA_TableWidget(TableWidget):
def __init__(self, parent=None):
super(AMaDiA_TableWidget, self).__init__(parent)
self.TheDelegate = AMaDiA_TableWidget_Delegate(self)
self.setItemDelegate(self.TheDelegate)
class AMaDiA_TableWidget_Delegate(TableWidget_Delegate):
def __init__(self, parent=None):
super(AMaDiA_TableWidget_Delegate, self).__init__(parent)
def createEditor(self, parent, options, index):
return AMaDiA_LineEdit(parent)
# -----------------------------------------------------------------------------------------------------------------
class HistoryWidget(ListWidget):
def __init__(self, parent=None):
super(HistoryWidget, self).__init__(parent)
self.installEventFilter(self)
def keyPressEvent(self,event):
try:
if event == QtGui.QKeySequence.Copy:
SelectedItems = self.selectedItems()
if len(SelectedItems)==1:
item = SelectedItems[0]
if QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()=="Normal":
if (self == QtWidgets.QApplication.instance().MainWindow.Tab_1_History
or self == QtWidgets.QApplication.instance().MainWindow.Tab_4_History) and item.data(100).Solution != "Not evaluated yet":
Qt.QApplication.clipboard().setText(item.data(100).Equation)
else:
Qt.QApplication.clipboard().setText(item.text())
elif (QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()=="Solution"
and item.data(100).Solution != "Not evaluated yet"):
Qt.QApplication.clipboard().setText(item.data(100).Solution)
elif (QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()=="Equation"
and item.data(100).Solution != "Not evaluated yet"):
Qt.QApplication.clipboard().setText(item.data(100).Equation)
elif QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()=="Text":
Qt.QApplication.clipboard().setText(item.data(100).Text)
elif (QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()=="LaTeX"
and item.data(100).LaTeX != r"\text{Not converted yet}"
and item.data(100).LaTeX != r"\text{Could not convert}"):
Qt.QApplication.clipboard().setText(item.data(100).LaTeX)
elif (QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()=="LaTeX Equation"
and item.data(100).LaTeX_E != r"\text{Not converted yet}"
and item.data(100).LaTeX_E != r"\text{Could not convert}"):
Qt.QApplication.clipboard().setText(item.data(100).LaTeX_E)
else:
NC(4,QtWidgets.QApplication.instance().optionWindow.comb_O_HCopyStandard.currentText()+" can not be copied. Using normal copy mode",win=self.window().windowTitle(),func=str(self.objectName())+".(HistoryWidget).keyPressEvent",input=item.text())
Qt.QApplication.clipboard().setText(item.text())
event.accept()
return
elif self == QtWidgets.QApplication.instance().MainWindow.Tab_1_History:
string = ""
for i in SelectedItems:
string += i.data(100).Equation
string += "\n"
Qt.QApplication.clipboard().setText(string)
event.accept()
return
super(HistoryWidget, self).keyPressEvent(event)
except common_exceptions:
NC(lvl=2,exc=sys.exc_info(),win=self.window().windowTitle(),func=str(self.objectName())+".(HistoryWidget).keyPressEvent",input=str(event))
super(HistoryWidget, self).keyPressEvent(event)
def eventFilter(self, source, event): #TODO: Add Tooltips for the Actions! These should also specify whether the action will be executed on all selected items or only the right-clicked-one! "Delete" should also mention "Del" as the hotkey!
try:
if event.type() == 82: # QtCore.QEvent.ContextMenu
# ---------------------------------- History Context Menu ----------------------------------
if source.itemAt(event.pos()):
menu = QtWidgets.QMenu()
if source.itemAt(event.pos()).data(100).Solution != "Not evaluated yet":
action = menu.addAction('Copy Solution')
action.triggered.connect(lambda: self.action_H_Copy_Solution(source,event))
action = menu.addAction('Copy Equation')
action.triggered.connect(lambda: self.action_H_Copy_Equation(source,event))
action = menu.addAction('Copy Text')
action.triggered.connect(lambda: self.action_H_Copy_Text(source,event))
action = menu.addAction('Copy LaTeX')
action.triggered.connect(lambda: self.action_H_Copy_LaTeX(source,event))
if source.itemAt(event.pos()).data(100).LaTeX_E != r"\text{Not converted yet}" and source.itemAt(event.pos()).data(100).LaTeX_E != r"\text{Could not convert}":
action = menu.addAction('Copy LaTeX Equation')
action.triggered.connect(lambda: self.action_H_Copy_LaTeX_E(source,event))
if QtWidgets.QApplication.instance().advanced_mode:
action = menu.addAction('+ Copy Input')
action.triggered.connect(lambda: self.action_H_Copy_Input(source,event))
action = menu.addAction('+ Copy cString')
action.triggered.connect(lambda: self.action_H_Copy_cstr(source,event))
menu.addSeparator()
# MAYBE: Only "Calculate" if the equation has not been evaluated yet or if in Advanced Mode? Maybe? Maybe not?
# It currently is handy to have it always because of the EvalF thing...
action = menu.addAction('Calculate')
action.triggered.connect(lambda: self.action_H_Calculate(source,event))
action = menu.addAction('Display LaTeX')
action.triggered.connect(lambda: self.action_H_Display_LaTeX(source,event))
if source.itemAt(event.pos()).data(100).Solution != "Not evaluated yet":
action = menu.addAction('Display LaTeX Equation')
action.triggered.connect(lambda: self.action_H_Display_LaTeX_Equation(source,event))
action = menu.addAction('Display LaTeX Solution')
action.triggered.connect(lambda: self.action_H_Display_LaTeX_Solution(source,event))
menu.addSeparator()
if source.itemAt(event.pos()).data(100).plot_data_exists :
action = menu.addAction('Load Plot')
action.triggered.connect(lambda: self.action_H_Load_Plot(source,event))
if source.itemAt(event.pos()).data(100).plottable :
action = menu.addAction('New Plot')
action.triggered.connect(lambda: self.action_H_New_Plot(source,event))
elif QtWidgets.QApplication.instance().advanced_mode :
action = menu.addAction('+ New Plot')
action.triggered.connect(lambda: self.action_H_New_Plot(source,event))
if source.itemAt(event.pos()).data(100).plot_data_exists and QtWidgets.QApplication.instance().advanced_mode:
menu.addSeparator()
action = menu.addAction('+ Copy x Values')
action.triggered.connect(lambda: self.action_H_Copy_x_Values(source,event))
action = menu.addAction('+ Copy y Values')
action.triggered.connect(lambda: self.action_H_Copy_y_Values(source,event))
menu.addSeparator()
action = menu.addAction('Delete')
action.triggered.connect(lambda: self.action_H_Delete(source,event))
menu.setPalette(self.palette())
menu.setFont(self.font())
menu.exec_(event.globalPos())
return True
elif event.type() == 6: # QtCore.QEvent.KeyPress
if event.key() == QtCore.Qt.Key_Delete:
self.action_H_Delete(source,event)
return super(HistoryWidget, self).eventFilter(source, event)
except common_exceptions:
NC(lvl=1,exc=sys.exc_info(),win=self.window().windowTitle(),func=str(self.objectName())+".(HistoryWidget).eventFilter",input=str(event))
return super(HistoryWidget, self).eventFilter(source, event)
# ---------------------------------- History Context Menu Actions/Functions ----------------------------------
# ----------------
def action_H_Copy_Solution(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).Solution)
def action_H_Copy_Equation(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).Equation)
def action_H_Copy_Text(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).Text)
def action_H_Copy_LaTeX(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).LaTeX)
def action_H_Copy_LaTeX_E(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).LaTeX_E)
def action_H_Copy_Input(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).Input)
def action_H_Copy_cstr(self,source,event):
item = source.itemAt(event.pos())
Qt.QApplication.clipboard().setText(item.data(100).cstr)
# ----------------
def action_H_Calculate(self,source,event):
item = source.itemAt(event.pos())
self.window().tabWidget.setCurrentIndex(0)
self.window().Tab_1_F_Calculate(item.data(100))
def action_H_Display_LaTeX(self,source,event): #TODO: Move all selected items to the LaTeX Tab History but only display LaTeX of the right-clicked-one (and update the tooltip for this action)
item = source.itemAt(event.pos())
self.window().tabWidget.setCurrentIndex(1)
self.window().Tab_2_F_Display(item.data(100))
def action_H_Display_LaTeX_Equation(self,source,event):
item = source.itemAt(event.pos())
self.window().tabWidget.setCurrentIndex(1)
self.window().Tab_2_F_Display(item.data(100),part="Equation")
def action_H_Display_LaTeX_Solution(self,source,event):
item = source.itemAt(event.pos())
self.window().tabWidget.setCurrentIndex(1)
self.window().Tab_2_F_Display(item.data(100),part="Solution")
# ----------------
def action_H_Load_Plot(self,source,event):
TheItem = source.itemAt(event.pos())
if source is self.window().Tab_3_1_History:
listItems=source.selectedItems()
if not listItems: return
else:
listItems = [TheItem]
for item in listItems:
self.window().tabWidget.setCurrentIndex(2)
if not item.data(100).Plot_is_initialized:
item.data(100).init_2D_plot()
if item.data(100).current_ax != None:
item.data(100).current_ax.remove()
item.data(100).current_ax = None
self.window().Tab_3_1_F_RedrawPlot()
self.window().Tab_3_1_F_Plot(item.data(100))
def action_H_New_Plot(self,source,event):
TheItem = source.itemAt(event.pos())
if source is self.window().Tab_3_1_History:
listItems=source.selectedItems()
if not listItems: return
else:
listItems = [TheItem]
for item in listItems:
self.window().tabWidget.setCurrentIndex(2)
if not item.data(100).Plot_is_initialized:
item.data(100).init_2D_plot()
if item.data(100).current_ax != None:
item.data(100).current_ax.remove()
item.data(100).current_ax = None
self.window().Tab_3_1_F_RedrawPlot()
self.window().Tab_3_1_F_Plot_init(item.data(100))
# ----------------
def action_H_Copy_x_Values(self,source,event):
try:
item = source.itemAt(event.pos())
Text = "[ "
for i in item.data(100).plot_x_vals:
Text += str(i)
Text += " , "
Text = Text[:-3]
Text += " ]"
Qt.QApplication.clipboard().setText(Text)
except common_exceptions:
NC(lvl=2,msg="Could not copy x values",exc=sys.exc_info(),func="HistoryWidget.action_H_Copy_x_Values",win=self.window().windowTitle(),input=item.data(100).Input)
def action_H_Copy_y_Values(self,source,event):
try:
item = source.itemAt(event.pos())
Text = "[ "
for i in item.data(100).plot_y_vals:
Text += str(i)
Text += " , "
Text = Text[:-3]
Text += " ]"
Qt.QApplication.clipboard().setText(Text)
except common_exceptions:
NC(lvl=2,msg="Could not copy y values",exc=sys.exc_info(),func="HistoryWidget.action_H_Copy_y_Values",win=self.window().windowTitle(),input=item.data(100).Input)
# ----------------
def action_H_Delete(self,source,event):
listItems=source.selectedItems()
if not listItems: return
for item in listItems:
source.takeItem(source.row(item))
# The cleanup below is apparently unnecessary but it is cleaner to do it anyways...
if source is self.window().Tab_1_History:
item.data(100).tab_1_is = False
item.data(100).tab_1_ref = None
elif source is self.window().Tab_2_History:
item.data(100).tab_2_is = False
item.data(100).tab_2_ref = None
elif source is self.window().Tab_3_1_History:
item.data(100).Tab_3_1_is = False
item.data(100).Tab_3_1_ref = None
if item.data(100).current_ax != None:
item.data(100).current_ax.remove()
item.data(100).current_ax = None
self.window().Tab_3_1_F_RedrawPlot()
elif source is self.window().Tab_4_History:
if item.data(100) == self.window().Tab_4_Active_Equation:
self.window().Tab_4_History.addItem(item)
else:
item.data(100).Tab_4_is = False
item.data(100).Tab_4_ref = None