ResizeEvent is called indefintely when fitInView is called (QGraphicsScene) - qgraphicsview

I have this weird problem.
I create my scene inside a QGraphicsView extended class like so:
scene = new QGraphicsScene(this);
this->setScene(scene);
this->setAlignment(Qt::AlignTop|Qt::AlignLeft);
showRect.setCoords(0,0,sceneWidth,sceneHeight);
However. This same class has reimplemented the resizeEvent Method according to documentation:
void ConversationView::resizeEvent(QResizeEvent *e){
//Q_UNUSED(e);
this->fitInView(showRect,Qt::KeepAspectRatioByExpanding);
qWarning() << e->size();
}
Now I add a box the scene and nothing happens. But when I start resizing the window, there comes a point where I stop and the program hangs and I keep seing the sizing message, forever and ever with very very small variations on its size:
QSize(1342, 190)
QSize(1356, 190)
QSize(1342, 190)
QSize(1356, 190)
Any ideas?

I figured out what the problem was. Instead of redefining the ConversationView's resize event (which extends from QGraphicsView), I redefined the containing Widget's resizeEvent (in this case a class based of a QDialog).
With the exact same parameters this made the problem go away.

Related

Godot: How to move KinematicBody2D node from one scene to another

I want to move my Player node (KinematicBody2D) from one scene to another. My code successfully moves Node2D node, but fails with KinematicBody2D node.
I have two Location scenes (inheriting from base Location scene) with the following structure:
FirstLocation (inherits Location)
- YSort
- Player
In the base Location scene I have two methods:
func add_player(player: Player):
get_node("YSort").add_child(player)
func remove_player() -> Player:
var player = get_node("YSort/Player")
get_node("YSort").remove_child(player)
return player
In GameWorld scene I store the possible locations inside a dictionary and the moving of the player happens inside change_location() function:
onready var _locations = {
Location.FOO: $CurrentLocation,
Location.BAR: load("res://locations/Bar.tscn").instance(),
}
onready var _current_location = $CurrentLocation
func change_location(location: int):
var player = _current_location.remove_player()
remove_child(_current_location)
_current_location = _locations[location]
add_child(_current_location)
_current_location.add_player(player)
The switching of the location works
The moving of the player also works in case the player is plain Node2D.
But when Player is KinematicBody2D then the game simply crashes, giving me no hint as to what's causing the problem.
The code works without crashing when I comment out the last line:
_current_location.add_player(player)
...but of course then the player simply doesn't get added to the other scene.
I verified that the player does get removed from the scene.
I tested with a dummy scene (which only contains KinematicBody2D as root node and a simple Sprite as a single child) instead of my actual more complex Player scene to make sure it's not related to any other code I might have in my Player scene. Node2D as root works, KinematicBody2D crashes. Must have been a fluke. Tested again and now both work, so there must be something different in my Player object.
I tried adding the Player node as a direct child of Location node (not having a YSort node in the middle) - nope, still crashes.
I tried setting the position/global_position of player before/after adding it to new scene - no difference.
I'm able to create new Player scene instance and add it to new location.
What might it be in KinematicBody2D that prevents me from moving it from scene to scene?
Found a solution, but I don't know why it works.
I was performing the location change as a response to Area2D.body_entered signal. I triggered my own signal "location_change" and then called the change_location() function in another part of the code in response to it.
Adding a tiny timeout (0.00000001 seconds) before doing the location change solved the issue. However I have no idea why, and I'm pretty sure adding timeouts is not a proper way to solve this problem.
I'm having trouble visualizing the situation. However, given that the problem happens when removing and adding a physics body, and that "body_entered" was involved, and that the solution you found was adding a time out…
Sounds like the issue was removing the physics body while Godot was still resolving physics response. And then the solution was to wait until the next frame.
You could wait until the next graphics frame with this:
yield(get_tree(), "idle_frame")
Or until the next physics frame with this:
yield(get_tree(), "physics_frame")
Which would be better than some arbitrary small timeout.
Alternatively, you could also make the signal connection deferred.
If you are connecting from the UI, there will be an "advanced" toggle in the "Connect a Signal to a Method" dialog. Enabling "advanced" toggle will reveal some extra including making the connection deferred.
If you are connecting form code, you can accomplish the same thing by passing the CONNECT_DEFERRED flag.

Why is my game scene getting errors when the main scene is actually the menu in Godot

I am adding a main menu to my game. The thing I am doing is adding them in a scene and then changing the scene to the game scene. However, my game has errors which belong to other scenes that do not have an instance in the existing scene. I get errors such as:
Invalid get index 'HasEntered' (on base: 'null instance').
My entire project is here: https://github.com/Ripple-Studios/Godot-Wild-Jam-36-Game-Ripple-Studios
I would appreciate if someone would help me.
Thanks,
I had a look at the linked code.
What is happening is that the practice of getting nodes of the parent has come back to bite you. I'm talking code that looks like this:
get_parent().get_node(...)
You have an scene (HospitalScene.tscn) instanced in your main scene (MainMenu.tscn), which has code similar to the shown above.
The particular error you refer to comes from code that looks like this:
func _on_Area2D_body_entered(body):
var HospitalPosition = get_parent().get_node("Hospital")
if HospitalPosition.HasEntered == true:
HospitalPosition.isInHospital = false
This code is firing at the start because the Area2D is overlapping an StaticBody.
The code is trying to get a sibling "Hospital" which does not exist in the scene tree. And thus HospitalPosition is null. And trying to access HospitalPosition.HasEntered when HospitalPosition is null results in the error you mention:
Invalid get index 'HasEntered' (on base: 'null instance').
The scene is trying to reach a node outside of itself. And there is no guarantee that the scene will be instanced where such node is available. Thus, in each and every case of get_parent().get_node(...) this could happen.
In fact, when running the game, I get four error that look like this (but with different paths):
E 0:00:01.494 get_node: (Node not found: "Hospital" (relative to "/root/CanvasLayer/ParentSprite/Control").)
<C++ Error> Condition "!node" is true. Returned: nullptr
<C++ Source> scene/main/node.cpp:1325 # get_node()
<Stack Trace> HospitalScene.gd:28 # _on_Area2D_body_entered()
You could use get_node_or_null instead of get_node and check for null.
You could also export NodePath variables instead of hard-coding the paths.
Better decoupling strategies than checking if instances are valid include:
Connect to the signal from outside the scene. It is a common pattern in Godot to call down the scene tree, and signal up the scene tree. See Node communication (the right way).
Connect all the signals through an Autoload (singleton). Which is another common pattern in Godot called a Signal Bus or Event Bus. See Best practices: Godot GDScript - Event Bus.

