admin管理员组文章数量:1621657
第二十一章:图形用户界面 习题答案
- 习题要求所设计的函数类和数据图类
- My_window.h
- My_window.cpp
- main.cpp
- 21.1
- 21.2
- 21.3
- 21.4
- 21.5
- 21.6 Clock
- 21.7 Plane
- 21.8 Currency Exchange Rate
- 21.9 Calculator GUI
习题要求所设计的函数类和数据图类
My_window.h
#include <iostream>
#include<random>
#include<cmath>
#include<map>
#include "../../GUI/Graph.h" // get access to our graphics library facilities
#include "../../GUI/Gui.h"
#include "../../GUI/Window.h"
#include"../../GUI/My_Shape_V2.h" //我把第19章创建的 My_Shape_V2 实现放到 GUI 文件夹中了
using namespace Graph_lib;
double statement(istream& is);
void read_exchange_rate(const string& fname, map<string, double> er);
//------------------------------------------------------------------------------
namespace My_windows {
using Graph_lib::Window;
using Graph_lib::Rectangle;
//------------------------------------------------------------------------------
struct My_window : Graph_lib::Window {
My_window(Point xy, int w, int h, const string& title);
bool wait_for_button(); // simple event loop
protected:
Button next_button; // the "next" button
Button quit_button; // the "quit" button
bool button_pushed; // implementation detail
static void cb_next(Address, Address); // callback for next_button
static void cb_quit(Address, Address);
virtual void next(); // action to be done when next_button is pressed
virtual void quit();
};
//------------------------------------------------------------------------------
struct Button_window : My_window {
Button_window(Point xy, int w, int h, const string& title);
private:
// widgets
Menu menu1;
Menu menu2;
Menu menu3;
Menu menu4;
Out_box xy_out;
// actions invoked by callback: relabel button, print coordinates
void reset_labels(); // helper function
void action(Button& b);
void action11() { action(menu1.selection[0]); }
void action12() { action(menu1.selection[1]); }
void action13() { action(menu1.selection[2]); }
void action14() { action(menu1.selection[3]); }
void action21() { action(menu2.selection[0]); }
void action22() { action(menu2.selection[1]); }
void action23() { action(menu2.selection[2]); }
void action24() { action(menu2.selection[3]); }
void action31() { action(menu3.selection[0]); }
void action32() { action(menu3.selection[1]); }
void action33() { action(menu3.selection[2]); }
void action34() { action(menu3.selection[3]); }
void action41() { action(menu4.selection[0]); }
void action42() { action(menu4.selection[1]); }
void action43() { action(menu4.selection[2]); }
void action44() { action(menu4.selection[3]); }
// callback functions
static void cb_action11(Address, Address pw) { reference_to<Button_window>(pw).action11(); }
static void cb_action12(Address, Address pw) { reference_to<Button_window>(pw).action12(); }
static void cb_action13(Address, Address pw) { reference_to<Button_window>(pw).action13(); }
static void cb_action14(Address, Address pw) { reference_to<Button_window>(pw).action14(); }
static void cb_action21(Address, Address pw) { reference_to<Button_window>(pw).action21(); }
static void cb_action22(Address, Address pw) { reference_to<Button_window>(pw).action22(); }
static void cb_action23(Address, Address pw) { reference_to<Button_window>(pw).action23(); }
static void cb_action24(Address, Address pw) { reference_to<Button_window>(pw).action24(); }
static void cb_action31(Address, Address pw) { reference_to<Button_window>(pw).action31(); }
static void cb_action32(Address, Address pw) { reference_to<Button_window>(pw).action32(); }
static void cb_action33(Address, Address pw) { reference_to<Button_window>(pw).action33(); }
static void cb_action34(Address, Address pw) { reference_to<Button_window>(pw).action34(); }
static void cb_action41(Address, Address pw) { reference_to<Button_window>(pw).action41(); }
static void cb_action42(Address, Address pw) { reference_to<Button_window>(pw).action42(); }
static void cb_action43(Address, Address pw) { reference_to<Button_window>(pw).action43(); }
static void cb_action44(Address, Address pw) { reference_to<Button_window>(pw).action44(); }
};
//------------------------------------------------------------------------------
inline int rand_int(int min, int max)
{
static default_random_engine ran;
return uniform_int_distribution<>{min, max}(ran);
}
struct Image_button : Button {
Image_button(Point xy, int w, int h, const string& label,
const string& image_fname, Callback cb);
void move(int dx, int dy) override;
void attach(Window&) override;
private:
Image img;
};
struct Image_button_window : My_window {
Image_button_window(Point xy, int w, int h, const string& title, const string& fname);
private:
Image_button ib;
void pos_change();
static void cb_ib(Address, Address pw) { reference_to<Image_button_window>(pw).pos_change(); }
};
//------------------------------------------------------------------------------
struct Draw_shape_window : My_window {
Draw_shape_window(Point xy, int w, int h, const string& title);
private:
In_box orign_x;
In_box orign_y;
In_box rlen; //radius or length of side
Button clear_button;
Button shape_menu_button;
bool shape_button_pushed;
Menu shape_menu;
protected:
Point current; //当前形状的位置
Vector_ref<Shape> rshapes;
private:
int i_begin; //用于标记清屏后rshapes的起始位置
static void cb_clear(Address, Address pw) { reference_to<Draw_shape_window>(pw).clear_pressed(); }
static void cb_menu(Address, Address pw) { reference_to<Draw_shape_window>(pw).menu_pressed(); }
static void cb_circle(Address, Address pw) { reference_to<Draw_shape_window>(pw).circle_pressed(); }
static void cb_square(Address, Address pw) { reference_to<Draw_shape_window>(pw).square_pressed(); }
static void cb_eqtri(Address, Address pw) { reference_to<Draw_shape_window>(pw).eqtri_pressed(); }
static void cb_rhexag(Address, Address pw) { reference_to<Draw_shape_window>(pw).rhexag_pressed(); }
//按下按钮后,回调函数调用的功能函数
void clear_pressed();
void menu_pressed();
void circle_pressed();
void square_pressed();
void eqtri_pressed();
void rhexag_pressed();
void add_shape(Shape*);
void hide_menu();
protected:
struct Shape_info {
Point xy;
int rlen;
};
bool get_shape_info(Shape_info& si);
};
//------------------------------------------------------------------------------
struct Move_shape_window : Draw_shape_window {
Move_shape_window(Point xy, int w, int h, const string& title);
private:
In_box mov_x; //要移动到的新位置
In_box mov_y;
void next() override; //覆盖next按钮原功能,设置为新的移动功能
};
//------------------------------------------------------------------------------
struct Clock_dial : Shape {
Clock_dial(Point cen, int rr);
void draw_lines() const override;
void move(int dx, int dy) override;
private:
Circle center; //中心点
Circle outline; //外圈表盘
Vector_ref<Text> digital; //外圈12个数字
Vector_ref<Line> dial; //外圈60个刻度
};
//------------------------------------------------------------------------------
struct Clock_hands : Shape {
Clock_hands(Point cen, int rr);
//显示时分秒针
void show_sechand(int s, int prev_s);
void show_minhand(int m, int prev_m);
void show_hourhand(int h, int prev_h);
void draw_lines() const override;
void move(int dx, int dy) override;
private:
Vector_ref<Line> sec_hand; //秒针
Vector_ref<Line> min_hand; //分针
Vector_ref<Line> hour_hand; //时针
void show_hand(Vector_ref<Line>& vh, int i, int prev_i);
};
//------------------------------------------------------------------------------
struct Clock_window : My_window {
Clock_window(Point xy, int w, int h, const string& tt = "Simple Clock");
private:
Clock_dial cd;
Clock_hands ch;
int h; //时
int m; //分
int s; //秒
static void cb_run_clock(Address pw)
{
//这是Fl_Timeout_Handler 类型的函数,不能用this_call调用,得设置为静态函数
reference_to<Clock_window>(pw).run_clock();
}
void run_clock();
};
//------------------------------------------------------------------------------
struct Plane_flying_window : My_window {
Plane_flying_window(Point xy, int w, int h, int v,
const string& fname, const string& tt = "Flying Plane");
enum class Direction {
up, down, left, right
};
private:
static const int img_width{ 200 }; //图像的尺寸
static const int img_height{ 200 };
Button start_button; //start按钮,next按钮变为stop按钮
Image plane;
int v; //速度,从1到10
Direction dir;
bool swt; //标记是否开始飞行
clock_t base;
static void cb_start(Address, Address pw)
{
reference_to<Plane_flying_window>(pw).start_pressed();
}
static void cb_fly_plane(Address pw)
{
//这是Fl_Timeout_Handler 类型的函数,不能用this_call调用,得设置为静态函数
reference_to<Plane_flying_window>(pw).fly_plane();
}
void start_pressed();
void next() override; //设置为 stop_pressed()
void fly_plane();
Direction rand_dir();
};
//------------------------------------------------------------------------------
class Calculator_window : public My_window
{
public:
Calculator_window(Point xy, int w, int h, const string& tt = "Calculator GUI");
private:
In_box stat; //输入的声明语句
Out_box res; //输出结果
//Button cal; //用next按钮代替
void next() override;
};
//------------------------------------------------------------------------------
class Currency_exchg_rate_window : public My_window
{
Button curr1_button;
Button curr2_button;
Menu curr1_menu;
Menu curr2_menu;
Out_box rate1; //第一个输出框输出基准汇率,固定为1
Out_box rate2;
bool is_menu1_hidden;
bool is_menu2_hidden;
pair<string, double> base; //基准货币及其与美元的汇率
pair<string, double> exchg; //用于换算的货币
map<string, double> exchg_rates; //汇率映射表
static void cb_curr1_button(Address, Address pw) { reference_to<Currency_exchg_rate_window>(pw).curr1_button_pressed(); }
static void cb_curr2_button(Address, Address pw) { reference_to<Currency_exchg_rate_window>(pw).curr2_button_pressed(); }
static void cb_curr1_menu(Address pb, Address pw)
{
//string cur_name = reference_to<Fl_Widget>(pb).label();
reference_to<Currency_exchg_rate_window>(pw).curr1_menu_pressed(reference_to<Fl_Widget>(pb).label());
}
static void cb_curr2_menu(Address pb, Address pw)
{
reference_to<Currency_exchg_rate_window>(pw).
curr2_menu_pressed(reference_to<Fl_Widget>(pb).label());
}
void curr1_button_pressed();
void curr2_button_pressed();
void curr1_menu_pressed(const string&);
void curr2_menu_pressed(const string&);
public:
Currency_exchg_rate_window(Point xy, int w, int h,
const string& fname = "exchange_rate.txt",
const string& tt = "Currentcy Exchange Rate Window");
};
}
My_window.cpp
#include"My_window.h"
namespace My_windows {
//------------------------------------------------------------------------------
My_window::My_window(Point xy, int w, int h, const string& title) :
Window(xy, w, h, title),
next_button(Point(x_max() - 70, 0), 70, 20, "Next", cb_next),
quit_button(Point(x_max() - 70, 25), 70, 20, "Quit", cb_quit),
button_pushed(false)
{
attach(next_button);
attach(quit_button);
}
bool My_window::wait_for_button()
// modified event loop:
// handle all events (as per default), quit when button_pushed becomes true
// this allows graphics without control inversion
{
show();
button_pushed = false;
#if 1
// Simpler handler
while (!button_pushed) Fl::wait();
Fl::redraw();
#else
// To handle the case where the user presses the X button in the window frame
// to kill the application, change the condition to 0 to enable this branch.
Fl::run();
#endif
return button_pushed;
}
void My_window::cb_next(Address, Address pw)
// call Simple_window::next() for the window located at pw
{
reference_to<My_window>(pw).next();
}
void My_window::cb_quit(Address, Address pw)
// call Simple_window::next() for the window located at pw
{
reference_to<My_window>(pw).quit();
}
void My_window::next()
{
button_pushed = true;
hide();
}
void My_window::quit()
{
hide();
}
//------------------------------------------------------------------------------
Button_window::Button_window(Point xy, int w, int h, const string& title)
:My_window(xy, w, h, title),
menu1(Point(0, 0), 100, 100, Menu::vertical, "menu1"),
menu2(Point(100, 0), 100, 100, Menu::vertical, "menu2"),
menu3(Point(200, 0), 100, 100, Menu::vertical, "menu3"),
menu4(Point(300, 0), 100, 100, Menu::vertical, "menu4"),
xy_out(Point(x_max() - 70, 60), 70, 20, "(x,y)")
{
menu1.attach(new Button(Point(0, 0), 0, 0, "1-1", cb_action11));
menu1.attach(new Button(Point(0, 0), 0, 0, "1-2", cb_action12));
menu1.attach(new Button(Point(0, 0), 0, 0, "1-3", cb_action13));
menu1.attach(new Button(Point(0, 0), 0, 0, "1-4", cb_action14));
attach(menu1);
menu2.attach(new Button(Point(0, 0), 0, 0, "2-1", cb_action21));
menu2.attach(new Button(Point(0, 0), 0, 0, "2-2", cb_action22));
menu2.attach(new Button(Point(0, 0), 0, 0, "2-3", cb_action23));
menu2.attach(new Button(Point(0, 0), 0, 0, "2-4", cb_action24));
attach(menu2);
menu3.attach(new Button(Point(0, 0), 0, 0, "3-1", cb_action31));
menu3.attach(new Button(Point(0, 0), 0, 0, "3-2", cb_action32));
menu3.attach(new Button(Point(0, 0), 0, 0, "3-3", cb_action33));
menu3.attach(new Button(Point(0, 0), 0, 0, "3-4", cb_action34));
attach(menu3);
menu4.attach(new Button(Point(0, 0), 0, 0, "4-1", cb_action41));
menu4.attach(new Button(Point(0, 0), 0, 0, "4-2", cb_action42));
menu4.attach(new Button(Point(0, 0), 0, 0, "4-3", cb_action43));
menu4.attach(new Button(Point(0, 0), 0, 0, "4-4", cb_action44));
attach(menu4);
attach(xy_out);
xy_out.put("no point");
}
void Button_window::reset_labels()
{
for (int i = 0; i < 4; ++i) {
menu1.selection[i].label = "1-" + to_string(i + 1);
menu2.selection[i].label = "2-" + to_string(i + 1);
menu3.selection[i].label = "3-" + to_string(i + 1);
menu4.selection[i].label = "4-" + to_string(i + 1);
}
redraw();
}
void Button_window::action(Button& b)
{
reset_labels();
b.label = "CLICKED";
ostringstream os;
os << '(' << b.loc.x << ',' << b.loc.y << ')';
xy_out.put(os.str());
}
//------------------------------------------------------------------------------
Image_button::Image_button(Point xy, int w, int h, const string& label,
const string& image_fname, Callback cb)
: Button(xy, w, h, label, cb), img{ xy,image_fname }
{
img.set_mask(Point{ 0,0 }, w, h);
}
void Image_button::move(int dx, int dy)
{
hide();
pw->position(loc.x += dx, loc.y += dy);
img.move(dx, dy);
show();
}
void Image_button::attach(Window& win)
{
Button::attach(win);
win.attach(img);
}
//------------------------------------------------------------------------------
Image_button_window::Image_button_window(Point xy, int w, int h,
const string& title, const string& fname)
:My_window(xy, w, h, title),
ib{ Point{x_max()/2,y_max()/2}, 70,20,"image button",fname,cb_ib}
{
attach(ib);
}
void Image_button_window::pos_change()
{
int rx = rand_int(0, x_max()-ib.width);
int ry = rand_int(0, y_max()-ib.height);
ib.move(rx - ib.loc.x, ry - ib.loc.y);
}
//------------------------------------------------------------------------------
Draw_shape_window::Draw_shape_window(Point xy, int w, int h, const string& title)
:My_window{ xy,w,h,title },
orign_x{ Point{70,0},100,30,"Orign x:"},
orign_y{ Point{300,0},100,30,"Orign y:"},
rlen{ Point{600,0},100,30,"radius or side length"},
clear_button{ Point{x_max()-150,0},70,20, "clear", cb_clear },
shape_menu_button{ Point{10,40},100,20, "shape menu", cb_menu },
shape_button_pushed{ false },
shape_menu{ Point{120,40},100,30,Menu::vertical,"shape" },
current{0,0},
i_begin{ 0 }
{
attach(orign_x);
attach(orign_y);
attach(rlen);
attach(clear_button);
attach(shape_menu_button);
shape_menu.attach(new Button{ Point{0,0},0,0,"circle",cb_circle });
shape_menu.attach(new Button{ Point{0,0},0,0,"square",cb_square });
shape_menu.attach(new Button{ Point{0,0},0,0,"equil-triangle",cb_eqtri });
shape_menu.attach(new Button{ Point{0,0},0,0,"reg-hexagon",cb_rhexag });
attach(shape_menu);
shape_menu.hide();
}
void Draw_shape_window::clear_pressed()
{
//清屏操作,释放附加到窗口的形状
for (int i = i_begin; i < rshapes.size(); ++i)
detach(rshapes[i]);
i_begin = rshapes.size();
redraw();
hide_menu();
}
void Draw_shape_window::menu_pressed()
{
if (!shape_button_pushed)
shape_menu.show();
else
shape_menu.hide();
shape_button_pushed = !shape_button_pushed;
}
void Draw_shape_window::circle_pressed()
{
Shape_info si;
if (get_shape_info(si))
{
Circle* pc = new Circle{ si.xy,si.rlen };
add_shape(pc);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::square_pressed()
{
//这里的orign_x/y 是正方形的左上角顶点
Shape_info si;
if (get_shape_info(si))
{
Rectangle* pr = new Rectangle{ si.xy, si.rlen,si.rlen };
add_shape(pr);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::eqtri_pressed()
{
//orign_x/y 是正三角形中心点
Shape_info si;
if (get_shape_info(si))
{
//rlen 是正三角形的边长
int r = int(round(si.rlen / sqrt(3)));
My_shape::Regular_triangle* prt =
new My_shape::Regular_triangle{ si.xy, r };
add_shape(prt);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::rhexag_pressed()
{
//orign_x/y 是正六边形中心点
Shape_info si;
if (get_shape_info(si))
{
//rlen 是正六边形的边长,也是中心点到顶角的距离
My_shape::Regular_hexagon* prh =
new My_shape::Regular_hexagon{ si.xy, si.rlen };
add_shape(prh);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::add_shape(Shape* ps)
{
ps->set_color(Color::black);
rshapes.push_back(ps);
attach(*ps);
}
void Draw_shape_window::hide_menu()
{
shape_menu.hide();
shape_button_pushed = false;
}
bool Draw_shape_window::get_shape_info(Shape_info& si)
{
string s = orign_x.get_string();
if (s == "")
return false;
si.xy.x = orign_x.get_int();
s = orign_y.get_string();
if (s == "")
return false;
si.xy.y = orign_y.get_int();
s = rlen.get_string();
if (s == "")
return false;
si.rlen = rlen.get_int();
return true;
}
//------------------------------------------------------------------------------
Move_shape_window::Move_shape_window(Point xy, int w, int h, const string& title)
:Draw_shape_window{ xy,w,h,title },
mov_x{ Point{300,40},100,30,"move x:" },
mov_y{ Point{600,40},100,30,"move y:" }
{
attach(mov_x);
attach(mov_y);
}
void Move_shape_window::next()
{
//从输入框中获取坐标对
string s = mov_x.get_string();
if (s == "")
return;
int x = mov_x.get_int();
s = mov_y.get_string();
if (s == "")
return;
int y = mov_y.get_int();
rshapes[rshapes.size() - 1].move(x - current.x, y - current.y);
current.x = x;
current.y = y;
redraw();
}
//------------------------------------------------------------------------------
Clock_dial::Clock_dial(Point cen, int rr)
:center{cen,rr/50?rr/50:2},
outline{cen,rr}
{
center.set_fill_color(Color::black);
outline.set_color(Color::black);
outline.set_style(Line_style{ Line_style::solid,4 });
const double half_pi = My_shape::PI / 2; //从90度开始顺时针旋转
const double gap = My_shape::PI / 180.0 * 6.0; //60刻度,每个间隔6°
double rad = -gap; //从刻度1开始
const int dlen = rr / 20 ? rr / 20 : 2; //刻度长度
const int rdm = rr - 3 * dlen; //数字的左下角点距离圆心的距离
const int rsec = int(round(3.0 / 4.0 * rr)); //秒针长度
const int rmin = int(round(2.0 / 4.0 * rr)); //分针长度
const int rhour = int(round(1.0 / 4.0 * rr)); //时针长度
for (int i = 1; i <= 60; ++i)
{
//每个刻度线两端点的位置
int x1 = int(cen.x + round(rr * cos(half_pi + rad)));
int y1 = int(cen.y - round(rr * sin(half_pi + rad)));
int x2 = int(cen.x + round((rr-dlen) * cos(half_pi + rad)));
int y2 = int(cen.y - round((rr-dlen) * sin(half_pi + rad)));
dial.push_back(new Line{ Point{x1,y1}, Point{x2,y2} });
dial[dial.size() - 1].set_color(Color::black);
if (i % 5 == 0) //每隔5刻度
{
dial[dial.size() - 1].set_style(Line_style{ Line_style::solid,2 }); //加粗
int n = i / 5;
//数字中心点的位置
int x = int(cen.x + round(rdm * cos(half_pi + rad)));
int y = int(cen.y - round(rdm * sin(half_pi + rad)));
digital.push_back(new Text{ Point{x - 7,y + 7},to_string(n) });
digital[digital.size() - 1].set_color(Color::black);
digital[digital.size() - 1].set_font(Font::times_bold);
}
rad -= gap; //逆时针旋转
}
}
void Clock_dial::draw_lines() const
{
center.draw();
outline.draw();
for (int i = 0; i < 12; ++i)
digital[i].draw();
for (int i = 0; i < 60; ++i)
dial[i].draw();
}
void Clock_dial::move(int dx, int dy)
{
center.move(dx, dy);
outline.move(dx, dy);
for (int i = 0; i < 12; ++i)
digital[i].move(dx, dy);
for (int i = 0; i < 60; ++i)
dial[i].move(dx, dy);
}
//------------------------------------------------------------------------------
Clock_hands::Clock_hands(Point cen, int rr)
{
const double half_pi = My_shape::PI / 2; //从90度开始顺时针旋转
const double gap = My_shape::PI / 180.0 * 6.0; //60刻度,每个间隔6°
double rad = 0;
const int rsec = int(round(5.0 / 6.0 * rr)); //秒针长度
const int rmin = int(round(4.0 / 6.0 * rr)); //分针长度
const int rhour = int(round(3.0 / 6.0 * rr)); //时针长度
for (int i = 0; i < 60; ++i)
{
//设置秒针
int xs = int(cen.x + round(rsec * cos(half_pi + rad)));
int ys = int(cen.y - round(rsec * sin(half_pi + rad)));
sec_hand.push_back(new Line{ cen,Point{xs,ys} });
//颜色默认设置为黑色,而且不可见
sec_hand[sec_hand.size() - 1].set_color(Color{ Color::black,Color::invisible });
//设置分针
int xm = int(cen.x + round(rmin * cos(half_pi + rad)));
int ym = int(cen.y - round(rmin * sin(half_pi + rad)));
min_hand.push_back(new Line{ cen,Point{xm,ym} });
min_hand[min_hand.size() - 1].set_color(Color{ Color::black,Color::invisible });
//设置线的粗细,秒针默认,分针2,时针4
min_hand[min_hand.size() - 1].set_style(Line_style{ Line_style::solid, 2 });
//设置时针
int xh = int(cen.x + round(rhour * cos(half_pi + rad)));
int yh = int(cen.y - round(rhour * sin(half_pi + rad)));
hour_hand.push_back(new Line{ cen,Point{xh,yh} });
hour_hand[hour_hand.size() - 1].set_color(Color{ Color::black,Color::invisible });
hour_hand[hour_hand.size() - 1].set_style(Line_style{ Line_style::solid, 4 });
rad -= gap;
}
}
void Clock_hands::show_hand(Vector_ref<Line>& vh, int i, int prev_i)
{
if (i == prev_i)
return;
//先获得之前时刻指针的颜色
Color c = vh[prev_i].color();
//设置i时刻单位指针的颜色
c.set_visibility(Color::Transparency::visible);
vh[i].set_color(c);
//将前一时刻单位指针置为不可见
c.set_visibility(Color::Transparency::invisible);
vh[prev_i].set_color(c);
}
void Clock_hands::show_sechand(int s, int prev_s)
{
show_hand(sec_hand, s, prev_s);
}
void Clock_hands::show_minhand(int m, int prev_m)
{
show_hand(min_hand, m, prev_m);
}
void Clock_hands::show_hourhand(int h, int prev_h)
{
show_hand(hour_hand, h, prev_h);
}
void Clock_hands::draw_lines() const
{
for (int i = 0; i < 60; ++i)
{
sec_hand[i].draw();
min_hand[i].draw();
hour_hand[i].draw();
}
}
void Clock_hands::move(int dx, int dy)
{
for (int i = 0; i < 60; ++i)
{
sec_hand[i].move(dx, dy);
min_hand[i].move(dx, dy);
hour_hand[i].move(dx, dy);
}
}
//------------------------------------------------------------------------------
Clock_window::Clock_window(Point xy, int ww, int hh, const string& tt)
:My_window{ xy, ww, hh, tt},
cd{ Point{ww / 3, hh / 3 },hh / 3 },
ch{ Point{ww / 3, hh / 3 },hh / 3 },
h{ 0 }, m{ 0 }, s{ 0 }
{
attach(cd);
attach(ch);
/*
* Fl::add_timeout的作用是添加运行一次的回调函数,其函数原型如下
* void Fl::add_timeout (double t, Fl_Timeout_Handler cb, void* argp=0);
* t代表每t秒触发一次,cb是要重复执行的回调函数, argp是要传递的数据指针,我这里传递this指针。
*/
Fl::add_timeout(0.001, cb_run_clock, this);
}
void Clock_window::run_clock()
{
//获取系统时间
time_t t = time(nullptr); //UNIX时间戳
struct tm now;
localtime_s(&now, &t); //转变为本地时间,VS特供的安全函数
int now_h = now.tm_hour % 12; //12小时制
int now_m = now.tm_min;
int now_s = now.tm_sec;
//小时需要特殊处理,因为只有12小时,但是指针有60个刻度
now_h = now_h * 5 + now_m / 12;
ch.show_hourhand(now_h, h);
ch.show_minhand(now_m, m);
ch.show_sechand(now_s, s);
Fl::redraw();
h = now_h;
m = now_m;
s = now_s;
/*
* Fl::repeat_timeout的作用是重复运行回调函数,其函数原型如下
* void Fl::add_timeout (double t, Fl_Timeout_Handler cb, void* argp=0);
* t代表每t秒触发一次,cb是要重复执行的回调函数, argp是要传递的数据指针,我这里传递this指针。
*/
Fl::repeat_timeout(1.0, cb_run_clock, this);
}
//------------------------------------------------------------------------------
Plane_flying_window::Plane_flying_window(Point xy, int w, int h, int vv,
const string& fname, const string& tt)
:My_window{xy,w,h,tt},
start_button{ Point{next_button.loc.x - next_button.width - 10, next_button.loc.y},
next_button.width, next_button.height, "start", cb_start},
v{vv},
plane{ Point{w / 3, h / 3}, fname},
dir{ Direction::right },
swt{ false },
base{clock()}
{
next_button.label = "stop";
attach(start_button);
if (v < 1)
v = 1;
else if (v > 10)
v = 10;
plane.set_mask(Point{ 28,28 }, img_width, img_height);
attach(plane);
}
void Plane_flying_window::start_pressed()
{
swt = true;
Fl::add_timeout(0, cb_fly_plane, this);
}
void Plane_flying_window::next()
{
Fl::remove_timeout(cb_fly_plane);
swt = false;
}
void Plane_flying_window::fly_plane()
{
if ((clock() - base) / CLOCKS_PER_SEC >= 1)
{
//每隔 1 秒换方向
Direction old_dir = dir;
while ((dir = rand_dir()) == old_dir)
continue;
base = clock();
}
if (swt)
{
switch (dir)
{
case Direction::up:
if (plane.point(0).y <= 0)
{
dir = rand_dir();
}
else
plane.move(0, -v);
break;
case Direction::down:
if (plane.point(0).y >= y_max() - img_height)
{
dir = rand_dir();
}
else
plane.move(0, v);
break;
case Direction::left:
if (plane.point(0).x <= 0)
{
dir = rand_dir();
}
else
plane.move(-v, 0);
break;
case Direction::right:
if (plane.point(0).x >= x_max() - img_width)
{
dir = rand_dir();
}
else
plane.move(v, 0);
break;
default:
error("Wrong direction");
}
redraw();
//Sleep(10);
}
Fl::repeat_timeout(1.0/100, cb_fly_plane, this);
}
Plane_flying_window::Direction Plane_flying_window::rand_dir()
{
return Direction(rand_int(0,3));
}
//------------------------------------------------------------------------------
Calculator_window::Calculator_window(Point xy, int w, int h, const string& tt)
:My_window(xy, w, h, tt),
stat{Point{xy.x + 50, xy.y + 50}, 200, 20, "statement"},
res{Point{xy.x + 50, xy.y + 80}, 200, 20, "result"}
//cal{ Point{xy.x + 170, xy.y + 50}, 50, 20, "calculate", }
{
attach(stat);
attach(res);
//修改next按钮的位置和名称
Point next_loc = next_button.loc;
int dx = stat.loc.x + stat.width + 20 - next_loc.x;
int dy = stat.loc.y - next_loc.y;
next_button.move(dx, dy);
next_button.label = "calculate";
}
void Calculator_window::next()
{
istringstream iss{ stat.get_string() };
ostringstream oss;
try {
double result = statement(iss);
oss << result;
}
catch (runtime_error& e) {
oss << e.what();
}
res.put(oss.str());
}
//------------------------------------------------------------------------------
static void skip_line(istream& is)
{
//功能:跳过该行,即读入并忽略改行剩余字符
char ch{ 0 };
while (is.get(ch) && ch != '\n')
continue;
}
void read_exchange_rate(const string& fname, map<string, double>& er)
{
//功能:从汇率文件读入汇率到映射表中,如果遇到文件中的注释则跳过改行,
//注释是以'#'开始的一行
ifstream ifs{ fname };
if (!ifs)
error("Can't open ", fname);
char ch{ 0 };
while(ifs >> ch)
{
if (ch == '#')
skip_line(ifs);
else
{
ifs.putback(ch);
string cur;
double rate{ -1.0 };
ifs >> cur >> rate;
if (cur == "")
error("Read currency name failed");
else if (rate <= 0) //确保不读入0或者负数
error("Invalid rate: non-positive");
else
er[cur] = rate;
}
}
}
Currency_exchg_rate_window::Currency_exchg_rate_window(Point xy, int w, int h,
const string& fname, const string& tt)
:My_window(xy, w, h, tt),
curr1_button(Point{x_max()/2-150, y_max()/3},100,30,"Currency 1", cb_curr1_button),
curr2_button(Point{x_max()/2+50, y_max()/3},100,30,"Currency 2", cb_curr2_button),
curr1_menu(Point{ curr1_button.loc.x,curr1_button.loc.y + curr1_button.height }, curr1_button.width, curr1_button.height, Menu::vertical, "Currency 1 menu"),
curr2_menu(Point{ curr2_button.loc.x,curr2_button.loc.y + curr2_button.height }, curr2_button.width,curr2_button.height,Menu::vertical,"Currency 2 menu"),
rate1(Point{curr1_button.loc.x, curr1_button.loc.y + curr1_button.height + 30},curr1_button.width,curr1_button.height,""),
rate2(Point{curr2_button.loc.x, curr2_button.loc.y + curr2_button.height + 30},curr2_button.width,curr2_button.height,""),
is_menu1_hidden{true}, is_menu2_hidden{true},
base{"",0.0}, exchg{"", 0.0}
{
read_exchange_rate(fname, exchg_rates);
next_button.hide(); //不需要next按钮
attach(curr1_button);
attach(curr2_button);
attach(rate1);
attach(rate2);
rate1.put("1");
//接下来根据读入的汇率创建货币菜单
for (const auto& p : exchg_rates)
{
curr1_menu.attach(new Button{ Point{0,0},0,0,p.first,cb_curr1_menu });
curr2_menu.attach(new Button{ Point{0,0},0,0,p.first,cb_curr2_menu });
}
attach(curr1_menu);
attach(curr2_menu);
curr1_menu.hide();
curr2_menu.hide();
}
void Currency_exchg_rate_window::curr1_button_pressed()
{
if (is_menu1_hidden)
curr1_menu.show();
else
curr1_menu.hide();
is_menu1_hidden = !is_menu1_hidden;
//还会影响菜单2
if (!is_menu2_hidden)
{
curr2_menu.hide();
is_menu2_hidden = true;
}
}
void Currency_exchg_rate_window::curr2_button_pressed()
{
if (is_menu2_hidden)
curr2_menu.show();
else
curr2_menu.hide();
is_menu2_hidden = !is_menu2_hidden;
//还会影响菜单1
if (!is_menu1_hidden)
{
curr1_menu.hide();
is_menu1_hidden = true;
}
}
void Currency_exchg_rate_window::curr1_menu_pressed(const string& curr_name)
{
//设置基准货币,隐藏菜单,显示带有当前货币名的按钮,计算并输出新的汇率
base.first = curr_name;
base.second = exchg_rates[curr_name];
curr1_menu.hide();
is_menu1_hidden = true;
curr1_button.label = curr_name;
if(exchg.second > 0.0)
{
double res = exchg.second / base.second;
ostringstream oss;
oss << res;
rate2.put(oss.str());
}
}
void Currency_exchg_rate_window::curr2_menu_pressed(const string& curr_name)
{
//设置基准货币,隐藏菜单,显示带有当前货币名的按钮,计算并输出新的汇率
exchg.first = curr_name;
exchg.second = exchg_rates[curr_name];
curr2_menu.hide();
is_menu2_hidden = true;
curr2_button.label = curr_name;
if (base.second > 0.0)
{
double res = exchg.second / base.second;
ostringstream oss;
oss << res;
rate2.put(oss.str());
}
}
}
main.cpp
#include"My_window.h"
using namespace My_windows;
int main()
try {
Point tt{ 100,100 };
/*My_window mywin{ tt, 1200,800,"My Window" };
Line ll{ Point{100,100},Point{200,200} };
ll.set_color(Color::black);
mywin.attach(ll);
Graph_lib::Rectangle rr{ Point{300,100},Point{600,200} };
rr.set_color(Color::blue);
mywin.attach(rr);*/
//Button_window bwin{ tt,1200,800,"Button Window" };
//Image_button_window ibwin{ tt,1200,800,"Image Button Window","snow_cpp.gif" };
//Draw_shape_window dswin{ tt,1200,800,"Draw Shape Window" };
//Move_shape_window mvwin{ tt,1200,800,"Move Shape Window" };
//Clock_window cwin{ tt, 1200, 800 };
//Plane_flying_window pfwin{ tt, 1200, 800, 5, "plane.jpg" };
//Currency_exchg_rate_window cerwin{ tt,1200,800 };
Calculator_window calwin{ tt, 1200,800 };
return gui_main();
}
catch (exception& e) {
cerr << "exception: " << e.what() << '\n';
return 1;
}
catch (...) {
cerr << "Some exception\n";
return 2;
}
21.1
struct My_window : Graph_lib::Window {
My_window(Point xy, int w, int h, const string& title);
bool wait_for_button(); // simple event loop
protected:
Button next_button; // the "next" button
Button quit_button; // the "quit" button
bool button_pushed; // implementation detail
static void cb_next(Address, Address); // callback for next_button
static void cb_quit(Address, Address);
virtual void next(); // action to be done when next_button is pressed
virtual void quit();
};
//------------------------------------------------------------------------------
My_window::My_window(Point xy, int w, int h, const string& title) :
Window(xy, w, h, title),
next_button(Point(x_max() - 70, 0), 70, 20, "Next", cb_next),
quit_button(Point(x_max() - 70, 25), 70, 20, "Quit", cb_quit),
button_pushed(false)
{
attach(next_button);
attach(quit_button);
}
bool My_window::wait_for_button()
// modified event loop:
// handle all events (as per default), quit when button_pushed becomes true
// this allows graphics without control inversion
{
show();
button_pushed = false;
#if 1
// Simpler handler
while (!button_pushed) Fl::wait();
Fl::redraw();
#else
// To handle the case where the user presses the X button in the window frame
// to kill the application, change the condition to 0 to enable this branch.
Fl::run();
#endif
return button_pushed;
}
void My_window::cb_next(Address, Address pw)
// call Simple_window::next() for the window located at pw
{
reference_to<My_window>(pw).next();
}
void My_window::cb_quit(Address, Address pw)
// call Simple_window::next() for the window located at pw
{
reference_to<My_window>(pw).quit();
}
void My_window::next()
{
button_pushed = true;
hide();
}
void My_window::quit()
{
hide();
}
21.2
struct Button_window : My_window {
Button_window(Point xy, int w, int h, const string& title);
private:
// widgets
Menu menu1;
Menu menu2;
Menu menu3;
Menu menu4;
Out_box xy_out;
// actions invoked by callback: relabel button, print coordinates
void reset_labels(); // helper function
void action(Button& b);
void action11() { action(menu1.selection[0]); }
void action12() { action(menu1.selection[1]); }
void action13() { action(menu1.selection[2]); }
void action14() { action(menu1.selection[3]); }
void action21() { action(menu2.selection[0]); }
void action22() { action(menu2.selection[1]); }
void action23() { action(menu2.selection[2]); }
void action24() { action(menu2.selection[3]); }
void action31() { action(menu3.selection[0]); }
void action32() { action(menu3.selection[1]); }
void action33() { action(menu3.selection[2]); }
void action34() { action(menu3.selection[3]); }
void action41() { action(menu4.selection[0]); }
void action42() { action(menu4.selection[1]); }
void action43() { action(menu4.selection[2]); }
void action44() { action(menu4.selection[3]); }
// callback functions
static void cb_action11(Address, Address pw) { reference_to<Button_window>(pw).action11(); }
static void cb_action12(Address, Address pw) { reference_to<Button_window>(pw).action12(); }
static void cb_action13(Address, Address pw) { reference_to<Button_window>(pw).action13(); }
static void cb_action14(Address, Address pw) { reference_to<Button_window>(pw).action14(); }
static void cb_action21(Address, Address pw) { reference_to<Button_window>(pw).action21(); }
static void cb_action22(Address, Address pw) { reference_to<Button_window>(pw).action22(); }
static void cb_action23(Address, Address pw) { reference_to<Button_window>(pw).action23(); }
static void cb_action24(Address, Address pw) { reference_to<Button_window>(pw).action24(); }
static void cb_action31(Address, Address pw) { reference_to<Button_window>(pw).action31(); }
static void cb_action32(Address, Address pw) { reference_to<Button_window>(pw).action32(); }
static void cb_action33(Address, Address pw) { reference_to<Button_window>(pw).action33(); }
static void cb_action34(Address, Address pw) { reference_to<Button_window>(pw).action34(); }
static void cb_action41(Address, Address pw) { reference_to<Button_window>(pw).action41(); }
static void cb_action42(Address, Address pw) { reference_to<Button_window>(pw).action42(); }
static void cb_action43(Address, Address pw) { reference_to<Button_window>(pw).action43(); }
static void cb_action44(Address, Address pw) { reference_to<Button_window>(pw).action44(); }
};
//------------------------------------------------------------------------------
Button_window::Button_window(Point xy, int w, int h, const string& title)
:My_window(xy, w, h, title),
menu1(Point(0, 0), 100, 100, Menu::vertical, "menu1"),
menu2(Point(100, 0), 100, 100, Menu::vertical, "menu2"),
menu3(Point(200, 0), 100, 100, Menu::vertical, "menu3"),
menu4(Point(300, 0), 100, 100, Menu::vertical, "menu4"),
xy_out(Point(x_max() - 70, 60), 70, 20, "(x,y)")
{
menu1.attach(new Button(Point(0, 0), 0, 0, "1-1", cb_action11));
menu1.attach(new Button(Point(0, 0), 0, 0, "1-2", cb_action12));
menu1.attach(new Button(Point(0, 0), 0, 0, "1-3", cb_action13));
menu1.attach(new Button(Point(0, 0), 0, 0, "1-4", cb_action14));
attach(menu1);
menu2.attach(new Button(Point(0, 0), 0, 0, "2-1", cb_action21));
menu2.attach(new Button(Point(0, 0), 0, 0, "2-2", cb_action22));
menu2.attach(new Button(Point(0, 0), 0, 0, "2-3", cb_action23));
menu2.attach(new Button(Point(0, 0), 0, 0, "2-4", cb_action24));
attach(menu2);
menu3.attach(new Button(Point(0, 0), 0, 0, "3-1", cb_action31));
menu3.attach(new Button(Point(0, 0), 0, 0, "3-2", cb_action32));
menu3.attach(new Button(Point(0, 0), 0, 0, "3-3", cb_action33));
menu3.attach(new Button(Point(0, 0), 0, 0, "3-4", cb_action34));
attach(menu3);
menu4.attach(new Button(Point(0, 0), 0, 0, "4-1", cb_action41));
menu4.attach(new Button(Point(0, 0), 0, 0, "4-2", cb_action42));
menu4.attach(new Button(Point(0, 0), 0, 0, "4-3", cb_action43));
menu4.attach(new Button(Point(0, 0), 0, 0, "4-4", cb_action44));
attach(menu4);
attach(xy_out);
xy_out.put("no point");
}
void Button_window::reset_labels()
{
for (int i = 0; i < 4; ++i) {
menu1.selection[i].label = "1-" + to_string(i + 1);
menu2.selection[i].label = "2-" + to_string(i + 1);
menu3.selection[i].label = "3-" + to_string(i + 1);
menu4.selection[i].label = "4-" + to_string(i + 1);
}
redraw();
}
void Button_window::action(Button& b)
{
reset_labels();
b.label = "CLICKED";
ostringstream os;
os << '(' << b.loc.x << ',' << b.loc.y << ')';
xy_out.put(os.str());
}
21.3
inline int rand_int(int min, int max)
{
static default_random_engine ran;
return uniform_int_distribution<>{min, max}(ran);
}
struct Image_button : Button {
Image_button(Point xy, int w, int h, const string& label,
const string& image_fname, Callback cb);
void move(int dx, int dy) override;
void attach(Window&) override;
private:
Image img;
};
struct Image_button_window : My_window {
Image_button_window(Point xy, int w, int h, const string& title, const string& fname);
private:
Image_button ib;
void pos_change();
static void cb_ib(Address, Address pw) { reference_to<Image_button_window>(pw).pos_change(); }
};
//------------------------------------------------------------------------------
Image_button::Image_button(Point xy, int w, int h, const string& label,
const string& image_fname, Callback cb)
: Button(xy, w, h, label, cb), img{ xy,image_fname }
{
img.set_mask(Point{ 0,0 }, w, h);
}
void Image_button::move(int dx, int dy)
{
hide();
pw->position(loc.x += dx, loc.y += dy);
img.move(dx, dy);
show();
}
void Image_button::attach(Window& win)
{
Button::attach(win);
win.attach(img);
}
//------------------------------------------------------------------------------
Image_button_window::Image_button_window(Point xy, int w, int h,
const string& title, const string& fname)
:My_window(xy, w, h, title),
ib{ Point{x_max()/2,y_max()/2}, 70,20,"image button",fname,cb_ib}
{
attach(ib);
}
void Image_button_window::pos_change()
{
int rx = rand_int(0, x_max()-ib.width);
int ry = rand_int(0, y_max()-ib.height);
ib.move(rx - ib.loc.x, ry - ib.loc.y);
}
21.4
struct Draw_shape_window : My_window {
Draw_shape_window(Point xy, int w, int h, const string& title);
private:
In_box orign_x;
In_box orign_y;
In_box rlen; //radius or length of side
Button clear_button;
Button shape_menu_button;
bool shape_button_pushed;
Menu shape_menu;
protected:
Point current; //当前形状的位置
Vector_ref<Shape> rshapes;
private:
int i_begin; //用于标记清屏后rshapes的起始位置
static void cb_clear(Address, Address pw) { reference_to<Draw_shape_window>(pw).clear_pressed(); }
static void cb_menu(Address, Address pw) { reference_to<Draw_shape_window>(pw).menu_pressed(); }
static void cb_circle(Address, Address pw) { reference_to<Draw_shape_window>(pw).circle_pressed(); }
static void cb_square(Address, Address pw) { reference_to<Draw_shape_window>(pw).square_pressed(); }
static void cb_eqtri(Address, Address pw) { reference_to<Draw_shape_window>(pw).eqtri_pressed(); }
static void cb_rhexag(Address, Address pw) { reference_to<Draw_shape_window>(pw).rhexag_pressed(); }
void clear_pressed();
void menu_pressed();
void circle_pressed();
void square_pressed();
void eqtri_pressed();
void rhexag_pressed();
void add_shape(Shape*);
void hide_menu();
protected:
struct Shape_info {
Point xy;
int rlen;
};
bool get_shape_info(Shape_info& si);
};
//------------------------------------------------------------------------------
Draw_shape_window::Draw_shape_window(Point xy, int w, int h, const string& title)
:My_window{ xy,w,h,title },
orign_x{ Point{70,0},100,30,"Orign x:"},
orign_y{ Point{300,0},100,30,"Orign y:"},
rlen{ Point{600,0},100,30,"radius or side length"},
clear_button{ Point{x_max()-150,0},70,20, "clear", cb_clear },
shape_menu_button{ Point{10,40},100,20, "shape menu", cb_menu },
shape_button_pushed{ false },
shape_menu{ Point{120,40},100,30,Menu::vertical,"shape" },
current{0,0},
i_begin{ 0 }
{
attach(orign_x);
attach(orign_y);
attach(rlen);
attach(clear_button);
attach(shape_menu_button);
shape_menu.attach(new Button{ Point{0,0},0,0,"circle",cb_circle });
shape_menu.attach(new Button{ Point{0,0},0,0,"square",cb_square });
shape_menu.attach(new Button{ Point{0,0},0,0,"equil-triangle",cb_eqtri });
shape_menu.attach(new Button{ Point{0,0},0,0,"reg-hexagon",cb_rhexag });
attach(shape_menu);
shape_menu.hide();
}
void Draw_shape_window::clear_pressed()
{
//清屏操作,释放附加到窗口的形状
for (int i = i_begin; i < rshapes.size(); ++i)
detach(rshapes[i]);
i_begin = rshapes.size();
redraw();
hide_menu();
}
void Draw_shape_window::menu_pressed()
{
if (!shape_button_pushed)
shape_menu.show();
else
shape_menu.hide();
shape_button_pushed = !shape_button_pushed;
}
void Draw_shape_window::circle_pressed()
{
Shape_info si;
if (get_shape_info(si))
{
Circle* pc = new Circle{ si.xy,si.rlen };
add_shape(pc);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::square_pressed()
{
//这里的orign_x/y 是正方形的左上角顶点
Shape_info si;
if (get_shape_info(si))
{
Rectangle* pr = new Rectangle{ si.xy, si.rlen,si.rlen };
add_shape(pr);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::eqtri_pressed()
{
//orign_x/y 是正三角形中心点
Shape_info si;
if (get_shape_info(si))
{
//rlen 是正三角形的边长
int r = int(round(si.rlen / sqrt(3)));
My_shape::Regular_triangle* prt =
new My_shape::Regular_triangle{ si.xy, r };
add_shape(prt);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::rhexag_pressed()
{
//orign_x/y 是正六边形中心点
Shape_info si;
if (get_shape_info(si))
{
//rlen 是正六边形的边长,也是中心点到顶角的距离
My_shape::Regular_hexagon* prh =
new My_shape::Regular_hexagon{ si.xy, si.rlen };
add_shape(prh);
current = si.xy;
}
hide_menu();
}
void Draw_shape_window::add_shape(Shape* ps)
{
ps->set_color(Color::black);
rshapes.push_back(ps);
attach(*ps);
}
void Draw_shape_window::hide_menu()
{
shape_menu.hide();
shape_button_pushed = false;
}
bool Draw_shape_window::get_shape_info(Shape_info& si)
{
string s = orign_x.get_string();
if (s == "")
return false;
si.xy.x = orign_x.get_int();
s = orign_y.get_string();
if (s == "")
return false;
si.xy.y = orign_y.get_int();
s = rlen.get_string();
if (s == "")
return false;
si.rlen = rlen.get_int();
return true;
}
21.5
struct Move_shape_window : Draw_shape_window {
Move_shape_window(Point xy, int w, int h, const string& title);
private:
In_box mov_x; //要移动到的新位置
In_box mov_y;
void next() override; //覆盖next按钮原功能,设置为新的移动功能
};
//------------------------------------------------------------------------------
Move_shape_window::Move_shape_window(Point xy, int w, int h, const string& title)
:Draw_shape_window{ xy,w,h,title },
mov_x{ Point{300,40},100,30,"move x:" },
mov_y{ Point{600,40},100,30,"move y:" }
{
attach(mov_x);
attach(mov_y);
}
void Move_shape_window::next()
{
//从输入框中获取坐标对
string s = mov_x.get_string();
if (s == "")
return;
int x = mov_x.get_int();
s = mov_y.get_string();
if (s == "")
return;
int y = mov_y.get_int();
rshapes[rshapes.size() - 1].move(x - current.x, y - current.y);
current.x = x;
current.y = y;
redraw();
}
21.6 Clock
struct Clock_dial : Shape {
Clock_dial(Point cen, int rr);
void draw_lines() const override;
void move(int dx, int dy) override;
private:
Circle center; //中心点
Circle outline; //外圈表盘
Vector_ref<Text> digital; //外圈12个数字
Vector_ref<Line> dial; //外圈60个刻度
};
//------------------------------------------------------------------------------
struct Clock_hands : Shape {
Clock_hands(Point cen, int rr);
//显示时分秒针
void show_sechand(int s, int prev_s);
void show_minhand(int m, int prev_m);
void show_hourhand(int h, int prev_h);
void draw_lines() const override;
void move(int dx, int dy) override;
private:
Vector_ref<Line> sec_hand; //秒针
Vector_ref<Line> min_hand; //分针
Vector_ref<Line> hour_hand; //时针
void show_hand(Vector_ref<Line>& vh, int i, int prev_i);
};
//------------------------------------------------------------------------------
struct Clock_window : My_window {
Clock_window(Point xy, int w, int h, const string& tt = "Simple Clock");
private:
Clock_dial cd;
Clock_hands ch;
int h; //时
int m; //分
int s; //秒
static void cb_run_clock(Address pw)
{
//这是Fl_Timeout_Handler 类型的函数,不能用this_call调用,得设置为静态函数
reference_to<Clock_window>(pw).run_clock();
}
void run_clock();
};
//------------------------------------------------------------------------------
Clock_dial::Clock_dial(Point cen, int rr)
:center{cen,rr/50?rr/50:2},
outline{cen,rr}
{
center.set_fill_color(Color::black);
outline.set_color(Color::black);
outline.set_style(Line_style{ Line_style::solid,4 });
const double half_pi = My_shape::PI / 2; //从90度开始顺时针旋转
const double gap = My_shape::PI / 180.0 * 6.0; //60刻度,每个间隔6°
double rad = -gap; //从刻度1开始
const int dlen = rr / 20 ? rr / 20 : 2; //刻度长度
const int rdm = rr - 3 * dlen; //数字的左下角点距离圆心的距离
const int rsec = int(round(3.0 / 4.0 * rr)); //秒针长度
const int rmin = int(round(2.0 / 4.0 * rr)); //分针长度
const int rhour = int(round(1.0 / 4.0 * rr)); //时针长度
for (int i = 1; i <= 60; ++i)
{
//每个刻度线两端点的位置
int x1 = int(cen.x + round(rr * cos(half_pi + rad)));
int y1 = int(cen.y - round(rr * sin(half_pi + rad)));
int x2 = int(cen.x + round((rr-dlen) * cos(half_pi + rad)));
int y2 = int(cen.y - round((rr-dlen) * sin(half_pi + rad)));
dial.push_back(new Line{ Point{x1,y1}, Point{x2,y2} });
dial[dial.size() - 1].set_color(Color::black);
if (i % 5 == 0) //每隔5刻度
{
dial[dial.size() - 1].set_style(Line_style{ Line_style::solid,2 }); //加粗
int n = i / 5;
//数字中心点的位置
int x = int(cen.x + round(rdm * cos(half_pi + rad)));
int y = int(cen.y - round(rdm * sin(half_pi + rad)));
digital.push_back(new Text{ Point{x - 7,y + 7},to_string(n) });
digital[digital.size() - 1].set_color(Color::black);
digital[digital.size() - 1].set_font(Font::times_bold);
}
rad -= gap; //逆时针旋转
}
}
void Clock_dial::draw_lines() const
{
center.draw();
outline.draw();
for (int i = 0; i < 12; ++i)
digital[i].draw();
for (int i = 0; i < 60; ++i)
dial[i].draw();
}
void Clock_dial::move(int dx, int dy)
{
center.move(dx, dy);
outline.move(dx, dy);
for (int i = 0; i < 12; ++i)
digital[i].move(dx, dy);
for (int i = 0; i < 60; ++i)
dial[i].move(dx, dy);
}
//------------------------------------------------------------------------------
Clock_hands::Clock_hands(Point cen, int rr)
{
const double half_pi = My_shape::PI / 2; //从90度开始顺时针旋转
const double gap = My_shape::PI / 180.0 * 6.0; //60刻度,每个间隔6°
double rad = 0;
const int rsec = int(round(5.0 / 6.0 * rr)); //秒针长度
const int rmin = int(round(4.0 / 6.0 * rr)); //分针长度
const int rhour = int(round(3.0 / 6.0 * rr)); //时针长度
for (int i = 0; i < 60; ++i)
{
//设置秒针
int xs = int(cen.x + round(rsec * cos(half_pi + rad)));
int ys = int(cen.y - round(rsec * sin(half_pi + rad)));
sec_hand.push_back(new Line{ cen,Point{xs,ys} });
//颜色默认设置为黑色,而且不可见
sec_hand[sec_hand.size() - 1].set_color(Color{ Color::black,Color::invisible });
//设置分针
int xm = int(cen.x + round(rmin * cos(half_pi + rad)));
int ym = int(cen.y - round(rmin * sin(half_pi + rad)));
min_hand.push_back(new Line{ cen,Point{xm,ym} });
min_hand[min_hand.size() - 1].set_color(Color{ Color::black,Color::invisible });
//设置线的粗细,秒针默认,分针2,时针4
min_hand[min_hand.size() - 1].set_style(Line_style{ Line_style::solid, 2 });
//设置时针
int xh = int(cen.x + round(rhour * cos(half_pi + rad)));
int yh = int(cen.y - round(rhour * sin(half_pi + rad)));
hour_hand.push_back(new Line{ cen,Point{xh,yh} });
hour_hand[hour_hand.size() - 1].set_color(Color{ Color::black,Color::invisible });
hour_hand[hour_hand.size() - 1].set_style(Line_style{ Line_style::solid, 4 });
rad -= gap;
}
}
void Clock_hands::show_hand(Vector_ref<Line>& vh, int i, int prev_i)
{
if (i == prev_i)
return;
//先获得之前时刻指针的颜色
Color c = vh[prev_i].color();
//设置i时刻单位指针的颜色
c.set_visibility(Color::Transparency::visible);
vh[i].set_color(c);
//将前一时刻单位指针置为不可见
c.set_visibility(Color::Transparency::invisible);
vh[prev_i].set_color(c);
}
void Clock_hands::show_sechand(int s, int prev_s)
{
show_hand(sec_hand, s, prev_s);
}
void Clock_hands::show_minhand(int m, int prev_m)
{
show_hand(min_hand, m, prev_m);
}
void Clock_hands::show_hourhand(int h, int prev_h)
{
show_hand(hour_hand, h, prev_h);
}
void Clock_hands::draw_lines() const
{
for (int i = 0; i < 60; ++i)
{
sec_hand[i].draw();
min_hand[i].draw();
hour_hand[i].draw();
}
}
void Clock_hands::move(int dx, int dy)
{
for (int i = 0; i < 60; ++i)
{
sec_hand[i].move(dx, dy);
min_hand[i].move(dx, dy);
hour_hand[i].move(dx, dy);
}
}
//------------------------------------------------------------------------------
Clock_window::Clock_window(Point xy, int ww, int hh, const string& tt)
:My_window{ xy, ww, hh, tt},
cd{ Point{ww / 3, hh / 3 },hh / 3 },
ch{ Point{ww / 3, hh / 3 },hh / 3 },
h{ 0 }, m{ 0 }, s{ 0 }
{
attach(cd);
attach(ch);
/*
* Fl::add_timeout的作用是添加运行一次的回调函数,其函数原型如下
* void Fl::add_timeout (double t, Fl_Timeout_Handler cb, void* argp=0);
* t代表每t秒触发一次,cb是要重复执行的回调函数, argp是要传递的数据指针,我这里传递this指针。
*/
Fl::add_timeout(0.001, cb_run_clock, this);
}
void Clock_window::run_clock()
{
//获取系统时间
time_t t = time(nullptr); //UNIX时间戳
struct tm now;
localtime_s(&now, &t); //转变为本地时间,VS特供的安全函数
int now_h = now.tm_hour % 12; //12小时制
int now_m = now.tm_min;
int now_s = now.tm_sec;
//小时需要特殊处理,因为只有12小时,但是指针有60个刻度
now_h = now_h * 5 + now_m / 12;
ch.show_hourhand(now_h, h);
ch.show_minhand(now_m, m);
ch.show_sechand(now_s, s);
Fl::redraw();
h = now_h;
m = now_m;
s = now_s;
/*
* Fl::repeat_timeout的作用是重复运行回调函数,其函数原型如下
* void Fl::add_timeout (double t, Fl_Timeout_Handler cb, void* argp=0);
* t代表每t秒触发一次,cb是要重复执行的回调函数, argp是要传递的数据指针,我这里传递this指针。
*/
Fl::repeat_timeout(1.0, cb_run_clock, this);
}
21.7 Plane
struct Plane_flying_window : My_window {
Plane_flying_window(Point xy, int w, int h, int v,
const string& fname, const string& tt = "Flying Plane");
enum class Direction {
up, down, left, right
};
private:
static const int img_width{ 200 }; //图像的尺寸
static const int img_height{ 200 };
Button start_button; //start按钮,next按钮变为stop按钮
Image plane;
int v; //速度,从1到10
Direction dir;
bool swt; //标记是否开始飞行
clock_t base;
static void cb_start(Address, Address pw)
{
reference_to<Plane_flying_window>(pw).start_pressed();
}
static void cb_fly_plane(Address pw)
{
//这是Fl_Timeout_Handler 类型的函数,不能用this_call调用,得设置为静态函数
reference_to<Plane_flying_window>(pw).fly_plane();
}
void start_pressed();
void next() override; //设置为 stop_pressed()
void fly_plane();
Direction rand_dir();
};
//------------------------------------------------------------------------------
Plane_flying_window::Plane_flying_window(Point xy, int w, int h, int vv,
const string& fname, const string& tt)
:My_window{xy,w,h,tt},
start_button{ Point{next_button.loc.x - next_button.width - 10, next_button.loc.y},
next_button.width, next_button.height, "start", cb_start},
v{vv},
plane{ Point{w / 3, h / 3}, fname},
dir{ Direction::right },
swt{ false },
base{clock()}
{
next_button.label = "stop";
attach(start_button);
if (v < 1)
v = 1;
else if (v > 10)
v = 10;
plane.set_mask(Point{ 28,28 }, img_width, img_height);
attach(plane);
}
void Plane_flying_window::start_pressed()
{
swt = true;
Fl::add_timeout(0, cb_fly_plane, this);
}
void Plane_flying_window::next()
{
Fl::remove_timeout(cb_fly_plane);
swt = false;
}
void Plane_flying_window::fly_plane()
{
if ((clock() - base) / CLOCKS_PER_SEC >= 1)
{
//每隔 1 秒换方向
Direction old_dir = dir;
while ((dir = rand_dir()) == old_dir)
continue;
base = clock();
}
if (swt)
{
switch (dir)
{
case Direction::up:
if (plane.point(0).y <= 0)
{
dir = rand_dir();
}
else
plane.move(0, -v);
break;
case Direction::down:
if (plane.point(0).y >= y_max() - img_height)
{
dir = rand_dir();
}
else
plane.move(0, v);
break;
case Direction::left:
if (plane.point(0).x <= 0)
{
dir = rand_dir();
}
else
plane.move(-v, 0);
break;
case Direction::right:
if (plane.point(0).x >= x_max() - img_width)
{
dir = rand_dir();
}
else
plane.move(v, 0);
break;
default:
error("Wrong direction");
}
redraw();
//Sleep(10);
}
Fl::repeat_timeout(1.0/100, cb_fly_plane, this);
}
Plane_flying_window::Direction Plane_flying_window::rand_dir()
{
return Direction(rand_int(0,3));
}
21.8 Currency Exchange Rate
class Currency_exchg_rate_window : public My_window
{
Button curr1_button;
Button curr2_button;
Menu curr1_menu;
Menu curr2_menu;
Out_box rate1; //第一个输出框输出基准汇率,固定为1
Out_box rate2;
bool is_menu1_hidden;
bool is_menu2_hidden;
pair<string, double> base; //基准货币及其与美元的汇率
pair<string, double> exchg; //用于换算的货币
map<string, double> exchg_rates; //汇率映射表
static void cb_curr1_button(Address, Address pw) { reference_to<Currency_exchg_rate_window>(pw).curr1_button_pressed(); }
static void cb_curr2_button(Address, Address pw) { reference_to<Currency_exchg_rate_window>(pw).curr2_button_pressed(); }
static void cb_curr1_menu(Address pb, Address pw)
{
//string cur_name = reference_to<Fl_Widget>(pb).label();
reference_to<Currency_exchg_rate_window>(pw).curr1_menu_pressed(reference_to<Fl_Widget>(pb).label());
}
static void cb_curr2_menu(Address pb, Address pw)
{
reference_to<Currency_exchg_rate_window>(pw).
curr2_menu_pressed(reference_to<Fl_Widget>(pb).label());
}
void curr1_button_pressed();
void curr2_button_pressed();
void curr1_menu_pressed(const string&);
void curr2_menu_pressed(const string&);
public:
Currency_exchg_rate_window(Point xy, int w, int h,
const string& fname = "exchange_rate.txt",
const string& tt = "Currentcy Exchange Rate Window");
};
//------------------------------------------------------------------------------
static void skip_line(istream& is)
{
//功能:跳过该行,即读入并忽略改行剩余字符
char ch{ 0 };
while (is.get(ch) && ch != '\n')
continue;
}
void read_exchange_rate(const string& fname, map<string, double>& er)
{
//功能:从汇率文件读入汇率到映射表中,如果遇到文件中的注释则跳过改行,
//注释是以'#'开始的一行
ifstream ifs{ fname };
if (!ifs)
error("Can't open ", fname);
char ch{ 0 };
while(ifs >> ch)
{
if (ch == '#')
skip_line(ifs);
else
{
ifs.putback(ch);
string cur;
double rate{ -1.0 };
ifs >> cur >> rate;
if (cur == "")
error("Read currency name failed");
else if (rate <= 0) //确保不读入0或者负数
error("Invalid rate: non-positive");
else
er[cur] = rate;
}
}
}
Currency_exchg_rate_window::Currency_exchg_rate_window(Point xy, int w, int h,
const string& fname, const string& tt)
:My_window(xy, w, h, tt),
curr1_button(Point{x_max()/2-150, y_max()/3},100,30,"Currency 1", cb_curr1_button),
curr2_button(Point{x_max()/2+50, y_max()/3},100,30,"Currency 2", cb_curr2_button),
curr1_menu(Point{ curr1_button.loc.x,curr1_button.loc.y + curr1_button.height }, curr1_button.width, curr1_button.height, Menu::vertical, "Currency 1 menu"),
curr2_menu(Point{ curr2_button.loc.x,curr2_button.loc.y + curr2_button.height }, curr2_button.width,curr2_button.height,Menu::vertical,"Currency 2 menu"),
rate1(Point{curr1_button.loc.x, curr1_button.loc.y + curr1_button.height + 30},curr1_button.width,curr1_button.height,""),
rate2(Point{curr2_button.loc.x, curr2_button.loc.y + curr2_button.height + 30},curr2_button.width,curr2_button.height,""),
is_menu1_hidden{true}, is_menu2_hidden{true},
base{"",0.0}, exchg{"", 0.0}
{
read_exchange_rate(fname, exchg_rates);
next_button.hide(); //不需要next按钮
attach(curr1_button);
attach(curr2_button);
attach(rate1);
attach(rate2);
rate1.put("1");
//接下来根据读入的汇率创建货币菜单
for (const auto& p : exchg_rates)
{
curr1_menu.attach(new Button{ Point{0,0},0,0,p.first,cb_curr1_menu });
curr2_menu.attach(new Button{ Point{0,0},0,0,p.first,cb_curr2_menu });
}
attach(curr1_menu);
attach(curr2_menu);
curr1_menu.hide();
curr2_menu.hide();
}
void Currency_exchg_rate_window::curr1_button_pressed()
{
if (is_menu1_hidden)
curr1_menu.show();
else
curr1_menu.hide();
is_menu1_hidden = !is_menu1_hidden;
//还会影响菜单2
if (!is_menu2_hidden)
{
curr2_menu.hide();
is_menu2_hidden = true;
}
}
void Currency_exchg_rate_window::curr2_button_pressed()
{
if (is_menu2_hidden)
curr2_menu.show();
else
curr2_menu.hide();
is_menu2_hidden = !is_menu2_hidden;
//还会影响菜单1
if (!is_menu1_hidden)
{
curr1_menu.hide();
is_menu1_hidden = true;
}
}
void Currency_exchg_rate_window::curr1_menu_pressed(const string& curr_name)
{
//设置基准货币,隐藏菜单,显示带有当前货币名的按钮,计算并输出新的汇率
base.first = curr_name;
base.second = exchg_rates[curr_name];
curr1_menu.hide();
is_menu1_hidden = true;
curr1_button.label = curr_name;
if(exchg.second > 0.0)
{
double res = exchg.second / base.second;
ostringstream oss;
oss << res;
rate2.put(oss.str());
}
}
void Currency_exchg_rate_window::curr2_menu_pressed(const string& curr_name)
{
//设置基准货币,隐藏菜单,显示带有当前货币名的按钮,计算并输出新的汇率
exchg.first = curr_name;
exchg.second = exchg_rates[curr_name];
curr2_menu.hide();
is_menu2_hidden = true;
curr2_button.label = curr_name;
if (base.second > 0.0)
{
double res = exchg.second / base.second;
ostringstream oss;
oss << res;
rate2.put(oss.str());
}
}
21.9 Calculator GUI
首先是经过修改后的 calculator.cpp
/*
calculator.cpp
本程序实现了一个简单的表达式计算器,支持变量操作
相比第七章的计算机,
从In_box读入,输出到Out_box,
取消了 quit 符号,
虽然没有取消 print 符号,但是用户自己输入print符号是非法的
输入文法如下:
Calculation:
Statement
Print
Quit
Calculation Statement
Print:
';'
Quit:
'q'
"quit"
Statement:
Declaration
Expression
Declaration:
"let" name '=' Expression
"const" name '=' Expression
Expression:
Term
Expression '+' Term
Expression '-' Term
Term:
Primary
Term * Primary
Term / Primary
Term % Primary
Primary:
Number
'(' Expression ')'
- Primary //处理负数
Assignment
Number:
floating-point-literal
Assignment:
name '=' Expression
*/
#include "My_window.h"
struct Token {
char kind;
double value;
string name;
Token(char ch) :kind(ch), value(0) { }
Token(char ch, double val) :kind(ch), value(val) { }
Token(char ch, string n) :kind(ch), name(n) { }
};
class Token_stream {
private:
bool full;
Token buffer;
public:
Token_stream() :full(false), buffer(0) { } // The constructor just sets full to indicate that the buffer is empty:
Token get(istream& is);
void putback(Token t);
void ignore(istream&, char);
};
const string declkey = "let";
const char let = 'L';
//const string quitkey = "quit";
//const char quit = 'q';
const char print = ';';
const char number = '8';
const char name = 'a';
const char assign = '=';
const char con = 'C';
const string constkey = "const";
Token Token_stream::get(istream& is)
{
if (full)
{
full = false;
return buffer;
}
char ch;
if (!(is >> ch)) // note that >> skips whitespace (space, newline, tab, etc.)
return Token{ print };
switch (ch) {
case '(': case ')':
case '+': case '-':
case '*': case '/': case '%':
case '=':
//case quit:
//case print:
return Token{ ch }; // let each character represent itself
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
is.putback(ch); // put digit back into the input stream
//is.unget(); // same as putback(char), except no parameter
double val;
is >> val; // read a floating-point number
return Token{ number, val };
}
default:
if (isalpha(ch)) {
string s;
s += ch;
//名字为字母开头的,带字母数字和下划线的字符串
while (is.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_'))
s += ch;
is.putback(ch);
if (s == declkey) return Token{ let }; // 声明变量关键字
if (s == constkey) return Token{ con }; // 声明常量关键字
//if (s == quitkey) return Token{ quit };
return Token{ name, s };
}
error("Bad token");
}
}
// The putback() member function puts its argument back into the Token_stream's buffer:
void Token_stream::putback(Token t)
{
if (full)
error("putback() into a full buffer.");
buffer = t;
full = true;
}
void Token_stream::ignore(istream& is, char c)
{
if (full && c == buffer.kind) {
full = false;
return;
}
full = false;
char ch;
while (is >> ch)
if (ch == c) return;
}
//---------------------------------------------------------
// set Variable
class Variable {
public:
string name;
double value;
bool is_const;
Variable(string n, double v, bool b) :name(n), value(v), is_const(b) { }
};
vector<Variable> var_table;
double get_value(string s)
{
for (const Variable& v : var_table)
if (v.name == s)
return v.value;
error("get: undefined name ", s);
}
void set_value(string s, double d)
{
for (Variable& v : var_table)
if (v.name == s)
{
if (v.is_const)
error("set: can not assign to a const ", s);
v.value = d;
return;
}
error("set: undefined name ", s);
}
bool is_declared(string var)
{
//判断var是否已经在var_table中了
for (const Variable& v : var_table)
if (v.name == var)
return true;
return false;
}
double define_name(string var, double val, bool is_const)
{
//将(var, val)加入var_table中
if (is_declared(var))
error(var, " declared twice");
var_table.push_back(Variable{ var, val, is_const });
return val;
}
Token_stream ts;
//--------Expression---------------
double expression(istream& is);
double assignment(istream& is, string var)
{
// 函数假设已经读入了赋值语句的左值和赋值符号
double right = expression(is);
set_value(var, right);
return right;
}
double primary(istream& is)
{
Token t = ts.get(is);
switch (t.kind) {
case '(':
{
double d = expression(is);
t = ts.get(is);
if (t.kind != ')')
error("'(' expected");
return d;
}
case '-':
return -primary(is);
case number:
return t.value;
case name:
{
Token t2 = ts.get(is);
if (t2.kind != assign)
{
ts.putback(t2);
return get_value(t.name); // if next char is not a assignment operator, then return variable value
}
else
return assignment(is, t.name);
}
default:
error("primary expected");
}
}
double term(istream& is)
{
double left = primary(is);
while (true) {
Token t = ts.get(is);
switch (t.kind) {
case '*':
left *= primary(is);
break;
case '/':
{
double d = primary(is);
if (d == 0) error("divide by zero");
left /= d;
break;
}
case '%':
{
double d = primary(is);
if (d == 0) error("divide by zero");
left = fmod(left, d);
break;
}
default:
ts.putback(t);
return left;
}
}
}
double expression(istream& is)
{
double left = term(is);
while (true) {
Token t = ts.get(is);
switch (t.kind) {
case '+':
left += term(is);
break;
case '-':
left -= term(is);
break;
case print:
return left;
default:
ts.putback(t);
return left;
}
}
}
double declaration(istream& is, char kind)
{
Token t = ts.get(is);
if (t.kind != name)
error("name expected in declaration");
string var_name = t.name;
Token t2 = ts.get(is);
if (t2.kind != '=')
error("= missing in declaration of ", var_name);
double d = expression(is);
bool is_const = kind == con;
define_name(var_name, d, is_const);
return d;
}
extern double statement(istream& is)
{
Token t = ts.get(is);
while (t.kind == print) t = ts.get(is);
switch (t.kind) {
case let:
case con:
return declaration(is, t.kind);
default:
ts.putback(t);
return expression(is);
}
}
//void clean_up_mess()
//{
// ts.ignore(print);
//}
接下来是计算器极度简陋的图形化界面的实现
class Calculator_window : public My_window
{
public:
Calculator_window(Point xy, int w, int h, const string& tt = "Calculator GUI");
private:
In_box stat; //输入的声明语句
Out_box res; //输出结果
//Button cal; //用next按钮代替
void next() override;
};
//------------------------------------------------------------------------------
Calculator_window::Calculator_window(Point xy, int w, int h, const string& tt)
:My_window(xy, w, h, tt),
stat{Point{xy.x + 50, xy.y + 50}, 200, 20, "statement"},
res{Point{xy.x + 50, xy.y + 80}, 200, 20, "result"}
//cal{ Point{xy.x + 170, xy.y + 50}, 50, 20, "calculate", }
{
attach(stat);
attach(res);
//修改next按钮的位置和名称
Point next_loc = next_button.loc;
int dx = stat.loc.x + stat.width + 20 - next_loc.x;
int dy = stat.loc.y - next_loc.y;
next_button.move(dx, dy);
next_button.label = "calculate";
}
void Calculator_window::next()
{
istringstream iss{ stat.get_string() };
ostringstream oss;
try {
double result = statement(iss);
oss << result;
}
catch (runtime_error& e) {
oss << e.what();
}
res.put(oss.str());
}
版权声明:本文标题:C++程序设计原理与实践 习题答案 第二十一章 第21章习题答案 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1728850135a1176570.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论