Предыдущая тема

Вариантов отобразить электронные часы на языке программирования С++ очень множество, в данной статье рассмотрим электронные часы для 2D игр написанных используя мультимедийную библиотеку SFML. Для удобства дальнейшего внедрения данного кода в наши проекты создадим класс электронные часы, в котором реализуем возможность менять шрифт, размер и цвет электронных часов.

Для написания кода нам понадобится IDE Visual Studio, С++ проект с настроенной библиотекой SFML, шаблон кода SFML, иконки и шрифты.

Шаблон кода SFML C++

#include <SFML/Graphics.hpp>

using namespace sf;

int main()
{      
    // Графическое окно размером 900х900 с  названием "Часы"
    RenderWindow window(VideoMode(900, 900), L"Часы", Style::Default);
    // Иконка графического окна
    Image icon;
    if (!icon.loadFromFile("Image/clock.png"))  //Загрузка иконки
    {
        return 3; // Возвращает 3 если иконка не загрузилась 
    }
    window.setIcon(128, 128, icon.getPixelsPtr()); // Ссылка на иконку
    // Рабочий цикл графического окна
    while (window.isOpen())
    {
        // Переменная для хранения события 
        Event event;
        // Цикл обработки событий
        while (window.pollEvent(event))
        {
            // Обработка события нажатия на крестик графического окна
            if (event.type == Event::Closed) window.close();              
        }
        // Очистка графического окна, с окраской фона в синий цвет
        window.clear(Color::Blue);
        // Вывод графики в графическое окно      
        window.display();
    }
    return 0;
}
Выполнение кода шаблона SFML C++
Выполнение кода шаблона SFML C++

Если Вы с первой часть справились успешно, тогда можем добавить к нашему проекту новый класс назовём его SFMLWorldTime.

Объявление закрытых свойств класса SFMLWorldTime, файл SFMLWorldTime.h

#pragma once
#include <SFML/Graphics.hpp>

class SFMLWorldTime
{
	struct tm newtime;
	__time64_t long_time;
	errno_t err;

	int position_x, position_y;

	int font_size;

	sf::Font time_font;

	sf::Color time_color;

	sf::Text clock_text;
	
};

newtime содержит необходимые поля для отображения времени: часы, минуты и секунды. long_time - переменная системного времени. err - сохраняет код ошибки при неверном подключении времени.

position_x, position_y - координаты электронных часов.

font_size - размер шрифта электронных часов.

Электронные часы
Электронные часы
Элементы времени
Элементы времени
Элементы границ
Элементы границ
Элементы дополнительных нулей
Элементы дополнительных нулей

time_font - хранит шрифт электронных часов. Переменная time_color хранит цвет электронных часов. clock_text - текстовый объект, который рисует элементы электронных часов в графическом окне.

Объявление закрытых методов класса SFMLWorldTime, файл SFMLWorldTime.h

void InitText();

void what_time();

Метод InitText устанавливает настройки объекта clock_text.

Метод what_time записывает текущее системное время в структуру newtime.

Определение закрытых методов класса SFMLWorldTime, файл SFMLWorldTime.cpp

void SFMLWorldTime::InitText()
{
    clock_text.setFont(time_font);          // тип шрифта
    clock_text.setCharacterSize(font_size); // размер шрифта
    clock_text.setFillColor(time_color);    // цвет шрифта
}

void SFMLWorldTime::what_time()
{
    _time64(&long_time); // получаем системное время
    // записываем системное время в структуру newtime
    err = _localtime64_s(&newtime, &long_time);
    if (err) exit(23); // при возникновении ошибки выходим из программы и возвращаем код ошибки 23
}

Объявление конструктора класса SFMLWorldTime, файл SFMLWorldTime.h

public:
    SFMLWorldTime(int x = 50, int y = 50, int size = 3, sf::Color mycolor = sf::Color::White, std::string font = "lib/Электро.ttf");

Определение конструктора класса SFMLWorldTime, файл SFMLWorldTime.cpp

