How to track the local position of a movable QTab? - python-3.x

I am currently attempting to set boundaries so that tabs cannot move beyond on each end of my QTabBar. By default the movable tab just disappears into void whenever it is dragged outside of the QTabBar's area -
An example of the QTab being dragged outside of the QTabBar's Area.
This is fine for the most part, but visually I would like the tab to stop moving entirely once it reaches the edge of the QTabBar. I've attempted to accomplish this goal via Qt's event system, and currently only have support for the left boundary. Whenever the user clicks on a QTab, I record his mouse's initial position and the current tabs unmoved position and size. I then use these values to determine at which x-position the current tab would reach the edges of the QTabBar if the user were to move his mouse left. If this position is ever reached the event is filtered preventing any further movement of the tab.
def eventFilter(self, source, event):
if event.type() == QEvent.Type.MouseButtonPress:
self.startingpos = event.x()
self.tabOrigin = self.curTab.x()
self.tabRoom = self.startingpos - self.tabOrigin
if event.type() == QEvent.Type.MouseMove:
if self.curIndex != self.tabs.currentIndex():
self.curIndex = self.tabs.currentIndex()
self.curTab = self.tabs.tabBar().tabRect(self.curIndex)
if event.x() < self.tabRoom:
return True
return False
This tactic is effective unless the user quickly moves his mouse left. This causes the moving tab to get stuck at the last recorded position of the mouse before it went past the specified boundary, before reaching the QTabBar's edge -
An example of the QTab getting stuck before reaching the left side of the QTabBar.
I'm not exactly sure how to get around this issue. I understand that the moving tab is not actually the same one as the tab that is originally clicked. Is there anyway to access the position of the moving tab and manually set its position? If not, any advice about how else to go about solving this problem would be greatly appreciated.

Mouse events are not linear, they "skip" pixels if the user moves the mouse too fast.
The problem is that the "moving tab" is completely implemented internally, so there's no access to that tab (which, in fact, is temporarily a "fake" widget drawn over the remaining tabs).
A possible solution is to detect the current pressed tab, get the horizontal mouse position relative to that tab, check whether the movement goes beyond the limits (0 on the left, the right of the last tab for the right margin), synthesize a new mouse event based on that, and post that event.
def eventFilter(self, source, event):
if (event.type() == event.MouseButtonPress and
event.button() == QtCore.Qt.LeftButton):
tabRect = source.tabRect(source.tabAt(event.pos()))
self.leftDistance = event.x() - tabRect.left()
self.rightMargin = tabRect.right() - event.x()
self.rightLimit = source.tabRect(self.tabs.count() - 1).right()
elif (event.type() == event.MouseMove and
event.buttons() == QtCore.Qt.LeftButton):
if event.x() - self.leftDistance < 0:
pos = QtCore.QPoint(self.leftDistance, event.y())
newEvent = QtGui.QMouseEvent(
event.type(), pos,
event.button(), event.buttons(),
event.modifiers())
QtWidgets.QApplication.postEvent(source, newEvent)
return True
elif event.x() + self.rightMargin > self.rightLimit:
pos = QtCore.QPoint(
self.rightLimit - self.rightMargin, event.y())
newEvent = QtGui.QMouseEvent(
event.type(), pos,
event.button(), event.buttons(),
event.modifiers())
QtWidgets.QApplication.postEvent(source, newEvent)
return True
return super().eventFilter(source, event)
Note that eventFilter() should always return the base implementation, otherwise you could face unexpected behavior in some situations.

Related

Why does rock image does not move alongside the mouse when in motion although I called the node and got its position-Godot Game Engine

I am trying to get my rock.png to move to the side(+x axis) when my mouse is moving. You can see that _target is the position of the event and the event is the mouse moving.
I then got the node image and set it to a variable.
In the else statement I made the rock.position the position of the _target and gave it somee space away from the _target
I want this to move because my camera moves and I want it to move with the flow of the camera
``
extends Camera2D
const zone = 10
onready var rock = get_node("rocks_png_1")
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
var _target = event.position
if _target.length() < zone:
self.position = Vector2(640,350)
else:
self.position = _target.normalized() * (_target.length() - zone) * 0.5
rock.position = Vector2(int(_target)+40, int(_target)+20)
``
From the code I used above I get this error
Invalid get Index 'position' (on base: 'TextureRact')
I tried just using the same code as I used in my self.position for the camera that made it move, but when I try it for the rock.position it gives me an error that tells me I need a Vector?
Invalid get Index 'position' (on base: 'TextureRact')
The error is telling you that TextureRect does not have a position property.
What happens is that a TextureRect is a Control which are positioned differently from Node2D (Camera2D is a Node2D). You can try using rect_position with it. Although I think you would be better off using a Sprite instead of a TextureRect (Sprite is a Node2D).
Notice that your TextureRect is a child of the Camera2D in the scene tree. Well, a Node2D would move with its parent automatically. So changing it to Sprite would negate the need of the line of code you are using to move it.
… Actually, due to the way Camera2D works (it has drag and limits) you might still want do something (depending what your goal is). So, know that the get_camera_screen_center method will give you the actual visual center of the screen (which is not the target of the Camera2D, again due to drag and limits).