PySide QFileDialog window size

As far as I understand widget window size could be defined by calling 'setGeometry' function.
import_dialog = QtGui.QFileDialog()
import_dialog.setWindowTitle('Import File')
import_dialog.setDirectory(FILE_DIR)
import_dialog.setGeometry(100, 100, 200, 200)
import_file, _ = import_dialog.getOpenFileNames()
print(import_file)
But when I'm executing this part of my gui code, I'm facing pop up window that covers entire screen. I tried to make it smaller by calling 'setGeometry' function but with no results.
How can I make it to appear smaller?
Thanks
getOpenFileNames is a convenience static method of the QFileDialog class. It should handle creating the dialog, setting the right size as appropriate for your OS, and retrieving the result. Try calling it like this:
filenames, _ = QFileDialog.getOpenFileNames(parent, "Select file", FILE_DIR)
If that does not help, you can create the dialog yourself (as you did) and call show(), change the size, and bind the fileSelected signal to a slot.

How to run one function in another thread

I'm standing in front of a small (maybe not) problem. I have one function which parses XML file (very big xml ~1Gb) so it takes many time (5-6 mins to finish the func). I don't want to use it in GUI-thread because of known issues (mainwindow freezes and nothing happened, so user thinks everything goes wrong). I've tried to solve this problem by using
QtConcurrent::run
But one more problem appeared: if user press X (close button in top right corner) main GUI-thread goes down, but child-thread which was generated my QtConcurrent::run continue his work and I can kill him only by task manager.
I've decided to use QThread instead of QtConcurrent::run6 but I don't understand how can I run MainWindow class function:
void MainWindow::parseXML()
I've tried to create smth like this:
class pThread : public QThread
{
Q_OBJECT
private:
void run();
};
void pThread::run(){
MainWindow::parseXML();
}
But when I'm trying to compile it error appears:
cannot call member function 'void MainWindow::parseXML()' without object
Moreover, I don't know if it possible to update GUI-thread through this method (parseXML function changes statusBar)
What should I do?
The recommended ways to work with threads in Qt is not to inherit from QThread class, see the documentation here and you should be able to do it after that.
And yes it is possible to update the mainwindow from the thread, just code the signals and slots for that functionality, into mainwindow class code a slot that updates the progress and into the class that does the work (the xml parsing you need - there is no reason that functionality should be into the mainwindow class anyway) you code the signal that emit the progress and connect it with mainwindow's slot with Qt::QueuedConnection (note that the default auto-connection will become queued if the objects are in separate threads).
Another option is to use start a QRunnable with QThreadPool. you may want to check documentation. Be ware to wait the spawned threads with QThreadPool::waitForDone().

Creating a non-displayed, sized, UIView outside the UI thread?

Is there a way to manipulate the size of a non-displayed UIView from outside the UI thread prior to adding it to a displayed view?
While working through some asynchronous iOS code, I thought I would try to have an async method build up a UIView that would be displayed later [on the UI thread]. In this case, and this appears to be the "gotcha", it was a UILabel where I want to give it a predetermined frame size derived from a StringSize call. Unfortunately, the UIView constructor that takes a RectangleF frame calls UIApplication.EnsureOnUIThread first.
// Throws UIKitThreadAccessException on Frame-setting UILabel constructor.
Task<UILabel> getView = Task.Factory.StartNew(() => {
//... Do some async fun (e.g., call web service for some data for someNSString)
SizeF requiredStringSize = someNSString.StringSize(someFont, new SizeF(maxWidth, float.MaxValue), UILineBreakMode.WordWrap);
RectangleF someViewFrame = new RectangleF(PointF.Empty, requiredStringSize)
return new UILabel(someViewFrame);
});
Since I don't really need to set a valid location at the point of this task execution, I figured I could avoid setting the frame in the constructor and set the size afterwards. Unfortunately, you only seem to be able to set size by modifying UIView.Frame as a whole. While the parameter-less constructor does not make this UI thread call, as soon as I try to set the Frame to the size needed, the UIView.Frame accessor does and it blows up.
// Also throws UIKitThreadAccessException, this time when setting the Frame directly.
Task<UILabel> getView = Task.Factory.StartNew(() => {
//...do all the above stuff...
UIView someView = new UILabel();
someView.Frame = new RectangleF(someView.Frame.Location, requiredStringSize);
});
I've already decided to make my code more specific to the case at hand and use a Task<string> instead, letting the displaying code (run on the UI thread) handle the view creation, but it would be nice to know if this is possible since it would make the code I am writing more extensible.
UIKit is not designed to be used outside of the main thread.
I've seen some bizarre behaviour creep in when this rule is ignored, so I strongly advise against this.

Resources