I have a question regarding timers and publishers. I'm working with a timer that fires every second and does a small calculation and an update of two published values for counting down in the UI.
I've put everything that is not UI related on background threads and when the timer only runs the background code the interval is fine at 1.000 with some tolerance. But when the timer is also updating the two published values on the main thread the interval is between 1.015 and 1.019.
That doesnt sound much but after 20 minutes the total offset is at about 20 seconds.
Is there a best practice or a solution for that?
View:
#EnvironmentObject var timer: TimerData
let counter = Timer.publish(every: 1.0, tolerance: 0.2, on: .main, in: .common).autoconnect()
...
.onReceive(counter) { _ in
DispatchQueue.global(qos: .userInteractive).async {
timer.countingTime()
}
}
Model:
class TimerData: ObservableObject {
#AppStorage("time") var time: Int = 0
#AppStorage("lastAction") var lastAction: Double = 0
func countingTime() {
let now = Double(NSDate().timeIntervalSince1970)
let diff = Int(round(now - self.lastAction))
print(now - self.lastAction)
DispatchQueue.main.async {
self.lastAction = now
self.time -= diff
}
}
}
I've tried no tolerance value when creating the timer as well as a higher tolerance value. The only way I found to get the interval to the estimated 1.000 is to make "time" and "lastAction" not published (#AppStorage or #Published didnt made a difference). But I need both values published (I guess).
Related
Consider the following toy example
import SwiftUI
struct WeirdExample: View {
#State private var greetings = ["Hello", "Bye"]
#State private var count = 0
#State private var greetingIndex = 0
func modifyCount() {
count = (count + 1) % 100
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(150) , execute: modifyCount)
}
var body: some View {
VStack {
Spacer()
Text(greetings[greetingIndex])
Spacer()
Button("Change (maybe)"){
if count % 2 == 0 {
greetingIndex = (greetingIndex + 1) % 2
}
}
.padding()
}
.onAppear(){modifyCount()}
}
}
As far as I understand, everything in the example is running on the UI thread. However, I'm concerned by the fact that assigning to the count variable is not atomic. Is it conceivable to have a situation in which pressing the button catches that variable in a weird, undefined state? In other words, is an item dispatched to the main queue guaranteed to run uninterrupted until completion? Of course, being a serial queue guarantees that a job on the queue won't begin executing until the previous ones are finished running, but since there is a distinction between main thread and main queue, I wonder if there is reason for concern about this, or somehow the fact that everything is running on the main thread obviates any problems. Thanks.
but since there is a distinction between main thread and main queue.
The main queue is guaranteed to run on the main thread. It is also where the main RunLoop is, where #MainActor things run, and where the main OperationQueue runs, and all of these things are serialized. While there is a difference between threads and queues (and you're wise to keep that in mind), "main" is the one case where all of the relevant things are equivalent. No, there is no race condition here.
I am trying to code a blackjack deal button where the cards are dealt like so on hitting the button:
Deal face-up card to a player, delay 1 second
Deal face down card to a dealer, delay 1 second
Deal face-up card to a player, delay 1 second
Deal face-up card to a dealer, delay 1 second
My struggle is getting the delays in between each dealt card to make it seem more realistic as opposed to all 4 cards just appearing upon hitting the button. I currently have a deal function defined as so that is called when the deal button is hit:
for dealRound in 1...2{
var dealCard = deck.last
deck.removeLast()
Timer.scheduledTimer(withTimeInterval: 1, repeats: false){ (_) in
player1.hand.append(dealCard!)
}
//player1.hand.append(dealCard!)
switch dealCard!.rank {
case 1:
player1.cardScore += 1
player1.hasAce = true
case 11,12,13:
player1.cardScore += 10
default:
player1.cardScore += dealCard!.rank
}
dealCard = deck.last
deck.removeLast()
Timer.scheduledTimer(withTimeInterval: 1, repeats: false){ (_) in
dealer.hand.append(dealCard!)
}
//If dealRound == 2, dealing upcard to dealer so include that in score
//Otherwise the down card is not included in the score until after the downcard is revealed
if dealRound == 2{
switch dealCard!.rank {
case 1:
dealer.cardScore += 1
dealer.hasAce = true
case 11,12,13:
dealer.cardScore += 10
default:
dealer.cardScore += dealCard!.rank
}
}
}
isDealt = true
}
However, when I hit the deal button, there is a delay and then all 4 cards appear at once. The images for the cards are defined as:
Image(dealer.hand[1].suit.rawValue + String(dealer.hand[1].rank) ).resizable().frame(width:120, height:160).offset(x: 40, y: -40)
I use dealer.hand[0] and player.hand[0] and [1] to show the other cards. The player and dealer are State variables of a Player class. I have tried using DispatchQueue.main.asyncAfter and sleep(1) but have run into similar problems. The only thing I can think to do would be to create 4 separate state Boolean variables and have them set to true after a delay (in a separate function from the deal) so the cards will appear. However, this seems like a poor way of doing it and I feel as though there may be a way similar to what I am trying but can't figure out.
In short: how would I go about adding a delay to an image appearing where the image is dependent on a #State class's array being appended to in a function call?
I have a performance issue while displaying large amounts MapItems on a Map provided by the QML Location module. I already asked the question here (https://forum.qt.io/topic/79229/large-amount-of-qml-mapitems), but nobody could help me, so I wanted to try it here once. I also found this question (How to use the QML/QtLocation module for displaying a large amount of offline data on a map?), but before adding another dependency, I wanted to see if my code can be improved so that QML can handle this situation without any help.
I am currently trying to plot a large amount of items onto a QML Map (30,000 - 120,000 points). These items shall be updated dependent of the position of a QSlider. Performance decreases strongly from about 1,000 items upwards, when I use 30,000 it takes several minutes until the QML Map has all the data visualized and is responsive again. I have a machine which is absolutely capable of fulfilling this task in general, so I think the problem is QML. I am using Qt 5.8.
Is there any way to improve this performance or is it just not possible with a QML-map to plot so many MapItems at a time? I tried MapCircles, Polylines, Polygons and MapQuickItems with images, but for me it seems like the performance issue just arises from adding this amount of MapItems, as I could not see a significant difference in processing time between these types.
I have more data on the map visualized, which should not be refreshed every time the QSlider is moved. Even though I tried just to clear all MapItems and add the new ones for performance tests, but even this did not improve the performance.
My code (a bit abstracted) looks like this:
///-------------- Widget.cpp-----------------///
void ProcessInput(int qslider_pos) {
QVariantList lat_vec;
QVariantList lon_vec;
// Fill vectors with lateral and longitudinal positions
// ...
// Clean current points on map and draw new ones
SendToQmlFuncRemovePoints();
SendToQmlFuncAddPoints(lat_vec, lon_vec);
}
void QmlConnector::SendToQmlFuncRemovePoints()
{
QVariant returnedValue;
QMetaObject::invokeMethod(QmlMapSingleton::instance()->GetRoot(), "remove_points",
Q_RETURN_ARG(QVariant, returnedValue));
}
void QmlConnector::SendToQmlFuncAddPoints(QVariantList input_one, QVariantList input_two)
{
QVariant returnedValue;
QMetaObject::invokeMethod(QmlMapSingleton::instance()->GetRoot(), "add_points",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, QVariant::fromValue(input_one)), Q_ARG(QVariant, QVariant::fromValue(input_two)));
}
.
///-------------- Map.qml -----------------///
Map {
anchors.fill: parent
property variant points: ({})
property int pointCounter: 0
Plugin
{
id: osmplugin
name: "osm"
PluginParameter { name: "osm.mapping.highdpi_tiles"; value: true }
}
Component.onCompleted: {
points = new Array();
}
id: map
plugin: osmplugin
//Javascript functions
function add_points(array_lat, array_lon) {
var myArray = new Array()
var component = Qt.createComponent("mapcircle.qml");
for (var i=0; i<array_lat.length; i++)
{
var object = component.createObject(map, { "center": QtPositioning.coordinate(array_lat[i], array_lon[i]})
map.addMapItem(object)
myArray.push(object)
}
map.points = myArray
}
function remove_points() {
var count = map.points.length
for (var i = 0; i<count; i++){
map.removeMapItem(map.points[i])
map.points[i].destroy()
}
map.points = []
}
}
.
///-------------- mapcircle.qml -----------------///
import QtQuick 2.0
import QtLocation 5.6
MapCircle {
radius: 1
border.width: 0
color: 'green'
}
Qt says that the performance decreases with the number of elements added to the map. Do you need all the points to be visible on the map in the same time, if not you can play around with visibility.
Can't you use QQuickPaintedItem to paint the points in C++ and wrap it into an MapQuickItem, if you have multiple polygonsfor e.g? But also there are some limitation, you cannot have to big images displayed.
If you need all the points maybe you can have different points based on the map zoom level and reduce the number of points added to the map at small zoom level, as was recommended on the other group ...
I want to call a method at Irregular time interval means it should be random time plus i want it in some define range too.
Like : it should call at any second between 3 to 8 .
I tried this one :
[NSTimer scheduledTimerWithInterval: 1.0 target:self selector:#selector(myMethod:) userInfo:nil repeats: YES];
void mymethod()
{
if(arc4random() % 10 == 1)
{
// calling my method here;
}
}
This way , i am not getting randomization which i want.
Any one can please help me on this !!!
Here you can make a scheduler which will get called at random interval.
-(void)randomTimeScheduler{
int time = arc4random()%5;
int nextTimeOfCall = 3+time;
NSLog("it will be called after:%d",nextTimeOfCall);
[self performSelector:#selector(randomTimeScheduler) withObject:self afterDelay:nextTimeOfCall];
}
You have to call it from your class and then it will work as a scheduler. And it has finite interval time 3-8.
In coco2d game, update function is call every 1/60 of second.
this.scheduleUpdate();
update: function (dt) { }
The score Label is
var scoreLabel = cc.LabelTTF.create("0", "fantasy", 20, cc.size(0, 0), cc.TEXT_ALIGNMENT_LEFT);
scoreLabel.setPosition(cc.p(winSize.width - 80, winSize.height));
scoreLabel.schedule(function () {
var showingScore = parseInt(scoreLabel.getString());
if (showingScore < b2.getUserScore()) {
scoreLabel.setString((showingScore + 5)
.toString());
}
});
this.addChild(scoreLabel, 5);
How often is the scoreLabel being updated? How do you setup a timer to update every 2 seconds for the scoreLabel?
You can setup a schedule for this :
-(void) schedule: #SEL(selector) interval: ccTime ;
selector : your function that you want to update .
interval : time interval between each update .
you could once initialize Label then in a function (for example "Update Label") change it's
attributes .