Search code examples
winformsf#real-time-clock

How do I make an analog clock using winforms in F#?


I have to create an analog clock using winforms in F#. The clock needs also to have a label showing date and time in digital form. I have figured how make the label with time and date, as well as drawing the circle and the clockhands, but I'm having trouble with how i'm gonna implement the timer function to the clock hands. So they move, and move to what the time is right now.

I don't know how to call the things i need in the third last line. Is there anyone who can help me with this problem? Because as it stands right now I have no idea how to it.

open System  
open System.Windows.Forms  
open System.ComponentModel  
open System.Drawing  

// ********* Winforms specifics *********
let win = new Form()
win.ClientSize <- Size (400, 400)

/// ********* Working Digital Clock *********
// make a label to show time
let digitalTimerLabel = new Label ()
win.Controls.Add digitalTimerLabel
digitalTimerLabel.Width <- 200
digitalTimerLabel.Location <- new Point (140,300)
digitalTimerLabel.Text <- string System.DateTime.Now // get present time and date

// make a timer and link to label
let timer = new Timer ()
timer.Interval <- 1000 // create an event every 1000 millisecond
timer.Enabled <- true // activiate the timer
timer.Tick.Add (fun e ->
digitalTimerLabel.Text <- string System.DateTime.Now
win.Invalidate()
)

// ********* Translate the clock *********
let translate (d : Point) (arr : Point []) : Point [] =
    let add (d : Point) (p : Point) : Point =
        Point (d.X + p.X, d.Y + p.Y)
    Array.map (add d) arr

// ********* Rotate the clock hands *********
let rotate (theta : float) (arr : Point []) : Point [] =
        let toInt = int << round
        let rot (t : float) (p : Point) : Point =
            let (x, y) = (float p.X, float p.Y)
            let (a, b) = (x * cos t - y * sin t, x * sin t + y * cos t)
            Point (toInt a, toInt b)
        Array.map (rot theta) arr

/// ********* ClockHands (Ur-visere) *********
let myPaint (e : PaintEventArgs) : unit =
    // HourHand
    let black = new Pen (Color.Black,Width=2.0f)
    let hourHand =
    //   [bot cord]    [top cord]
        [|Point (0,0);Point (0,-45)|]
    e.Graphics.DrawLines (black, hourHand)

    // MinuteHand
    let red = new Pen (Color.Red,Width=4.0f)
    let minuteHand =
    //   [bot cord]    [top cord]
        [|Point (0,0);Point (0,-20)|]
    e.Graphics.DrawLines (red, minuteHand)

    // SecondHand
    let green = new Pen (Color.Green,Width=1.0f)
    let secondHand =
    //   [bot cord]    [top cord]
        [|Point (0,0);Point (0,-20)|]
    e.Graphics.DrawLines (green, secondHand)

    // Circle
    let circleBlack = new Pen(Color.Black,Width=4.0f)
    let circle =
        e.Graphics.DrawEllipse(circleBlack,-100.0f,-100.0f,200.0f,200.0f)
    circle

    // CenterDot
    let CenterDotBrush = new SolidBrush(Color.Red)
    let center =
        e.Graphics.FillEllipse(CenterDotBrush,-2.5f,-2.5f,5.0f,5.0f)
    center

    let dt = DateTime.Now
    let s = dt.Second
    let m = dt.Minute
    let h = dt.Hour
    let newS = rotate (float s/60.0*2.0*System.Math.PI) secondHand
    let newM = rotate (float m/60.0*2.0*System.Math.PI) minuteHand
    let newH = rotate (float h/12.0*2.0*System.Math.PI) hourHand
    let finalS = translate (Point (200, 200)) secondHand
    let finalM = translate (Point (200, 200)) minuteHand
    let finalH = translate (Point (200, 200)) hourHand
    ()

win.Paint.Add myPaint

Application.Run win // start event - loop

Solution

  • I have sorted it out. It works now:

    open System
    open System.Windows.Forms
    open System.Drawing  
    
    // ********* Winforms specifics *********
    // Extended the default Form to avoid display flickered
    type SmoothForm() as x =
        inherit Form()
        do x.DoubleBuffered <- true
    
    let win = new SmoothForm()
    win.ClientSize <- Size (400, 400)
    
    /// ********* Digital Clock *********
    // make a label to show time
    let digitalTimerLabel = new Label ()
    win.Controls.Add digitalTimerLabel
    digitalTimerLabel.Width <- 200
    digitalTimerLabel.Location <- Point (150,320)
    
    // Timer
    let timer = new Timer ()
    timer.Interval <- 1000 // create an event every 1000 millisecond
    timer.Enabled <- true // activiate the timer
    timer.Tick.Add (fun _e ->
        digitalTimerLabel.Text <- string System.DateTime.Now
        win.Invalidate()
    )
    
    // ********* Translate function *********
    let translate (d : Point) (arr : Point []) : Point [] =
        let add (d : Point) (p : Point) : Point =
            Point (d.X + p.X, d.Y + p.Y)
        Array.map (add d) arr
    
    // ********* Rotate the clock hands *********
    let rotate (theta : float) (arr : Point []) : Point [] =
        let toInt = int << round
        let rot (t : float) (p : Point) : Point =
            let (x, y) = (float p.X, float p.Y)
            let (a, b) = (x * cos t - y * sin t, x * sin t + y * cos t)
            Point (toInt a, toInt b)
        Array.map (rot theta) arr
    
    
    /// ********* ClockHands (Ur-visere) *********
    let myPaint (e : PaintEventArgs) : unit =
        // HourHand
        let black = new Pen (Color.Black,Width=4.0f)
        let hourHand = 
            [|Point (0,0); Point (0,-60)|]
    
        // MinuteHand
        let red = new Pen (Color.Red,Width=4.0f)
        let minuteHand =
            [|Point (0,0);Point (0,-90)|]
    
        // SecondHand
        let green = new Pen (Color.Green,Width=1.0f)
        let secondHand =
            [|Point (0,0);Point (0,-90)|]
    
        // Circle
        let circleBlack = new Pen(Color.Black,Width=4.0f)
        let circle =
            e.Graphics.DrawEllipse(circleBlack,100.0f,100.0f,200.0f,200.0f)
        circle
    
        //Circle color
        let circlecolorBrush = new SolidBrush(Color.LightSalmon)
        let circle =
            e.Graphics.FillEllipse(circlecolorBrush,100.0f,100.0f,200.0f,200.0f)
        circle
    
        // CenterDot
        let centerDotBrush = new SolidBrush(Color.Red)
        let center =
            e.Graphics.FillEllipse(centerDotBrush,197.5f,197.5f,5.0f,5.0f)
        center
    
        // Time
        let dt = DateTime.Now
        let s = dt.Second
        let m = dt.Minute
        let h = dt.Hour
    
        // Make Rotation
        let secondHand = rotate (float s/60.0*2.0*System.Math.PI) secondHand
        let minuteHand = rotate (float m/60.0*2.0*System.Math.PI) minuteHand
        let hourHand   = rotate (float h/12.0*2.0*System.Math.PI) hourHand
        // Make Translate (Moving Point)
        let secondHand = translate (Point (200, 200)) secondHand
        let minuteHand = translate (Point (200, 200)) minuteHand
        let hourHand   = translate (Point (200, 200)) hourHand
        e.Graphics.DrawLines (black, hourHand)
        e.Graphics.DrawLines (red,   minuteHand)
        e.Graphics.DrawLines (green, secondHand)
    
    win.Paint.Add myPaint
    
    Application.Run win // start event - loop