navigation tilemaps without placing walkable tiles manually

I have a tilemap in godot, but all tiles are obstacles and provide collision. Only cells without any tile are walkable. I'm now trying to add navigation via Navigation2D node. As far as I see there is no way to tell it "everything is walkable, but not where these tiles are" (all one can say is "this part of the tile is walkable", but in my current setup there is no tile at the walkable space).
As a workaround I tried to set every cell with no tile to a "dummy tile" which is fully walkable with the following code:
func _ready():
for x in size.x:
for y in size.y:
var cell = get_cell(x, y)
if cell == -1:
set_cell(x, y, WALKABLE)
But the Navigation2D node does not recognize these tiles. If I place the WALKABLE tile manually, everything works as expected.
I think I might be hitting this issue, and need to call update_dirty_quadrants() but this does not fix the problem.
I tried this with the versions 3.0.6stable, 3.1Alpha2 and a recent commit 9a8569d (provided by godotwild), and the result was always the same.
Is there anyway to get navigation with tilemaps working, without placing every tile beforehand manually?
For anyone coming across this in the future, the 'dummy navigation tile' solution works now (using Godot 3.2.3). Put this script on the tilemap in question:
extends TileMap
# Empty/invisible tile marked as completely walkable. The ID of the tile should correspond
# to the order in which it was created in the tileset editor.
export(int) var _nav_tile_id := 0
func _ready() -> void:
# Find the bounds of the tilemap (there is no 'size' property available)
var bounds_min := Vector2.ZERO
var bounds_max := Vector2.ZERO
for pos in get_used_cells():
if pos.x < bounds_min.x:
bounds_min.x = int(pos.x)
elif pos.x > bounds_max.x:
bounds_max.x = int(pos.x)
if pos.y < bounds_min.y:
bounds_min.y = int(pos.y)
elif pos.y > bounds_max.y:
bounds_max.y = int(pos.y)
# Replace all empty tiles with the provided navigation tile
for x in range(bounds_min.x, bounds_max.x):
for y in range(bounds_min.y, bounds_max.y):
if get_cell(x, y) == -1:
set_cell(x, y, _nav_tile_id)
# Force the navigation mesh to update immediately
update_dirty_quadrants()
I couldn't find the ID from my tile in the autotiler (when checking in the editor, having the tilemap selected, these all show the same imagename.png 0). So instead of using the ID, I used the coordinates of the walkable blank tile in the Tileset. The code remains the same as LukeZaz', but with set_cell replaced by:
set_cell(x, y, 0, false, false, false, Vector2(7,4)); where Vector2(7,4) is the coordinate of the blank tile in my tileset that has the navigationpolygon on it. (Remember that coördinates start at 0)
(Godot 3.2.3.stable)

Why isn't my path2d position updating?

I created a new path2d following the instructions in the my first game article: http://docs.godotengine.org/en/3.0/getting_started/step_by_step/your_first_game.html
I wanted to move the "box" on screen so that I could see how the mobs spawn in, but when I ran the scene, it stayed off screen.
I created a new path2d, centered this one in the middle of the screen, and it works like I wanted it to, but now I moving this one in the editor doesn't update the position in game.
What's going on?
Thanks
func _on_mobtimer_timeout():
$mobtimer.wait_time = 0.1 + randf() / 2
$mobspawn/moblocation.set_offset(randi())
var mob = Mob.instance()
add_child(mob)
var direction = $mobspawn/moblocation.rotation + PI/2
mob.position = $mobspawn/moblocation.position
direction += rand_range(-PI/8, PI/8)
mob.rotation = direction
mob.set_linear_velocity(Vector2(rand_range(200, 200 + score * 30), 0).rotated(direction))
A Node2D's position property is relative to it's parent's position. The code from the Dodge The Creeps tutorial assumes that MobPath is located at 0, 0 and fails when that assumption is false.
In your case you are taking a MobSpawnLocation's position relative to MobPath and then setting it as the new Mob's global position.
Luckily Node2D's have another property that we can use in these circumstances global_position. It can be used like this:
mob.position = $mobspawn/moblocation.global_position
http://docs.godotengine.org/en/stable/classes/class_node2d.html#member-variables
This isn't a full solution, but I found a weird workaround. Instead of changing the position in the editor, if you use the nodes on the orange box (at the intersection of orange and blue), you can kind of alternate to move the box around.

