Compare commits
4 Commits
cc4c61bc4d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
fe726bd935
|
|||
|
7513fe88cf
|
|||
|
b4d255e19b
|
|||
|
70c7975eeb
|
BIN
Л4-В6/Lab_4.pdf
Normal file
BIN
Л4-В6/Lab_4.pdf
Normal file
Binary file not shown.
205
Л4-В6/Блоксхема.drawio
Normal file
205
Л4-В6/Блоксхема.drawio
Normal file
@@ -0,0 +1,205 @@
|
||||
<mxfile host="app.diagrams.net" modified="2024-04-21T20:49:14.389Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" etag="L70BZ2SPwnqLO1KppO7W" version="24.2.7" type="device" pages="2">
|
||||
<diagram name="difference_n" id="QT74YzUSpF6bmaEzPSn7">
|
||||
<mxGraphModel dx="472" dy="736" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-1" target="DQ9j4h5ksd5OBPriSE9t-2">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-1" value="difference_n(func, x_array, n)" style="rounded=1;whiteSpace=wrap;html=1;arcSize=50;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="229.5" y="150" width="230" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-2" target="DQ9j4h5ksd5OBPriSE9t-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-2" value="result = 0" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="295" y="210" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-4" target="DQ9j4h5ksd5OBPriSE9t-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-4" target="DQ9j4h5ksd5OBPriSE9t-19">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="490" y="290" />
|
||||
<mxPoint x="490" y="760" />
|
||||
<mxPoint x="345" y="760" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-4" value="k = 0(1)n" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="270" width="110" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-6" target="DQ9j4h5ksd5OBPriSE9t-9">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-6" value="prod = 1" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="295" y="340" width="100" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-8" target="DQ9j4h5ksd5OBPriSE9t-13">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-8">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="345" y="620" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="440" y="505" />
|
||||
<mxPoint x="440" y="620" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-8" value="j != k" style="rhombus;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="305" y="480" width="80" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-9" target="DQ9j4h5ksd5OBPriSE9t-8">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-9" target="DQ9j4h5ksd5OBPriSE9t-12">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="460" y="430" />
|
||||
<mxPoint x="460" y="650" />
|
||||
<mxPoint x="344" y="650" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-9" value="j = 0(1)n" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="410" width="110" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-12" target="DQ9j4h5ksd5OBPriSE9t-4">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="345" y="740" />
|
||||
<mxPoint x="210" y="740" />
|
||||
<mxPoint x="210" y="290" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-12" value="result += func(x_array[k]) / prod" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="276" y="670" width="137" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-13" target="DQ9j4h5ksd5OBPriSE9t-9">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="345" y="620" />
|
||||
<mxPoint x="240" y="620" />
|
||||
<mxPoint x="240" y="430" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-13" value="prod *= x_array[k] - x_array[j]" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="260" y="560" width="170" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-24" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="DQ9j4h5ksd5OBPriSE9t-19" target="DQ9j4h5ksd5OBPriSE9t-23">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-19" value="return result" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="296.75" y="780" width="95.5" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-21" value="Да" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="340" y="528" width="40" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-22" value="Нет" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="380" y="478" width="40" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="DQ9j4h5ksd5OBPriSE9t-23" value="Конец" style="rounded=1;whiteSpace=wrap;html=1;arcSize=50;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="295" y="840" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
<diagram id="YRw7BwRxA9GMbmWKKpqw" name="forward_interpolate">
|
||||
<mxGraphModel dx="477" dy="613" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-2" target="FBLyEU0xljRjRjxMH2o2-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-2" value="forward_interpolate(func, x_array, x)" style="rounded=1;whiteSpace=wrap;html=1;arcSize=50;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="229.5" y="140" width="230" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-4" target="FBLyEU0xljRjRjxMH2o2-7">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-4" value="n = len(x_array)<div>L = func(x_array[0])</div>" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="263.5" y="200" width="163" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-7" target="FBLyEU0xljRjRjxMH2o2-26">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="345" y="330" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-7" target="FBLyEU0xljRjRjxMH2o2-21">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="490" y="290" />
|
||||
<mxPoint x="490" y="650" />
|
||||
<mxPoint x="345" y="650" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-7" value="k = 1(1)n-1" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="270" width="110" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-26" target="FBLyEU0xljRjRjxMH2o2-15">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="345" y="380" as="sourcePoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-15" target="FBLyEU0xljRjRjxMH2o2-17">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="460" y="430" />
|
||||
<mxPoint x="460" y="560" />
|
||||
<mxPoint x="345" y="560" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-25" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-15" target="FBLyEU0xljRjRjxMH2o2-19">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-15" value="i = 0(1)k-1" style="shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;fixedSize=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="410" width="110" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-17" target="FBLyEU0xljRjRjxMH2o2-7">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="345" y="640" />
|
||||
<mxPoint x="210" y="640" />
|
||||
<mxPoint x="210" y="290" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-17" value="L += prod" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="292.5" y="585" width="104" height="35" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-19" target="FBLyEU0xljRjRjxMH2o2-15">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="345" y="540" />
|
||||
<mxPoint x="240" y="540" />
|
||||
<mxPoint x="240" y="430" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-19" value="prod *= x - x_array[i]" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="260" y="480" width="170" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-20" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="FBLyEU0xljRjRjxMH2o2-21" target="FBLyEU0xljRjRjxMH2o2-24">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-21" value="return L" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="296.75" y="670" width="95.5" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-24" value="Конец" style="rounded=1;whiteSpace=wrap;html=1;arcSize=50;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="295" y="720" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="FBLyEU0xljRjRjxMH2o2-26" value="<span style="font-family: &quot;Cascadia Code&quot;;">prod = difference_n(func, x_array, k)</span>" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;size=0.07142857142857142;" vertex="1" parent="1">
|
||||
<mxGeometry x="264" y="330" width="162.5" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
Л4-В6/Отчет.docx
Normal file
BIN
Л4-В6/Отчет.docx
Normal file
Binary file not shown.
2
Л4-В6/Программа/.gitignore
vendored
Normal file
2
Л4-В6/Программа/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.venv
|
||||
*/__pycache__
|
||||
5
Л4-В6/Программа/requirements.txt
Normal file
5
Л4-В6/Программа/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
sympy
|
||||
numpy
|
||||
scipy
|
||||
pyqt6
|
||||
matplotlib
|
||||
60
Л4-В6/Программа/src/algorithm.py
Normal file
60
Л4-В6/Программа/src/algorithm.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import numpy as np
|
||||
import typing
|
||||
|
||||
|
||||
def difference_n(
|
||||
func: typing.Callable[[float], float], x_array: np.ndarray, start: int, end: int
|
||||
) -> float:
|
||||
if len(x_array) < end + 1:
|
||||
return None
|
||||
|
||||
result = 0
|
||||
|
||||
for k in range(start, end + 1):
|
||||
prod = 1
|
||||
for j in range(start, end + 1):
|
||||
if j != k:
|
||||
prod *= x_array[k] - x_array[j]
|
||||
result += func(x_array[k]) / prod
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def make_forward_interpolate_function(
|
||||
func: typing.Callable[[float], float], x_array: np.ndarray
|
||||
) -> typing.Callable[[float], float]:
|
||||
n = len(x_array)
|
||||
diffs = [difference_n(func, x_array, 0, k) for k in range(1, n)]
|
||||
|
||||
def forward_interpolate(x: float) -> float:
|
||||
L = func(x_array[0])
|
||||
|
||||
for k in range(1, n):
|
||||
prod = diffs[k - 1]
|
||||
for i in range(k):
|
||||
prod *= x - x_array[i]
|
||||
L += prod
|
||||
|
||||
return L
|
||||
|
||||
return forward_interpolate
|
||||
|
||||
|
||||
def make_backward_interpolate_function(
|
||||
func: typing.Callable[[float], float], x_array: np.ndarray
|
||||
) -> typing.Callable[[float], float]:
|
||||
n = len(x_array)
|
||||
diffs = [difference_n(func, x_array, n - k - 1, n - 1) for k in range(1, n)]
|
||||
|
||||
def backward_interpolate(x: float) -> float:
|
||||
L = func(x_array[-1])
|
||||
|
||||
for k in range(n - 1):
|
||||
prod = diffs[k]
|
||||
for i in range(n - 1, n - k - 2, -1):
|
||||
prod *= x - x_array[i]
|
||||
L += prod
|
||||
|
||||
return L
|
||||
|
||||
return backward_interpolate
|
||||
14
Л4-В6/Программа/src/main.py
Normal file
14
Л4-В6/Программа/src/main.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import sys
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from PyQt6.QtGui import QFont
|
||||
|
||||
import window
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
font = QFont("Cascadia Code", 14)
|
||||
app.setFont(font)
|
||||
window = window.MainWindow()
|
||||
window.show()
|
||||
|
||||
sys.exit(app.exec())
|
||||
10
Л4-В6/Программа/src/task.py
Normal file
10
Л4-В6/Программа/src/task.py
Normal file
@@ -0,0 +1,10 @@
|
||||
func_str = "exp(-b * x) * cos(pi * (x + x ** 2))"
|
||||
func_args = {"b": 0.15}
|
||||
|
||||
|
||||
def f_str() -> str:
|
||||
return func_str
|
||||
|
||||
|
||||
def f_args() -> dict:
|
||||
return func_args.copy()
|
||||
219
Л4-В6/Программа/src/widgets.py
Normal file
219
Л4-В6/Программа/src/widgets.py
Normal file
@@ -0,0 +1,219 @@
|
||||
from PyQt6.QtWidgets import (
|
||||
QLabel,
|
||||
QWidget,
|
||||
QLineEdit,
|
||||
QHBoxLayout,
|
||||
QSlider,
|
||||
)
|
||||
from PyQt6.QtCore import Qt
|
||||
|
||||
import abc
|
||||
import typing
|
||||
|
||||
|
||||
class DisplayField(QWidget):
|
||||
label_text: str
|
||||
|
||||
def __init__(self, parent, label_text="", text=""):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.label_text = label_text
|
||||
self.text = QLabel(f"{self.label_text}{text}")
|
||||
self.text.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
|
||||
self._layout.addWidget(self.text)
|
||||
|
||||
def set_visible(self, visible):
|
||||
self.text.setVisible(visible)
|
||||
|
||||
def set_text(self, text):
|
||||
self.text.setText(f"{self.label_text}{text}")
|
||||
|
||||
|
||||
class InputWidget(QWidget):
|
||||
value: typing.Any
|
||||
previous_value: typing.Any
|
||||
callback: typing.Callable[[typing.Self], bool] | None = None
|
||||
|
||||
def init(self):
|
||||
self.value = self._get_value_internal()
|
||||
self.previous_value = self.value
|
||||
self._set_callback_internal(self._on_change_internal)
|
||||
|
||||
def get_value(self) -> typing.Any:
|
||||
return self.value
|
||||
|
||||
def get_previous_value(self) -> typing.Any:
|
||||
return self.previous_value
|
||||
|
||||
def set_value(self, value: typing.Any):
|
||||
self._set_value_internal(value)
|
||||
self._set_value_no_sync(value)
|
||||
|
||||
def set_callback(self, callback: typing.Callable[[typing.Self], bool]):
|
||||
self.callback = callback
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_value_internal(self) -> typing.Any:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_value_internal(self, value: typing.Any):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_callback_internal(self, callback: typing.Callable[[], None]):
|
||||
pass
|
||||
|
||||
def _set_value_no_sync(self, value: typing.Any):
|
||||
self.previous_value = self.value
|
||||
self.value = value
|
||||
|
||||
def _on_change_internal(self):
|
||||
self._set_value_no_sync(self._get_value_internal())
|
||||
|
||||
if self.callback:
|
||||
self.callback(self)
|
||||
|
||||
|
||||
def make_callback_chain(
|
||||
callbacks: typing.List[typing.Callable[[InputWidget], bool]]
|
||||
) -> typing.Callable[[InputWidget], bool]:
|
||||
def callback(widget: InputWidget):
|
||||
for callback in callbacks:
|
||||
if not callback(widget):
|
||||
break
|
||||
return True
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def make_min_max_callback(
|
||||
min: typing.Any, max: typing.Any
|
||||
) -> typing.Callable[[InputWidget], bool]:
|
||||
def callback(widget: InputWidget):
|
||||
value = widget.get_value()
|
||||
|
||||
if type(min)(value) < min:
|
||||
widget.set_value(min)
|
||||
if type(max)(value) > max:
|
||||
widget.set_value(max)
|
||||
|
||||
return True
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def float_check_callback(widget: InputWidget):
|
||||
try:
|
||||
internal_value = widget._get_value_internal()
|
||||
if not (internal_value == "" or internal_value == "-" or internal_value == "."):
|
||||
_ = float(widget.get_value())
|
||||
return True
|
||||
else:
|
||||
widget.value = widget.previous_value
|
||||
return False
|
||||
except:
|
||||
widget.set_value(widget.previous_value)
|
||||
return False
|
||||
|
||||
|
||||
def int_check_callback(widget: InputWidget):
|
||||
try:
|
||||
internal_value = widget._get_value_internal()
|
||||
if not (internal_value == "" or internal_value == "-"):
|
||||
_ = int(widget.get_value())
|
||||
return True
|
||||
else:
|
||||
widget.value = widget.previous_value
|
||||
return False
|
||||
except:
|
||||
widget.set_value(widget.previous_value)
|
||||
return False
|
||||
|
||||
|
||||
class InputField(InputWidget):
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
placeholder_text="",
|
||||
lineedit_text="",
|
||||
label_position="left",
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label = QLabel(label_text)
|
||||
self.lineedit = QLineEdit(lineedit_text)
|
||||
self.lineedit.setPlaceholderText(placeholder_text)
|
||||
self.lineedit.setMinimumWidth(50)
|
||||
|
||||
if label_position == "left":
|
||||
self._layout.addWidget(self.label)
|
||||
self._layout.addWidget(self.lineedit)
|
||||
elif label_position == "right":
|
||||
self._layout.addWidget(self.lineedit)
|
||||
self._layout.addWidget(self.label)
|
||||
elif label_position == "none":
|
||||
self._layout.addWidget(self.lineedit)
|
||||
|
||||
super().init()
|
||||
super().set_value(lineedit_text)
|
||||
|
||||
def _get_value_internal(self):
|
||||
return self.lineedit.text()
|
||||
|
||||
def _set_value_internal(self, value):
|
||||
self.lineedit.setText(str(value))
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.lineedit.textChanged.connect(callback)
|
||||
|
||||
|
||||
class InputSlider(InputWidget):
|
||||
slider: QSlider
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
value=0,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
step=1,
|
||||
ticks=False,
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label = QLabel(label_text)
|
||||
self.slider = QSlider(Qt.Orientation.Horizontal)
|
||||
if ticks:
|
||||
self.slider.setTickPosition(QSlider.TickPosition.TicksBelow)
|
||||
self.slider.setTickInterval(step)
|
||||
|
||||
self.slider.setMinimum(min_value)
|
||||
self.slider.setMaximum(max_value)
|
||||
self.slider.setValue(value)
|
||||
self.slider.setSingleStep(step)
|
||||
|
||||
self._layout.addWidget(self.label)
|
||||
self._layout.addWidget(self.slider)
|
||||
|
||||
super().init()
|
||||
super().set_value(value)
|
||||
|
||||
def _get_value_internal(self):
|
||||
return self.slider.value()
|
||||
|
||||
def _set_value_internal(self, value):
|
||||
self.slider.setValue(value)
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.slider.valueChanged.connect(callback)
|
||||
588
Л4-В6/Программа/src/window.py
Normal file
588
Л4-В6/Программа/src/window.py
Normal file
@@ -0,0 +1,588 @@
|
||||
from PyQt6.QtWidgets import (
|
||||
QMainWindow,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QFormLayout,
|
||||
QWidget,
|
||||
QHBoxLayout,
|
||||
)
|
||||
from PyQt6 import QtWidgets
|
||||
import numpy as np
|
||||
import sympy
|
||||
from typing import *
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.axes import Axes
|
||||
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
|
||||
|
||||
matplotlib.use("QtAgg")
|
||||
|
||||
import widgets
|
||||
import task
|
||||
import algorithm
|
||||
|
||||
|
||||
class ParamsField(widgets.InputWidget):
|
||||
_layout: QFormLayout
|
||||
fields: Dict[str, widgets.InputWidget]
|
||||
|
||||
callback_internal: Callable
|
||||
|
||||
def __init__(self, parent, fields: List[str]):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self._create(fields)
|
||||
|
||||
super().init()
|
||||
|
||||
def _initialize(self):
|
||||
self.fields = {}
|
||||
self._layout = QFormLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
def _create(self, fields: List[str]):
|
||||
fields = sorted(fields)
|
||||
|
||||
for field in fields:
|
||||
val = "0.0"
|
||||
if not self.value.get(field) is None:
|
||||
val = str(self.value.get(field))
|
||||
|
||||
self.fields[field] = widgets.InputField(
|
||||
self, label_text=f"{field:>4}: ", lineedit_text=val
|
||||
)
|
||||
self.fields[field].set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._callback]
|
||||
)
|
||||
)
|
||||
self._layout.addRow(self.fields[field])
|
||||
|
||||
def recreate(self, fields: List[str]):
|
||||
for field in self.fields:
|
||||
self.fields[field].deleteLater()
|
||||
|
||||
self.fields = {}
|
||||
self._create(fields)
|
||||
self.callback_internal()
|
||||
|
||||
def _get_value_internal(self) -> Dict[str, float]:
|
||||
value = {}
|
||||
for field in self.fields:
|
||||
value[field] = float(self.fields[field].get_value())
|
||||
|
||||
return value
|
||||
|
||||
def _set_value_internal(self, value: Dict[str, float]):
|
||||
for field in self.fields:
|
||||
self.fields[field].set_value(value[field])
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.callback_internal = callback
|
||||
|
||||
def _callback(self, widget: widgets.InputWidget):
|
||||
self.callback_internal()
|
||||
return True
|
||||
|
||||
|
||||
class PointsField(widgets.InputWidget):
|
||||
_layout: QFormLayout
|
||||
points: List[widgets.InputField]
|
||||
|
||||
min: float
|
||||
max: float
|
||||
|
||||
DIVISIONS = 1_000_000
|
||||
|
||||
callback_internal: Callable[[None], None]
|
||||
|
||||
def __init__(self, parent, start: int, points: List[float], min: float, max: float):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self._create(start, points, min, max)
|
||||
|
||||
super().init()
|
||||
|
||||
def _initialize(self):
|
||||
self.points = []
|
||||
self.min = 0.0
|
||||
self.max = 1.0
|
||||
self._layout = QFormLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
def _create(self, start: int, points: List[float], min: float, max: float):
|
||||
self.min = min
|
||||
self.max = max
|
||||
|
||||
for i in range(0, len(points)):
|
||||
value = int((points[i] - min) / (max - min) * self.DIVISIONS)
|
||||
self.points.append(
|
||||
widgets.InputSlider(
|
||||
self,
|
||||
label_text=f"{i + start:>4}: ",
|
||||
value=value,
|
||||
min_value=0,
|
||||
max_value=self.DIVISIONS,
|
||||
)
|
||||
)
|
||||
self.points[i].set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._callback]
|
||||
)
|
||||
)
|
||||
self._layout.addRow(self.points[i])
|
||||
|
||||
def recreate(self, start: int, points: List[float], min: float, max: float):
|
||||
for point in self.points:
|
||||
point.deleteLater()
|
||||
|
||||
self.points = []
|
||||
self._create(start, points, min, max)
|
||||
self.callback_internal()
|
||||
|
||||
def _get_value_internal(self) -> List[float]:
|
||||
value = []
|
||||
for point in self.points:
|
||||
value.append(
|
||||
float(point.get_value()) / self.DIVISIONS * (self.max - self.min)
|
||||
+ self.min
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
def _set_value_internal(self, value: List[float]):
|
||||
for i in range(0, len(value)):
|
||||
self.points[i].set_value(
|
||||
(value[i] - self.min) / (self.max - self.min) * self.DIVISIONS
|
||||
)
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.callback_internal = callback
|
||||
|
||||
def _callback(self, widget: widgets.InputWidget):
|
||||
self.callback_internal()
|
||||
return True
|
||||
|
||||
|
||||
class PlotWidget(QWidget):
|
||||
figure: Figure
|
||||
axes: Axes
|
||||
canvas: FigureCanvasQTAgg
|
||||
|
||||
pre_style: Callable[[Axes], None]
|
||||
post_style: Callable[[Axes], None]
|
||||
plots: List[Callable[[Axes], None]]
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
|
||||
self.figure = plt.figure()
|
||||
self.axes = self.figure.add_subplot(111)
|
||||
|
||||
self.figure.tight_layout(pad=3)
|
||||
self.figure.subplots_adjust(left=0.15, right=0.975, top=0.95)
|
||||
self.figure.patch.set_facecolor("#f0f0f0")
|
||||
|
||||
self.canvas = FigureCanvasQTAgg(self.figure)
|
||||
self._layout.addWidget(self.canvas)
|
||||
|
||||
self.pre_style = lambda _: None
|
||||
self.plots = []
|
||||
|
||||
def set_pre_style(self, style: Callable[[Axes], None]):
|
||||
self.pre_style = style
|
||||
|
||||
def set_post_style(self, style: Callable[[Axes], None]):
|
||||
self.post_style = style
|
||||
|
||||
def clear_plots(self):
|
||||
self.plots = []
|
||||
|
||||
def add_plot(self, plot: Callable[[Axes], None]):
|
||||
self.plots.append(plot)
|
||||
|
||||
def redraw(self):
|
||||
self.axes.clear()
|
||||
self.pre_style(self.axes)
|
||||
for plot in self.plots:
|
||||
plot(self.axes)
|
||||
self.post_style(self.axes)
|
||||
|
||||
self.canvas.draw()
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
central_widget: QWidget
|
||||
|
||||
function_field: widgets.InputField
|
||||
function_param_fields: ParamsField
|
||||
|
||||
x_0_field: widgets.InputField
|
||||
x_n_field: widgets.InputField
|
||||
|
||||
n_display: widgets.DisplayField
|
||||
n_slider: widgets.InputSlider
|
||||
|
||||
points_field: PointsField
|
||||
|
||||
margin_display: widgets.DisplayField
|
||||
margin_slider: widgets.InputSlider
|
||||
|
||||
function_plot: PlotWidget
|
||||
error_plot: PlotWidget
|
||||
|
||||
function: Callable[[float], float]
|
||||
interpolated_function: Callable[[float], float]
|
||||
function_params: Dict[str, float]
|
||||
|
||||
x_0: float
|
||||
x_n: float
|
||||
n: int
|
||||
points: List[float]
|
||||
|
||||
margin: float
|
||||
|
||||
parsing_function: bool
|
||||
updating: bool
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.n = 2
|
||||
self.margin = 5
|
||||
self.x_0 = 0.0
|
||||
self.x_n = 1.0
|
||||
self.points = [0.5]
|
||||
self.function = None
|
||||
self.function_params = {}
|
||||
self.parsing_function = False
|
||||
|
||||
self.setup_ui()
|
||||
self.reset()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Лабораторная работа №4")
|
||||
self.setFixedSize(1400, 800)
|
||||
|
||||
self.central_widget = QWidget(self)
|
||||
self.setCentralWidget(self.central_widget)
|
||||
|
||||
layout = QHBoxLayout(self.central_widget)
|
||||
|
||||
left_row = QWidget(self.central_widget)
|
||||
left_row_layout = QFormLayout(left_row)
|
||||
right_row = QWidget(self.central_widget)
|
||||
right_row_layout = QFormLayout(right_row)
|
||||
|
||||
self.function_field = widgets.InputField(self, label_text="Функция: ")
|
||||
self.function_field.set_callback(self._function_field_changed)
|
||||
left_row_layout.addWidget(self.function_field)
|
||||
|
||||
self.function_param_fields = ParamsField(self, [])
|
||||
self.function_param_fields.set_callback(self._function_param_field_changed)
|
||||
left_row_layout.addWidget(self.function_param_fields)
|
||||
|
||||
self.x_0_field = widgets.InputField(
|
||||
self, label_text="x_0: ", lineedit_text="0.0"
|
||||
)
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
left_row_layout.addWidget(self.x_0_field)
|
||||
|
||||
self.x_n_field = widgets.InputField(
|
||||
self, label_text="x_n: ", lineedit_text="1.0"
|
||||
)
|
||||
self.x_n_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_n_field_changed]
|
||||
)
|
||||
)
|
||||
left_row_layout.addWidget(self.x_n_field)
|
||||
|
||||
self.n_display = widgets.DisplayField(self, "n: ", "")
|
||||
left_row_layout.addWidget(self.n_display)
|
||||
|
||||
self.n_slider = widgets.InputSlider(self, "", 1, 1, 20, 1, True)
|
||||
self.n_slider.set_callback(self._n_slider_changed)
|
||||
left_row_layout.addWidget(self.n_slider)
|
||||
|
||||
self.points_field = PointsField(self, 1, [0.5], 0.0, 1.0 - 0.001)
|
||||
self.points_field.set_callback(self._points_field_changed)
|
||||
left_row_layout.addWidget(self.points_field)
|
||||
|
||||
self.margin_display = widgets.DisplayField(self, "отступ (%): ", "")
|
||||
left_row_layout.addWidget(self.margin_display)
|
||||
|
||||
self.margin_slider = widgets.InputSlider(self, "", 5, 1, 500, 1)
|
||||
self.margin_slider.set_callback(self._margin_slider_changed)
|
||||
left_row_layout.addWidget(self.margin_slider)
|
||||
|
||||
reset_button = QPushButton("Сброс", self)
|
||||
reset_button.clicked.connect(self.reset)
|
||||
left_row_layout.addWidget(reset_button)
|
||||
|
||||
label1 = QLabel("График функции", self)
|
||||
right_row_layout.addWidget(label1)
|
||||
self.function_plot = PlotWidget(self)
|
||||
self.function_plot.set_pre_style(self.function_plot_pre_style)
|
||||
self.function_plot.set_post_style(self.function_plot_post_style)
|
||||
right_row_layout.addWidget(self.function_plot)
|
||||
|
||||
label2 = QLabel("График ошибки", self)
|
||||
right_row_layout.addWidget(label2)
|
||||
self.error_plot = PlotWidget(self)
|
||||
self.error_plot.set_pre_style(self.function_plot_pre_style)
|
||||
self.error_plot.set_post_style(self.function_plot_post_style)
|
||||
right_row_layout.addWidget(self.error_plot)
|
||||
|
||||
left_row.setLayout(left_row_layout)
|
||||
right_row.setLayout(right_row_layout)
|
||||
|
||||
layout.addWidget(left_row)
|
||||
layout.addWidget(right_row)
|
||||
|
||||
self.central_widget.setLayout(layout)
|
||||
|
||||
def function_plot_pre_style(self, axes: Axes):
|
||||
axes.set_xlabel("x")
|
||||
axes.set_ylabel("y")
|
||||
axes.axhline(0, color="black", linewidth=1)
|
||||
axes.axvline(0, color="black", linewidth=1)
|
||||
axes.grid(True)
|
||||
|
||||
def function_plot_post_style(self, axes: Axes):
|
||||
axes.legend(loc="upper left")
|
||||
|
||||
def reset(self):
|
||||
self.function_field.set_value(task.f_str())
|
||||
self.x_0_field.set_value(0.0)
|
||||
self.x_n_field.set_value(1.0)
|
||||
self.n_slider.set_value(5)
|
||||
self.margin_slider.set_value(300)
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
self.function_params = task.f_args()
|
||||
self.function_param_fields.set_value(self.function_params)
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
self.redraw_plots()
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def redraw_plots(self):
|
||||
length = abs(self.x_n - self.x_0)
|
||||
x_margin = length / 2 * self.margin
|
||||
x_min = self.x_0 - x_margin
|
||||
x_max = self.x_n + x_margin
|
||||
|
||||
visible_length = abs(x_max - x_min)
|
||||
plot_x_margin = visible_length / 2 * 0.025
|
||||
|
||||
steps = max(
|
||||
min(int(500 * 1 / visible_length**1.1), 5000),
|
||||
min(int(750 * visible_length**1.5), 10000),
|
||||
)
|
||||
x_values = np.linspace(x_min, x_max, steps)
|
||||
y_values = self.function(x_values)
|
||||
y_interpolated = self.interpolated_function(x_values)
|
||||
|
||||
self.function_plot.clear_plots()
|
||||
|
||||
self.function_plot.add_plot(
|
||||
lambda axes: axes.set_xlim(x_min - plot_x_margin, x_max + plot_x_margin)
|
||||
)
|
||||
|
||||
self.function_plot.add_plot(
|
||||
lambda axes: axes.plot(x_values, y_values, label="y = f(x)", color="red")
|
||||
)
|
||||
self.function_plot.add_plot(
|
||||
lambda axes: axes.plot(
|
||||
x_values,
|
||||
y_interpolated,
|
||||
label="y = L(x)",
|
||||
color="blue",
|
||||
linestyle="dashed",
|
||||
)
|
||||
)
|
||||
|
||||
points = [self.x_0, self.x_n]
|
||||
points[1:1] = self.points
|
||||
points = np.array(points)
|
||||
|
||||
self.function_plot.add_plot(
|
||||
lambda axes: axes.scatter(
|
||||
points, self.function(points), color="green", zorder=10
|
||||
)
|
||||
)
|
||||
|
||||
def label_func(axes):
|
||||
for i in range(len(points)):
|
||||
axes.text(
|
||||
points[i],
|
||||
self.function(points[i]) + 0.05,
|
||||
str(i),
|
||||
ha="center",
|
||||
va="bottom",
|
||||
color="green",
|
||||
)
|
||||
|
||||
self.function_plot.add_plot(label_func)
|
||||
|
||||
self.error_plot.clear_plots()
|
||||
|
||||
self.error_plot.add_plot(
|
||||
lambda axes: axes.set_xlim(x_min - plot_x_margin, x_max + plot_x_margin)
|
||||
)
|
||||
|
||||
self.error_plot.add_plot(
|
||||
lambda axes: axes.plot(
|
||||
x_values,
|
||||
np.abs(y_values - y_interpolated),
|
||||
color="blue",
|
||||
label="|f(x) - L(x)|",
|
||||
)
|
||||
)
|
||||
|
||||
self.function_plot.redraw()
|
||||
self.error_plot.redraw()
|
||||
|
||||
def parse_function(self, func_str: str) -> Callable[[float], float]:
|
||||
func = sympy.sympify(func_str)
|
||||
params = []
|
||||
for name in func.atoms(sympy.Symbol):
|
||||
params.append(str(name))
|
||||
|
||||
if len(params) == 0 or params.count("x") < 1:
|
||||
raise ValueError("No x in function")
|
||||
|
||||
func_lambda = sympy.lambdify(params, func, cse=True)
|
||||
|
||||
if func_lambda.__code__.co_argcount != len(params):
|
||||
raise ValueError("Bad arguments count")
|
||||
|
||||
params = [p for p in params if p != "x"]
|
||||
old_params = self.function_params
|
||||
if set(old_params.keys()) != set(params):
|
||||
self.recreate_params_field(params)
|
||||
|
||||
def f(x: float) -> float:
|
||||
return func_lambda(x=x, **self.function_params)
|
||||
|
||||
return f
|
||||
|
||||
def update_function(self):
|
||||
if self.parsing_function:
|
||||
return
|
||||
|
||||
try:
|
||||
self.recalculate_interpolated_function()
|
||||
self.redraw_plots()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def recalculate_interpolated_function(self):
|
||||
points = [self.x_0, self.x_n]
|
||||
points[1:1] = self.points
|
||||
points = np.array(points)
|
||||
|
||||
self.interpolated_function = algorithm.make_forward_interpolate_function(
|
||||
self.function, points
|
||||
)
|
||||
|
||||
# self.interpolated_function = algorithm.make_backward_interpolate_function(
|
||||
# self.function, points
|
||||
# )
|
||||
|
||||
def recreate_params_field(self, params: List[str]):
|
||||
self.function_param_fields.recreate(params)
|
||||
|
||||
def recreate_points_field(self):
|
||||
points = np.linspace(self.x_0, self.x_n, self.n + 1)
|
||||
points = [float(p) for p in points[1:-1]]
|
||||
self.points_field.recreate(
|
||||
1,
|
||||
points,
|
||||
self.x_0 + 1 / self.points_field.DIVISIONS,
|
||||
self.x_n - 1 / self.points_field.DIVISIONS,
|
||||
)
|
||||
|
||||
self.points = points
|
||||
|
||||
def _function_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
try:
|
||||
self.parsing_function = True
|
||||
self.function = self.parse_function(w.get_value())
|
||||
self.parsing_function = False
|
||||
except Exception as e:
|
||||
print(f"{e}")
|
||||
self.parsing_function = False
|
||||
return False
|
||||
|
||||
self.update_function()
|
||||
|
||||
return True
|
||||
|
||||
def _function_param_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.function_params = self.function_param_fields.get_value()
|
||||
|
||||
self.update_function()
|
||||
return True
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
value = float(w.get_value())
|
||||
if value == self.x_n:
|
||||
w.value = w.previous_value
|
||||
return False
|
||||
|
||||
self.x_0 = value
|
||||
|
||||
self.recreate_points_field()
|
||||
|
||||
self.update_function()
|
||||
return True
|
||||
|
||||
def _x_n_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
value = float(w.get_value())
|
||||
if value == self.x_0:
|
||||
w.value = w.previous_value
|
||||
return False
|
||||
|
||||
self.x_n = value
|
||||
|
||||
self.recreate_points_field()
|
||||
|
||||
self.update_function()
|
||||
return True
|
||||
|
||||
def _n_slider_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.n = int(w.get_value())
|
||||
self.n_display.set_text(str(self.n))
|
||||
|
||||
self.recreate_points_field()
|
||||
|
||||
# self.update_function()
|
||||
return True
|
||||
|
||||
def _points_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.points = w.get_value()
|
||||
|
||||
self.update_function()
|
||||
return True
|
||||
|
||||
def _margin_slider_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.margin = (float(w.get_value()) - 250) / 250.0
|
||||
self.margin_display.set_text(f"{self.margin * 100:.2f}%")
|
||||
|
||||
self.redraw_plots()
|
||||
return True
|
||||
BIN
Л5-В6/Lab_5.pdf
Normal file
BIN
Л5-В6/Lab_5.pdf
Normal file
Binary file not shown.
71
Л5-В6/Блоксхема.drawio
Normal file
71
Л5-В6/Блоксхема.drawio
Normal file
@@ -0,0 +1,71 @@
|
||||
<mxfile host="app.diagrams.net" modified="2024-05-05T16:30:15.640Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" etag="G6RgyNxifMlW16WjdJVi" version="24.2.7" type="device">
|
||||
<diagram name="Page-1" id="IsdQuIT6SBaa60DIysJm">
|
||||
<mxGraphModel dx="989" dy="509" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-1" target="9QVsrVUUbt5jk-MIr5Bh-2">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-1" value="<div>runge_kutta_4(<span style="background-color: initial;">x_0,&nbsp;</span><span style="background-color: initial;">y_0,&nbsp;</span><span style="background-color: initial;">x_n,&nbsp;</span><span style="background-color: initial;">h,&nbsp;</span><span style="background-color: initial;">f</span><span style="background-color: initial;">)</span></div>" style="rounded=1;whiteSpace=wrap;html=1;arcSize=50;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="265" y="180" width="260" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-2" target="9QVsrVUUbt5jk-MIr5Bh-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-2" value="<div>result = []</div>x_i = x_0<div>y_i = y_0</div><div>result[x_i] = y_i</div>" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="335" y="230" width="120" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-4" target="9QVsrVUUbt5jk-MIr5Bh-6">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-4" target="9QVsrVUUbt5jk-MIr5Bh-11">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="560" y="380" />
|
||||
<mxPoint x="560" y="640" />
|
||||
<mxPoint x="395" y="640" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-4" value="x_i &lt; x_n + h / 2" style="rhombus;whiteSpace=wrap;html=1;spacingLeft=0;spacingRight=0;perimeterSpacing=0;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="350" width="150" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-6" target="9QVsrVUUbt5jk-MIr5Bh-8">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-6" value="k_1 = f(x_i, y_i)<div>k_2 = f(x_i + h / 2, y_i + h / 2 * k_1)<br></div><div>k_3 = f(x_i + h / 2, y_i + h / 2 * k_2)<br></div><div>k_4 = f(x_i + h, y_i + h * k_3)<br></div>" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="250" y="430" width="290" height="90" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-8">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="395" y="330" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="395" y="630" />
|
||||
<mxPoint x="230" y="630" />
|
||||
<mxPoint x="230" y="330" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-8" value="y_i + h / 6 * (k_1 + 2 * k_2 + 2 * k_3 + k_4)<div>x_i += h</div><div>&nbsp;result[x_i] = y_i<br></div>" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="300" y="540" width="190" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontFamily=Cascadia Code;" edge="1" parent="1" source="9QVsrVUUbt5jk-MIr5Bh-11" target="9QVsrVUUbt5jk-MIr5Bh-13">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-11" value="return result" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="340" y="660" width="110" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-13" value="Конец" style="rounded=1;whiteSpace=wrap;html=1;arcSize=50;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="352.5" y="710" width="85" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-15" value="Да" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="391" y="403" width="40" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="9QVsrVUUbt5jk-MIr5Bh-16" value="Нет" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontFamily=Cascadia Code;" vertex="1" parent="1">
|
||||
<mxGeometry x="470" y="355" width="40" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
Л5-В6/Отчет.docx
Normal file
BIN
Л5-В6/Отчет.docx
Normal file
Binary file not shown.
2
Л5-В6/Программа/.gitignore
vendored
Normal file
2
Л5-В6/Программа/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.venv
|
||||
*/__pycache__
|
||||
5
Л5-В6/Программа/requirements.txt
Normal file
5
Л5-В6/Программа/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
sympy
|
||||
numpy
|
||||
scipy
|
||||
pyqt6
|
||||
matplotlib
|
||||
45
Л5-В6/Программа/src/algorithm.py
Normal file
45
Л5-В6/Программа/src/algorithm.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import numpy as np
|
||||
import typing
|
||||
|
||||
|
||||
def runge_kutta_4(
|
||||
x_0: float,
|
||||
y_0: typing.List[float],
|
||||
x_n: float,
|
||||
h: float,
|
||||
f: typing.List[typing.Callable[[float, typing.List[float]], float]],
|
||||
) -> typing.Dict[float, np.ndarray]:
|
||||
count = len(y_0)
|
||||
|
||||
if count != len(f):
|
||||
raise ValueError("len(y_0) != len(f)")
|
||||
|
||||
x_i = x_0
|
||||
y_i = np.array(y_0, dtype=np.float64)
|
||||
result = {x_i: y_i}
|
||||
|
||||
while x_i < x_n + h / 2:
|
||||
y_i = calculate_y(x_i, y_i, h, f)
|
||||
x_i += h
|
||||
|
||||
result[x_i] = y_i
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def calculate_y(x_i, y_i, h, f):
|
||||
k_1 = np.array([f[i](x_i, y_i) for i in range(len(y_i))], dtype=np.float64)
|
||||
k_2 = np.array(
|
||||
[f[i](x_i + h / 2, y_i + h * k_1 / 2) for i in range(len(y_i))],
|
||||
dtype=np.float64,
|
||||
)
|
||||
k_3 = np.array(
|
||||
[f[i](x_i + h / 2, y_i + h * k_2 / 2) for i in range(len(y_i))],
|
||||
dtype=np.float64,
|
||||
)
|
||||
k_4 = np.array(
|
||||
[f[i](x_i + h, y_i + h * k_3) for i in range(len(y_i))], dtype=np.float64
|
||||
)
|
||||
|
||||
y_i = y_i + h / 6 * (k_1 + 2 * k_2 + 2 * k_3 + k_4)
|
||||
return y_i
|
||||
17
Л5-В6/Программа/src/main.py
Normal file
17
Л5-В6/Программа/src/main.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import sys
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from PyQt6.QtGui import QFont
|
||||
|
||||
import window
|
||||
import widgets
|
||||
|
||||
if __name__ == "__main__":
|
||||
widgets.setup_dark_theme()
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
font = QFont("Cascadia Code", 14)
|
||||
app.setFont(font)
|
||||
window = window.MainWindow()
|
||||
window.show()
|
||||
|
||||
sys.exit(app.exec())
|
||||
68
Л5-В6/Программа/src/task.py
Normal file
68
Л5-В6/Программа/src/task.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import sympy
|
||||
import typing
|
||||
|
||||
f_str = "y * ln(y) / sin(x)"
|
||||
f_expr = sympy.sympify(f_str)
|
||||
f_l = sympy.lambdify("x, y", f_expr, cse=True)
|
||||
|
||||
|
||||
def task1_f(x: float, y: typing.List[float]) -> float:
|
||||
return f_l(x, *y)
|
||||
|
||||
|
||||
def task1_f_display() -> str:
|
||||
return f_str
|
||||
|
||||
|
||||
f_1_str = "sin(x**2 + y_2**2)"
|
||||
f_1_expr = sympy.sympify(f_1_str)
|
||||
f_1_l = sympy.lambdify("x, y_1, y_2", f_1_expr, cse=True)
|
||||
|
||||
f_2_str = "cos(x * y_1)"
|
||||
f_2_expr = sympy.sympify(f_2_str)
|
||||
f_2_l = sympy.lambdify("x, y_1, y_2", f_2_expr, cse=True)
|
||||
|
||||
|
||||
def task2_f_1(x: float, y: typing.List[float]) -> float:
|
||||
return f_1_l(x, *y)
|
||||
|
||||
|
||||
def task2_f_2(x: float, y: typing.List[float]) -> float:
|
||||
return f_2_l(x, *y)
|
||||
|
||||
|
||||
def task2_f_1_display() -> str:
|
||||
return f_1_str
|
||||
|
||||
|
||||
def task2_f_2_display() -> str:
|
||||
return f_2_str
|
||||
|
||||
|
||||
def task3_f_display() -> str:
|
||||
return "y'' + a1 * y' + a2 * y"
|
||||
|
||||
|
||||
x, a1, a2 = sympy.symbols("x a1 a2", real=True)
|
||||
y = sympy.symbols("y", cls=sympy.Function)(x)
|
||||
yp = y.diff(x)
|
||||
ypp = y.diff(x, 2)
|
||||
|
||||
f_xy = ypp + a1 * yp + a2 * y
|
||||
|
||||
task3_generic_solution = sympy.dsolve(f_xy, y).rhs
|
||||
|
||||
|
||||
def task3_solution_display() -> str:
|
||||
return str(task3_generic_solution)
|
||||
|
||||
|
||||
def task3_make_f_sympy(a1_, a2_, x_0_, y_0_, y_d_0_):
|
||||
solution = task3_generic_solution.subs({a1: a1_, a2: a2_})
|
||||
cnd1 = sympy.Eq(solution.subs(x, x_0_), y_0_)
|
||||
cnd2 = sympy.Eq(solution.diff(x).subs(x, x_0_), y_d_0_)
|
||||
|
||||
C1, C2 = sympy.symbols("C1 C2")
|
||||
C1C2_sl = sympy.solve([cnd1, cnd2], (C1, C2))
|
||||
|
||||
return sympy.simplify(solution.subs(C1C2_sl))
|
||||
330
Л5-В6/Программа/src/widgets.py
Normal file
330
Л5-В6/Программа/src/widgets.py
Normal file
@@ -0,0 +1,330 @@
|
||||
from io import BytesIO
|
||||
from PyQt6.QtWidgets import (
|
||||
QLabel,
|
||||
QWidget,
|
||||
QLineEdit,
|
||||
QHBoxLayout,
|
||||
QSlider,
|
||||
QVBoxLayout,
|
||||
)
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtSvgWidgets import QSvgWidget
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.figure
|
||||
import matplotlib.axes
|
||||
from matplotlib.backends.backend_qtagg import (
|
||||
FigureCanvasQTAgg as FigureCanvas,
|
||||
NavigationToolbar2QT as NavigationToolbar,
|
||||
)
|
||||
|
||||
import sympy
|
||||
|
||||
import abc
|
||||
import typing
|
||||
|
||||
import matplotlib.pyplot
|
||||
|
||||
|
||||
def setup_dark_theme():
|
||||
matplotlib.pyplot.style.use(
|
||||
{
|
||||
"figure.facecolor": "#1e1e1e",
|
||||
"savefig.facecolor": "#1e1e1e",
|
||||
"text.color": "white",
|
||||
"xtick.color": "white",
|
||||
"ytick.color": "white",
|
||||
"legend.facecolor": "#1e1e1e",
|
||||
"legend.edgecolor": "white",
|
||||
"legend.labelcolor": "white",
|
||||
"grid.color": "white",
|
||||
"figure.edgecolor": "white",
|
||||
"axes.facecolor": "#1e1e1e",
|
||||
"axes.axisbelow": "true",
|
||||
"axes.edgecolor": "white",
|
||||
"axes.labelcolor": "white",
|
||||
"axes.grid": True,
|
||||
"axes.spines.left": False,
|
||||
"axes.spines.right": False,
|
||||
"axes.spines.top": False,
|
||||
"axes.spines.bottom": False,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class DisplayField(QWidget):
|
||||
label: str
|
||||
|
||||
def __init__(self, parent, label="", text="", wrap=False):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.label = label
|
||||
self.text = QLabel(f"{self.label}{text}")
|
||||
self.text.setWordWrap(wrap)
|
||||
self.text.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
|
||||
self._layout.addWidget(self.text)
|
||||
|
||||
def set_visible(self, visible):
|
||||
self.text.setVisible(visible)
|
||||
|
||||
def set_text(self, text):
|
||||
self.text.setText(f"{self.label}{text}")
|
||||
|
||||
|
||||
class InputWidget(QWidget):
|
||||
value: typing.Any
|
||||
previous_value: typing.Any
|
||||
callback: typing.Callable[[typing.Self], bool] | None = None
|
||||
|
||||
def init(self):
|
||||
self.value = self._get_value_internal()
|
||||
self.previous_value = self.value
|
||||
self._set_callback_internal(self._on_change_internal)
|
||||
|
||||
def get_value(self) -> typing.Any:
|
||||
return self.value
|
||||
|
||||
def get_previous_value(self) -> typing.Any:
|
||||
return self.previous_value
|
||||
|
||||
def set_value(self, value: typing.Any):
|
||||
self._set_value_internal(value)
|
||||
self._set_value_no_sync(value)
|
||||
|
||||
def set_callback(self, callback: typing.Callable[[typing.Self], bool]):
|
||||
self.callback = callback
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_value_internal(self) -> typing.Any:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_value_internal(self, value: typing.Any):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_callback_internal(self, callback: typing.Callable[[], None]):
|
||||
pass
|
||||
|
||||
def _set_value_no_sync(self, value: typing.Any):
|
||||
self.previous_value = self.value
|
||||
self.value = value
|
||||
|
||||
def _on_change_internal(self):
|
||||
self._set_value_no_sync(self._get_value_internal())
|
||||
|
||||
if self.callback:
|
||||
self.callback(self)
|
||||
|
||||
|
||||
def make_callback_chain(
|
||||
callbacks: typing.List[typing.Callable[[InputWidget], bool]]
|
||||
) -> typing.Callable[[InputWidget], bool]:
|
||||
def callback(widget: InputWidget):
|
||||
for callback in callbacks:
|
||||
if not callback(widget):
|
||||
break
|
||||
return True
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def make_min_max_callback(
|
||||
min: typing.Any, max: typing.Any
|
||||
) -> typing.Callable[[InputWidget], bool]:
|
||||
def callback(widget: InputWidget):
|
||||
value = widget.get_value()
|
||||
|
||||
if type(min)(value) < min:
|
||||
widget.set_value(min)
|
||||
if type(max)(value) > max:
|
||||
widget.set_value(max)
|
||||
|
||||
return True
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def float_check_callback(widget: InputWidget):
|
||||
try:
|
||||
internal_value = widget._get_value_internal()
|
||||
if not (internal_value == "" or internal_value == "-" or internal_value == "."):
|
||||
_ = float(widget.get_value())
|
||||
return True
|
||||
else:
|
||||
widget.value = widget.previous_value
|
||||
return False
|
||||
except:
|
||||
widget.set_value(widget.previous_value)
|
||||
return False
|
||||
|
||||
|
||||
def int_check_callback(widget: InputWidget):
|
||||
try:
|
||||
internal_value = widget._get_value_internal()
|
||||
if not (internal_value == "" or internal_value == "-"):
|
||||
_ = int(widget.get_value())
|
||||
return True
|
||||
else:
|
||||
widget.value = widget.previous_value
|
||||
return False
|
||||
except:
|
||||
widget.set_value(widget.previous_value)
|
||||
return False
|
||||
|
||||
|
||||
class InputField(InputWidget):
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
placeholder_text="",
|
||||
lineedit_text="",
|
||||
label_position="left",
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label = QLabel(label_text)
|
||||
self.lineedit = QLineEdit(lineedit_text)
|
||||
self.lineedit.setPlaceholderText(placeholder_text)
|
||||
self.lineedit.setMinimumWidth(50)
|
||||
|
||||
if label_position == "left":
|
||||
self._layout.addWidget(self.label)
|
||||
self._layout.addWidget(self.lineedit)
|
||||
elif label_position == "right":
|
||||
self._layout.addWidget(self.lineedit)
|
||||
self._layout.addWidget(self.label)
|
||||
elif label_position == "none":
|
||||
self._layout.addWidget(self.lineedit)
|
||||
|
||||
super().init()
|
||||
super().set_value(lineedit_text)
|
||||
|
||||
def _get_value_internal(self):
|
||||
return self.lineedit.text()
|
||||
|
||||
def _set_value_internal(self, value):
|
||||
self.lineedit.setText(str(value))
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.lineedit.textChanged.connect(callback)
|
||||
|
||||
|
||||
class MathInputField(InputField):
|
||||
internal_value: typing.Any
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
placeholder_text="",
|
||||
lineedit_value="0.0",
|
||||
label_position="left",
|
||||
):
|
||||
super().__init__(
|
||||
parent, label_text, placeholder_text, lineedit_value, label_position
|
||||
)
|
||||
|
||||
def _get_value_internal(self):
|
||||
try:
|
||||
self.internal_value = str(
|
||||
float(
|
||||
sympy.sympify(self.lineedit.text()).evalf(
|
||||
subs={
|
||||
"e": sympy.E,
|
||||
"Pi": sympy.pi,
|
||||
"PI": sympy.pi,
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
return self.internal_value
|
||||
|
||||
|
||||
class InputSlider(InputWidget):
|
||||
slider: QSlider
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
value=0,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
step=1,
|
||||
ticks=False,
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label = QLabel(label_text)
|
||||
self.slider = QSlider(Qt.Orientation.Horizontal)
|
||||
if ticks:
|
||||
self.slider.setTickPosition(QSlider.TickPosition.TicksBelow)
|
||||
self.slider.setTickInterval(step)
|
||||
|
||||
self.slider.setMinimum(min_value)
|
||||
self.slider.setMaximum(max_value)
|
||||
self.slider.setValue(value)
|
||||
self.slider.setSingleStep(step)
|
||||
|
||||
self._layout.addWidget(self.label)
|
||||
self._layout.addWidget(self.slider)
|
||||
|
||||
super().init()
|
||||
super().set_value(value)
|
||||
|
||||
def _get_value_internal(self):
|
||||
return self.slider.value()
|
||||
|
||||
def _set_value_internal(self, value):
|
||||
self.slider.setValue(value)
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.slider.valueChanged.connect(callback)
|
||||
|
||||
|
||||
class PlotWidget(QWidget):
|
||||
figure: matplotlib.figure.Figure
|
||||
|
||||
toolbar: NavigationToolbar = None
|
||||
canvas: FigureCanvas
|
||||
|
||||
def __init__(self, parent, figure: matplotlib.figure.Figure, toolbar: bool = False):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QVBoxLayout(self)
|
||||
|
||||
self.figure = figure
|
||||
self.canvas = FigureCanvas(self.figure)
|
||||
self.canvas.setStyleSheet("background-color:transparent;")
|
||||
|
||||
if toolbar:
|
||||
self.toolbar = NavigationToolbar(self.canvas, self)
|
||||
self._layout.addWidget(self.toolbar)
|
||||
|
||||
self._layout.addWidget(self.canvas)
|
||||
|
||||
def set_figure(self, figure: matplotlib.figure.Figure):
|
||||
self.figure = figure
|
||||
self.canvas = FigureCanvas(self.figure)
|
||||
self._layout.addWidget(self.canvas)
|
||||
|
||||
self.redraw()
|
||||
|
||||
def get_figure(self) -> matplotlib.figure.Figure:
|
||||
return self.figure
|
||||
|
||||
def redraw(self):
|
||||
self.canvas.draw()
|
||||
536
Л5-В6/Программа/src/window.py
Normal file
536
Л5-В6/Программа/src/window.py
Normal file
@@ -0,0 +1,536 @@
|
||||
from PyQt6.QtWidgets import (
|
||||
QMainWindow,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QFormLayout,
|
||||
QWidget,
|
||||
QHBoxLayout,
|
||||
QTabWidget,
|
||||
)
|
||||
from PyQt6 import QtWidgets
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
matplotlib.use("QtAgg")
|
||||
|
||||
import numpy as np
|
||||
import sympy
|
||||
import typing
|
||||
|
||||
import widgets
|
||||
import task
|
||||
import algorithm
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Лабораторная работа №5")
|
||||
self.setFixedSize(800, 900)
|
||||
|
||||
central_widget = QTabWidget(self)
|
||||
central_widget.setTabsClosable(False)
|
||||
central_widget.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
central_widget.addTab(Tab1(central_widget), "Задание 1")
|
||||
central_widget.addTab(Tab2(central_widget), "Задание 2")
|
||||
central_widget.addTab(Tab3(central_widget), "Задание 3")
|
||||
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
|
||||
class Tab1(QWidget):
|
||||
initialized = False
|
||||
|
||||
x_0_field: widgets.InputField
|
||||
y_0_field: widgets.InputField
|
||||
x_n_field: widgets.InputField
|
||||
|
||||
h_display: widgets.DisplayField
|
||||
h_field: widgets.InputSlider
|
||||
|
||||
x_0: float
|
||||
y_0: float
|
||||
x_n: float
|
||||
h: float
|
||||
|
||||
solution_plot: widgets.PlotWidget
|
||||
solution_figure: plt.Figure
|
||||
solution_axes: plt.Axes
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self.reset()
|
||||
self.initialized = True
|
||||
self.update_solution()
|
||||
|
||||
def _initialize(self):
|
||||
self._layout = QFormLayout(self)
|
||||
|
||||
self._function_display = widgets.DisplayField(self, "f(x, y) = ", "")
|
||||
self._layout.addWidget(self._function_display)
|
||||
|
||||
self.x_0_field = widgets.MathInputField(self, label_text="x_0: ")
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
|
||||
self._layout.addWidget(self.x_0_field)
|
||||
|
||||
self.y_0_field = widgets.MathInputField(self, label_text="y_0: ")
|
||||
self.y_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_0_field)
|
||||
|
||||
self.x_n_field = widgets.MathInputField(self, label_text="x_n: ")
|
||||
self.x_n_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_n_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.x_n_field)
|
||||
|
||||
self.h_display = widgets.DisplayField(self, "h: ", "")
|
||||
self._layout.addWidget(self.h_display)
|
||||
|
||||
self.h_field = widgets.InputSlider(
|
||||
self, label_text="", value=0, min_value=1, max_value=100, step=1
|
||||
)
|
||||
self.h_field.set_callback(self._h_field_changed)
|
||||
self._layout.addWidget(self.h_field)
|
||||
|
||||
self.solution_figure = plt.figure()
|
||||
self.solution_figure.patch.set_facecolor("none")
|
||||
self.solution_axes = self.solution_figure.add_subplot(111)
|
||||
self.solution_figure.tight_layout(pad=3.0)
|
||||
|
||||
self.solution_plot = widgets.PlotWidget(self, self.solution_figure, True)
|
||||
self._layout.addWidget(self.solution_plot)
|
||||
|
||||
def reset(self):
|
||||
self._function_display.set_text(task.task1_f_display())
|
||||
self.x_0_field.set_value("pi / 2")
|
||||
self.y_0_field.set_value("e")
|
||||
self.x_n_field.set_value("pi")
|
||||
self.h_field.set_value(5)
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_0 = float(w.get_value())
|
||||
|
||||
return True
|
||||
|
||||
def _x_n_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_n = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _h_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.h = float(w.get_value()) / 100.0
|
||||
self.h_display.set_text(str(self.h))
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def update_solution(self):
|
||||
if not self.initialized:
|
||||
return
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0, [self.y_0], self.x_n, self.h, [task.task1_f]
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
|
||||
self.solution_axes.clear()
|
||||
self.solution_axes.set_xlabel("x")
|
||||
self.solution_axes.set_ylabel("y")
|
||||
self.solution_axes.grid(True)
|
||||
|
||||
self.solution_axes.plot(x, y, label="y(x)")
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0, [self.y_0], self.x_n, self.h / 2, [task.task1_f]
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
|
||||
self.solution_axes.plot(x, y, label="y(x) (h/2)")
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0, [self.y_0], self.x_n, self.h * 2, [task.task1_f]
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
|
||||
self.solution_axes.plot(x, y, label="y(x) (h*2)")
|
||||
|
||||
self.solution_axes.set_yscale("log")
|
||||
|
||||
self.solution_axes.legend(loc="upper left")
|
||||
self.solution_plot.redraw()
|
||||
|
||||
|
||||
class Tab2(QWidget):
|
||||
initialized = False
|
||||
|
||||
_function1_display: widgets.DisplayField
|
||||
_function2_display: widgets.DisplayField
|
||||
|
||||
x_0_field: widgets.InputField
|
||||
y_10_field: widgets.InputField
|
||||
y_20_field: widgets.InputField
|
||||
x_n_field: widgets.InputField
|
||||
|
||||
h_display: widgets.DisplayField
|
||||
h_field: widgets.InputSlider
|
||||
|
||||
x_0: float
|
||||
y_10: float
|
||||
y_20: float
|
||||
x_n: float
|
||||
h: float
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self.reset()
|
||||
self.initialized = True
|
||||
self.update_solution()
|
||||
|
||||
def _initialize(self):
|
||||
self._layout = QFormLayout(self)
|
||||
|
||||
self._function1_display = widgets.DisplayField(self, "f1(x, y) = ", "")
|
||||
self._layout.addWidget(self._function1_display)
|
||||
|
||||
self._function2_display = widgets.DisplayField(self, "f2(x, y) = ", "")
|
||||
self._layout.addWidget(self._function2_display)
|
||||
|
||||
self.x_0_field = widgets.MathInputField(self, label_text="x_0: ")
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
|
||||
self._layout.addWidget(self.x_0_field)
|
||||
|
||||
self.y_10_field = widgets.MathInputField(self, label_text="y_10: ")
|
||||
self.y_10_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_10_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_10_field)
|
||||
|
||||
self.y_20_field = widgets.MathInputField(self, label_text="y_20: ")
|
||||
self.y_20_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_20_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_20_field)
|
||||
|
||||
self.x_n_field = widgets.MathInputField(self, label_text="x_n: ")
|
||||
self.x_n_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_n_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.x_n_field)
|
||||
|
||||
self.h_display = widgets.DisplayField(self, "h: ", "")
|
||||
self._layout.addWidget(self.h_display)
|
||||
|
||||
self.h_field = widgets.InputSlider(
|
||||
self, label_text="", value=0, min_value=1, max_value=100, step=1
|
||||
)
|
||||
self.h_field.set_callback(self._h_field_changed)
|
||||
self._layout.addWidget(self.h_field)
|
||||
|
||||
self.solution_figure = plt.figure()
|
||||
self.solution_figure.patch.set_facecolor("none")
|
||||
self.solution_axes = self.solution_figure.add_subplot(111)
|
||||
self.solution_figure.tight_layout(pad=3.0)
|
||||
|
||||
self.solution_plot = widgets.PlotWidget(self, self.solution_figure)
|
||||
self._layout.addWidget(self.solution_plot)
|
||||
|
||||
def reset(self):
|
||||
self._function1_display.set_text(task.task2_f_1_display())
|
||||
self._function2_display.set_text(task.task2_f_2_display())
|
||||
self.x_0_field.set_value("0")
|
||||
self.y_10_field.set_value("0")
|
||||
self.y_20_field.set_value("0")
|
||||
self.x_n_field.set_value("4")
|
||||
self.h_field.set_value(5)
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_10_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_10 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_20_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_20 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _x_n_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_n = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _h_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.h = float(w.get_value()) / 100.0
|
||||
self.h_display.set_text(str(self.h))
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def update_solution(self):
|
||||
if not self.initialized:
|
||||
return
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0,
|
||||
[self.y_10, self.y_20],
|
||||
self.x_n,
|
||||
self.h,
|
||||
[task.task2_f_1, task.task2_f_2],
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y1 = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
y2 = np.array(list(map(lambda x: x[1], solution.values())))
|
||||
|
||||
self.solution_axes.cla()
|
||||
self.solution_axes.plot(x, y1, label="y_1")
|
||||
self.solution_axes.plot(x, y2, label="y_2")
|
||||
|
||||
self.solution_axes.legend()
|
||||
|
||||
self.solution_plot.redraw()
|
||||
|
||||
|
||||
class Tab3(QWidget):
|
||||
initialized = False
|
||||
|
||||
a1_field: widgets.InputField
|
||||
a2_field: widgets.InputField
|
||||
x_0_field: widgets.InputField
|
||||
y_0_field: widgets.InputField
|
||||
y_d_0_field: widgets.InputField
|
||||
|
||||
generic_solution_display: widgets.DisplayField
|
||||
solution_display: widgets.DisplayField
|
||||
|
||||
a1: float
|
||||
a2: float
|
||||
x_0: float
|
||||
y_0: float
|
||||
y_d_0: float
|
||||
|
||||
solution_plot: widgets.PlotWidget
|
||||
solution_figure: plt.Figure
|
||||
solution_axes: plt.Axes
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self.reset()
|
||||
self.initialized = True
|
||||
self.update_solution()
|
||||
|
||||
def _initialize(self):
|
||||
self._layout = QFormLayout(self)
|
||||
|
||||
self._function_display = widgets.DisplayField(
|
||||
self, task.task3_f_display(), " = 0"
|
||||
)
|
||||
self._layout.addWidget(self._function_display)
|
||||
|
||||
self.a1_field = widgets.InputField(
|
||||
self,
|
||||
"a1: ",
|
||||
)
|
||||
self.a1_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._a1_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.a1_field)
|
||||
|
||||
self.a2_field = widgets.InputField(
|
||||
self,
|
||||
"a2: ",
|
||||
)
|
||||
self.a2_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._a2_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.a2_field)
|
||||
|
||||
self.x_0_field = widgets.InputField(
|
||||
self,
|
||||
"x_0: ",
|
||||
)
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.x_0_field)
|
||||
|
||||
self.y_0_field = widgets.InputField(
|
||||
self,
|
||||
"y_0: ",
|
||||
)
|
||||
self.y_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_0_field)
|
||||
|
||||
self.y_d_0_field = widgets.InputField(
|
||||
self,
|
||||
"y_d_0: ",
|
||||
)
|
||||
self.y_d_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_d_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_d_0_field)
|
||||
|
||||
self.generic_solution_display = widgets.DisplayField(
|
||||
self, "Общее решение: ", wrap=True
|
||||
)
|
||||
self._layout.addWidget(self.generic_solution_display)
|
||||
|
||||
self.solution_display = widgets.DisplayField(self, "Решение: ", wrap=True)
|
||||
self._layout.addWidget(self.solution_display)
|
||||
|
||||
self.solution_figure = plt.figure()
|
||||
self.solution_figure.patch.set_facecolor("none")
|
||||
self.solution_axes = self.solution_figure.add_subplot(111)
|
||||
self.solution_figure.tight_layout(pad=3.0)
|
||||
|
||||
self.solution_plot = widgets.PlotWidget(self, self.solution_figure, True)
|
||||
self._layout.addWidget(self.solution_plot)
|
||||
|
||||
def reset(self):
|
||||
self.a1_field.set_value("-4.0")
|
||||
self.a2_field.set_value("4.0")
|
||||
self.x_0_field.set_value("0.3")
|
||||
self.y_0_field.set_value("1.0")
|
||||
self.y_d_0_field.set_value("1.0")
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _a1_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.a1 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _a2_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.a2 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_d_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_d_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def update_solution(self):
|
||||
if not self.initialized:
|
||||
return
|
||||
|
||||
self.generic_solution_display.set_text(
|
||||
"\n" + str(task.task3_solution_display())
|
||||
)
|
||||
solution = task.task3_make_f_sympy(
|
||||
self.a1, self.a2, self.x_0, self.y_0, self.y_d_0
|
||||
)
|
||||
self.solution_display.set_text(str(solution))
|
||||
|
||||
try:
|
||||
f = sympy.lambdify("x", solution, cse=True)
|
||||
x = np.linspace(self.x_0, self.x_0 + 5, 100)
|
||||
y = f(x)
|
||||
|
||||
self.solution_axes.clear()
|
||||
self.solution_axes.grid(True)
|
||||
self.solution_axes.plot(x, y, label="y")
|
||||
self.solution_axes.legend()
|
||||
|
||||
self.solution_plot.redraw()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
Reference in New Issue
Block a user