Related
I'm trying to create an app with a file browser that is 1/5 the width of a tab pane.
Why is the layout columnSpan backwards from what I set it?
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("My App")
layout = QGridLayout()
layout.addWidget(FileTree(), 0, 0, 1, 1)
layout.addWidget(Tabs(), 0, 1, 1, 5)
widget = QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
The row and column span refer to the grid cells of the layout, not the ratio of each row or column.
Setting a span means that the widget (or layout) will occupy that amount of cells, not that it will be 5 times bigger, as the size of each cell depends on the size (and hint) of its content. Since you have nothing else in the other 4 columns, those cells are actually empty.
It works exactly like a table: when you span a cell to make it occupy 2 columns, but the second column has no width, the cell will keep the same size.
Note that even if you had widgets in another row and in any of those columns, this would still not mean that the tab widget would occupy 5 times the space of the first, especially if those widgets were small: QTreeView inherits QAbstractItemView, which by default has an expanding size policy in both directions.
To achieve the wanted ratio, you must use setColumnStretch(), and not set any span unless you actually need it for widgets in other rows:
layout.addWidget(FileTree())
layout.addWidget(Tabs(), 0, 1)
layout.setColumnStretch(0, 1)
layout.setColumnStretch(1, 5)
I try to get a GUI application to work but unfortunately the spacing of the single canvas objects is not as expected. I want to have the following layout:
For the Grid Layout I use the following code:
# Adapt Widget StyleSheet
self.button_classification.setStyleSheet("font: bold 10pt Consolas; color: #009682") # #009682 is the KIT-Green
self.button_start.setStyleSheet('font: bold 10pt Consolas; color: #009682; border-style: outset; border-width: 2px; border-radius: 10px; border-color: #646873; padding: 6px')
self.layout4_image_widget.setStyleSheet('background-image: url(Python_logo.png);')
# Define Layouts
self.window = QWidget()
self.layout = QGridLayout()
# Add Widgets to layouts
self.layout.addWidget(self.canvas_left, 0, 0, 5, 5)
self.layout.addWidget(self.canvas_right, 0, 5, 5, 5)
self.layout.addWidget(self.canvas_right_buttom, 8, 7, 3, 3)
self.layout.addWidget(self.canvas_right_top, 5, 7, 3, 3)
self.layout.addWidget(self.canvas_middle_buttom, 8, 3, 3, 4)
self.layout.addWidget(self.canvas_middle_top, 5, 3, 3, 4)
self.layout.addWidget(self.layout4_image_widget, 5, 0, 3, 3)
self.layout.addWidget(self.button_start, 8, 0, 1, 3)
self.layout.addWidget(self.button_classification, 9, 0, 1, 3)
self.layout.addWidget(self.LCD_Number, 10, 0, 1, 3)
self.window.setLayout(self.layout)
But unfortunately what I receive as result is:
So the rows in the middle 5-7 are to wide.
Using column and row spans for widgets doesn't work as you seem to believe. Each "cell" in a grid layout might have different sizes, and it all depends on each widget sizeHint and sizePolicy.
All widget have a default size policy, which is a property that a widget uses to tell its container how it behaves when it is resized (should it expand, have a fixed height/width, can it shrink, etc).
By default, buttons and checkboxes have a fixed vertical policy, which means that they can never grow or shrink.
Every layout has a stretch value that can be assigned for each of its "slots". The stretch always defaults to 0, which leaves to each widget the possibility to grow or shrink to fit its needs, but when that value is set the available spaces are computed by the sum of each stretch in the layout and then divided equally. For example, if you have a simple layout with 2 widgets and a stretch=1 for the first and 2 for the second, the available space will be divided by 3 (1 + 2), with the first occupying a third of it and the second 2 thirds.
For grid layouts you can then use setRowStretch() and setColumnStretch() to achieve something like you proposed, but this would work only for widget that have compatible policies (preferred or expanding), and since in your case those buttons have fixed height it wouldn't be enough.
There are two possibilities, but in both cases I'll use a simpler layout, since there's no need to use a high count of rows and columns for the reasons explained above and setRowStretch/setColumnStretch can be used instead.
The layout uses just 4 columns:
+----+---+---+----+
| | |
+----+---+---+----+
| | | |
+----+---+---+----+
| | | |
+----+---+---+----+
The first row has two "cells" with a 2 column span, and the following rows have the 2 central slots united as a single cell with a similar span.
1. Manually set the policy for those widgets
In this case there are actually 5 rows, the first has a rowStretch=5, the second 3, and the remaining 1. By manually setting the vertical size policy to the widgets, they can grow to the height required by the stretch of the row they are occupying.
# first row, notice the spans
self.layout.addWidget(self.canvas_left, 0, 0, 1, 2)
self.layout.addWidget(self.canvas_right, 0, 2, 1, 2)
# second row, if the span is not specified defaults to (1, 1)
self.layout.addWidget(self.layout4_image_widget, 1, 0)
self.layout.addWidget(self.canvas_middle_top, 1, 1, 1, 2)
self.layout.addWidget(self.canvas_right_top, 1, 3)
# left widgets for third, fourth and fifth rows
self.layout.addWidget(self.button_start, 2, 0)
self.layout.addWidget(self.button_classification, 3, 0)
self.layout.addWidget(self.LCD_Number, 4, 0)
# remaining widgets, notice the vertical and horizontal spans
self.layout.addWidget(self.canvas_right_buttom, 2, 1, 3, 2)
self.layout.addWidget(self.canvas_middle_buttom, 2, 3, 3, 1)
# the column stretches set based on the grid given by you
self.layout.setColumnStretch(0, 3)
self.layout.setColumnStretch(1, 2)
self.layout.setColumnStretch(2, 2)
self.layout.setColumnStretch(3, 3)
# the row stretches, with the final 3 rows set to 1
self.layout.setRowStretch(0, 5)
self.layout.setRowStretch(1, 3)
self.layout.setRowStretch(2, 1)
self.layout.setRowStretch(3, 1)
self.layout.setRowStretch(4, 1)
# the default policy for both buttons and checkboxes is QSizePolicy.Minimum
# horizontally, and QSizePolicy.Fixed vertically, let's just change the latter
self.button_start.setSizePolicy(
QSizePolicy.Minimum, QSizePolicy.Preferred)
self.button_classification.setSizePolicy(
QSizePolicy.Minimum, QSizePolicy.Preferred)
Unfortunately this isn't perfect: as you can see the button might become too big and the checkbox has a lot of unnecessary margins that could be used by the LCD widget instead.
2. Use a container widget
In this case there only 3 rows, and an extra QWidget is used as container for the widgets on the bottom left.
self.layout.addWidget(self.canvas_left, 0, 0, 1, 2)
self.layout.addWidget(self.canvas_right, 0, 2, 1, 2)
self.layout.addWidget(self.layout4_image_widget, 1, 0)
self.layout.addWidget(self.canvas_middle_top, 1, 1, 1, 2)
self.layout.addWidget(self.canvas_right_top, 1, 3)
# create a container widget and add it to the main layout
self.container = QWidget()
self.layout.addWidget(self.container, 2, 0)
containerLayout = QVBoxLayout(self.container)
# add the widgets to the container
containerLayout.addWidget(self.button_start)
containerLayout.addWidget(self.button_classification)
containerLayout.addWidget(self.LCD_Number)
# the remaining widgets on the third row
self.layout.addWidget(self.canvas_right_buttom, 2, 1, 1, 2)
self.layout.addWidget(self.canvas_middle_buttom, 2, 3)
self.layout.setColumnStretch(0, 3)
self.layout.setColumnStretch(1, 2)
self.layout.setColumnStretch(2, 2)
self.layout.setColumnStretch(3, 3)
# there are only 3 rows, the second and third have the same row span
self.layout.setRowStretch(0, 5)
self.layout.setRowStretch(1, 3)
self.layout.setRowStretch(2, 3)
In this case the space usage is more optimized, leaving more space to those widgets that need it (the LCD).
https://doc.qt.io/qtforpython/PySide2/QtWidgets/QGridLayout.html#PySide2.QtWidgets.PySide2.QtWidgets.QGridLayout.setRowStretch
maybe there is something to do with this function... as they say the stretch factor has something to do with the row's relative space occupation and its default value is 0. You could maybe try setting them at 1 for each row.
With Blender 2.8 I have created a complex object that I wanted to split in two seperate objects.
The procedure I followed (all scripted): Create object; duplicate object; go to edit mode and bisect with '''clear_inner=True'''. (works Perfect!)
Then select other (original) object; go to edit mode and bisect with '''clear_outer=True'''.
Now the first object seems also to have been subject to the bisect: Only some points/faces were the bisect is remain.
I am including the code for a simple cube:
import bpy
bpy.ops.mesh.primitive_cube_add(size=4, enter_editmode=False, location=(0, 0, 0))
bpy.context.object.name="left"
bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'})
bpy.context.object.name="right"
# cutting in two (bisect)
bpy.data.objects['left'].select_set(False)
bpy.data.objects['right'].select_set(True)
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.bisect(plane_co=(0, 28.5249, 5.80484), plane_no=(1, 0, 0), use_fill=True, clear_inner=True, threshold=0.0001, xstart=1042, xend=1068, ystart=647, yend=130)
bpy.ops.object.editmode_toggle()
bpy.data.objects['right'].select_set(False)
bpy.data.objects['left'].select_set(True)
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.bisect(plane_co=(0, 28.5249, 5.80484), plane_no=(1, 0, 0), use_fill=True, clear_outer=True, threshold=0.0001, xstart=1042, xend=1068, ystart=647, yend=130)
bpy.ops.object.editmode_toggle()
using bisect to seperate cube in two halfs
In the picture you see that the result of the second bisect succesfully halved the first cube ('left'). But it also splitted the duplictated cube('right') which was already halved resulting only in a face at the bisect plane.
Why doesn't it work? What am I doing wrong?
The problem is caused by second bpy.ops.mesh.select_all(action='SELECT'). None of those is needed because the whole mesh is selected until you change that. This cause seleciton of mesh of both cubes and then aplying bisect on both of them. This is possible because Blender 2.80 allows you to edit multiple objects at once. Source of the problem is select_set(False) though. This will deselect the object but it does not change its active status. When you toggle edit mode for the second cube you are editing both because one is selected and second is active. You can try that by removing last 3 lines of your code and you will see what is going on. You have to change that by setting bpy.context.view_layer.objects.active as seen in second code.
Also you do not need to fill all parameters of bpy.ops.mesh.bisect function and plane_co is point on the cutting plane which I think should be zero but that is up to you.
Documentation: https://docs.blender.org/api/current/bpy.ops.mesh.html?highlight=mesh#bpy.ops.mesh.bisect
Edited code using bpy.ops.object.select_all(action='DESELECT'):
...
# deselect all objects
bpy.ops.object.select_all(action='DESELECT')
# select just right object
bpy.data.objects['right'].select_set(True)
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.bisect(plane_co=(0, 0, 0), plane_no=(1, 0, 0), use_fill=True, clear_inner=True)
bpy.ops.object.editmode_toggle()
# deselect all objects
bpy.ops.object.select_all(action='DESELECT')
# select just left object
bpy.data.objects['left'].select_set(True)
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.bisect(plane_co=(0, 0, 0), plane_no=(1, 0, 0), use_fill=True, clear_outer=True)
bpy.ops.object.editmode_toggle()
This one also works - using bpy.context.view_layer.objects.active:
...
bpy.data.objects['left'].select_set(False)
bpy.data.objects['right'].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects['right'] # set right as active
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.bisect(plane_co=(0, 0, 0), plane_no=(1, 0, 0), use_fill=True, clear_inner=True)
bpy.ops.object.editmode_toggle()
bpy.data.objects['right'].select_set(False)
bpy.data.objects['left'].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects['left'] # set left as active
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.bisect(plane_co=(0, 0, 0), plane_no=(1, 0, 0), use_fill=True, clear_outer=True)
bpy.ops.object.editmode_toggle()
I'm using Pyqt5 to build a very simple GUI.
In this window I want to place some text with info on top of a grid layout. The grid is made of 2 columns and I want the text to go full width (like HTML attribute colspan). I can't find a way to place the entirety of the text.
Text is: "Return a list of same-distance points couples from a file of 2D points"
I tried setting the Qlabel containing the text as the 1x1 element of the grid and give it a width of 2 columns, I tried place i manually with the move function; either of these solutions does not show the text properly.
class MatchDistance(QWidget):
def initUI(self):
super().initUI()
self.setWindowTitle('Match distance')
info_label = QLabel("Return a list of same-distance points couples from a file of 2D points", self)
info_label.move(10, 10)
# QPushButton and QLineEdit setup [...]
self.grid.addWidget(self.input_file_1, 1, 1)
self.grid.addWidget(self.output_file, 2, 1)
self.grid.addWidget(self.btn_input_1, 1, 2)
self.grid.addWidget(self.btn_output, 2, 2)
self.grid.addWidget(self.btn_run, 3, 2)
self.grid.addWidget(self.btn_mainwindow, 4, 2)
self.setWindowTitle("script#1: Match distance")
self.show()
Your description is confusing so I will not refer to your implementation, but to respond you must take into account the following:
The indices that set the row and column start at 0.
If you use the layouts then you can no longer use move since the position is handled by the layout.
Considering the above, the solution is:
self.grid.addWidget(info_label, 0, 0, 1, 2)
self.grid.addWidget(self.input_file_1, 1, 0)
self.grid.addWidget(self.output_file, 2, 0)
self.grid.addWidget(self.btn_input_1, 1, 1)
self.grid.addWidget(self.btn_output, 2, 1)
self.grid.addWidget(self.btn_run, 3, 1)
self.grid.addWidget(self.btn_mainwindow, 4, 1)
I wrote a Python script using matplotlib, to visualize and rotate polycubes together with planes or axes that reveal symmetry in the polycube. Up to a certain point I succeeded as you can see from the illustrations below. There you see one of seventy-seven possible 3D polycubes of six cubes. My script uses matplotlib and it seems to do a nice job: matplotlib allows me to rotate the polycube and view it at any wanted angle. There is a big problem with most polycubes, which is illustrated in the second image below. A little after I start rotating the polycube, matplotlib shows planes that are partly behind other planes and are therefore partly invisble and not to be drawn or only partly drawn.
I have been searching a lot on forums and with Google, but to no avail. There were hits suggesting that I should use mayavi instead of matplotlib. So I studied extensively on mayavi. I spent literally weeks trying to figure out how to get mayavi going. The hits on docs.enthought seemed promising at first, but although mayavi is clearly suited to the purpose and superb in visualizing objects, I cannot find understandable documentation. I could use a real programmers guide on vtk or tvtk. There is a lot of documentation, but mostly for designers, not programmers. If not available (?) I would also be happy with an example of a script with hexahedrons or irregular grids, that works in canopy's (1.6.2) implementation of python.
I've modified an example from the vtk documentation (http://www.vtk.org/gitweb?p=VTK.git;a=blob_plain;f=Examples/DataManipulation/Python/BuildUGrid.py)
This example can be turned into a function which can create the geometry from a point coordinate array and an element array.
I haven't used any guides for vtk, I usually refer to the python examples found here: http://www.vtk.org/Wiki/VTK/Examples/Python
import vtk
# Initialize the vtkPoints variable and set the number of points
points = vtk.vtkPoints()
points.SetNumberOfPoints(8)
# Add points to the variable, with the point number first, then the x, y, z coordinates.
# For demonstration purposes, I started numbering the ponts at 10 (normally they would start at 0).
points.InsertPoint(0, 0, 0, 0)
points.InsertPoint(1, 1, 0, 0)
points.InsertPoint(2, 1, 1, 0)
points.InsertPoint(3, 0, 1, 0)
points.InsertPoint(4, 0, 0, 1)
points.InsertPoint(5, 1, 0, 1)
points.InsertPoint(6, 1, 1, 1)
points.InsertPoint(7, 0, 1, 1)
points.InsertPoint(8, 0, 0, 1.1)
points.InsertPoint(9, 1, 0, 1.1)
points.InsertPoint(10, 1, 1, 1.1)
points.InsertPoint(11, 0, 1, 1.1)
points.InsertPoint(12, 0, 0, 2)
points.InsertPoint(13, 1, 0, 2)
points.InsertPoint(14, 1, 1, 2)
points.InsertPoint(15, 0, 1, 2.5)
# Define the hexahedron, then set the point Ids of the hexahedron cell/element.
# From the documentation: points (0,1,2,3) is the base of the hexahedron which, using the right hand rule, forms a
# quadrilaterial whose normal points in the direction of the opposite face (4,5,6,7)
aHexahedron1 = vtk.vtkHexahedron()
aHexahedron1.GetPointIds().SetId(0, 0) # Cell point 0 corresponds to point 0 which was defined above
aHexahedron1.GetPointIds().SetId(1, 1)
aHexahedron1.GetPointIds().SetId(2, 2)
aHexahedron1.GetPointIds().SetId(3, 3)
aHexahedron1.GetPointIds().SetId(4, 4)
aHexahedron1.GetPointIds().SetId(5, 5)
aHexahedron1.GetPointIds().SetId(6, 6)
aHexahedron1.GetPointIds().SetId(7, 7)
# Define a second hexahedron
aHexahedron2 = vtk.vtkHexahedron()
aHexahedron2.GetPointIds().SetId(0, 8) # Cell point 0 corresponds to point 8 which was defined above
aHexahedron2.GetPointIds().SetId(1, 9)
aHexahedron2.GetPointIds().SetId(2, 10)
aHexahedron2.GetPointIds().SetId(3, 11)
aHexahedron2.GetPointIds().SetId(4, 12)
aHexahedron2.GetPointIds().SetId(5, 13)
aHexahedron2.GetPointIds().SetId(6, 14)
aHexahedron2.GetPointIds().SetId(7, 15)
# Define an unstructured grid.
aHexahedronGrid = vtk.vtkUnstructuredGrid()
# Add the hexahedron to the unstructured grid
# Note: this operation defines the point ids, and not the actual point coordinates
aHexahedronGrid.InsertNextCell(aHexahedron1.GetCellType(), aHexahedron1.GetPointIds())
aHexahedronGrid.InsertNextCell(aHexahedron2.GetCellType(), aHexahedron2.GetPointIds())
# Set the points which includes the coordinates. The point ids defined in the line above correspond to the point ids
# that were defined earlier (i.e. points.InsertPoint(10, 0, 0, 0))
aHexahedronGrid.SetPoints(points)
# Now we have defined one hexahedron, and added it an unstructured grid.
# We could create more hexahedrons, and add them to the same unstructured grid.
# To view the unstructured grid, we need to define a mapper and set the unstructured grid as the input
aHexahedronMapper = vtk.vtkDataSetMapper()
aHexahedronMapper.SetInputData(aHexahedronGrid)
# Define an actor, and set the mapper as the input
aHexahedronActor = vtk.vtkActor()
aHexahedronActor.SetMapper(aHexahedronMapper)
# Create the usual rendering stuff.
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
iren.SetInteractorStyle(vtk.vtkInteractorStyleTrackballCamera()) # Change the rotation type from the default to 'trackball'
ren.SetBackground(.1, .2, .4)
# Add the actor to the renderer to actually view the geometry
ren.AddActor(aHexahedronActor)
# Render the scene and start interaction.
iren.Initialize()
renWin.Render()
iren.Start()