How can I detect uiview is an activated viewport

I need to detect whether a Uiview is a standard opened view or if it is an activated viewport on a sheet. Querying the uiview’s view Id returns the Id of the activated viewport's view. I have found no direct way to detect that a uiview is actually a sheet with an activated viewport.
I am already tracking opened views in the view activated event for another purpose. So I considered storing the view Id with the uiview hashcode for later checking that it was indeed a sheetview prior to becoming an activated view. Unfortunately, and I think in opposition to standard use, the uiview hashcode is not stable. Multiple hashcode requests from the uiview object return different values.
Does anyone have a way to detect this condition? I need to be able to use the the methods on the uiview still. So any help to find the actual child windows I would like to relate to the uiview object. The view still says "Sheet: ..." in the title when a view is activated.
TaskDialog mainDialog = new TaskDialog("Hello, viewport check!");
mainDialog.MainInstruction = "Hello, viewport check!";
mainDialog.MainContent =
"Sadly Revit API doesn't automatically know if the user is in an active viewport. "
+ "Please click 'Yes' if your are, or 'No' if your not.";
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1,
"Yes, I am in an active viewport on a sheet.");
mainDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink2,
"No, I am just in an ordinary view.");
mainDialog.CommonButtons = TaskDialogCommonButtons.Close;
mainDialog.DefaultButton = TaskDialogResult.Close;
TaskDialogResult tResult = mainDialog.Show();
bool YesOrNo = true;
if (TaskDialogResult.CommandLink1 == tResult)
{
YesOrNo = true;
}
else if (TaskDialogResult.CommandLink2 == tResult)
{
YesOrNo = false;
}
else{
return;
}
You can use the ViewSheet GetAllViewports method to determine all the viewports on a given sheet. Using that, you could put together a bi-directional dictionary lookup system map any sheet to all the viewports it hosts and vice versa. That should help solve your task. Here is some example usage:
http://thebuildingcoder.typepad.com/blog/2014/04/determining-the-size-and-location-of-viewports-on-a-sheet.html
Im late to the party - but another way to sense if the user is in a viewport is to investigate the Process.MainWindow title. Something like this (in RevitPythonShell):
import threading, clr
from System.Diagnostics import Process
# need winform libraries for feedback form only
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Form, Label
app = __revit__.Application
doc = __revit__.ActiveUIDocument.Document
ui = __revit__.ActiveUIDocument
def lookAtWindow(activeView):
# Looking for one of three conditions:
# 1. User is on a sheet (ActiveView will be DrawingSheet)
# 2. User is in an active ViewPort on a sheet (ActiveView will NOT be be DrawingSheet, but MainWindowTitle will contain " - [Sheet: " string)
# 3. User is on a View (neither of the previous two conditions)
result = False
if str(activeView.ViewType) == 'DrawingSheet':
result = 'Youre on a sheet'
else:
processes = list(Process.GetProcesses())
for process in processes:
window = process.MainWindowTitle
if window and 'Autodesk Revit '+app.VersionName[-4:] in window and ' - [Sheet: ' in window and ' - '+doc.Title+']' in window:
result = 'I reckon youre in a Viewport'
if not result:
result = 'so you must be in a '+str(activeView.ViewType)
form = Form()
form.Width = 300
form.Height = 100
label = Label()
label.Width = 280
label.Height = 70
label.Text = result
label.Parent = form
form.ShowDialog()
# need to close RevitPythonShell console before checking MainWindowTitle, so run on timer
threading.Timer(1, lookAtWindow, [ui.ActiveView]).start()
__window__.Close()

how to check if you cklicked (with your mouse) in an defined area in python/pygame

I want to check if you clicked in an defined area, for example an area from 0,0 to 400,40 (pixel coordinates). I have this:
x = 0
y = 0
(mouse_posx, mouse_posy) = pygame.mouse.get_pos()
press = pygame.key.get_pressed()
if mouse_posx > x and mouse_posx < x+400 and mouse_posy > y and mouse_posy < y+40 and press != 0:
function()
I get no error but it does nothing.
Can someone tell me what i am doing wrong?
What you want to do is have your program listen for events. Once the event of mouse button is triggered, then record the mouse position. Then verify if the mouse position is inside the box.
Pygame mouse
Oh and also, I dont think the event of mouse button gets recorded in the > pygame.keys.get_pressed <. I believe the pygame.keys.get_pressed is just for the keyboard.
I may be wrong, im using my mobile and dont have a computer with me.

Resources