SFMLWorldTime::SFMLWorldTime(int x, int y, int size, sf::Color mycolor, std::string font)
{
    (size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
    position_x = x;
    position_y = y;
    if (!time_font.loadFromFile(font)) exit(13);
    time_color = mycolor;
    font_size = size * 10;
    InitText();
}

Объявление открытых методов класса SFMLWorldTime, файл SFMLWorldTime.h

void setposition(int x, int y);

void setcolor(sf::Color color);

void setTime_size(int size);

int getsec();

int getmin();

int gethour();

void drawTime(sf::RenderWindow& window);

Методы: setposition, setcolor, setTime_size устанавливаю свойства электронных часов, координаты, цвет и размер.

Методы: getsec, getmin, gethour, возвращаю целочисленное значение системного времени секунды, минуты, часы.

Метод drawTime рисует электронные часы в графическом окне. В параметрах передаётся объект графическое окно.

Определение открытых методов класса SFMLWorldTime, файл SFMLWorldTime.cpp

void SFMLWorldTime::setposition(int x, int y)
{
    position_x = x;
    position_y = y;
}

void SFMLWorldTime::setcolor(sf::Color color)
{
    time_color = color;
    InitText();
}

void SFMLWorldTime::setTime_size(int size)
{
    (size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
    font_size = size * 10;
    InitText();
}

int SFMLWorldTime::getsec()
{
    what_time();  // обновление свойств структуры newtime
    return newtime.tm_sec;
}

int SFMLWorldTime::getmin()
{
    what_time();
    return newtime.tm_min;
}

int SFMLWorldTime::gethour()
{
    what_time();
    return newtime.tm_hour;
}

Определение метода drawTime

void SFMLWorldTime::drawTime(sf::RenderWindow& window)
{
    what_time();
    std::string tmpstr;

    if (newtime.tm_hour < 10) 
    {
        tmpstr = "0";
        tmpstr.append(std::to_string(newtime.tm_hour));
    }
    else tmpstr = std::to_string(newtime.tm_hour);
    tmpstr.append(":");
    if (newtime.tm_min < 10) tmpstr.append("0"); 
    tmpstr.append(std::to_string(newtime.tm_min));
    tmpstr.append(":");
    if (newtime.tm_sec < 10) tmpstr.append("0");
    tmpstr.append(std::to_string(newtime.tm_sec));

    clock_text.setPosition(position_x, position_y);
    clock_text.setString(tmpstr);
    window.draw(clock_text);
}
  

В методе drawTime проверяется условие для отображения элементов электронных часов, если элемент, например секунды меньше 10, тогда к нему добавляется ноль, который устанавливается перед секундами. Все элементы электронных часов склеиваются в одну строку с помощью метода append() и сохраняются в переменную tmpstr. Функция to_string() переводит целое значение в строковое.

После формирования строки с электронными часами, она выводится в графическое окно с помощью объекта window и метода draw().

Объект электронные часы SFMLWorldTime.

В функции main объявляем тип SFMLWorldTime (электронные часы) и с помощью цикла for выводим в графическое окно 10 электронных часов разных размеров.

#include "SFMLWorldTime.h"

using namespace sf;

int main()
{
        
    RenderWindow window(VideoMode(900, 900), L"Часы", Style::Default);
    
    Image icon;
    if (!icon.loadFromFile("Image/clock.png"))
    {
        return 3;
    }

    window.setIcon(128, 128, icon.getPixelsPtr());

   SFMLWorldTime mytime(50,50,4);
   mytime.setcolor(Color::Yellow);
           
    while (window.isOpen())
    {
        Event event;
        while (window.pollEvent(event))
        {
            if (event.type == Event::Closed) window.close();
               
        }
        
        window.clear(Color::Blue);

        for (int i = 10, kof = 0, kof2 = 20, posy = 50, posx = 200; i > 0; i--,
            kof += 10, posy += 130 - kof, kof -= 2, posx += kof2)
        {
            mytime.setTime_size(i);
            mytime.setposition(posx, posy);
            mytime.drawTime(window);
        }
        
        window.display();
    }
    return 0;
}

Полный код файла SFMLWorldTime.h

#pragma once
#include <SFML/Graphics.hpp>

class SFMLWorldTime
{
	struct tm newtime;
	__time64_t long_time;
	errno_t err;

	int position_x, position_y;

	int font_size;

	sf::Font time_font;

	sf::Color time_color;

	sf::Text clock_text;

	void InitText();

	void what_time();

public:

	SFMLWorldTime(int x = 50, int y = 50, int size = 3, sf::Color mycolor = sf::Color::White, std::string font = "lib/Электро.ttf");

	void setposition(int x, int y);

	void setcolor(sf::Color color);

	void setTime_size(int size);

	int getsec();

	int getmin();

	int gethour();
	
	void drawTime(sf::RenderWindow& window);

};
	

Полный код файла SFMLWorldTime.cpp

#include "SFMLWorldTime.h"

SFMLWorldTime::SFMLWorldTime(int x, int y, int size, sf::Color mycolor, std::string font)
{
    (size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
    position_x = x;
    position_y = y;
    if (!time_font.loadFromFile(font)) exit(13);
    time_color = mycolor;
    font_size = size * 10;
    InitText();
}

void SFMLWorldTime::drawTime(sf::RenderWindow& window)
{

    what_time();
    std::string tmpstr;

    if (newtime.tm_hour < 10) 
    {
        tmpstr = "0";
        tmpstr.append(std::to_string(newtime.tm_hour));
    }
    else tmpstr = std::to_string(newtime.tm_hour);
    tmpstr.append(":");
    if (newtime.tm_min < 10) tmpstr.append("0"); 
    tmpstr.append(std::to_string(newtime.tm_min));
    tmpstr.append(":");
    if (newtime.tm_sec < 10) tmpstr.append("0");
    tmpstr.append(std::to_string(newtime.tm_sec));

    clock_text.setPosition(position_x, position_y);
    clock_text.setString(tmpstr);
    window.draw(clock_text);
}

void SFMLWorldTime::InitText()
{
    clock_text.setFont(time_font);
    clock_text.setCharacterSize(font_size);
    clock_text.setFillColor(time_color);
}

void SFMLWorldTime::what_time()
{
    _time64(&long_time);
    err = _localtime64_s(&newtime, &long_time);
    if (err) exit(23);
}

void SFMLWorldTime::setposition(int x, int y)
{
    position_x = x;
    position_y = y;
}

void SFMLWorldTime::setcolor(sf::Color color)
{
    time_color = color;
    InitText();
}

void SFMLWorldTime::setTime_size(int size)
{
    (size > 10) ? size = 10 : (size < 1) ? size = 1 : size = size;
    font_size = size * 10;
    InitText();
}

int SFMLWorldTime::getsec()
{
    what_time();
    return newtime.tm_sec;
}

int SFMLWorldTime::getmin()
{
    what_time();
    return newtime.tm_min;
}

int SFMLWorldTime::gethour()
{
    what_time();
    return newtime.tm_hour;
}

Более подробную инструкцию вы можете получить посмотрев видео "Электронные часы SFML C++".

Предыдущая тема

Комментарии (20)


  1. JordanCpp
    12.12.2022 22:58
    +1

    У вас много дублированного кода. Вместо создания и копирования методов отличающихся лишь аргументами. Сделайте один метод и пусть он принимает 4-5 параметра. Это методы SFMLWorldTime.

    Плюсом будет сделать клас тайм инкапсулирующий функционал time.h и уже данный класс передавать как зависимость в класс SFMLWorldTime. Это разобьёт ваш монолитный класс на два. Что повысит читабельность и упростит отделение рисования времени от вычисления времени.

    Привыкайте сразу писать по возможности понятный и читаемый код.

    Обязательно продолжайте.


    1. Troyy Автор
      12.12.2022 23:19

      Спасибо большое !


  1. JordanCpp
    12.12.2022 23:44

    Метод SFMLWorldTime::drawTime тяжело читается. Лучше вынести рисование в отдельный метод с установкой цвета, положения и т. д drawTime(int sec, int minutes, int hours) А в самом drawTime 'е вызывать метод drawDigit(int x, int y, color)

    Ещё смущает частое дерганье clock_text.setString и window.draw(clock_text).

    В идеале нужно сначала сформировать строку часов минут секунд и потом уже одним вызовом отрисовать.

    И делать window.draw(clock_text) один раз в конце цикла событий.


    1. Troyy Автор
      13.12.2022 21:34

      Согласен с Вами! Немного модифицирую и обновлю статью.


  1. JordanCpp
    12.12.2022 23:47
    +2

    Ещё замечу. Залейте примеры на гитхаб и добавьте в проект cmake. Тогда сборка ваших примеров будет элементарна. Клонируем репозиторий и жмём в студии собрать. Красота и минимум телодвижений.


    1. Troyy Автор
      13.12.2022 21:34

      Ок)


  1. Tujh
    14.12.2022 09:55
    +1

    Недостаток статьи - используемый стандарт языка на уровне С++03. Многое было бы проще с использованием современного С++.

    На вскидку - std::chrono, to_string() вместо ostingstream и т.д.


    1. Tujh
      14.12.2022 09:59
      +1

      Ещё замечание, никогда не пишите это в заголовочных файлах:

      using namespace sf;
      
      using namespace std;

      это считается очень дурным стилем.


      1. Troyy Автор
        14.12.2022 11:12

        Спасибо код уже обновил


      1. yatanai
        14.12.2022 12:03

        А внутри методов-функций можно? ????


        1. Troyy Автор
          14.12.2022 14:11

          Можно код разместить вне класса внутри функций


        1. Tujh
          14.12.2022 17:43

          В примере using размещено в самом начале файла, а не в методах.

          http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rs-using-directive

          Да и, опять же, сложные методы, требующие using в теле, я бы тоже перенёс в cpp/cxx файл, а для коротких - четыре-пять лишних символов не большая помеха. И в целом, хорошим тоном, даже для методов, считается вкладывать не всё пространство имён, а только нужную часть.


  1. JordanCpp
    14.12.2022 15:48

    Только сейчас посмотрел видео. Не лучше ли озвучить ролик своим голосом, а не этим роботизированным?


    1. Troyy Автор
      14.12.2022 18:18

      Возможно в будущем


  1. JordanCpp
    14.12.2022 19:54

    Я бы убрал из класса SFMLWorldTime "struct tm newtime" и перенес работу с датой в класс который бы возвращал время в секундах, минутах и часах. SFMLWorldTime переименовал в SFMLTimePainter. SFMLWorldTime может возвращать время по Now

    Вот пример.

    https://github.com/JordanCpp/Lt/blob/master/src/Lt/Core/DateTime.cpp

    Оборачиваете struct tm в DateTime

    Это все на ваше усмотрение. Я понимаю, что это всего лишь пример. Может и не стоит настолько мелко разбивать задачу.


    1. Tujh
      14.12.2022 21:55
      +1

      Оборачиваете struct tm в DateTime

      Но зачем если есть std::chrono?

      Потребуется С++20 или внешняя библиотека для предыдущих стандартов

      #include <chrono>
      
      int
      main()
      {
          using namespace std::chrono;
      
          // Get a local time_point with system_clock::duration precision
          auto now = zoned_time{current_zone(), system_clock::now()}.get_local_time();
      
          // Get a local time_point with days precision
          auto ld = floor<days>(now);
      
          // Convert local days-precision time_point to a local {y, m, d} calendar
          year_month_day ymd{ld};
      
          // Split time since local midnight into {h, m, s, subseconds}
          hh_mm_ss hms{now - ld};
      
          // This part not recommended.  Stay within the chrono type system.
          int year{ymd.year()};
          int month = unsigned{ymd.month()};
          int day = unsigned{ymd.day()};
          int hour = hms.hours().count();
          int minute = hms.minutes().count();
          int second = hms.seconds().count();
      }

      https://stackoverflow.com/questions/61190884/current-time-and-date-in-c20-days

      библиотека для С++11/14/17

      https://github.com/HowardHinnant/date


      1. Troyy Автор
        15.12.2022 11:31

        Попробую С++20


        1. Troyy Автор
          15.12.2022 12:52

          #include <chrono>   
          #include <iostream>
          #include <thread>
          
          int main() { 
            using namespace std::chrono;
          while (true)
          {   // получаем локальное время
              auto now = zoned_time{ current_zone(), system_clock::now() }.get_local_time();
              // получаем точность локального времени день
              auto ld = floor<days>(now);
              // получаем точность суточное время 
              hh_mm_ss hms{ now - ld };
              // форматированный вывод времени в консоль часы, минуты, секунды
              std::cout << "\n\n\n\t\t\t" << hms.hours().count() << ":" << hms.minutes().count() << ":" << hms.seconds().count();
              // задержка 500 милисекунд 
              std::this_thread::sleep_for(500ms);
              // очистка экрана
              system("cls");
          }
          

          }


  1. JordanCpp
    14.12.2022 19:56

    Найду время и подрефакторю код. Выложу на гитхаб. Типа как бы я сделал:)


    1. Troyy Автор
      14.12.2022 21:28

      Буду рад )))