= SALOME = [[PageOutline]] == Namestitev Za namestitev SALOME-a shranite Windows paket SALOME-8.3.0-WIN64.exe ki se nahaja na spodnji povezavi: http://www.salome-platform.org/downloads/current-version Odpakirajte Windows paket SALOME-8.3.0-WIN64.exe kot administrator (desni klik --> Run as Administrator) v C:\Program Files. Program SALOME se zažene s skriptom run_salome.bat ki se nahaja v C:\Program Files\SALOME-8.3.0-WIN64\WORK. == Struktura programa v programskem jeziku Python Uporabljali bomo [htdocs:doc8.3.0/index.html navodila za Salome 8.3]. Za programiranje v geometrijskem modulu si poglejte [htdocs:doc8.3.0/gui/GEOM/python_interface.html Geometry module Python Interface]. Primeri iz teh navodil se nahajajo v imeniku {{{C:\Program Files\SALOME-8.3.0-WIN64\MODULES\GEOM\RELEASE\GEOM_INSTALL\share\doc\salome\examples\GEOM}}}. V glavi programa je potrebno inicializirati knjižnico SALOME, ki nam omogoči preprosto manipulacijo predstavljenega objekta (glava dokumenta): {{{ #!python import salome salome.salome_init() }}} Nato je potrebno uvoziti python-ov modul **GEOM**, ki vsebuje funkcije, s katerimi izdelujemo geometrijske modele. {{{ #!python import GEOM from salome.geom import geomBuilder geompy = geomBuilder.New(salome.myStudy) }}} === Osnovni program - Izdelava skatle Funkcija [http://docs.salome-platform.org/5/geom/user/geompy_doc/group__l3__3d__primitives.html#ga0ec4f2fdbec2bfbbda8d2acfe5d73164 geompy.MakeBox()] sprejme koordinate dveh tock, ki lezita na diagonali. {{{ #!python import salome import GEOM from salome.geom import geomBuilder geompy = geomBuilder.New(salome.myStudy) box = geompy.MakeBox(10.0,10.0,10.0, 20, 20, 20) # Dodamo novo spremenljivko v studijo geompy.addToStudy(box, 'Skatla') salome.sg.updateObjBrowser(True) }}} === Osnovni program - Primer izdelave izvleka v prostor (Extrusion) Postopek modeliranja v komercialnih modelirnikih (SolidWorks, Creo, Catia, NX, ProEngineer, Inventor,...) je sledeči: * izbira ravnine, na katero se nariše skica * izris oblike skice (kvadrat) * definiranje skice (dimenzije, geometrijske relacije, pozicija v prostoru) * izbira ustrezne značilke za izdelavo 3D objekta (Izvlek- Extrude) * določitev parametrov izvleka (smer, dolžina izvleka) * model je zmodeliran V okolju Salome poteka izris objekta nekoliko drugače. Razložen je preprosti postopek izdelave kocke, ki poteka v večih korakih. Pri izdelavi je potrebno manualno določiti vse parametre, ki jih komercialni modelirnik običajno določi namesto nas. '''Korak 1''': Uvoz knjiznic Z uvozom knjiznic je omogocena uporaba vseh funkcij, ki jih potrebujemo za izdelavo kocke. {{{ #!python import salome salome.salome_init() import GEOM from salome.geom import geomBuilder geompy = geomBuilder.New(salome.myStudy) gg = salome.ImportComponentGUI("GEOM") }}} '''Korak 2''': Izdelava tock V prostoru je potrebno definirati tocke. Definiramo jih s funkcijo [http://docs.salome-platform.org/5/geom/user/geompy_doc/group__l3__basic__go.html#gaff61924656b186b1bbed2d8acbaeaea2 geompy.MakeVertex()]. [[Image(pointsSalomePrismExample.png, 300, right)]] {{{ #!python p1 = geompy.MakeVertex( 0., 0., 0.) p2 = geompy.MakeVertex( 100., 0., 0.) p3 = geompy.MakeVertex( 100., 100., 0.) p4 = geompy.MakeVertex( 0., 100., 0.) }}} Za prikaz objektov v modulu Geometry v okolju Salome uporabimo naslednje ukaze {{{ #!python # Dodamo nove tocke v studijo geompy.addToStudy(p1,"point1") geompy.addToStudy(p2,"point2") geompy.addToStudy(p3,"point3") geompy.addToStudy(p4,"point4") salome.sg.updateObjBrowser(True) }}} '''Korak 3''': Izdelava krivulje, ki povezuje vse tocke Z ukazom [http://docs.salome-platform.org/5/geom/user/geompy_doc/group__l4__curves.html#ga599e70c2624244a5b6ef742568d1b925 geompy.MakePolyline()] izdelamo krivuljo, ki je sestavljena iz stirih daljic. [[Image(polylineSalomePrismExample.png, 300, right)]] {{{ #!python polyline = geompy.MakePolyline([p1, p2, p3, p4, p1]) # Dodamo novo krivuljo v studijo geompy.addToStudy(polyline,"polyline") # Posodobimo drevesno strukturo salome.sg.updateObjBrowser(True) }}} [[BR]][[BR]][[BR]][[BR]][[BR]][[BR]] '''Korak 4''': Izdelava povrsine. Sedaj je potrebno izdelati povrsino, ki bo omejena s prej izdelano krivuljo. To storimo z ukazom [http://docs.salome-platform.org/5/geom/user/geompy_doc/group__l3__advanced.html#ga808d5981a10de3c662ab9781c959b738 geompy.MakeFace()]. Ta funkcija sprejme zaprto krivuljo (**polyline**). Nato je spremenljivko potrebno dodati v studijo modula '''GEOM''' z ukazom [http://docs.salome-platform.org/7/gui/GEOM/geompy_doc/group__l1__publish__data.html#gab203ccf5cab3daaf83bdf5d640e0c9f6 addToStudy()]. [[Image(faceSalomePrismExample.png, 300, right)]] {{{ #!python face = geompy.MakeFace(polyline, True) # Dodamo novo povrsino v studijo geompy.addToStudy(face, "face") # Posodobimo drevesno strukturo salome.sg.updateObjBrowser(True) }}} Drevesno strukturo posodobimo, ce zelimo prikazati nove geometrijske elemente v modelnem oknu. [[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]] '''Korak 5''': Izvlek povrsine Izvlek povrsine izvedemo z ukazom [http://docs.salome-platform.org/5/geom/user/geompy_doc/group__l3__complex.html#ga9cec1d70a613916e8bb47e7b45edc3c4 geompy.MakePrism()]. Omenjeni ukaz sprejme tri argumente(povrsina, tocka1, tocka2), pri cemer sta tocka1 in tocka2 tocki, ki definirata vektor. [[Image(prismSalomePrismExample.png, 300, right)]] {{{ #!python p5 = geompy.MakeVertex(0.,0.,100.) prism1 = geompy.MakePrism(face, p1, p5) # Dodamo nov izvlek v studijo geompy.addToStudy(prism1, "prism1") # Posodobimo drevesno strukturo salome.sg.updateObjBrowser(True) }}} Celoten postopek izdelave izvleka je v priponki (''salomePrismExample.py''). [[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]][[BR]] === Primer izdelave panela v fuzijskem reaktorju ITER V tem primeru bo prikazana izdelava krivulj v 3D prostoru in izvlek krivulj, s katerim generiramo površino v prostoru. Končni cilj je izdelava panela, prikazanega na sliki. V datoteki data.txt imamo podane koordinate točk (x, y in z), iz katerih bomo generirali krivujo. To krivuljo bomo nato izvlekli v prostor s pomočjo daljic, ki so prav tako definirana s točkami (xe, ye in ze). Podatki so podani v formatu [[Image(modelSalomePanelExample.png, 300, right)]] {{{ x1 y1 z1 x2 y2 z2 x3 y3 z3 ... xn yn zn }}} '''Korak 1''': Uvoz modulov Najprej je potrebno v python uvoziti vse knjižnice, ki jih bomo uporabili v Salome okolju. Nato pa je potrebno definirati novo študijo. {{{ #!python # Uvoz knjiznic import salome import GEOM from salome.geom import geomBuilder geompy = geomBuilder.New(salome.myStudy) }}} [[BR]][[BR]][[BR]][[BR]][[BR]] '''Korak 2''': Uvoz podatkov v Python Podatke v stolpcih v tekstovnem formatu lahko preberemo na vec načinov, v tem primeru smo uporabili funkcijo `open`. Podatke za krivuljo shranimo v spremenljivko tipa `list` z imenoma ''coord_x'' in ''coord_y''. Nato te točke zrcalimo preko ravnine y-z, da dobimo krivuljo še za levo stran panela, in jih shranimo v spremenljivki `coord_x_mirror` in `coord_y_mirror`. Nato izvedemo iteracijo s for zanko in shranimo točke kot `geompy.MakeVertex()` ter jih shranimo v python-ov seznam. [[Image(b_splineSalomePanelExample.png, 700, right)]] {{{ #!python # Load data # If file is not opened, determine path with os library # import os # os.chdir(os.path.expanduser('~/')) with open('data.txt','r') as f: fileread = f.readlines() content = [x.strip() for x in fileread] # Define data list data = [] for coord in content: data.append([x.strip() for x in coord.split(" ")]) f.close() coord_x = [] coord_y = [] # convert coordinates to mm and to float type for i in range(len(data)): data[i][0] = float(data[i][0])*(1000) data[i][1] = float(data[i][1])*(1000) data[i][2] = float(data[i][1])*(1000) coord_x.append(data[i][0]) coord_y.append(data[i][1]) # mirror x coordinatex over yz - plane coord_mirror_x = coord_x[::-1] for n in range(len(coord_mirror_x)): coord_mirror_x[n] = coord_mirror_x[n]*(-1) coord_mirror_y = coord_y[::-1] # Iterate over coord_x and create geompy vertices and store those # vertices into python list. points_x = [] for i in range(len(coord_x)): point_x = geompy.MakeVertex(coord_x[i],coord_y[i],0) points_x.append(point_x) points_y = [] for i in range(len(coord_x)): point_y = geompy.MakeVertex(coord_mirror_x[i],coord_mirror_y[i],0) points_y.append(point_y) }}} '''Korak 3''': Izdelava krivulj Nato generiramo B-zlepek krivuljo s pomočjo metode `geompy.MakeInterpol()`, ki sprejme seznam točk, na katerih izvede interpolacijo. Rezultat je polinom, ki predstavlja podano krivuljo. Nato izračunano krivuljo dodamo v študijo. {{{ #!python # Create B-Spline curve on the set of points with # geompy.MakeInterpol() and add curve to Salome study. b_spline = geompy.MakeInterpol(points_x) b_splineID = geompy.addToStudy(b_spline,"b_spline_ft") b_spline_mirror = geompy.MakeInterpol(points_y) b_splineID_mirror = geompy.addToStudy(b_spline_mirror,"b_spline_ft_mirrored") }}} Sedaj je potrebno kreirati še daljico, s katero bomo povezali obe krivulji (na sliki označena z rumeno barvo). {{{ #!python # Create line between both curves. ml_point1 = geompy.MakeVertex(-coord_x[0],coord_y[0],0) ml_point2 = geompy.MakeVertex(coord_x[0],coord_y[0],0) middle_line = geompy.MakeLineTwoPnt(ml_point1,ml_point2) middle_lineID = geompy.addToStudy(middle_line,"middle_line") }}} Nato je potrebno vse tri komponente združiti v skupen objekt, ki ga bomo izvlekli v prostor. To storimo z metodo `geompy.MakeWire()`, ki sprejme seznam krivulj, robov in daljic, ki jih želimo združiti v en objekt. {{{ #!python # Create wire out of all three components wire = geompy.MakeWire([b_spline_mirror,middle_line,b_spline]) wireID = geompy.addToStudy(wire,"wire") }}} '''Korak 4''': Izdelava krivulje za izvlek Nato izdelamo še krivuljo za izvlek, ki je prikazana na sliki. Ponovno uvozimo točke in jih shranimo v spremenljivke tipa `list`. [[Image(polylineSalomePanelExample.png, 300, right)]] {{{ #!python # Load data for extrusion dataExtrusion = [] with open('dataPolyline.txt','r') as f: fileread = f.readlines() content = [x.strip() for x in fileread] # Define data list for coord in content: dataExtrusion.append([x.strip() for x in coord.split(" ")]) f.close() # Read data for extrusion coord_xe = [] coord_ye = [] coord_ze = [] for n in range(len(dataExtrusion)): coord_xe.append(float(dataExtrusion[n][0])) coord_ye.append(float(dataExtrusion[n][1])) coord_ze.append(float(dataExtrusion[n][2])) }}} Koordinate ponovno pretvorimo v točke tipa `geompy` z ukazom `geompy.MakeVertex()` ter nato generiramo polyline. {{{ #!python # Convert data to geompy point and store it in a list. points_e = [] for point_e in range(len(coord_xe)): point_e = geompy.MakeVertex(coord_xe[point_e],coord_ye[point_e],coord_ze[point_e]) points_e.append(point_e) # Create polyline for extrusion and add it to study. polylineVert = geompy.MakePolyline(points_e) polylineVertID = geompy.addToStudy(polylineVert,"b_spline_ft_vert") }}} '''Korak 5''': Izvlek iz krivulj Na koncu ustvarimo izvlek iz obeh danih krivulj. Izvlek dodamo v študijo in posodobimo drevesno strukturo. {{{ #!python # Create panel shape panel_FW = geompy.MakePipe(polylineVert, wire) geompy.addToStudy(panel_FW,'Panel') salome.sg.updateObjBrowser(True) }}} Celoten postopek izdelave programa je v priponki [attachment:salomePanelExample.py]. Podatki točk so prav tako dostopni ([attachment:data.txt] in [attachment:dataPolyline.txt]). Vse tri datoteke shranite v isto mapo in poženite program. === Razširitev programa panela na grafični vmesnik Celoten program lahko razširimo na grafični vmesnik, da bo lahko uporabnik lažje uporabljal napisani program. Grafični vmesnik je napisan s pomočjo knjižnice ''PyQt5''. Vmesnik bo uporabljal vertikalno razporeditev grafičnih elementov, ki so ''QPushButton'', ''QLineEdit'' in ''QLabel''. * **QPushButton** predstavlja gumb. S klikom nanj se izvede funkcija, ki opravi določeno nalogo. * **QLineEdit** omogoča vnos teksta v program. * **QLabel** služi za prikaz besedila in je statični element, ki podaja neko informacijo. '''Opis uporabe programa''' Na sliki je prikazan primer izgleda uporabniškega vmesnika, ki ga bomo napisali v tem poglavju. Ta vmesnik grafične elemente razporedi v vertikalni smeri z metodo ''QVBoxLayout()''. Na vrhu je definiran ''QLabel'', pod njim pa gumb ''QPushButton''. S klikom nanj se izvede funkcija, ki izriše panel in ga pozicionira v prostoru. Nato lahko panel še rotiramo in naredimo kopije panela. V ''QLineEdit'' pod ''Set name:'' zapišemo imena rotiranih panelov, v ''QLineEdit'' pod ''Set rotation angle::'' zapišemo kot rotacije okoli ''z''-osi tipa `float`, v ''QLineEdit'' pod ''Set number of rotations:'' pa število panelov tipa `int`. [[Image(salomePanelDialogExample.png, 500, center)]] Za primer vzemimo, da želimo izdelati panel, nato pa narediti še štiri kopije pri lemer je vsaka kopija zamaknjena za 20 stopinj okoli ''z''-osi. Najprej s klikom na gumb ''Create Panel'', naredimo panel iz prejšnjega primera, ki je že predhodno pozicioniran v prostoru (glede na prejšnji primer je pomaknjen v y smeri za -4200 milimetrov). Sedaj želimo izdelati še štiri panele, ki bodo zarotirani za 20, 40, 60 in 80 stopinj okoli ''z''-osi glede na prvega. Najprej določimo ime panelov ('panelsrotated'), nato določimo začetni kot rotacije, ki znaša 20 stopinj, nato pa se pri vsakem panelu poveča za 20 stopinj. Določimo še število vseh zasukanih panelov oziroma število vseh rotacij, torej 4. Nato pritisnemo na gumb ''Apply''. Rezultat programa je viden na spodnji sliki. [[Image(salomePanelsRotatedExample.png, 900, center)]] '''Opis izdelave programa''' Na začetku ponovno uvozimo vse potrebne knjižnice, tokrat tudi grafično knjižnico `PyQt5`. {{{ #!python # import libraries import salome import GEOM from salome.geom import geomBuilder import os from functools import partial # GUI library from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5 import QtWidgets import math geompy = geomBuilder.New(salome.myStudy) }}} Sedaj lahko definiramo obe funkciji, ki se bosta izvedli, ko bomo pritisnili na oba gumba tipa ''QPushButton''. Funkcija, ki izdela panel in ga premakne v ''y'' osi za -4200 milimetrov, se imenuje ''createPanel'' in je v osnovi isti program kot pri izdelavi panela, le da je shranjen v funkcijo. {{{ #!python def createPanel(): global panelIsMade, panel_FW panelIsMade = True # If file is not opened, determine path with os library os.chdir(os.path.expanduser('d:\\vaje_KT\\')) with open('data.txt','r') as f: fileread = f.readlines() content = [x.strip() for x in fileread] # Define data list data = [] for coord in content: data.append([x.strip() for x in coord.split(" ")]) f.close() coord_x = [] coord_y = [] # convert coordinates to mm and to float type for i in range(len(data)): data[i][0] = float(data[i][0])*(1000) data[i][1] = float(data[i][1])*(1000) data[i][2] = float(data[i][1])*(1000) coord_x.append(data[i][0]) coord_y.append(data[i][1]) # mirror x coordinatex over yz - plane coord_mirror_x = coord_x[::-1] for n in range(len(coord_mirror_x)): coord_mirror_x[n] = coord_mirror_x[n]*(-1) coord_mirror_y = coord_y[::-1] # Iterate over coord_x and create geompy vertices and store those # vertices into python list. points_x = [] for i in range(len(coord_x)): point_x = geompy.MakeVertex(coord_x[i],coord_y[i],0) points_x.append(point_x) points_y = [] for i in range(len(coord_x)): point_y = geompy.MakeVertex(coord_mirror_x[i],coord_mirror_y[i],0) points_y.append(point_y) # Create B-Spline curve on the set of points with # geompy.MakeInterpol() and add curve to Salome study. b_spline = geompy.MakeInterpol(points_x) b_splineID = geompy.addToStudy(b_spline,"b_spline_ft") b_spline_mirror = geompy.MakeInterpol(points_y) b_splineID_mirror = geompy.addToStudy(b_spline_mirror,"b_spline_ft_mirrored") # Create line between both curves. ml_point1 = geompy.MakeVertex(-coord_x[0],coord_y[0],0) ml_point2 = geompy.MakeVertex(coord_x[0],coord_y[0],0) middle_line = geompy.MakeLineTwoPnt(ml_point1,ml_point2) middle_lineID = geompy.addToStudy(middle_line,"middle_line") # Create wire out of all three components wire = geompy.MakeWire([b_spline_mirror,middle_line,b_spline]) wireID = geompy.addToStudy(wire,"wire") # Load data for extrusion dataExtrusion = [] with open('dataPolyline.txt','r') as f: fileread = f.readlines() content = [x.strip() for x in fileread] # Define data list for coord in content: dataExtrusion.append([x.strip() for x in coord.split(" ")]) f.close() # Read data for extrusion coord_xe = [] coord_ye = [] coord_ze = [] for n in range(len(dataExtrusion)): coord_xe.append(float(dataExtrusion[n][0])) coord_ye.append(float(dataExtrusion[n][1])) coord_ze.append(float(dataExtrusion[n][2])) # Convert data to geompy point and store it in a list. points_e = [] for coord_e in range(len(coord_xe)): point_e = geompy.MakeVertex(coord_xe[coord_e],coord_ye[coord_e],coord_ze[coord_e]) points_e.append(point_e) # Create polyline for extrusion and add it to study. polylineVert = geompy.MakePolyline(points_e) polylineVertID = geompy.addToStudy(polylineVert,"polyline_ft_vert") panel_FW = geompy.MakePipe(wire, polylineVert) panel_FW = geompy.MakeTranslation(panel_FW, 0,-4200,0) geompy.addToStudy(panel_FW,'Panel') salome.sg.updateObjBrowser(True) }}} Nato zapišemo še funkcijo ''createNewPanel'', ki se izvede ob pritisku na gumb ''Apply'', in izdela zasukane panele. Omenjena funkcija kot vhodne argumente prejme tri spremenljivke tipa **QLineEdit**. V našem primeru je ''lin1'' objekt razreda **QLineEdit**, ki sprejme tekst, ki predstavlja ime zasukanih panelov, ''lin2'' predstavlja kot rotacije panelov, ''lin3'' pa število zasukanih panelov. Do teskta dostopamo z metodo `QLineEdit.text()`, npr. `lin1.text()` vrne spremenljivko, ki vsebuje vnešen tekst v omenjeni **QLineEdit()**. Omenjena funkcija bo zdelala zasukane panele, če bo uporabnik predhodno izdelal panel, ki ga definira gumb ''Create panel''. {{{ #!python def createNewPanel(lin1, lin2, lin3): # If panel is not made, there is nothing to rotate # If lin2.text() is not float, angle cannot be determined # If lin3.text() is not int, number of rotated panels cannot be determined if panelIsMade and float(lin2.text()) and int(lin3.text()): p1 = geompy.MakeVertex(0, 0, 0) p2 = geompy.MakeVertex(0, 0, 1) v = geompy.MakeVector(p1, p2) # This for loop iterates over number of roteted panels and # adds angle to every new panel. Result is panel_FW_rotated # Name of panel is given by user + angle of rotation for nr in range(1,int(lin3.text())+1): panel_FW_rotated = geompy.MakeRotation(panel_FW, v, math.pi/180*nr*float(str(lin2.text()))) panel_name = str(lin1.text())+"_rotated_"+str(float(str(lin2.text()))*nr)+"_deg" geompy.addToStudy(panel_FW_rotated,panel_name) salome.sg.updateObjBrowser(True) }}} Sedaj lahko izdelamo uporabniški vmesnik. Vsak uporabniški vmesnik je definiran kot objekt. Pri knjižnici ''PyQt5'' je to objekt, definiran kot **QtWidgets.QWidget()**. Objekt ima svoje metode, kot so npr. `setFixedSize()`, ki definira točno velikost dialoga, ter `setWindowTitle()`, ki definira naslov dialoga. {{{ #!python widgetHDF = QtWidgets.QWidget() widgetHDF.setFixedSize(500, 250) widgetHDF.setWindowTitle('Rotate panel') }}} Nato definiramo uporabljene grafične elemente. Spremenljivka panelIsMade je uporabljena v funkciji ''createNewPanel''. Če je njena vrednost `False`, se funkcija ne izvede, saj to pomeni, da predhodni panel, ki ga kopiramo, ni bil izdelan. {{{ #!python lblPanel = QLabel('Create panel:') btnPanel = QPushButton('Create Panel') lbl1 = QLabel('Set name for rotation panels:') lin1 = QLineEdit('panel4') lbl2 = QLabel('Set rotation angle:') lin2 = QLineEdit('0') lbl3 = QLabel('Set number of rotations:') lin3 = QLineEdit('1') btn = QPushButton('Apply') panelIsMade = False }}} Metoda `setToolTip` prikaže namig, če kurzor premaknemo na gumb. Ko kliknemo na gumb, se mora izvesti neka funkcija. V ''PyQt5'' knjižnici ima gumb metodo `clicked.connect()`, ki kot vhodni argument sprejme ime funkcije, ki se mora izvesti. Gumb ''btnPanel'' razreda **QPushButton** pokliče funkcijo ''createPanel()'', ki ne sprejme nobenega vhodnega argumenta, saj je njena naloga samo izrisati predhodno določen in pozicioniran panel. Drugače je pri gumbu ''btn''. Ta kliče funkcijo ''createNewPanel()'', ki sprejme tri vhodne argumente, ki so ime panelov, kot in število panelov. Metoda `clicked.connect()` pa sprejme samo en argument. To rešimo s funkcijo `partial`. Prvi argument te funkcije je ''createNewPanel'', ostali argumenti pa so vhodni argumenti za našo funkcijo. Funkcija `partial()` tako vhodne argumente poda v ''createNewPanel''. in rezultat prenese v `clicked.connect()`. {{{ #!python btnPanel.setToolTip('Create panel') btn.setToolTip('Create rotated panels') btnPanel.clicked.connect(createPanel) btn.clicked.connect(partial(createNewPanel,lin1,lin2,lin3)) }}} Sedaj lahko definiramo razporeditev grafičnih elementov v našem dialogu. Možne izbire so **QGridLayout()**, kar pomeni mrežna razporeditev elementov, **QHBoxLayout()**, ki elemente razporedi horizontalno in **QVBoxLayout()**, ki elemente razporedi vertikalno. V našem primeru uporabimo **QVBoxLayout()**, ki grafične elemente glede na zaporedje dodajanja v dialog razvršča vertikalno od zgoraj navzdol (prvi element, ki je dodan v dialog, v našem primeru ''lblPanel'' tipa **QLabel**, je na vrhu, nato sledijo drugi). {{{ #!python layout = QtWidgets.QVBoxLayout() layout.addWidget(lblPanel) layout.addWidget(btnPanel) layout.addWidget(lbl1) layout.addWidget(lin1) layout.addWidget(lbl2) layout.addWidget(lin2) layout.addWidget(lbl3) layout.addWidget(lin3) layout.addWidget(btn) }}} Na koncu v objekt, ki definira dialog, namestimo porazdelitev grafičnih elementov. Z metodo `move()` lahko določimo, kje na ekranu bo pozicioniran dialog, z metodo `show()` pa dokončno prikažemo dialog na zaslonu. {{{ #!python widgetHDF.setLayout(layout) widgetHDF.move(500, 500) widgetHDF.show() }}} Celoten postopek izdelave programa je v priponki [attachment:salomeDialogPanelExample.py]. Podatki točk so prav tako dostopni ([attachment:data.txt] in [attachment:dataPolyline.txt]). Vse tri datoteke shranite v isto mapo in poženite program. Ta program morda ni najbolj smiseln, saj je najprej potrebno izdelati že vnaprej pozicioniran panel, če želimo izdelati še zasukane kopije omenjenega panela. V razmislek tako lahko vzamemo delovanje omenjenega programa in programov na splošno ter kaj želimo s programom uporabniku olajšati. == Uporaba orodja designer za SALOME Za pripravo uporabniškega vmesnika lahko uporabimo Qt **designer**. Poženemo ga kar iz salome z naslednjimi ukazi: {{{ #!python >>> import os >>> os.system("designer") ### SALOME tu zmrzne dokler ne zaprete designerja >>> os.system("python.exe -m PyQt5.uic.pyuic -o c:/temp/part.py c:/temp/part.ui") 0 }}} Zadnji ukaz pretvori "user interface" datoteko (*.ui) neposredno v Python, ki jo lahko neposredno uvozimo v naš program ali pa jo vlepimo neposredno v našo Python kodo. Če ukaz python vrne 0 potem se je uspešno izvedel. Sicer ste napačno napisali poti do datotek. Vse datoteke UI lahko tudi prevedemo ukazom {{{ #!python >>> from PyQt5.uic import compileUiDir >>> compileUiDir("c:/temp") }}} ki preišče imenik {{{c:\temp}}} in na isto mesto shrani datoteke v Python-u. Datoteko UI je najbolje kar vlepiti v naš program saj potem ni težav s podajanjem poti do nje. Generirano kodo uporabimo v razredu, ki nastavlja gradnike. Pomembno je, da generirano kodo nikoli ne popravljamo ročno ampak jo le vlepimo. {{{ #!python from PyQt5.QtWidgets import QDialog ### Tu vstavimo celotno vsebino Python datoteke generirane ### z compileUiDir(). Objekt QDialog smo preimenovali v ### objectName : DivertorDialog in tako je novemu razredu ime ### class Ui_DivertorDialog(object): ... divertor_dialog = QDialog() ui_divertor = Ui_DivertorDialog() ui_divertor.setupUi(divertor_dialog) if divertor_dialog.exec_(): print(str(ui_divertor.textEdit.toPlainText())) }}} === Kako vključimo sliko v dialog? Će želimo imeti v oknu dialoga še skico o označbami mer potem je najbolje, da se PNG, BMP ali JPG slike pripravi v obliki "resource". To pomeni, da pripravimo seznam datotek, ki jih potem pretvorimo v Python in vlepimo v kodo tako kot pretvorjen UI datoteko in se jih potem lahko uporabi ne da bi iskali datoteke v pravih imenikih. Ko imamo sliko v ustrezni velikosti napišemo datoteko {{{divertor.qrc}}} v naslednji obliki: {{{ #!xml divertor.png }}} Ta seznam datotek pretvorimo v Python z ukazom v Salome: {{{ #!python >>> os.system("pyrcc5 -o c:/tmp/divertor_rc.py c:/tmp/divertor.qrc") 0 }}} Datoteka {{{divertor_rc.py}}} je dokaj velika, saj vsebuje kodirano sliko. To Python datoteko potem vlepimo pred pred kodo, ki smo jo generirali z PyUIC. Da se v QT designerju slika naloži moramo najprej dodati {{{divertor.qrc}}} v **Resource Browser**, kjer lahko potem ob kliku na {{{}}} to sliko tudi vidimo. Postopek je opisan na [http://doc.qt.io/qt-5.6/designer-resources.html Qt resource strani]. Da jo vlepimo v dialog uporabimo element **QLabel**, ki mu potem v Property Editorju izberemo pixmap iz resourcev. Na koncu tako dobimo eno samo Python datoteko, ki jo lahko potem neposredno poženemo iz File->Run Script v v SALOME. Datoteka [attachment:divertor_dlg.ui] kot tudi zlepljena Python datoteka [attachment:divertor.py] je priložena tej strani. Končni dialog, narejen v QT designerju, je prikazan na spodnji sliki. [[Image(salomeDivertorGuiExampleImage.png, 700, center)]] Omenjeni dialog prikazje primer definiranja parametrov pri divertorju. Pri izdelavi dialoga so uporabljeni elementi grafičnega vmesnika. * '''QSpinBox''': Omogoča določitev celega števila v določenem intervalu (npr. radij kupole je možno nastaviti od 60° do 180°). * '''QDoubleSpinBox''': Omogoča določitev decimalnih vrednosti v določenem intervalu. * '''QTextEdit''': Omogoča urejanje teksta. * '''QCheckBox''': Vrne {{{boolean}}} tip spremenljivke. * '''QComboBox''': Omogoča nabor vrednosti, ki jih lahko izberemo. S klikom na gumb ''OK'' se nam v Pythonovi konzoli izpišejo vrednosti, določene s strani uporabnika. === Povezovanje signalov z uporabniškimi funkcijami V primeru, da želimo imeti interaktivni dialog lahko povežemo signale z navadnimi funkcijami, ali z metodami. {{{ #!python my_gui.comboBox.currentIndexChanged.connect(change_pixmap) }}} V primeru da Qt signal lahko pošlje podatke različnih tipov, lahko določimo pri povezavi (connect) v kakšnem podatkovnem tipu naj pošlje signal podatke. {{{ #!python my_gui.comboBox.currentIndexChanged[int].connect(change_pixmap_int) # ali my_gui.combobox.currentIndexChanged[str].connect(change_pixmap_str) }}} Primer je podan v [attachment:signal_primer.py]