I have a Qt widget with four partitions, separated with splitters.
The top level is a vertical splitter, changing the heights of two horizontal splitters, topSplitter and bottomSplitter.
How can I keep both horizontal splitters positions equal, as if it was just one horizontal splitter?
I looked at linking signal for splitterMoved, and connecting it to a slot on the other splitter but there are no equivalent slots in the splitter class.
This would obviously have to avoid the issue of an infinite loop where one splitter's position updates the second, which updates the first.
It's pretty simple. Initialization (splitter1 and splitter2 are the splitters that need to be syncronized):
connect(ui->splitter1, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved()));
connect(ui->splitter2, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved()));
The slot:
void MainWindow::splitterMoved() {
QSplitter* senderSplitter = static_cast<QSplitter*>(sender());
QSplitter* receiverSplitter = senderSplitter == ui->splitter1 ?
ui->splitter2 : ui->splitter1;
receiverSplitter->blockSignals(true);
receiverSplitter->setSizes(senderSplitter->sizes());
receiverSplitter->blockSignals(false);
}
blockSignals ensures that calls will not go to infinite recursion. Actually, setSizes doesn't cause emitting splitterMoved, so you can remove both blockSigals calls and the code will still work. However, there is no note about this in the documentation, so I wouldn't rely on that.
In the containing widget you can create slots to connect to the splitterMoved signal.
There needs to be one slot for each splitter, where it needs to check if the splitter is already the correct size (to avoid the infinite loop) then update the size if necessary.
I am only including one of the example slots and connections, but one will be needed for each splitter that needs to be linked.
Putting the following content into the new slots for updating the splitter positions.
QList<int> OtherSize,Current;
OtherSize=topSplitter->sizes();
Current=bottomSplitter->sizes();
if(OtherSize!=Current)
{
bottomSplitter->setSizes(OtherSize);
}
This will create two lists ready to hold the sizes for the splitters.
It gets the current sizes of both splitters, and compares them.
The comparison is necessary to avoid the infinite loop.
Then, if they are different, it sets the sizes to match that of the other splitter.
Connecting that slot to the appropriate splitterMoved signal should work.
This connection is used in the constructor of the containing widget (where you created the new slots).
connect(topSplitter,SIGNAL(splitterMoved(int,int)),this,SLOT(updateBottomSplitter()));
It is safe to ignore the position and index supplied by the signal, because we check the sizes in a different way in this slot.
This will set all sizes, so all splitter bars will match position.
If there are only two, it doesn't matter but if there are more than two, when any bar is moved, all will be updated to match.
Related
Hi I have managed to add a number of qgraphicsitems to a qgraphicsscene using the code below
def generate_graph_and_update_scene(self):
try:
local_params=locals() #for error log get local paramters
this_function_name=sys._getframe().f_code.co_name #for error log get function name
self.vertex_dict.clear()
self.clear() #clear graphicsscene
self.graph_pos.clear() #clear graph position holder object
#function that generates the node data
root_nodes=my_database_query.get_nodes_information()
for node in root_nodes:
# add nodes to nx.graph object
self.nx_graph.add_node(node['column1'])
# create networkx graph
self.graph_pos = nx.spring_layout(self.nx_graph, iterations=25,scale=10)
for node in self.nx_graph.nodes(): # Add nodes to qgraphicsscene
v=default_nodeobject.my_ellipse(node,self.graph_pos)
self.addItem(v) # Add ellipse to qgraphics scene
for edge in self.nx_graph.edges():
self.addItem(defaultedgeview.edgeview(edge[0], edge[1],self.graph_pos))#add edges to qgraphicscene
except:
#Exception handler
message=str(sys.exc_info())
message=message + str(local_params)+" "+ str(this_function_name)
print message
This allows me to add say 600 'nodes' to my qgraphics scene, however when I clear the scene and add another say 1500 nodes, adding the items blocks the UI and my whole application freezes for a few seconds.
Also whenever I am doing things like looping through the graphicsitems say looking for the nodes that have a certain property, again the main thread freezes while I am looping,
Could anyone suggest a good method of keeping the UI responsive while things are being done to the grpahicsscene/items in the scene.
Ideally would like to have smooth, non-blocking updates to the scene, even when I have a few thousand items showing.
The problem here is the management of each node as a graphics item. Adding and removing to a scene, as well as rendering each item is going to take time. With this many items, I suggest designing it differently.
Consider the node graph as a single, custom graphics item which stores a group of nodes and manages them as a single unit, rather than 600+ separate items.
Designed this way, you only add one item to the scene (the node graph) which allows rapid addition and removal of nodes and you will also see a performance improvement in rendering the scene, as all nodes are drawn in one call to paint().
Of-course, if you need to move nodes around by clicking and dragging them, you'll have to add additional code to handle detecting which node is being selected in the item and move it yourself.
However, this is the optimal way to handle such a large number of items in a scene.
I'm currently developping a small vector drawing program in wich you can create lines and modify them after creation (those lines are based on a custom QGraphicsItem). For instance, the picture below shows what happens when the leftmost (marked yellow) point of the line is dragged to the right of the screen, effectively lengthening the line :
Everything works fine when the point is moved slowly, however, when moved rapidly, some visual artifacts appear :
The piece of code I'm using to call for a repaint is located in the mouseMoveEvent redefined method, which holds the following lines of code :
QRectF br = boundingRect();
x2 = static_cast<int>(event->scenePos().x()-x());
y2 = static_cast<int>(event->scenePos().y()-y());
update(br);
There's apparently no problem with my boundingRect definition, since adding painter->drawRect(boundingRect()) in the paint method shows this :
And there are also no problem when the line is simply moved (flag QGraphicsItem::ItemIsMovable is set), even rapidly.
Does anyone know what is happening here ? My guess is that update is not being called immediately hence mouseMoveEvent can be called multiple times before a repaint occurs, maybe canceling previous calls ? I'm not sure.
Of course the easy fix is to set the viewport mode of the QGraphicsView object holding the line to QGraphicsView::FullViewportUpdate), but that is ugly (and slow).
Without seeing the full function for how you're updating the line, I would guess that you've omitted to call prepareGeometryChange() before updating the bounding rect of the items.
As the docs state: -
Prepares the item for a geometry change. Call this function before changing the bounding rect of an item to keep QGraphicsScene's index up to date.
I have a simple use case in a Qt app:
When the UI has been constructed and a QGraphicsView (or any other QWidget) has been populated, scroll the graphics view to a given location, based on the graphics views width and height (or any other attribute set by the layout engine).
However I can't find a reliable or known place to do this. In the constructors the layout has not yet been applied, so the width isn't the final width that the user sees once the UI is "up". Most suggestions seem to consist of hacks such as:
QTimer::singleShot(0, this, SLOT(initWidget()));
In my case this doesn't seem to work unless I set the delay to around 10msec which seems even more risky and hacky. Surely there must be something like win32's WM_INITDIALOG or the likes?
You can override showEvent() or resizeEvent() of the relevant class.
After a bit of experimenting, since you say you're using showMaximized(), you actually may want to override both, and then get the size from first resizeEvent() after first showEvent() (or 2nd resizeEvent() overall). Output from my test app which used showMaximized():
Starting /home/hyde/test/build-eventTest-Desktop-Debug/eventTest...
resizeEvent size QSize(200, 100) oldSize QSize(-1, -1)
showEvent geometry QRect(0,0 200x100)
resizeEvent size QSize(1440, 851) oldSize QSize(640, 480)
/home/hyde/test/build-eventTest-Desktop-Debug/eventTest exited with code 0
Then do what you need to do there. If you want to do it just once, then add a boolean member variable like
bool mFullyInited;
and initialize it to false, and then in the overriden event handler method, test it and set it to true when you've done the initialization.
I have designed a user interface by using Qt Designer, and I have set the tab order using the "edit tab order" mode.
Now what I'd like to know (for an other reason, not so important) is how to get the tab order of a specific QWidget in the ui?
I mean if I have several widgets, and say the tab order has been set, is there a way to do something like :
int nb = widget1->getTabOrder();
There is no way to get the tab order as an integer.
If you look into the C++ code that the uic tool creates from your ui file, it will call QWidget::setTabOrder() a few times, and that method just takes two QWidget pointers. Thus, Qt internally doesn't even store the tab order as an integer, but rather as a chained list of QWidget pointers.
You can query that chained list with QWidget::nextInFocusChain() and QWidget::previousInFocusChain(). This gives you the whole focus chain of the widget, containing all child widgets inside it, in the right order. Then you can get the real tab order list by checking their focusPolicy, enabled state and visible state, just like the inside implementation of the QWidget::focusNextPrevChild() function. If you really need an integer index here, you need to devise an algorithm yourself that calculates indices from that obtained tab order list.
(A bit late.) I had an a-ha moment: it's actually not at all difficult to determine the position of a widget in the tab sequence. It requires the use of Dynamic Properties, which allow you to "annotate" any QObject. The (default) focus list is simply a circular linked list with no distinguished (that I've seen identified) node. The function below annotates all the items in that linked list with a sequence number starting at the distinguished node (your choice) and provides a pointer to that in the parent widget/dialog.
Call it from your setupUi (etc.). (After any changes you may make to the focus sequence!) The parameters are the widget/dialog you want to sequence (and find the distinguished node in) and the widget you've decided should be "first" (distinguished).
// Add sequence numbers in the tab focus list starting at distinguishedNode
void sequenceFocus(QWidget *root, QWidget* distinguishedNode)
{
QVariant v;
v.setValue(distinguishedNode);
root->setProperty("focusRoot", v);
int itemCtr = 0;
QWidget* i = distinguishedNode;
do {
i->setProperty("focusPosition", itemCtr);
i = i->nextInFocusChain();
itemCtr++;
} while (i != distinguishedNode);
}
You can then get the distinguished (first) item from the list from the parent with:
QWidget* start = activeDialog->property("focusRoot").value<QWidget*>();
And get the sequence position of a widget with:
my_widget->property("focusPosition").toInt()
Note that (at least) Designer ends up putting more entries in the focus list than just those marked in the focus sequence as seen in Designer. The actual focusable widgets will be sparsely numbered. (Add debugging printouts in the function above to see everything.)
I try to code an OpenGL project with Qt (v5.1.1) on OS X 10.9, in the manner of the modern pipeline implementation. The program is supposed to be a multi-agent based system or particle system. However I lack in understanding how to draw something out of another class.
In cinder there were some simple drawThisAndThat() command you could call. I read the 6th edition of the 'OpenGL Superbible'. From this and several tutorials all examples seem to cover just programs where all modifications are made out of the class that initializes OpenGL.
I would like to instantiate some objects moving on a grid and draw pixel to display their position. I know I have to call void glVertexAttrib4fv(GLuint index, const GLfloat * vi); but this is not sufficient.
Do I need to call glEnableVertexAttribArray(1); and glDrawArrays(GL_POINTS, 0, 3); as well and what else?
Am I right, to instantiate the class controlling the particles after instantiating OpenGL and bevor the main loop?
How do I manage that the particle draws himself while erasing the position he was drawn bevor?
The program is based on this code.
To answer your questions completely I would have to write a wall of text, I will try to only point out the most important aspects. I hope this will help you enough to use your knowledge and probably further reading to get it to work.
all modifications are made out of the class that initializes OpenGL
You can encapsulate update(time) and draw() methods for your Objects which you then call in your main loop.
Do I need to call glEnableVertexAttribArray(1); and glDrawArrays(GL_POINTS, 0, 3); as well and what else?
I would put all particles into one vertex array to avoid rebinding of different vertex arrays after each particle. Then you would have to use glBindVertexArray(vaid); and glDrawArrays(GL_POINTS, 0, vertexCount); in your draw() call. Be careful with vertexCount, it's not the number of floats (as your question implies) but the number of vertices, which should be 1 in your example or the number of particles in my suggested approach (If I'm correct in assuming that the 3 stands for "x, y, and z of my vertex").
And since you only have particles glDrawElements(...); would probably already fit your needs.
Am I right, to instantiate the class controlling the particles after instantiating OpenGL and bevor the main loop?
Probably your instantiation order is correct that way. You definitely should do all instantiations before calling the main loop in your case.
How do I manage that the particle draws himself while erasing the position he was drawn bevor?
If understand your last question correctly: Simply by changing the elements in your buffer objects (glBufferData(...);). Since you will clear the screen and swap buffers after each loop this will make them move. Just update their position with an update(time) call, e.g. pos = pos + dir * time;, put the new positions into a buffer and push that buffer with glBufferData(...) to the vertex array. Remember to bind the vertex array before pushing the buffer.
Some additional things I'd like to point out.
glEnableVertexAttribArray(1); is to enable a vertex attribute in your shader program to be able to pass data to that attribute. You should create a shader program
id = glCreateProgram()
// ... create and attach shaders here
// then bind attribute locations, e.g. positionMC
glBindAttribLocation(id, 0, "positionMC");
glLinkProgram(id);
And after initializing the vertex array with glGenVertexArrays(); you should enable all attributes your vertex array needs in your shader program. In this example positionMC would be at location 0, so you would call something like
glUseProgram(pid);
glBindVertexArray(vaid);
glEnableVertexAttribArray(1);
glVertexAttribPointer(...);
This has only to be done once, since OpenGL stores the state for every particular vertex array. By rebinding a vertex array you will restore that state.
In the main loop all you have to do now is calling your update and draw methods, e.g.:
handleInputs();
update(deltaTime);
glClear(...);
draw();
swapBuffers();