Non-coding layman failed in putting steps in absolute numbers on plain watch face

Former Member
Former Member
Hi,

I've no experience in coding but I have been attempting to design my own watch face. So far, I have gotten the date and time the way I like it based on some stuff I found via google. But I have not had any success on steps. I have googled around and check various sources (including asking for tips from one of the senior members here) but couldn't figure out how to do it. The sample found in the sample folder of the SDK does it in terms of a bar and I can't for the life of me figure out how to change that to integers. So I was wondering if someone can see where I went wrong. I got the watch face to show, but the steps counter doesn't. And when I simulated the steps data, nothing changes until I get an error message:

"Failed invoking <symbol>
Invalid Font Specified
in onUpdate (C:\Users\....\workspace\bare_bones\source\bare_bonesView.mc:43)



using Toybox.WatchUi as Ui;
using Toybox.Graphics as Gfx;
using Toybox.System as Sys;
using Toybox.Lang as Lang;
using Toybox.Time as Time;
using Toybox.Time.Gregorian as Calendar;
using Toybox.ActivityMonitor as Act;

class bare_bonesView extends Ui.WatchFace {

function initialize() {
WatchFace.initialize();
}

// Load your resources here
function onLayout(dc) {
setLayout(Rez.Layouts.WatchFace(dc));
}

// Called when this View is brought to the foreground. Restore
// the state of this View and prepare it to be shown. This includes
// loading resources into memory.
function onShow() {
}

// Update the view
function onUpdate(dc) {
// Get and show the current day
var dateInfo = Calendar.info( Time.now(), Calendar.FORMAT_MEDIUM );
var dateString = Lang.format("$1$ $2$ $3$", [ dateInfo.day_of_week, dateInfo.day, dateInfo.month ]);
var date = View.findDrawableById("DateLabel"); date.setText(dateString);

// Get and show the current time
var clockTime = Sys.getClockTime();
var timeString = Lang.format("$1$:$2$", [clockTime.hour.format("%02d"), clockTime.min.format("%02d")]);
var view = View.findDrawableById("TimeLabel");
view.setText(timeString);

// Get and show the steps (this part's screwed)
var activityInfo = Act.getInfo();
var stepsString = activityInfo.steps;
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
dc.drawText(50, 100, stepsString, Gfx.FONT_SYSTEM_LARGE, Gfx.TEXT_JUSTIFY_RIGHT);
// end of screwed part


// Call the parent onUpdate function to redraw the layout
View.onUpdate(dc);
}

// Called when this View is removed from the screen. Save the
// state of this View here. This includes freeing resources from
// memory.
function onHide() {
}

// The user has just looked at their watch. Timers and animations may be started here.
function onExitSleep() {
}

// Terminate any active timers and prepare for slow updates.
function onEnterSleep() {
}

}
  • the step info is retrieved through Toybox.ActivityMonitor.getInfo()

    my first tutorial has an example on how to access it: http://starttorun.info/tutorial-1-create-a-connect-iq-datafield/
  • This:
    dc.drawText(50, 100, stepsString, Gfx.FONT_SYSTEM_LARGE, Gfx.TEXT_JUSTIFY_RIGHT);

    should be this:
    dc.drawText(50, 100, Gfx.FONT_SYSTEM_LARGE, stepString,Gfx.TEXT_JUSTIFY_RIGHT);

    (you were passing stepString where it expected a font, and that's why there was a font erreo)

    and

    var stepsString = activityInfo.steps;

    should be this if you want a string:

    var stepsString = activityInfo.steps.toString().

    (you can also use something like format() here)
  • Two notes...

    First, if you want to mix using layouts and explicit draw calls, you'll want to move the call to View.onUpdate(dc) to occur before you do do any calls that draw to the dc. As your code is currently written, the text that you are explicitly drawing is going to be hidden when the call to the base class clears the view.

    Second, it might be a good idea to modify the layout definition to add a label for the steps text. That would allow you to write the new code in a manner that is similar to what is already in place until you've found your way around.

    Travis
  • Yup Travis, I missed the layout bit. I saw the dc call and stopped after that.

    I'd probably go with using the layout for everything in this case.
  • I guess I can put it as "var steps = View.findDrawableById("StepsLabel"); steps.setText(stepsString);"?

    Exactly. You'd need to add another label element to the layout with the id="StepsLabel", and then that code would do what you want.

    To clarify your second suggestion, you're saying that if I still want to use the dc, I should move that part within View.onUpdate(dc)?

    You can't put code inside View.onUpdate(). That function is defined within the ConnectIQ framework.

    But if I am going to use the layouts method, then I should leave it within "function onLayout(dc)"?

    If you want to draw via a call to one of the dc.draw(...) functions, you need to do it after the call to View.onUpdate(dc). If you use the technique outlined and add a new label to the layout, you can leave your code as-is.

    To illustrate...

    // if you want to mix layouts and dc calls

    function onUpdate(dc) {
    function onUpdate(dc) {
    // Get and update the current day
    var date = View.findDrawableById("DateLabel");

    var dateInfo = Calendar.info( Time.now(), Calendar.FORMAT_MEDIUM );
    date.setText(Lang.format("$1$ $2$ $3$", [
    dateInfo.day_of_week,
    dateInfo.day,
    dateInfo.month
    ]));

    // Get and update the current time
    var time = View.findDrawableById("TimeLabel");

    var clockTime = Sys.getClockTime();
    time.setText(Lang.format("$1$:$2$", [
    clockTime.hour.format("%02d"),
    clockTime.min.format("%02d")
    ]));

    // Call the parent onUpdate function to clear the screen and draw things defined in the layout
    View.onUpdate(dc);

    // Draw on top of everything drawn as part of the layout
    var activityInfo = Act.getInfo();

    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.drawText(50, 100, Gfx.FONT_SYSTEM_LARGE, activityInfo.steps.toString(), Gfx.TEXT_JUSTIFY_RIGHT);
    }


    // if you want to use layouts only

    function onUpdate(dc) {
    // Get and update the current day
    var date = View.findDrawableById("DateLabel");

    var dateInfo = Calendar.info( Time.now(), Calendar.FORMAT_MEDIUM );
    date.setText(Lang.format("$1$ $2$ $3$", [
    dateInfo.day_of_week,
    dateInfo.day,
    dateInfo.month
    ]));

    // Get and update the current time
    var time = View.findDrawableById("TimeLabel");

    var clockTime = Sys.getClockTime();
    time.setText(Lang.format("$1$:$2$", [
    clockTime.hour.format("%02d"),
    clockTime.min.format("%02d")
    ]));

    // Get and update the step information
    var steps = View.findDrawableById("StepsLabel");

    var activityInfo = Act.getInfo();
    steps.setText(activityInfo.steps.toString());


    // Call the parent onUpdate function to clear the screen and draw things defined in the layout
    View.onUpdate(dc);
    }




    Travis
  • In your fillRectangle() call, you're setting x and y for the upper left corner and using a height based on battery level, so it will empty from the bottom.

    For the "y" part, you want to calculate that based on the bottom of what you want for the battery area and the battery level for the height using that Y

    Let's say your battery is in a rectangle at 10,12, is 15 wide, and 40 high, and the battery level is 75%

    right now you are doing the fillRectange() for 10,12, 15 wide and 30 high, filling the top part.

    you want to do something like (10+40-30) for y (top of your area+height of your area, minus the value based on the battery level) and height would be 30.

    at 100% your "y" would be 10+40-40, and the height would be 40, filling the whole thing.
  • The alternate approach would be to draw the battery fully charged first, and then draw the proportion of the charge that has already been consumed (starting from the top-left of the rectangle representing the battery level).
  • True, but that would mean doing an extra draw every time.
  • I'm not sure why that would be. jaesurn is using a glyph in his/her custom font for the empty battery cell, so I'll assume it is just as easy to replace it with (or simply add) a glyph that represents a fully charged battery cell. It's still a single draw whether to overlay the remaining charge or the consumed charge on the glyph.

    Personally I think drawing inside an empty battery cell is preferable simply because it allows for easy customisation of the colour of the remaining battery level.
  • It doesn't look like he's using a custom font, but yes, if the body of the batter is drawn with a fillRectangle(), and part used is done with a fillRectange() in the background color is wouldn't be more draws.