精华内容
参与话题
问答
  • java图形界面GUI编程(持续更新)

    万次阅读 多人点赞 2018-06-11 18:34:21
    文本编辑组件JTextField、JPasswordField、JTextArea 一、创建窗口程序 二、创建文本框、密码文本框、滚动文本区 三、创建按钮、单选按钮组和多选按钮组 ......
    展开全文
  • GUI(图形界面编程

    万次阅读 多人点赞 2018-08-27 08:32:23
    (1)GUI 与CLI  GUI  •Graphical User ... •用图形的方式,来显示计算机操作的界面,这样更方便更直观。  CLI  •Command line User Interface (命令行用户接口)  •就是常见的Dos命令行操作。  ...

    (1)GUI 与CLI

         GUI

              •Graphical User Interface(图形用户接口)。

              •用图形的方式,来显示计算机操作的界面,这样更方便更直观。

        CLI

              •Command line User Interface (命令行用户接口)

              •就是常见的Dos命令行操作。

              •需要记忆一些常用的命令,操作不直观。

          
        (2)相关的两个包:        

         java.awt

               Abstract Window ToolKit (抽象窗口工具包),需要调用本地系统方法实现功能。属重量级控件。

         javax.swing

              在AWT的基础上,建立的一套图形界面系统,其中提供了更多的组件,而且完全由Java实现。增强了移植性,属轻量级控件。
      

       (3)GUI的继承体系
            组件:组件就是对象
                容器组件:是可以存储基本组件和容器组件的组件。
                基本组件:是可以使用的组件,但是必须依赖容器。

       
        (4)事件监听机制
            事件源:事件发生的地方

            事件:就是要发生的事情

            事件处理:就是针对发生的事情做出的处理方案

            事件监听器:就是把事件源和事件关联起来
        (5)适配器模式
            接口
            抽象适配器类
            实现类
        (6)案例:
            A:创建窗体案例

    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象
    		// Frame f = new Frame();
    		// Frame(String title)
    		Frame f = new Frame("林青霞");
    
    		// 设置窗体标题
    		f.setTitle("HelloWorld");
    		// 设置窗体大小
    		f.setSize(400, 300); // 单位:像素
    		// 设置窗体位置
    		f.setLocation(400, 200);
    
            // 一个方法搞定
    		//f.setBounds(400, 200, 400, 300);
    
    		// 调用一个方法,设置让窗体可见
    		f.setVisible(true);
    
    		// System.out.println("helloworld");
    	}
    }


            B:窗体关闭案例

    import java.awt.Frame;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    import java.awt.event.WindowListener;
    
    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象
    		Frame f = new Frame("窗体关闭案例");
    
    		// 设置窗体属性
    		f.setBounds(400, 200, 400, 300);
    
    		// 让窗体关闭
    		//事件源
    		//事件:对窗体的处理
    		//事件处理:关闭窗口(System.exit(0));
    		//事件监听
    //		f.addWindowListener(new WindowListener() {
    //			@Override
    //			public void windowOpened(WindowEvent e) {
    //			}
    //			
    //			@Override
    //			public void windowIconified(WindowEvent e) {
    //			}
    //			
    //			@Override
    //			public void windowDeiconified(WindowEvent e) {
    //			}
    //			
    //			@Override
    //			public void windowDeactivated(WindowEvent e) {
    //			}
    //			
    //			@Override
    //			public void windowClosing(WindowEvent e) {
    //				System.exit(0);
    //			}
    //			
    //			@Override
    //			public void windowClosed(WindowEvent e) {
    //			}
    //			
    //			@Override
    //			public void windowActivated(WindowEvent e) {
    //			}
    //		});
    		
    		//用适配器类改进
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    
    		// 设置窗体可见
    		f.setVisible(true);
    	}
    }


            C:窗体添加按钮并对按钮添加事件案例。

    /*
     * 针对用户操作的四种功能
     */
    public interface UserDao {
    	public abstract void add();
    
    	public abstract void delete();
    
    	public abstract void update();
    
    	public abstract void find();
    }
    
    public class UserDaoImpl implements UserDao {
    
    	@Override
    	public void add() {
    		System.out.println("添加功能");
    	}
    
    	@Override
    	public void delete() {
    		System.out.println("删除功能");
    	}
    
    	@Override
    	public void update() {
    		System.out.println("修改功能");
    	}
    
    	@Override
    	public void find() {
    		System.out.println("查找功能");
    	}
    
    }
    

     /*
     * 问题:
     *         接口(方法比较多) -- 实现类(仅仅使用一个,也得把其他的实现给提供了,哪怕是空实现)
     *         太麻烦了,
     * 解决方案:
     *         接口(方法比较多) -- 适配器类(实现接口,仅仅空实现) -- 实现类(用哪个重写哪个)
     */

     

    public abstract class UserAdapter implements UserDao {
    
    	@Override
    	public void add() {
    	}
    
    	@Override
    	public void delete() {
    	}
    
    	@Override
    	public void update() {
    	}
    
    	@Override
    	public void find() {
    	}
    
    }
    
    public class UserDaoImpl2 extends UserAdapter {
    	@Override
    	public void add() {
    		System.out.println("添加功能");
    	}
    }
    

     

    
    public class UserDaoDemo {
    	public static void main(String[] args) {
    		UserDao ud = new UserDaoImpl();
    		ud.add();
    		// 我没有说我们需要四种功能都实现啊。
    		UserDao ud2 = new UserDaoImpl2();
    		ud2.add();
    	}
    }
    


                界面中的组件布局。

    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象
    		Frame f = new Frame("添加按钮");
    		// 设置属性
    		f.setBounds(400, 200, 400, 300);
    		// 设置布局为流式布局
    		f.setLayout(new FlowLayout());
    
    		// 创建按钮对象
    		Button bu = new Button("点我啊");
    		// bu.setSize(20, 10);
    
    		// 把按钮添加到窗体
    		f.add(bu);
    
    		// 设置窗体可以关闭
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    
    		bu.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				System.out.println("你再点试试");
    			}
    		});
    
    		// 窗体显示
    		f.setVisible(true);
    	}
    }


            D:把文本框里面的数据转移到文本域

    import java.awt.Button;
    import java.awt.FlowLayout;
    import java.awt.Frame;
    import java.awt.TextArea;
    import java.awt.TextField;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象
    		Frame f = new Frame("数据转移");
    		// 设置窗体属性和布局
    		f.setBounds(400, 200, 400, 300);
    		f.setLayout(new FlowLayout());
    
    		// 创建文本框
    		final TextField tf = new TextField(20);
    		// 创建按钮
    		Button bu = new Button("数据转移");
    		// 创建文本域
    		final TextArea ta = new TextArea(10, 40);
    
    		// 把组件添加到窗体
    		f.add(tf);
    		f.add(bu);
    		f.add(ta);
    
    		// 设置窗体关闭
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    
    		// 对按钮添加事件
    		bu.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				// 获取文本框的值
    				String tf_str = tf.getText().trim();
    				// 清空数据
    				tf.setText("");
    
    				// 设置给文本域
    				// ta.setText(tf_str);
    				// 追加和换行
    				ta.append(tf_str + "\r\n");
    				
    				//获取光标
    				tf.requestFocus();
    			}
    		});
    
    		// 设置窗体显示
    		f.setVisible(true);
    	}
    }
    


            E:更改背景色

    import java.awt.Button;
    import java.awt.Color;
    import java.awt.FlowLayout;
    import java.awt.Frame;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象
    		final Frame f = new Frame("更改背景色");
    		// 设置窗体属性和布局
    		f.setBounds(400, 200, 400, 300);
    		f.setLayout(new FlowLayout());
    
    		// 创建四个按钮
    		Button redButton = new Button("红色");
    		Button greenButton = new Button("绿色");
    		Button buleButton = new Button("蓝色");
    
    		// 添加按钮
    		f.add(redButton);
    		f.add(greenButton);
    		f.add(buleButton);
    
    		// 设置窗体关闭
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    
    		// 对按钮添加动作事件
    		// redButton.addActionListener(new ActionListener() {
    		// @Override
    		// public void actionPerformed(ActionEvent e) {
    		// f.setBackground(Color.RED);
    		// }
    		// });
    
    		// 对按钮添加鼠标点击事件
    		// redButton.addMouseListener(new MouseAdapter() {
    		// @Override
    		// public void mouseClicked(MouseEvent e) {
    		// f.setBackground(Color.RED);
    		// }
    		// });
    
    		// 对按钮添加鼠标的进入事件
    		redButton.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseEntered(MouseEvent e) {
    				f.setBackground(Color.RED);
    			}
    		});
    
    		redButton.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseExited(MouseEvent e) {
    				f.setBackground(Color.WHITE);
    			}
    		});
    
    		greenButton.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseEntered(MouseEvent e) {
    				f.setBackground(Color.GREEN);
    			}
    		});
    
    		greenButton.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseExited(MouseEvent e) {
    				f.setBackground(Color.WHITE);
    			}
    		});
    
    		buleButton.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseEntered(MouseEvent e) {
    				f.setBackground(Color.BLUE);
    			}
    		});
    
    		buleButton.addMouseListener(new MouseAdapter() {
    			@Override
    			public void mouseExited(MouseEvent e) {
    				f.setBackground(Color.WHITE);
    			}
    		});
    
    		// 设置窗体显示
    		f.setVisible(true);
    	}
    }
    


            F:设置文本框里面不能输入非数字字符

    import java.awt.FlowLayout;
    import java.awt.Frame;
    import java.awt.Label;
    import java.awt.TextField;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    /*
     * 你输入的如果是非数字字符,就取消你键盘录入的效果。
     */
    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象并设置属性
    		Frame f = new Frame("不能输入非数字字符");
    		f.setBounds(400, 200, 400, 300);
    		f.setLayout(new FlowLayout());
    
    		// 创建Label标签对象
    		Label label = new Label("请输入你的QQ号码,不能是非数字,不信你试试");
    		TextField tf = new TextField(40);
    
    		// 添加到窗体上
    		f.add(label);
    		f.add(tf);
    
    		// 设置窗体关闭
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    
    		// 给文本框添加事件
    		tf.addKeyListener(new KeyAdapter() {
    			@Override
    			public void keyPressed(KeyEvent e) {
    				// 如果你取得的字符不是数字字符就取消事件
    				// 思路:先获取字符,判断字符,取消事件
    				// char getKeyChar()  
    				char ch = e.getKeyChar();
    //				System.out.println(ch);
    				if(!(ch>='0' && ch<='9')){
    					e.consume();
    				}
    			}
    		});
    
    		// 设置窗体可见
    		f.setVisible(true);
    	}
    }


            G:一级菜单

    import java.awt.FlowLayout;
    import java.awt.Frame;
    import java.awt.Menu;
    import java.awt.MenuBar;
    import java.awt.MenuItem;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    /*
     * 一级菜单
     */
    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象并设置属性
    		Frame f = new Frame("一级菜单");
    		f.setBounds(400, 200, 400, 300);
    		f.setLayout(new FlowLayout());
    
    		// 创建菜单栏
    		MenuBar mb = new MenuBar();
    		// 创建菜单
    		Menu m = new Menu("文件");
    		// 创建菜单项
    		MenuItem mi = new MenuItem("退出系统");
    
    		// 谁添加谁呢
    		m.add(mi);
    		mb.add(m);
    
    		// 设置菜单栏
    		f.setMenuBar(mb);
    
    		// 设置窗体关闭
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    
    		mi.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				System.exit(0);
    			}
    		});
    
    		// 设置窗体可见
    		f.setVisible(true);
    	}
    }


            H:多级菜单

    import java.awt.FlowLayout;
    import java.awt.Frame;
    import java.awt.Menu;
    import java.awt.MenuBar;
    import java.awt.MenuItem;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    import java.io.IOException;
    
    /*
     * 多级菜单
     */
    public class FrameDemo {
    	public static void main(String[] args) {
    		// 创建窗体对象并设置属性
    		final Frame f = new Frame("多级菜单");
    		f.setBounds(400, 200, 400, 300);
    		f.setLayout(new FlowLayout());
    		
    		final String name = f.getTitle();
    
    		// 创建菜单栏
    		MenuBar mb = new MenuBar();
    		// 创建菜单
    		Menu m1 = new Menu("文件");
    		Menu m2 = new Menu("更改名称");
    		// 创建菜单项
    		final MenuItem mi1 = new MenuItem("好好学习");
    		final MenuItem mi2 = new MenuItem("天天向上");
    		MenuItem mi3 = new MenuItem("恢复标题");
    		MenuItem mi4 = new MenuItem("打开记事本");
    		MenuItem mi5 = new MenuItem("退出系统");
    
    		// 谁添加谁呢
    		m2.add(mi1);
    		m2.add(mi2);
    		m2.add(mi3);
    		
    		m1.add(m2);
    		m1.add(mi4);
    		m1.add(mi5);
    		
    		mb.add(m1);
    
    		// 设置菜单栏
    		f.setMenuBar(mb);
    
    		// 设置窗体关闭
    		f.addWindowListener(new WindowAdapter() {
    			@Override
    			public void windowClosing(WindowEvent e) {
    				System.exit(0);
    			}
    		});
    		
    		mi1.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				f.setTitle(mi1.getLabel());
    			}
    		});
    		
    		mi2.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				f.setTitle(mi2.getLabel());
    			}
    		});
    		
    		mi3.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				f.setTitle(name);
    			}
    		});
    		
    		mi4.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				Runtime r = Runtime.getRuntime();
    				try {
    					r.exec("notepad");
    				} catch (IOException e1) {
    					e1.printStackTrace();
    				}
    			}
    		});
    
    		mi5.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				System.exit(0);
    			}
    		});
    
    		// 设置窗体可见
    		f.setVisible(true);
    	}
    }

     

    展开全文
  • GUI图形界面编程

    千次阅读 2019-07-21 14:13:13
    GUI 图形用户界面编程 ​ 文章目录GUI 图形用户界面编程常用的 GUI 库tkinter 模块GUI 编程的核心步骤和第一个 GUI 程序tkinter 主窗口UI 编程整体描述常用组件汇总列表GUI 应用程序类的经典写法简单组件Label标签...

    GUI 图形用户界面编程


    ​ 我们前面实现的都是基于控制台的程序,程序和用户的交互通过控制台来完成。本章,我们将学习GUI(Graphics User Interface),即图形用户界面编程,我们可以通过 python 提供的丰富的组件,快速的实现使用图形界面和用户交互。
    ​ GUI 编程类似于“搭积木”,将一个个组件(Widget)放到窗口中。如下是 windows 中的画图软件,就是一个典型的 GUI 程序:上面的各种按钮、菜单、编辑区域等都是一个个组件,它们都放置到窗口中,并通过增加“对事件的处理”成为一个完整的程序。

    常用的 GUI 库

    1. Tkinter
      tkinter(Tk interface)是 Python 的标准 GUI 库,支持跨平台的 GUI 程序开发。tkinter适合小型的 GUI 程序编写,也特别适合初学者学习 GUI 编程。本书以 tkinter 为核心进行讲解。
    2. wxPython
      wxPython 是比较流行的 GUI 库,适合大型应用程序开发,功能强于 tkinter,整体设计框架类似于MFC(Microsoft Foundation Classes 微软基础类库)。
    3. PyQT
      Qt 是一种开源的 GUI 库,适合大型 GUI 程序开发,PyQT 是 Qt 工具包标准的 Python 实现。我们也可以使用 Qt Desginer 界面设计器快速开发 GUI 应用程序。

    tkinter 模块

    本章中,涉及大量的 API 讲解。学习 API 最好的来源就是官方提供的文档:tkinter 官方网址:
    https://docs.python.org/3.7/library/tk.html或者:http://effbot.org/tkinterbook/ (相对规整,适合初学者查找)。

    GUI 编程的核心步骤和第一个 GUI 程序

    基于 tkinter 模块创建 GUI 程序包含如下 4 个核心步骤:

    1. 创建应用程序主窗口对象(也称:根窗口)
    (1) 通过类 Tk 的无参构造函数
    from tkinter import *
    root = Tk()
    
    1. 在主窗口中,添加各种可视化组件,比如:按钮(Button)、文本框(Label)等。
    btn01 = Button(root)
    btn01["text"] = "点我就送花"
    
    1. 通过几何布局管理 器,管理组件的大小和位置
    btn01.pack()
    
    1. 事件处理
    (1) 通过绑定事件处理程序,响应用户操作所触发的事件(比如:单击、双击等)
    def songhua(e):
    messagebox.showinfo("Message","送你一朵玫瑰花,请你爱上我")
    print("送你 99 朵玫瑰花")
    btn01.bind("<Button-1>",songhua)
    

    【示例】使用 tkinter 模块,创建 GUI 应用程序,并实现点击按钮的事件处理

    from tkinter import *
    from tkinter import messagebox
    root = Tk()
    btn01 = Button(root)
    btn01["text"] = "点我就送花"
    btn01.pack()
    def songhua(e):
    messagebox.showinfo("Message","送你一朵玫瑰花,请你爱上我")
    print("送你 99 朵玫瑰花")
    btn01.bind("<Button-1>",songhua)
    root.mainloop() #调用组件的 mainloop 方法,进入事件循环
    

    tkinter 主窗口

    主窗口位置和大小
    ​ 通过 geometry(‘wxh±x±y’)进行设置。w 为宽度,h 为高度。+x 表示距屏幕左边的距离;-x 表示距屏幕右边的距离;+y 表示距屏幕上边的距离;-y 表示距屏幕下边的距离。

    【示例】测试 tkinter 主窗口位置和大小的设置

    from tkinter import *
    root = Tk()
    root.title("测试主窗口的位置和大小")
    root.geometry("500x400+100+200") #宽度 500,高度 400;距屏幕左边 100,距屏幕上边
    200
    root.mainloop()
    

    执行结果:

    在这里插入图片描述

    UI 编程整体描述

    ​ 图形用户界面是由一个个组件组成,就像小孩“搭积木”一样最终组成了整个界面。有的组件还能在里面再放置其他组件,我们称为“容器”。Tkinter 的 GUI 组件关系图如下:

    [外链图片转存失败(img-EcrnVCCL-1563689228532)(C:\Users\Administrator\Documents\gui组件关系图.jpg)]

    • Misc 和 Wm:

      Tkinter 的 GUI 组件有两个根父类,它们都直接继承了 object 类:

      ·Misc:它是所有组件的根父类。

      ·Wm:它主要提供了一些与窗口管理器通信的功能函数。

    • Tk

      Misc 和 Wm 派生出子类 Tk,它代表应用程序的主窗口。一般应用程序都需要直接或间接使用 Tk。

    • Pack、Place、Grid

      Pack、Place、Grid 是布局管理器。布局管理器管理组件的:大小、位置。通过布局管理器可以将容器中的组件实现合理的排布。

    • BaseWidget

      BaseWidget 是所有组件的父类

    • Widget

      Widget 是所有组件类的父类。Widget 一共有四个父类:BaseWidget、Pack、Grid、Place。意味着,所有 GUI 组件同时具备这四个父类的属性和方法。

    在这里插入图片描述

    【注】想观察类的层次结构可以在类定义处的类名上单击右键,选择 Diagram–>showDiagram。

    常用组件汇总列表

    Tkinter 类 名称 简介
    Toplevel 顶层 容器类,可用于为其他组件提供单独的容器;Toplevel 有点类似于窗口
    Button 按钮 代表按键组件
    Canvas 画布 提供绘画功能,包括直线、矩形、椭圆、多边形、位图等
    Checkbutton 复选框 可供用户勾选的复选框
    Entry 单行输入框 用户可输入内容
    Frame 容器 用于装载其他GUI组件
    Label 标签 用于显示不可编辑的文本或图标
    LabelFrame 容器 也是容器组件,类似于Frame,但它支持添加标题
    Listbox 列表框 列出多个选项,供用户选择

    GUI 应用程序类的经典写法

    ​ 本节程序也是 GUI 应用程序编写的一个主要结构,采用了面向对象的方式,更加合理的组织代码。
    通过类 Application 组织整个 GUI 程序,类 Application 继承了 Frame 及通过继承拥有了父类的特性。通过构造函数__init__()初始化窗口中的对象,通过 createWidgets()方法创建窗口中的对象。Frame 框架是一个 tkinter 组件,表示一个矩形的区域。Frame 一般作为容器使用,可以放置其他组件,从而实现复杂的布局。

    【栗子】 标准的 GUI 程序类的写法

    """测试一个经典的 GUI 程序的写法,使用面向对象的方式"""
    from tkinter import *
    from tkinter import messagebox
    
    class Application(Frame):
    """一个经典的 GUI 程序的类的写法"""
        def __init__(self, master=None):
            super().__init__(master)      # super()代表的是父类的定义,而不是父类对象
            self.master = master	
            self.pack()
            self.createWidget()
        
        def createWidget(self):
            """创建组件"""
            self.btn01 = Button(self)
            self.btn01["text"] = "点击送花"
            self.btn01.pack()
            self.btn01["command"] = self.songhua
           
            # 创建一个退出按钮
            self.btnQuit = Button(self, text="退出", command=root.destroy)
            self.btnQuit.pack()
            def songhua(self):
            messagebox.showinfo("送花","送你 99 朵玫瑰花")
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x100+200+300")
        root.title("一个经典的 GUI 程序类的测试")
        app = Application(master=root)
        root.mainloop()
    

    简单组件

    Label标签

    Label(标签)主要用于显示文本信息,也可以显示图像。

    Label(标签)有这样一些常见属性:

    1. width,height:
      用于指定区域大小,如果显示是文本,则以单个英文字符大小为单位(一个汉字宽度占2 个字符位置,高度和英文字符一样);如果显示是图像,则以像素为单位。默认值是根据具体显示的内容动态调整。
    2. font
      指定字体和字体大小,如:font = (font_name,size)
    3. image:
      显示在 Label 上的图像,目前 tkinter 只支持 gif 格式。
    4. fg 和 bg
      fg(foreground):前景色、bg(background):背景色。
    5. justify
      针对多行文字的对齐,可设置 justify 属性,可选值"left", “center” or "right。

    【栗子】

    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.create_widget()
    
        def create_widget(self):
            self.label01 = Label(self,text = "百战程序员", width=8, height=2,
                                 fg="blue", bg="white")
            #对上面的字典参数,重新赋值改变
            #self.label01["text"] = "ccc"       
            #self.label01.config(fg="red", bg="green")
            self.label01.pack()
            self.label02 = Label(self, text="Mr_huang,你好", width=15, height=2,
                                 fg="red",bg="black",font=("楷体",18))
            self.label02.pack()
            # 显示图片
            global photo   #声明为全局变量,如果是局部变量,本方法执行完毕后自动销毁,窗口显示不出来
            photo = PhotoImage(file="imgs/logo.gif")
            self.label03 = Label(self, image=photo)
            self.label03.pack()
            #多行显示
            self.label04 = Label(self,text = "北京尚学堂\nPython培训\n黄璞",
                                 borderwidth=3,relief="ridge", justify="left")
            self.label04.pack()
    
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x300+200+100")
        root.title("Gui/Lable组件测试")
        app = Application(master=root)
        root.mainloop()
    

    运行结果为:
    在这里插入图片描述

    Options 选项详解

    ​ 通过学习 Label 组件,我们发现可以通过 Options 设置组件的属性,从而控制组件的各种状态。比如:宽度、高度、颜色、位置等等。
    ​ 我们可以通过三种方式设置 Options 选项,这在各种 GUI 组件中用法都一致。

    1. 创建对象时,使用可变参数
    fred = Button(self, fg="red", bg="blue")
    
    1. 创建对象后,使用字典索引方式
    fred["fg"] = "red"
    fred["bg"] = "blue"
    
    1. 创建对象后,使用 config()方法

      fred.config(fg="red", bg="blue")
      

    常用的组件汇总如下:

    选项名 (别名) 含义
    activebackground 指定组件处于激活状态时的背景色
    activeforeground 指定组件处于激活状态时的前景色
    anchor
    指定组件内的信息(比如文本或图片)在组件中如何显示(当所在组件
    比信息大时,可以看出效果)。必须为下面的值之一:N、NE、E、SE、
    S、SW、W、NW 或 CENTER。比如 NW(NorthWest)指定将信息显示在组件的左上角
    background(bg) 指定组件正常显示时的背景色
    bitmap
    指定在组件上显示该选项指定的位图,该选项值可以是 Tk_GetBitmap
    接收的任何形式的位图。位图的显示方式受 anchor、justify 选项的
    影响。如果同时指定了 bitmap 和 text,那么 bitmap 覆盖文本;如果
    同时指定了 bitmap 和 image,那么 image 覆盖 bitmap
    borderwidth 指定组件正常显示时的 3D 边框的宽度,该值可以是 Tk_GetPixels 接
    收的任何格式
    cursor
    指定光标在组件上的样式。该值可以是 Tk_GetCursors 接受的任何格

    command 指定按组件关联的命令方法,该方法通常在鼠标离开组件时被触发调

    disabledforeground 指定组件处于禁用状态时的前景色
    font 指定组件上显示的文本字体
    foreground(fg) 指定组件正常显示时的前景色
    highlightbackgroun
    d 指定组件在高亮状态下的背景色
    highlightcolor 指定组件在高亮状态下的前景色
    highlightthickness
    指定组件在高亮状态下的周围方形区域的宽度,该值可以是
    Tk_GetPixels 接收的任何格式
    height
    指定组件的高度,以 font 选项指定的字体的字符高度为单位,至少为
    1
    image
    指定组件中显示的图像,如果设置了 image 选项,它将会覆盖 text、
    bitmap 选项
    justify
    指定组件内部内容的对齐方式,该选项支持 LEFT(左对齐)、CENTER
    (居中对齐)或 RIGHT(右对齐)这三个值
    padx
    指定组件内部在水平方向上两边的空白,该值可以是 Tk_GctPixels 接
    收的任何格式
    pady
    指定组件内部在垂直方向上两地的空白,该值可以是 Tk_GctPixels 接
    收的任何格式
    relief
    指定组件的 3D 效果,该选项支持的值包括 RAISED、SUNKEN、FLAT、
    RIDGE、SOLID、GROOVE。该值指出组件内部相对于外部的外观样式,
    比如 RAISED 表示组件内部相对于外部凸起
    selectbackground 指定组件在选中状态下的背景色
    selectborderwidth 指定组件在选中状态下的 3D 边框的宽度,该值可以是 Tk_GetPixels
    接收的任何格式
    selectforeground 指定组在选中状态下的前景色
    state
    指定组件的当前状态。该选项支持 NORMAL(正常)、DISABLE(禁用)
    这两个值
    takefocus
    指定组件在键盘遍历(Tab 或 Shift+Tab)时是否接收焦点,将该选
    项设为 1 表示接收焦点;设为 0 表示不接收焦点
    text 指定组件上显示的文本,文本显示格式由组件本身、anchor 及justify 选项决定
    textvariable
    指定一个变量名,GUI 组件负责显示该变量值转换得到的字符串,文
    本显示格式由组件本身、anchor 及 justify 选项决定
    underline
    指定为组件文本的第几个字符添加下画线,该选项就相当于为组件绑
    定了快捷键
    width 指定组件的宽度,以 font 选项指定的字体的字符高度为单位,至少为
    1
    wraplength 对于能支持字符换行的组件,该选项指定每行显示的最大字符数,超
    过该数量的字符将会转到下行显示
    xscrollcommand
    通常用于将组件的水平滚动改变(包括内容滚动或宽度发生改变)与
    水平滚动条的 set 方法关联,从而让组件的水平滚动改变传递到水平
    滚动条
    yscrollcommand
    通常用于将组件的垂直滚动改变(包括内容滚动或高度发生改变)与
    垂直滚动条的 set 方法关联,从而让组件的垂直滚动改变传递到垂直
    滚动条

    Button

    ​ Button(按钮)用来执行用户的单击操作。Button 可以包含文本,也可以包含图像。按钮被单击后会自动调用对应事件绑定的方法。

    【栗子】:

    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.create_widget()
    
        def create_widget(self):
            #创建组件
            self.btn01 = Button(root,text="登录", width=10, height=2,
                                 command=self.login)
            self.btn01.pack()
            #显示图片
            global photo    #声明全局变量
            photo = PhotoImage(file="imgs/start.gif")
            self.btn02 = Button(root, image=photo,  width=200, height=150, command=self.login)
            self.btn02.pack()
            # self.btn02.config(state="disabled")  # 设置按钮为禁用
    
        def login(self):
            messagebox.showinfo("登录", "登录成功!")
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x300+200+100")
        root.title("Gui/Lable组件测试")
        app = Application(master=root)
        root.mainloop()
    

    运行结果如下:

    在这里插入图片描述

    Entry 单行文本框

    ​ Entry 用来接收一行字符串的控件。如果用户输入的文字长度长于 Entry 控件的宽度时, 文字会自动向后滚动。如果想输入多行文本, 需要使用 Text 控件。

    from tkinter import *
    from tkinter import messagebox
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.create_widget()
    
        def create_widget(self):
            """建登录界面的组件 """
            self.label01 = Label(self,text="用户名")
            self.label01.pack()
            # StringVar 变量绑定到指定的组件。
            # StringVar 变量的值发生变化,组件内容也变化;
            # 组件内容发生变化,StringVar 变量的值也发生变化。
            v1 = StringVar()
            self.entry01 = Entry(self, textvariable=v1)
            self.entry01.pack()
            v1.set("admin")
            print(v1.get());print(self.entry01.get())
    
            #创建密码框
            self.label02 = Label(self, text="密码")
            self.label02.pack()
    
            v2 = StringVar()
            self.entry02 = Entry(self, textvariable=v2)
            self.entry02.pack()
            # 登录按钮
            Button(self, text="登录", command=self.login).pack()
    
        def login(self):
            username = self.entry01.get()
            pwd = self.entry02.get()
            print("去数据库比对用户密码!")
            print("用户名:"+username)
            print("密码:"+pwd)
    
            if username=="huangpu"and pwd=="123456":
                messagebox.showinfo("测试登录系统", "登录成功,你好棒!")
            else:
                messagebox.showinfo("测试登录系统", "登录失败,请核对用户名和密码!")
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x300+200+100")
        root.title("Gui/Lable组件测试")
        app = Application(master=root)
        root.mainloop()
    

    Text 多行文本框

    ​ Text(多行文本框)的主要用于显示多行文本,还可以显示网页链接, 图片, HTML 页面, 甚至 CSS 样式表,添加组件等。因此,也常被当做简单的文本处理器、文本编辑器或者网
    页浏览器来使用。比如 IDLE 就是 Text 组件构成的。
    【示例】Text 多行文本框基本用法(文本输入、组件和图像显示

    from tkinter import *
    from tkinter import messagebox
    import webbrowser
    
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.master = master
            self.pack()
            self.create_widget()
    
        def create_widget(self):
            self.w1 = Text(root,width=150,height=50,bg="gray")
            self.w1.pack()
    
            self.w1.insert(1.0,"0123456789\nabcdefg")
            self.w1.insert(2.7,"欲穷千里目,更上一层楼。")
    
            Button(self,text="重复插入文本",command=self.insertText).pack(side="left")
            Button(self,text="返回文本",command=self.returnText).pack(side="left")
            Button(self,text="添加图片",command=self.addImage).pack(side="left")
            Button(self,text="添加组件",command=self.addWidget).pack(side="left")
            Button(self,text="通过 tag 精确控制文本",command=self.textTag).pack(side="left")
    
        def insertText(self):
            # INSERT 索引表示在光标处插入
            self.w1.insert(INSERT, 'huangpu')
            #END 索引号表示在最后插入
            self.w1.insert(END, '[python]')
            self.w1.insert(1.8, 'Mr_huang')
    
        def returnText(self):
            # Indexes(索引)是用来指向Text组件中文本的位置,Text的组件索引也是对应实际字符之间的位置。
            # 核心:行号以1开始 列号以0开始
            print(self.w1.get(1.2, 1.6))
            print("所有文本内容:\n"+self.w1.get(1.0, END))
    
        def addImage(self):
            # global photo
            self.photo = PhotoImage(file="imgs/logo.gif")
            self.w1.image_create(END,image=self.photo)
    
        def addWidget(self):
            b1 = Button(self.w1,text="爱python")
            #在text创建组件的命令
            self.w1.window_create(INSERT, window=b1)
    
        def textTag(self):
            self.w1.delete(1.0, END)
            self.w1.insert(INSERT, "good good study,day day up!\n 人生苦短\n 快用python\n百度,嗖一下就知道")
            self.w1.tag_add("good", 1.0, 1.9)
            self.w1.tag_config("good", background="yellow", foreground="red")
            self.w1.tag_add("baidu", 4.0, 4.2)
            self.w1.tag_config("baidu", underline=True)
            self.w1.tag_bind("baidu", "<Button-1>", self.webshow)
    
        def webshow(self,event):
            webbrowser.open("http://www.baidu.com")
    if __name__ == '__main__':
        root = Tk()
        root.geometry("860x640+300+100")
        root.title("Gui/Lable组件测试")
        app = Application(master=root)
        root.mainloop()
    

    Radiobutton 单选按钮

    ​ Radiobutton 控件用于选择同一组单选按钮中的一个。Radiobutton 可以显示文本,也可以
    显示图像。

    from tkinter import *
    from tkinter import messagebox
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master) # super()代表的是父类的定义,而不是父类对象
            self.master = master
            self.pack()
            self.createWidget()
        def createWidget(self):
            self.v = StringVar();
            self.v.set("F")
            self.r1 = Radiobutton(self, text="男性", value="M", variable=self.v)
            self.r2 = Radiobutton(self, text="女性", value="F", variable=self.v)
            self.r1.pack(side="left");self.r2.pack(side="left")
            Button(self, text="确定", command=self.confirm).pack(side="left")
        def confirm(self):
            messagebox.showinfo("测试","选择的性别:"+self.v.get())
    if __name__ == '__main__':
    root = Tk()
    root.geometry("400x50+200+300")
    app = Application(master=root)
    root.mainloop()
    

    Checkbutton 复选按钮

    ​ Checkbutton 控件用于选择多个按钮的情况。Checkbutton 可以显示文本,也可以显示图像。

    from tkinter import *
    from tkinter import messagebox
    
    class Application(Frame):
        def __init__(self, master=None):
            super().__init__(master) # super()代表的是父类的定义,而不是父类对象
            self.master = master
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            self.codeHobby = IntVar()
            self.videoHobby = IntVar()
            print(self.codeHobby.get())   # 默认值为0
            # offvalue 是没选中为0, onvalue为选中为1
            self.c1 = Checkbutton(self, text="敲代码",variable=self.codeHobby, onvalue=1, offvalue=0)
            self.c2 = Checkbutton(self, text="看视频",variable=self.videoHobby, onvalue=1, offvalue=0)
            self.c1.pack(side="left");self.c2.pack(side="left")
            Button(self, text="确定", command=self.confirm).pack(side="left")
        def confirm(self):
            if self.videoHobby.get() == 1:
                messagebox.showinfo("测试","看视频,都是正常人有的爱好!你喜欢看什么类型?")
            if self.codeHobby.get() == 1:
                messagebox.showinfo("测试","抓获野生程序猿一只,赶紧送给他尚学堂的视频充饥")
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("400x50+200+300")
        app = Application(master=root)
        root.mainloop()
    

    canvas 画布

    ​ canvas(画布)是一个矩形区域,可以放置图形、图像、组件等。

    from tkinter import *
    from tkinter import messagebox
    import random
    
    class Application(Frame):
        def __init__(self,master=None):
            super().__init__(master)
            self.pack()
            self.master = master
            self.createWidget()
    
        def createWidget(self):
            self.canvas = Canvas(self,width=300, height=200, bg="green")
            self.canvas.pack()
            # 画一条直线
            line = self.canvas.create_line(10,10,30,20,40,50)
            # 画一个矩形:
            rect = self.canvas.create_rectangle(50,50,100,100)
            ocal = self.canvas.create_oval(50,50,100,100)
            global photo
            photo = PhotoImage(file="imgs/logo.gif")
            self.canvas.create_image(150,170,image=photo)
    
            Button(self,text="画十个矩形",command=self.draw50Recg).pack()
        def draw50Recg(self):
            for i in range(10):
                x1 = random.randrange(int(self.canvas["width"])/2)
                y1 = random.randrange(int(self.canvas["height"])/2)
                x2 = random.randrange(int(self.canvas["width"])/2)
                y2 = random.randrange(int(self.canvas["height"])/2)
                self.canvas.create_rectangle(x1,y1,x2,y2)
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("840x620+200+300")
        app = Application(master=root)
        root.mainloop()
    

    布局管理器

    ​ 一个 GUI 应用程序必然有大量的组件,这些组件如何排布?这时候,就需要使用 tkinter提供的布局管理器帮助我们组织、管理在父组件中子组件的布局方式。tkinter 提供了三种管理器:pack、grid、place。

    grid 布局管理器

    ​ grid 表格布局,采用表格结构组织组件。子组件的位置由行和列的单元格来确定,并且可以跨行和跨列,从而实现复杂的布局。

    grid()方法提供的选项
    选项 说明 取值范围
    column 单元格的列号 从 0 开始的正整数
    columnspan 跨列,跨越的列数 正整数
    row 单元格的行号 从 0 开始的正整数
    rowspan 跨行,跨越的行数 正整数
    ipadx, ipady 设置子组件之间的间隔,x 方向或者 y 方向,
    默认单位为像素
    非负浮点数,默认 0.0
    padx, pady 与之并列的组件之间的间隔,x 方向或者 y 方
    向,默认单位是像素
    非负浮点数,默认 0.0
    sticky 组件紧贴所在单元格的某一角,对应于东南西
    北中以及 4 个角
    “n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)

    【栗子】

    from tkinter import *
    from tkinter import messagebox
    import random
    
    class Application(Frame):
        def __init__(self,master=None):
            super().__init__(master)
            self.pack()
            self.master = master
            self.createWidget()
    
        def createWidget(self):
            self.label01 = Label(self,text="用户名")
            self.label01.grid(row=0, column=0)
            self.entry01 = Entry(self)
            self.entry01.grid(row=0,column=1)
            Label(self, text="用户手机号").grid(row=0,column=2)
            Label(self,text="密码").grid(row=1,column=0)
            Entry(self,show="*").grid(row=1,column=1)
    
            Button(self,text="登录").grid(row=2,column=1,sticky=EW)
            Button(self,text="取消").grid(row=2,column=2,sticky=E)
    
    if __name__ == '__main__':
        root = Tk()
        root.geometry("840x620+200+300")
        app = Application(master=root)
        root.mainloop()
    

    pack 布局管理器

    ​ pack 按照组件的创建顺序将子组件添加到父组件中,按照垂直或者水平的方向自然排布。如果不指定任何选项,默认在父组件中自顶向下垂直添加组件。pack 是代码量最少,最简单的一种,可以用于快速生成界面。

    ​ pack()方法提供的选项
    名称 描述 取值范围
    expand
    当值为“yes”时,side 选项无效。组件显示在父配
    件中心位置;若 fill 选项为”both”,则填充父组件
    的剩余空间
    “yes”, 自然数,”no”, 0
    (默认值”no”或 0)
    fill 填充 x(y)方向上的空间,当属性 side=”top”或”
    bottom”时,填充 x 方向;当属性 side=”left”或”
    right”时,填充”y”方向;当 expand 选项为”yes”
    时,填充父组件的剩余空间
    “x”, “y”, “both”,
    “none”
    (默认值为 none)
    ipadx
    ,
    ipady
    设置子组件之间的间隔,x 方向或者 y 方向,默认单
    位为像素
    非负浮点数,默认 0.0
    padx,
    pady
    与之并列的组件之间的间隔,x 方向或者 y 方向,默
    认单位是像素
    非负浮点数,默认 0.0
    side 定义停靠在父组件的哪一边上 “ top ” , “ bottom ” , “left”, “right”
    (默认为”top”)
    befor
    e
    将本组件于所选组建对象之前 pack,类似于先创建本
    组件再创建选定组件
    已经 pack 后的组件对象
    after 将本组件于所选组建对象之后 pack,类似于先创建选
    定组件再本组件
    已经 pack 后的组件对象
    in_ 将本组件作为所选组建对象的子组件,类似于指定本
    组件的 master 为选定组件
    已经 pack 后的组件对象
    anchor
    对齐方式,左对齐”w”,右对齐”e”,顶对齐”n”,
    底对齐”s”
    “n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)

    【建议】如上列出了 pack 布局所有的属性,但是不需要挨个熟悉,了解基本pack 适用于简单的垂直或水平排布,如果需要复杂的布局可以使用 grid 或 place。

    【栗子】制作钢琴按键布局

    from tkinter import *
    root = Tk(); root.geometry("800x300")
    # Frame是一个矩形区域,就是用来防止其他组件
    f1 = Frame(root)
    f1.pack()
    f2 = Frame(root); f2.pack()
    
    btntext = ("流行风","中国风","古典风","轻音乐","重金属")
    
    for txt in btntext:
        Button(f1, text=txt).pack(side="left", padx=10)
    for i in range(1,15):
        Button(f2, width=5, height=10, bg="black" if i % 2 == 0 else "white").pack(side="left")
    
    
    root.mainloop()
    

    place 布局管理器

    ​ place 布局管理器可以通过坐标精确控制组件的位置,适用于一些布局更加灵活的场景。
    place()方法的选项
    选项 说明 取值范围
    x,y 组件左上角的绝对
    ​ 坐标(相对于窗口)
    非负整数
    x 和 y 选项用于设置偏移(像素),如果同时设置
    relx(rely) 和 x(y),那么 place 将优先计算 relx 和
    rely,然后再实现 x 和 y 指定的偏移值
    relx
    rely
    组件左上角的坐标
    (相对于父容器)
    relx 是相对父组件的位置。0 是最左边,0.5 是正中间,1
    是最右边;
    rely 是相对父组件的位置。0 是最上边,0.5 是正中间,1
    是最下边;
    width,
    height
    组件的宽度和高度 非负整数
    relwid
    th,
    relhei
    组件的宽度和高度
    (相对于父容器)
    与 relx、rely 取值类似,但是相对于父组件的尺寸

    ght
    anchor 对齐方式,左对齐”
    w”,右对齐”e”,
    顶对齐”n”,底对
    齐”s”
    “n”, “s”, “w”, “e”, “nw”, “sw”, “se”, “ne”, “center”(默认)

    【栗子】用palce 布局管理-- 基本用法

    #coding=utf-8
    from tkinter import *
    
    root = Tk(); root.geometry("500x300")
    root.title("place 布局管理器"); root["bg"]= "white"
    
    f1 = Frame(root,width=200,height= 200, bg="green")
    f1.place(x=30, y= 30 )
    
    Button(root,text="python").place(relx=0.5,rely=0, x=100, y=200, relwidth= 0.2, relheight=0.2)
    Button(f1,text='程序猿').place(relx=0.6, rely=0.7)
    Button(f1,text="python程序猿").place(relx=0.2,rely=0.2)
    
    root.mainloop()
    

    在这里插入图片描述

    【栗子】place 布局管理 - 扑克游戏 demo

    # coding = utf-8
    from tkinter import *
    
    class Application(Frame):
    
        def __init__(self,master=None):
            super().__init__(master)
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            """  通过布局管理器实现puke 位置控制 """
            # self.photo = PhotoImage(file="imgs/puke/puke1.gif")
            # self.puke1 = Label(self.master,image=self.photo)
            # self.puke1.pack()
            self.photos = [PhotoImage(file = "imgs/puke/puke"+str(i+1)+".gif") for i in range(10)]
            self.pukes = [Label(self.master,image=self.photos[i]) for i in range(10)]
    
            for i in range(10):
                self.pukes[i].place(x=55+i*40,y=50)
    
            # 为所有的Label 增加时间处理
            self.pukes[0].bind_class("Label","<Button-1>",self.chupai)
    
        def chupai(self,event):
            print(event.widget.winfo_geometry())
            print(event.widget.winfo_y())
            if event.widget.winfo_y() == 50:
                event.widget.place(y=30)
            else:
                event.widget.place(y=50)
    
    if __name__ == '__main__':
        root=Tk()
        root.geometry("600x270+200+300")
        app = Application(master=root)
        root.mainloop()
    

    事件处理

    ​ 一个 GUI 应用整个生命周期都处在一个消息循环 (event loop) 中。它等待事件的发生,并作出相应的处理。Tkinter 提供了用以处理相关事件的机制. 处理函数可被绑定给各个控件的各种事件。widget.bind(event, handler)
    如果相关事件发生, handler 函数会被触发, 事件对象 event 会传递给 handler 函数。

    鼠标和键盘事件

    代码 说明


    <1>
    鼠标左键按下。 2 表示右键,3 表示中键;
    鼠标左键释放
    按住鼠标左键移动
    双击左键
    鼠标指针进入某一组件区域
    鼠标指针离开某一组件区域
    滚动滚轮;
    按下 a 键,a 可用其他键替代
    释放 a 键。
    按下 A 键(大写的 A)
    同时按下 alt 和 a;alt 可用 ctrl 和 shift 替代
    快速按两下 a
    CTRL 和 V 键被同时按下,V 可以换成其它键位
    event 对象常用属性
    名称 说明
    char 按键字符,仅对键盘事件有效
    keycode 按键编码,仅对键盘事件有效
    keysym 按键名称,仅对键盘事件有效
    比如按下空格键:
    键的 char: 键的 keycode:32 键的 keysym:space
    比如按下 a 键:
    键的 char:a 键的 keycode:65 键的 keysym:a
    num 鼠标按键,仅对鼠标事件有效

    type 所触发的事件类型
    widget 引起事件的组件
    width,height 组件改变后的大小,仅 Configure 有效
    x,y 鼠标当前位置,相对于父容器
    x_root,y_root 鼠标当前位置,相对于整个屏幕

    【栗子】键盘和鼠标事件的测试

    from tkinter import *
    
    root = Tk(); root.geometry("530x300")
    
    c1 = Canvas(root, width=200, height=200, bg="green")
    c1.pack()
    
    
    def mouseTest(event):
        print("鼠标左键单击位置(相对于父容器):{0},{1}".format(event.x,event.y))
        print("鼠标左键单击位置(相对于屏幕):{0},{1}".format(event.x_root,event.y_root))
        print("事件绑定的组件:{0}".format(event.widget))
    
    def testDrg(event):
        c1.create_oval(event.x, event.y, event.x+1, event.y+1)
    
    def keyboardTest(event):
        print("键的keycode:{0},键的char:{1},键的keysym:{2}".format(event.keycode, event.char, event.keysym))
    
    def press_a_test(event):
        print("press a")
    
    def release_a_test(event):
        print("release a")
    
    
    c1.bind("<Button-1>", mouseTest)
    c1.bind("<B1-Motion>", testDrg)    # 鼠标左键拖动事件
    
    root.bind("<KeyPress>",keyboardTest)
    root.bind("<KeyPress-a>", press_a_test)
    root.bind("<KeyRelease-a>",release_a_test)
    
    root.mainloop()
    

    lambda 表达式详解

    ​ lambda 表达式定义的是一个匿名函数,只适合简单输入参数,简单计算返回结果,不适合功能复杂情况。
    lambda 定义的匿名函数也有输入、也有输出,只是没有名字。语法格式如下:
    ​ lambda 参数值列表:表达式
    参数值列表即为输入,表达式计算的结构即为输出。
    我们写一个最简单的案例:

    add3args = lambda x,y,z:x+y+z
    #print(add3args(10,20,30))
    

    上面的 lambda 表达式相当于如下函数定义:

    def add3args(x,y,z):
    	return x+y+z
    

    lambda 表达式的参数值列表可以为如下内容:

    lambda 格式 说明
    lambda x, y: xy 函数输入是 x 和 y,输出是它们的积 xy
    lambda:None 函数没有输入参数,输出是 None
    lambda:aaa(3,4) 函数没有输入参数,输出是 aaa(3,4)的结果
    lambda *args: sum(args) 输入是任意个数的参数,输出是它们的和
    lambda **kwargs: 1 输入是任意键值对参数,输出是 1
    ​ 我们在平时使用时,注意 lambda 只是一个匿名函数(没有名字的函数),功能不强,不要过度使用;

    使用 lambda 表达式实现传参
    【示例】使用 lambda 帮助 command 属性绑定时传参

    # coding=utf-8
    # 测试 command 属性绑定事件,测试 lambda 表达式帮助传参
    from tkinter import *
    root = Tk();root.geometry("270x50")
    def mouseTest1():
        print("command 方式,简单情况:不涉及获取 event 对象,可以使用")
    def mouseTest2(a,b):
        print("a={0},b={1}".format(a,b))
    Button(root, text="测试 command1",command=mouseTest1).pack(side="left")
    
    Button(root, text="测试 command2",command=lambda:mouseTest2("gaoqi","xixi")).pack(side="left")
    
    root.mainloop()
    

    多种事件绑定方式汇总

    组件对象的绑定

    1. 通过 command 属性绑定(适合简单不需获取 event 对象)

    ​ Button(root,text=”登录”,command=login)

    1. 通过 bind()方法绑定(适合需要获取 event 对象)
      c1 = Canvas(); c1.bind(“”,drawLine) ·

    2. 组件类的绑定调用对象的 bind_class 函数,将该组件类所有的组件绑定事件:
      w.bind_class(“Widget”,”event”,eventhanler)
      比如:btn01.bind_class(“Button”,””,func)

    【栗子】组件对象的绑定

    #coding=utf-8
    # 多种事件绑定汇总
    from tkinter import *
    
    root = Tk();root.geometry("320x40")
    
    def mouseTest1(event):
        print("bind()方式绑定可以,可以获取event对象")
        print(event.widget)
    
    def mouseTest2(a,b):
        print("a={0},b={1}".format(a,b))
        print("command 方式绑定,不能直接获取event对象")
    
    def mouseTest3(event):
        print("右击事件,绑定所有按钮!")
        print(event.widget)
    
    b1 = Button(root,text= "测试bind()绑定")
    b1.pack(side= "left")
    
    b1.bind("<Button-1>",mouseTest1)
    
    b2 = Button(root, text="测试command2", command=lambda: mouseTest2("zhangsan","lisi"))
    b2.pack(side="left")
    
    b1.bind_class("Button", "<Button-3>", mouseTest3)
    
    root.mainloop()
    

    其他组件

    ​ 我们在前面介绍了最常用的几个组件,接下来我们介绍其他一些组件。

    OptionMenu 选择项

    ​ OptionMenu(选择项)用来做多选一,选中的项在顶部显示。

    from tkinter import *
    
    root = Tk(); root.geometry("200x100")
    v = StringVar(root)
    v.set("程序猿")
    om=OptionMenu(root,v,"程序猿","python程序猿","java程序猿","go程序猿")
    
    om["width"] = 10
    om.pack()
    
    def test1():
        print("最喜爱的计算机语言:",v.get())
    
    Button(root,text="确定",command=test1).pack()
    
    root.mainloop()
    

    Scale 移动滑块

    ​ Scale(移动滑块)用于在指定的数值区间,通过滑块的移动来选择值。

    from tkinter import *
    root = Tk(); root.geometry("400x150")
    
    def test1(value):
        print("滑块的值:",value)
        newFont= ("宋体",value)
        a.config(font=newFont)
    
    s1 =  Scale(root,from_=10, to=50,length=200, tickinterval=5, orient=HORIZONTAL, command=test1)
    s1.pack()
    
    a = Label(root,text="python程序猿",width=20, height=1, bg="black", fg="white")
    a.pack()
    
    root.mainloop()
    

    颜色选择框

    ​ 颜色选择框可以帮助我们设置背景色、前景色、画笔颜色、字体颜色等等。

    from tkinter import *
    from tkinter.colorchooser import *
    
    root = Tk();root.geometry("400x200")
    
    def test1():
        s1 = askcolor(color="red",title="选择背景")
        print(s1)
        print(s1[0])
        root.config(bg=s1[1])
    
    Button(root,text = "选择背景色",command=test1).pack()
    
    root.mainloop()
    

    文件对话框

    ​ 文件对话框帮助我们实现可视化的操作目录、操作文件。最后,将文件、目录的信息传入到
    程序中。文件对话框包含如下一些常用函数。

    函数名 对话框 说明
    askopenfilename(**options) 文 件 对 话框
    返回打开的文件名
    askopenfilenames(**options) 返回打开的多个文件名列表
    askopenfile(**options) 返回打开文件对象
    askopenfiles(**options) 返回打开的文件对象的列表
    askdirectory(**options) 目 录 对 话

    返回目录名
    asksaveasfile(**options) 保 存 对 话

    返回保存的文件对象
    asksaveasfilename(**options) 返回保存的文件名
    命名参数 options 的常见值如下:
    参数名 说明
    defaultextension 默认后缀:.xxx
    用户没有输入则自动添加
    filetypes=[(label1,pattern1),(labe2,patt
    ern2)]
    文件显示过滤器
    initialdir 初始目录
    initialfile 初始文件
    parent 父窗口,默认根窗口
    title 窗口标题

    【栗子】文件对话框获取文件

    from tkinter import *
    from tkinter.filedialog import *
    
    root = Tk(); root.geometry("400x200")
    
    def test1():
        f = askopenfilename(title ="上传文件", initialdir="e:/",filetypes=[("视频文件",".mp4")])
        print(f)
        show["text"] = f
    
    Button(root,text="选择文件",command=test1).pack()
    
    show = Label(root, width=40, height=3, bg="green")
    show.pack()
    
    root.mainloop()
    

    简单输入对话框

    simpledialog(简单对话框)包含如下常用函数:

    函数名 说明
    askfloat(title,prompt,**kw) 输入并返回浮点数
    askinteger(title,prompt,**kw) 输入并返回整数
    askstring(title,prompt,**kw) 输入并返回字符串

    ​ 参数中,title 表示窗口标题;prompt 是提示信息;命名参数 kw 为各种选项:initialvalue(初始值)、minvalue(最小值)、maxvalue(最大值)。

    【栗子】简单对话框的基本用法

    from tkinter.simpledialog import *
    
    root = Tk(); root.geometry("400x200")
    
    def test1():
        a = askinteger(title ="输入年龄", prompt="请输入年龄", initialvalue=18, minvalue=1, maxvalue=100)
        show["text"]=a
    
    
    Button(root,text="你多大了?请输入", command=test1).pack()
    
    show = Label(root, width=40, height=3, bg="green")
    show.pack()
    
    root.mainloop()
    

    通用消息框

    ​ messagebox(通用消息框)用于和用户简单的交互,用户点击确定、取消。如下列出了
    messagebox 的常见函数:

    函数名 说明 例子
    askokcancel(title,message,**opt
    ions)
    OK/Cancel 对话框
    askquestion(title,message,opt
    ions)
    Yes/No 问题对话框
    askretrycancel(title,message,

    options)
    Retry/Cancel问题对
    话框
    showerror(title,message,**optio
    ns)
    错误消息对话框
    showinfo(title,message,**option
    s)
    消息框
    showwarning(title,message,**opt
    ions)
    警告消息框

    【栗子】通用消息框的基本用法

    from tkinter import *
    from tkinter.messagebox import *
    
    root = Tk(); root.geometry("400x200")
    
    def test1():
        a = showwarning(title ="python", message="哈哈哈~ 你被骗了。")
        print(a)
    
    Button(root,text="点进去有惊喜~~", command=test1).pack()
    
    root.mainloop()
    

    ttk 子模块控件

    ​ 我们再前面学的组件是 tkinter 模块下的组件,整体风格较老较丑。为了弥补这点不足,推出了 ttk 组件。ttk 组件更加美观、功能更加强大。 新增了 LabeledScale(带标签的Scale)、Notebook(多文档窗口)、Progressbar(进度条)、Treeview(树)等组件。使用 ttk 组件与使用普通的 Tkinter 组件并没有多大的区别,只要导入 ttk 模块即可。

    ttk 子模块的官方文档:
    https://docs.python.org/3.7/library/tkinter.ttk.html
    【注】此处我们不展开细讲 ttk。如果你的项目确实需要用到复杂的界面,推荐大家使用wxpython 或者 pyQt。

    菜单

    ​ GUI 程序通常都有菜单,方便用户的交互。我们一般将菜单分为两种:

    1. 主菜单
      主菜单通常位于 GUI 程序上方。
    2. 快捷菜单(上下文菜单)
      通过鼠标右键单击某个组件对象而弹出的菜单,一般是与该组件相关的操作。

    主菜单

    ​ 主菜单一般包含:文件、编辑、帮助等,位于 GUI 窗口的上面。创建主菜单一般有如下 4步:

    1. 创建主菜单栏对象
      menubar = tk.Menu(root)
    2. 创建菜单,并添加到主菜单栏对象
      file_menu = tk.Menu(menubar)
      menubar.add_cascade(label=”文件”,menu=file_menu)
    3. 添加菜单项到 2 步中的菜单
      file_menu.add_command(label=”打开”)
      file_menu.add_command(label=”保存”,accelerator=”^p” command=mySaveFile)
      file_menu.add_separator()
      file_menu.add_command(label=”退出”)
    4. 将主菜单栏添加到根窗口
      root[“menu”]=menubar

    【栗子】记事本软件的主菜单

    """  开发记事本软件的菜单 """
    
    from tkinter import *
    from tkinter.colorchooser import *
    
    class Application(Frame):
    
        def __init__(self,master=None):
            super().__init__(master)
            self.master = master
            self.textpad = None
            self.pack()
            self.createWidget()
    
        def createWidget(self):
            # 创建主菜单
            menubar = Menu(root)
            menuFile = Menu(menubar)
            menuEdit = Menu(menubar)
            menuHelp = Menu(menubar)
    
            # 将子菜单加入主菜单栏
            menubar.add_cascade(label="文本(F)", menu=menuFile)
            menubar.add_cascade(label="编辑(E)", menu=menuEdit)
            menubar.add_cascade(label="帮助(H)", menu=menuHelp)
            # 添加菜单项
            menuFile.add_command(label="新建文件",accelerator="ctrl+n",command=self.test)
            menuFile.add_command(label="打开文件",accelerator="ctrl+o",command=self.test)
            menuFile.add_command(label="保存文件",accelerator="ctrl+s",command=self.test)
            menuFile.add_separator()   # 添加分割线
            menuFile.add_command(label="退出文件",accelerator="ctrl+q",command=self.test)
    
            # 将主菜单栏加入根窗口
            root["menu"] = menubar
    
            #文本编辑区
            self.textpad = Text(root,width=80,height=50)
            self.textpad.pack()
        def test(self):
            pass
    
    
    if __name__ == '__main__':
        root = Tk();root.geometry("450x300+300+200")
        root.title("Mr_huang的简易记事本")
        app = Application(master=root)
        root.mainloop()
    

    上下文菜单

    ​ 快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:

    1. 创建菜单
      menubar = tk.Menu(root)
      menubar.add_command(label=”字体”)
    2. 绑定鼠标右键单击事件
      def test(event):
      menubar.post(event.x_root,event.y_root) #在鼠标右键单击坐标处显示菜单
      root.bind(“”,test)
    #coding=utf-8
    from tkinter import *
    from tkinter.colorchooser import *
    from tkinter.filedialog import *
    root = Tk();root.geometry("400x400")
    def openAskColor():
        s1 = askcolor(color="red", title="选择背景色")
        root.config(bg=s1[1])
    #创建快捷菜单
    menubar2 = Menu(root)
    menubar2.add_command(label="颜色",command=openAskColor)
    menuedit = Menu(menubar2,tearoff=0)
    menuedit.add_command(label="剪切")
    menuedit.add_command(label="复制")
    menuedit.add_command(label="粘贴")
    menubar2.add_cascade(label="编辑",menu=menuedit)
    def test(event):
        #菜单在鼠标右键单击的坐标处显示
        menubar2.post(event.x_root,event.y_root)
    #编辑区
    w1 = Text(root,width=50,height=30)
    w1.pack()
    w1.bind("<Button-3>",test)
    root.mainloop()
    

    将主菜单栏加入根窗口
    root[“menu”] = menubar

        #文本编辑区
        self.textpad = Text(root,width=80,height=50)
        self.textpad.pack()
    def test(self):
        pass
    

    if name == ‘main’:
    root = Tk();root.geometry(“450x300+300+200”)
    root.title(“Mr_huang的简易记事本”)
    app = Application(master=root)
    root.mainloop()

    
    #### 上下文菜单
    
    ​	快捷菜单(上下文菜单)是通过鼠标右键单击组件而弹出的菜单,一般是和这个组件相关的操作,比如:剪切、复制、粘贴、属性等。创建快捷菜单步骤如下:
    
    1. 创建菜单
         menubar = tk.Menu(root)
           menubar.add_command(label=”字体”)
    2. 绑定鼠标右键单击事件
         def test(event):
           menubar.post(event.x_root,event.y_root) #在鼠标右键单击坐标处显示菜单
           root.bind(“<Button-3>”,test)
    
    ```python
    #coding=utf-8
    from tkinter import *
    from tkinter.colorchooser import *
    from tkinter.filedialog import *
    root = Tk();root.geometry("400x400")
    def openAskColor():
        s1 = askcolor(color="red", title="选择背景色")
        root.config(bg=s1[1])
    #创建快捷菜单
    menubar2 = Menu(root)
    menubar2.add_command(label="颜色",command=openAskColor)
    menuedit = Menu(menubar2,tearoff=0)
    menuedit.add_command(label="剪切")
    menuedit.add_command(label="复制")
    menuedit.add_command(label="粘贴")
    menubar2.add_cascade(label="编辑",menu=menuedit)
    def test(event):
        #菜单在鼠标右键单击的坐标处显示
        menubar2.post(event.x_root,event.y_root)
    #编辑区
    w1 = Text(root,width=50,height=30)
    w1.pack()
    w1.bind("<Button-3>",test)
    root.mainloop()
    
    展开全文
  • C语言控制台窗口图形界面编程

    万次阅读 多人点赞 2017-03-26 13:05:25
    下面介绍几个用于控制台窗口操作的API函数,如下: [cpp] view plain copy   //获取控制台窗口信息  GetConsoleScreenBufferInfo();    //获取控制台窗口标题  ...SetConsoleScreenBuffe

    一:设置句柄与窗口信息


    在Windows操作系统下用C语言编写控制台的窗口界面首先要获取当前标准输入和标准输出设备的句柄。通过调用函数GetStdHandle可以获取当前标准输入以及输出设备的句柄。函数原型为:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. HANDLE GetStdHandle(DWORD nStdHandle);  
    2. /* 
    3. 其中,nStdHandle可以是 
    4. STD_INPUT_HANDLE    标准输入设备句柄 
    5. STD_OUTPUT_HANDLE   标准输出设备句柄 
    6. STD_ERROR_HANDLE    标准错误设备句柄 
    7. */  
           

           需要说明的是,“句柄”是Windows最常用的一个概念。它通常用来标识Windows资源(如菜单、 图标、窗口等)和设备等对象。虽然可以把句柄理解为是一个指针变量类型,但它不是对象所在的地址指针,而是作为Windows系统内部表的索引值来使用 的。调用相关文本界面控制的API函数。这些函数可分为三类。一是用于控制台窗口操作的函数(包括窗口的缓冲区大小、窗口前景字符和背景颜色、窗口标题、大小和位置等);二是用于控制台输入输出的函数(包括字符属性操作函数);其他的函数并为最后一类。通过调用CloseHandle函数来关闭输入输出句柄。


           示例程序:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <windows.h>  
    3. #include <conio.h>  
    4.   
    5. int main(int argc,char *argv[])  
    6. {  
    7.     HANDLE handle_out;                              //定义一个句柄  
    8.     CONSOLE_SCREEN_BUFFER_INFO screen_info;         //定义窗口缓冲区信息结构体  
    9.     COORD pos = {0, 0};                             //定义一个坐标结构体  
    10.     handle_out = GetStdHandle(STD_OUTPUT_HANDLE);   //获得标准输出设备句柄  
    11.     GetConsoleScreenBufferInfo(handle_out, &screen_info);   //获取窗口信息  
    12.     _getch();   //输入一个字符,不会显示到屏幕上  
    13.     /* 
    14.     向整个缓冲区填充字符'A' 
    15.     其中填充的开始处为pos,坐标为{0, 0},也就是屏幕最左上角的字符处 
    16.     填充个数为screen_info.dwSize.X(缓冲区宽度,也就是横坐标最大值加1) * screen_info.dwSize.Y(缓冲区高度,也就是纵坐标最大值加1) 
    17.     因此可以达到向整个缓冲区填充字符'A'的效果 
    18.     */  
    19.     FillConsoleOutputCharacter(handle_out, 'A', screen_info.dwSize.X * screen_info.dwSize.Y, pos, NULL);  
    20.     CloseHandle(handle_out);    //关闭标准输出设备句柄  
    21.     return 0;  
    22. }  
    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. //程序中,COORD和CONSOLE_SCREEN_BUFFER_ INFO是wincon.h定义的控制台结构体类型  
    2. //原型如下  
    3.   
    4. //坐标结构体  
    5. typedef struct _COORD  
    6. {  
    7.     SHORT X;  
    8.     SHORT Y;  
    9. }COORD;  
    10.   
    11. //控制台窗口信息结构体  
    12. typedef struct _CONSOLE_SCREEN_BUFFER_INFO  
    13. {  
    14.     COORD dwSize;               //缓冲区大小  
    15.     COORD dwCursorPosition;     //当前光标位置  
    16.     WORD wAttributes;           //字符属性  
    17.     SMALL_RECT srWindow;        //当前窗口显示的大小和位置  
    18.     COORD dwMaximumWindowSize;  // 最大的窗口缓冲区大小  
    19. }CONSOLE_SCREEN_BUFFER_INFO;  

           还需要说明的是,虽然在C++中,iostream.h定义了cin和cout的标准输入和输出流对象。但它们只能实现基本的输入输出 操作,对于控制台窗口界面的控制却无能为力,而且不能与stdio.h和conio.h友好相处,因为iostream.h和它们是C++两套不同的输入 输出操作方式,使用时要特别注意。



    二:窗口缓冲区的设置


    下面介绍几个用于控制台窗口操作的API函数,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. //获取控制台窗口信息  
    2. GetConsoleScreenBufferInfo();  
    3.   
    4. //获取控制台窗口标题  
    5. GetConsoleTitle();  
    6.   
    7. //更改指定缓冲区大小  
    8. SetConsoleScreenBufferSize();  
    9.   
    10. //设置控制台窗口标题  
    11. SetConsoleTitle();  
    12.   
    13. //设置控制台窗口信息  
    14. SetConsoleWindowInfo();  


           下面的示例程序用于说明此类函数的使用:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <Windows.h>  
    4. #include <conio.h>  
    5. #define N 255  
    6.   
    7. int main()  
    8. {  
    9.     HANDLE handle_out;  //定义一个句柄  
    10.     CONSOLE_SCREEN_BUFFER_INFO scbi;    //定义一个窗口缓冲区信息结构体  
    11.     COORD size = {80, 25};      //定义一个坐标结构体  
    12.     char strtitle[N];  
    13.     handle_out = GetStdHandle(STD_OUTPUT_HANDLE);   //获得标准输出设备句柄  
    14.     GetConsoleScreenBufferInfo(handle_out, &scbi);  //获得窗口缓冲区信息  
    15.     GetConsoleTitle(strtitle, N);   //获得当前窗口标题  
    16.     printf("当前窗口标题为:%s\n", strtitle);  
    17.     _getch();  
    18.     SetConsoleTitle("控制台窗口操作");     //设置窗口标题为“控制台窗口操作”  
    19.     GetConsoleTitle(strtitle, N);           //获得当前窗口标题  
    20.     printf("当前窗口标题为:%s\n", strtitle);  
    21.     _getch();  
    22.     SetConsoleScreenBufferSize(handle_out, size);   // 重新设置缓冲区大小  
    23.     _getch();  
    24.     SMALL_RECT rc = {0, 0, 80-1, 25-1};     // 重置窗口位置和大小  
    25.     SetConsoleWindowInfo(handle_out, 1, &rc);  
    26.     CloseHandle(handle_out);    //关闭标准输出设备句柄  
    27.     return 0;  
    28. }  

           其中,SetConsoleScreenBufferSize函数指定新的控制台屏幕缓冲区的大小,以字符列和行为单位。指定的宽度和高度不能小于控制台屏幕缓冲区窗口的宽度和高度。指定的大小也不能小于系统允许的最小大小。这个最低取决于控制台当前的字体大小 (由用户选定)。


    三:文本属性


    在这里介绍一个设置文本属性的函数,原型如下

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. BOOL SetConsoleTextAttribute(   // 设置WriteConsole等函数的字符属性  
    2. HANDLE hConsoleOutput,          // 句柄  
    3. WORD wAttributes                // 文本属性  
    4. );  


           顺便提一下文本属性,其实就是颜色属性,有背景色和前景色(就是字符的颜色)两类,每一类只提供三原色(红,绿,蓝)和加强色(灰色,可与其他颜色搭配使用,使颜色变亮,后面会提到)。最后还有一个反色(不太清楚这个到底怎么用,很奇葩的东西)。示例程序如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <windows.h>  
    4. #include <conio.h>  
    5. /* 
    6. 基本文本属性 
    7. FOREGROUND_BLUE 蓝色 
    8. FOREGROUND_GREEN 绿色 
    9. FOREGROUND_RED 红色 
    10. FOREGROUND_INTENSITY 加强 
    11. BACKGROUND_BLUE 蓝色背景 
    12. BACKGROUND_GREEN 绿色背景 
    13. BACKGROUND_RED 红色背景 
    14. BACKGROUND_INTENSITY 背景色加强 
    15. COMMON_LVB_REVERSE_VIDEO 反色 
    16. */  
    17.   
    18. const WORD FORE_BLUE   = FOREGROUND_BLUE;           //蓝色文本属性  
    19. const WORD FORE_GREEN  = FOREGROUND_GREEN;          //绿色文本属性  
    20. const WORD FORE_RED    = FOREGROUND_RED;            //红色文本属性  
    21. const WORD FORE_PURPLE = FORE_BLUE | FORE_RED;      //紫色文本属性  
    22. const WORD FORE_CYAN   = FORE_BLUE | FORE_GREEN;    //青色文本属性  
    23. const WORD FORE_YELLOW = FORE_RED | FORE_GREEN;     //黄色文本属性  
    24. const WORD FORE_GRAY   = FOREGROUND_INTENSITY;      //灰色文本属性  
    25. const WORD BACK_BLUE   = BACKGROUND_BLUE;           //蓝色背景属性  
    26. const WORD BACK_GREEN  = BACKGROUND_GREEN;          //绿色背景属性  
    27. const WORD BACK_RED    = BACKGROUND_RED;            //绿色背景属性  
    28. const WORD BACK_PURPLE = BACK_BLUE | BACK_RED;      //紫色背景属性  
    29. const WORD BACK_CYAN   = BACK_BLUE | BACK_GREEN;    //青色背景属性  
    30. const WORD BACK_YELLOW = BACK_RED | BACK_GREEN;     //黄色背景属性  
    31. const WORD BACK_GRAY   = BACKGROUND_INTENSITY;      //灰色背景属性  
    32.   
    33. int main()  
    34. {  
    35.     HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄  
    36.     CONSOLE_SCREEN_BUFFER_INFO csbi;                        //定义窗口缓冲区信息结构体  
    37.     GetConsoleScreenBufferInfo(handle_out, &csbi);          //获得窗口缓冲区信息  
    38.     SetConsoleTextAttribute(handle_out, FORE_BLUE);  
    39.     printf("蓝色字符\n");  
    40.     SetConsoleTextAttribute(handle_out, FORE_RED);  
    41.     printf("红色字符\n");  
    42.     SetConsoleTextAttribute(handle_out, FORE_GREEN);  
    43.     printf("绿色字符\n");  
    44.     SetConsoleTextAttribute(handle_out, FORE_PURPLE);  
    45.     printf("紫色字符\n");  
    46.     SetConsoleTextAttribute(handle_out, FORE_CYAN);  
    47.     printf("青色字符\n");  
    48.     SetConsoleTextAttribute(handle_out, FORE_YELLOW);  
    49.     printf("黄色字符\n");  
    50.     SetConsoleTextAttribute(handle_out, FORE_GRAY);  
    51.     printf("灰色字符\n");  
    52.     SetConsoleTextAttribute(handle_out, FORE_GREEN | FORE_BLUE | FORE_RED);  
    53.     printf("白色字符\n");  
    54.     SetConsoleTextAttribute(handle_out, BACK_BLUE);  
    55.     printf("蓝色背景\n");  
    56.     SetConsoleTextAttribute(handle_out, BACK_RED);  
    57.     printf("红色背景\n");  
    58.     SetConsoleTextAttribute(handle_out, BACK_GREEN);  
    59.     printf("绿色背景\n");  
    60.     SetConsoleTextAttribute(handle_out, BACK_PURPLE);  
    61.     printf("紫色背景\n");  
    62.     SetConsoleTextAttribute(handle_out, BACK_CYAN);  
    63.     printf("青色背景\n");  
    64.     SetConsoleTextAttribute(handle_out, BACK_YELLOW);  
    65.     printf("黄色背景\n");  
    66.     SetConsoleTextAttribute(handle_out, BACK_GRAY);  
    67.     printf("灰色背景\n");  
    68.     SetConsoleTextAttribute(handle_out, BACK_BLUE | BACK_RED | BACK_GREEN);  
    69.     printf("白色背景\n");  
    70.     SetConsoleTextAttribute(handle_out, BACK_GREEN | FORE_RED); //示例:绿色背景红色字符  
    71.     printf("绿色背景与红色字符的混合\n");  
    72.     SetConsoleTextAttribute(handle_out, FOREGROUND_INTENSITY | FORE_RED);   //示例:亮红色字符  
    73.     printf("亮色的生成,与加强色融合\n");  
    74.     return 0;  
    75. }  

           上述示例程序最好用C++来中编译,因为有C语言的编译器或者IDE不支持上述的定义常量的方式。需要从这个示例中了解的是三原色的混合是用C语言位运算中的按位或 | 运算符,背景颜色与字符颜色的同时定义也是使用这个运算符融合。另外,将任意颜色与对应的加强色(灰色,有前景和背景两种,需要对应)融合后会成为对应颜色的高亮版,比如红色字符与前景加强色的融合会结合成亮红色。


           至于反色,大家可以试试,当我设置了文本属性为反色后,输入字符都不显示了,但是下标还在移动,我估计反色将白色字符变成了黑色字符,与黑色背景一样,所以没有显示出来。至于反色与其他的组合以及其他的颜色组合,还需要大家一起探索、、、


    四:文本属性


    文本颜色属性已经学会了,那么下面就学习几个比较常用的文本输出函数,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. BOOL FillConsoleOutputAttribute(    // 填充字符属性  
    2. HANDLE hConsoleOutput,              // 句柄  
    3. WORD wAttribute,                    // 文本属性  
    4. DWORD nLength,                      // 个数  
    5. COORD dwWriteCoord,                 // 开始位置  
    6. LPDWORD lpNumberOfAttrsWritten      // 返回填充的个数  
    7. );  
    8. BOOL FillConsoleOutputCharacter(    // 填充指定数据的字符  
    9. HANDLE hConsoleOutput,              // 句柄  
    10. TCHAR cCharacter,                   // 字符  
    11. DWORD nLength,                      // 字符个数  
    12. COORD dwWriteCoord,                 // 起始位置  
    13. LPDWORD lpNumberOfCharsWritten      // 已写个数  
    14. );  
    15. BOOL WriteConsoleOutputCharacter(   // 在指定位置处写指定数量的字符  
    16. HANDLE hConsoleOutput,              // 句柄  
    17. LPCTSTR lpCharacter,                // 字符串  
    18. DWORD nLength,                      // 字符个数  
    19. COORD dwWriteCoord,                 // 起始位置  
    20. LPDWORD lpNumberOfCharsWritten      // 已写个数  
    21. );  


           另外再介绍一个表示区域的结构体,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. typedef struct _SMALL_RECT  //表示矩形区域的结构体  
    2. {  
    3.   SHORT Left;       //左边界  
    4.   SHORT Top;        //上边界  
    5.   SHORT Right;      //右边界  
    6.   SHORT Bottom;     //下边界  
    7. } SMALL_RECT;  
    8. /* 
    9. 微软官方的说法是 
    10. Left    区域的左上顶点的X坐标 
    11. Top     区域的左上顶点的Y坐标 
    12. Right   区域的右下顶点的X坐标 
    13. Bottom  区域的右下顶点的Y坐标 
    14. */  


           通过以上的文本输出函数,我们来做一个简单的在一个具有阴影效果的窗口显示字符串的示例程序,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <Windows.h>  
    4. #include <conio.h>  
    5.   
    6. int main()  
    7. {  
    8.     char *str = "Hello World!";     //定义输出信息  
    9.     int len = strlen(str), i;  
    10.     WORD shadow = BACKGROUND_INTENSITY;     //阴影属性  
    11.     WORD text = BACKGROUND_GREEN | BACKGROUND_INTENSITY;    //文本属性  
    12.     HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄  
    13.     CONSOLE_SCREEN_BUFFER_INFO csbi;    //定义窗口缓冲区信息结构体  
    14.     GetConsoleScreenBufferInfo(handle_out, &csbi);  //获得窗口缓冲区信息  
    15.     SMALL_RECT rc;      //定义一个文本框输出区域  
    16.     COORD posText;      //定义文本框的起始坐标  
    17.     COORD posShadow;    //定义阴影框的起始坐标  
    18.     //确定区域的边界  
    19.     rc.Top = 8;     //上边界  
    20.     rc.Bottom = rc.Top + 4;     //下边界  
    21.     rc.Left = (csbi.dwSize.X - len) / 2 - 2;    //左边界,为了让输出的字符串居中  
    22.     rc.Right = rc.Left + len + 4;   //右边界  
    23.     //确定文本框起始坐标  
    24.     posText.X = rc.Left;  
    25.     posText.Y = rc.Top;  
    26.     //确定阴影框的起始坐标  
    27.     posShadow.X = posText.X + 1;  
    28.     posShadow.Y = posText.Y + 1;  
    29.     for (i=0; i<5; ++i)     //先输出阴影框  
    30.     {  
    31.         FillConsoleOutputAttribute(handle_out, shadow, len + 4, posShadow, NULL);  
    32.         posShadow.Y++;  
    33.     }  
    34.     for (i=0; i<5; ++i)     //在输出文本框,其中与阴影框重合的部分会被覆盖掉  
    35.     {  
    36.         FillConsoleOutputAttribute(handle_out, text, len + 4, posText, NULL);  
    37.         posText.Y++;  
    38.     }  
    39.     //设置文本输出处的坐标  
    40.     posText.X = rc.Left + 2;  
    41.     posText.Y = rc.Top + 2;  
    42.     WriteConsoleOutputCharacter(handle_out, str, len, posText, NULL);   //输出字符串  
    43.     SetConsoleTextAttribute(handle_out, csbi.wAttributes);   // 恢复原来的属性  
    44.     CloseHandle(handle_out);  
    45.     return 0;  
    46. }  


           以上样例在Code::Blocks 13.12中编译通过。


    五:文本移动


    控制文本的移动是控制台窗口界面编程的一个很重要的功能,有了这个功能我们可以实现界面的滚动。下面我们介绍一个控制文本移动的函数,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. BOOL ScrollConsoleScreenBuffer(             //文本移动函数  
    2.     HANDLE hConsoleOutput,                  //句柄  
    3.     const SMALL_RECT *lpScrollRectangle,    //移动区域  
    4.     const SMALL_RECT *lpClipRectangle,      //裁剪区域,如果为NULL,那么将代表整个屏幕缓冲区  
    5.     COORD dwDestinationOrigin,              //移动到的位置,这个点将成为移动区域的左上顶点  
    6.     const CHAR_INFO *lpFill                 //空出区域的填充字符  
    7. );  


           下面来看一个移动文本的样例程序,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <conio.h>  
    3. #include <Windows.h>  
    4. #include <stdlib.h>  
    5.   
    6. int main()  
    7. {  
    8.     HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄  
    9.     CONSOLE_SCREEN_BUFFER_INFO csbi;        //定义窗口缓冲区信息结构体  
    10.     SMALL_RECT scroll;      //定义移动区域  
    11.     COORD pos = {0, 5};     //移动位置  
    12.     CHAR_INFO chFill;       //定义填充字符  
    13.     GetConsoleScreenBufferInfo(handle_out, &csbi);  //获得窗口缓冲区信息  
    14.     //定义填充字符的各个参数及属性  
    15.     chFill.Char.AsciiChar = ' ';  
    16.     chFill.Attributes = csbi.wAttributes;  
    17.     //输出文本  
    18.     printf("00000000000000000000000000000\n");  
    19.     printf("11111111111111111111111111111\n");  
    20.     printf("22222222222222222222222222222\n");  
    21.     printf("33333333333333333333333333333\n");  
    22.     //确定区域  
    23.     scroll.Left = 1;  
    24.     scroll.Top = 1;  
    25.     scroll.Right = 10;  
    26.     scroll.Bottom = 2;  
    27.     ScrollConsoleScreenBuffer(handle_out, &scroll, NULL, pos, &chFill); //移动文本  
    28.     return 0;  
    29. }  


           在上面的样例程序中,裁剪区域是整个控制台窗口的屏幕缓冲区,现在如果我们把裁剪区域设定为与移动区域一样,也就是说ScrollConsoleScreenBuffer函数的第三个参数也改成&scroll,那么结果会怎么样呢?



           为什么会发生这种现象呢?很明显示因为裁剪区域的设定问题,现在我们把裁剪区域依旧设定成移动区域,但是我们只把移动区域下移一行而不是移动在别的位置,看看会有什么现象发生?



           现在我们应该可以猜想出结论了,别急,再做一个实验,现在我们将裁减区域又重新改为整个屏幕缓冲区,看看会有什么样的现象发生?



           再来最后一个实验,我们将裁减区域减小为移动区域的上半部分,继续执行下移一行的操作,看看最终结果会怎么样?



           好了,现在我们通过归纳可以得出几个结论了,那就是

           一,裁减区域以外的区域不会受文本移动的影响。具体是:

    1,裁减区域以外的区域不会被移动过来的区域覆盖,

    2,裁减区域以外的区域被移动到他处之后原区域不发生变化,因此不需要填充字符。

    总的归纳来说也就是原来是什么样子,文本移动后还是什么样子,不会改变。

           二,裁减区域以内的区域受文本移动的影响。具体是:

    1,当裁减区域以内的区域被移动到他处造成该区域为空时会被设定的字符填充,

    2,裁减区域以内的区域会被移动过来的区域覆盖。

    总的归纳来说也就是完全受文本移动的影响,移动过来就被覆盖,被移走就由设定的字符来填充



    六:光标操作


    控制文本的移动是控制台窗口界面编程的一个很重要的功能,有了这个功能我们可以实现界面的滚动。下面我们介绍一个控制文本移动的函数,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. BOOL ScrollConsoleScreenBuffer(             //文本移动函数  
    2.     HANDLE hConsoleOutput,                  //句柄  
    3.     const SMALL_RECT *lpScrollRectangle,    //移动区域  
    4.     const SMALL_RECT *lpClipRectangle,      //裁剪区域,如果为NULL,那么将代表整个屏幕缓冲区  
    5.     COORD dwDestinationOrigin,              //移动到的位置,这个点将成为移动区域的左上顶点  
    6.     const CHAR_INFO *lpFill                 //空出区域的填充字符  
    7. );  


           下面来看一个移动文本的样例程序,如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <conio.h>  
    3. #include <Windows.h>  
    4. #include <stdlib.h>  
    5.   
    6. int main()  
    7. {  
    8.     HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄  
    9.     CONSOLE_SCREEN_BUFFER_INFO csbi;        //定义窗口缓冲区信息结构体  
    10.     SMALL_RECT scroll;      //定义移动区域  
    11.     COORD pos = {0, 5};     //移动位置  
    12.     CHAR_INFO chFill;       //定义填充字符  
    13.     GetConsoleScreenBufferInfo(handle_out, &csbi);  //获得窗口缓冲区信息  
    14.     //定义填充字符的各个参数及属性  
    15.     chFill.Char.AsciiChar = ' ';  
    16.     chFill.Attributes = csbi.wAttributes;  
    17.     //输出文本  
    18.     printf("00000000000000000000000000000\n");  
    19.     printf("11111111111111111111111111111\n");  
    20.     printf("22222222222222222222222222222\n");  
    21.     printf("33333333333333333333333333333\n");  
    22.     //确定区域  
    23.     scroll.Left = 1;  
    24.     scroll.Top = 1;  
    25.     scroll.Right = 10;  
    26.     scroll.Bottom = 2;  
    27.     ScrollConsoleScreenBuffer(handle_out, &scroll, NULL, pos, &chFill); //移动文本  
    28.     return 0;  
    29. }  


           在上面的样例程序中,裁剪区域是整个控制台窗口的屏幕缓冲区,现在如果我们把裁剪区域设定为与移动区域一样,也就是说ScrollConsoleScreenBuffer函数的第三个参数也改成&scroll,那么结果会怎么样呢?



           为什么会发生这种现象呢?很明显示因为裁剪区域的设定问题,现在我们把裁剪区域依旧设定成移动区域,但是我们只把移动区域下移一行而不是移动在别的位置,看看会有什么现象发生?



           现在我们应该可以猜想出结论了,别急,再做一个实验,现在我们将裁减区域又重新改为整个屏幕缓冲区,看看会有什么样的现象发生?



           再来最后一个实验,我们将裁减区域减小为移动区域的上半部分,继续执行下移一行的操作,看看最终结果会怎么样?



           好了,现在我们通过归纳可以得出几个结论了,那就是

           一,裁减区域以外的区域不会受文本移动的影响。具体是:

    1,裁减区域以外的区域不会被移动过来的区域覆盖,

    2,裁减区域以外的区域被移动到他处之后原区域不发生变化,因此不需要填充字符。

    总的归纳来说也就是原来是什么样子,文本移动后还是什么样子,不会改变。

           二,裁减区域以内的区域受文本移动的影响。具体是:

    1,当裁减区域以内的区域被移动到他处造成该区域为空时会被设定的字符填充,

    2,裁减区域以内的区域会被移动过来的区域覆盖。

    总的归纳来说也就是完全受文本移动的影响,移动过来就被覆盖,被移走就由设定的字符来填充



    七:键盘事件


     输入事件中的键盘事件通常有字符事件和按键事件,这些事件的附带信息构成了键盘输入的信息,而想要读取这些信息,是要通过API函数ReadConsoleInput来获取的,函数原型如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. BOOL ReadConsoleInput(              //读取输入信息  
    2.     HANDLE hConsoleInput,           //句柄  
    3.     PINPUT_RECORD lpBuffer,         //输入事件结构体的指针  
    4.     DWORD nLength,                  //要读取的记录数  
    5.     LPDWORD lpNumberOfEventsRead    //用来接受成功读取记录数的指针  
    6. );  //如果该函数成功调用,返回非零值  
    7. //输入事件结构体的指针可以是结构体数组的首地址,这样就可以一次性读取多个记录数。  


           下面介绍几个和读取键盘输入事件有关的结构体,各结构体原型如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. typedef struct _INPUT_RECORD    //输入事件结构体  
    2. {  
    3.     WORD  EventType;    //事件类型  
    4.     union  
    5.     {  
    6.         KEY_EVENT_RECORD          KeyEvent;     //按键事件  
    7.         MOUSE_EVENT_RECORD        MouseEvent;   //鼠标事件  
    8.         WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;  
    9.         MENU_EVENT_RECORD         MenuEvent;  
    10.         FOCUS_EVENT_RECORD        FocusEvent;  
    11.     } Event;    //具体的事件  
    12. } INPUT_RECORD;  
    13. /* 
    14. 其中事件类型EventType的值有5种 
    15. KEY_EVENT                   代表Event包含一个KEY_EVENT_RECODE结构体 
    16. MOUSE_EVENT                 代表Event包含一个MOUSE_EVENT_RECODE结构体 
    17. WINDOW_BUFFER_SIZE_EVENT    代表Event包含一个WINDOW_BUFFER_SIZE_EVENT_RECORD结构体 
    18. MENU_EVENT                  代表Event包含一个MENU_EVENT_RECORD结构体 
    19. FOCUS_EVENT                 代表Event包含一个FOCUS_EVENT_RECORD结构体 
    20. */  
    21.   
    22. typedef struct _KEY_EVENT_RECORD    //键盘事件结构体   
    23. {  
    24.     BOOL  bKeyDown;             //按键状态,true代表键按下,false代表键释放  
    25.     WORD  wRepeatCount;         //按键次数  
    26.     WORD  wVirtualKeyCode;      //虚拟键  
    27.     WORD  wVirtualScanCode;     //虚拟键扫描码  
    28.     union  
    29.     {  
    30.         WCHAR UnicodeChar;      //解释成Unicode宽字符  
    31.         CHAR  AsciiChar;        //解释成ASCII码字符  
    32.     } uChar;  
    33.     DWORD dwControlKeyState;    //控制键状态  
    34. } KEY_EVENT_RECORD;  
    35. /* 
    36. 控制键各状态的值 
    37. ENHANCED_KEY        扩展键被按下 
    38. LEFT_ALT_PRESSED    左Alt键被按下 
    39. LEFT_CTRL_PRESSED   左Ctrl键被按下 
    40. RIGHT_ALT_PRESSED   右Alt键被按下 
    41. RIGHT_CTRL_PRESSED  右Ctrl键被按下 
    42. NUMLOCK_ON          数字锁定被打开 
    43. SCROLLLOCK_ON       滚动锁定被打开 
    44. CAPSLOCK_ON         大写锁定被打开 
    45. SHIFT_PRESSED       Shift键被按下 
    46. */  

           当输入事件为键盘事件时,事件类型就为键盘事件,为其他事件时,事件类型就为对应的事件。另外,键盘上每个有意义的键都对应着一个唯一的扫描码,虽然扫描码可以作为键的标识,但是它是依赖于具体的设备的。因此,在应用程序中,使用的往往是与具体设备无关的虚拟键代码。这种虚拟键代码是一种与具体设备无关的键盘编码。而控制键状态比如大写锁定开启状态,Ctrl键按下状态等、、、


           下面是部分常用虚拟键代码表:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /* 
    2. 虚拟键代码      值          键名称 
    3. -----------------------------------------------------             
    4. VK_BACK         0x08        退格键 
    5. VK_TAB          0x09        Tab键 
    6. VK_RETURN       0x0D        回车键 
    7. VK_SHIFT        0x10        Shift键 
    8. VK_LSHIFT       0xA0        左Shift键 
    9. VK_RSHIFT       0xA1        右Shift键 
    10. VK_CONTROL      0x11        Ctrl键 
    11. VK_LCONTROL     0xA2        左Ctrl键 
    12. VK_RCONTROL     0xA3        右Ctrl键 
    13. VK_MENU         0x12        Alt键 
    14. VK_LMENU        0xA4        左Alt键 
    15. VK_RMENU        0xA5        右Alt键 
    16. VK_PAUSE        0x13        Pause键 
    17. VK_CAPITAL      0x14        Caps Lock键 
    18. VK_NUMLOCK      0x90        Num Lock键 
    19. VK_SCROLL       0x91        Scroll Lock键 
    20. VK_ESCAPE       0x1B        Esc键 
    21. VK_SPACE        0x20        空格键 
    22. VK_PRIOR        0x21        Page Up键 
    23. VK_NEXT         0x22        Page Down键 
    24. VK_END          0x23        End键 
    25. VK_HOME         0x24        Home键 
    26. VK_LEFT         0x25        左方向键 
    27. VK_UP           0x26        上方向键 
    28. VK_RIGHT        0x27        右方向键 
    29. VK_DOWN         0x28        下方向键 
    30. VK_DELETE       0x2E        Delete键 
    31. VK_INSERT       0x2D        Insert键 
    32. '0'             0x30        0键(非小键盘) 
    33. '1'             0x31        1键(非小键盘) 
    34. '2'             0x32        2键(非小键盘) 
    35. ...             ...         ... 
    36. '9'             0x39        9键(非小键盘) 
    37. 'A'             0x41        A键 
    38. 'B'             0x42        B键 
    39. ...             ...         ... 
    40. 'Z'             0x5A        Z键 
    41. VK_SLEEP        0x5F        Sleep键 
    42. VK_NUMPAD0      0x60        小键盘0键 
    43. VK_NUMPAD1      0x61        小键盘1键 
    44. VK_NUMPAD2      0x62        小键盘2键 
    45. ...             ...         ... 
    46. VK_NUMPAD9      0x69        小键盘9键 
    47. VK_MULTIPLY     0x6A        小键盘乘键* 
    48. VK_ADD          0x6B        小键盘加键+ 
    49. VK_SUBTRACT     0x6D        小键盘减键- 
    50. VK_DIVIDE       0x6F        小键盘除键/ 
    51. VK_DECIMAL      0x6E        小键盘点键. 
    52. VK_F1           0x70        F1键 
    53. VK_F2           0x71        F2键 
    54. ...             ...         ... 
    55. VK_F12          0x7B        F12键 
    56. VK_F13          0x7C        F13键      注:别问我,我也不知道什么电脑有这么多键 
    57. ...             ...         ... 
    58. VK_F24          0x87        F24键 
    59. VK_OEM_1        0xBA        ;:键 
    60. VK_OEM_2        0xBF        /?键 
    61. VK_OEM_3        0xC0        ·~键 
    62. VK_OEM_4        0xDB        [{键 
    63. VK_OEM_5        0xDC        \|键 
    64. VK_OEM_6        0xDD        ]}键 
    65. VK_OEM_7        0xDE        '"键 
    66. VK_OEM_PLUS     0xBB        =+键 
    67. VK_OEM_MINUS    0xBD        -_键 
    68. VK_OEM_COMMA    0xBC        ,<键 
    69. VK_OEM_PERIOD   0xBE        .>键  
    70. */  

           以上是部分常用的微软虚拟键盘码表,想要知道更详细的,请参见MSDN。其中各个虚拟键的具体使用情况根据各人编译器或IDE等的不同而有所差异。下面是一个实现按Esc键就输出Esc的样例程序:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <windows.h>  
    4. #include <conio.h>  
    5. #define true 1  
    6. #define false 0  
    7.   
    8. int main()  
    9. {  
    10.     HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);      //获得标准输入设备句柄  
    11.     INPUT_RECORD keyrec;        //定义输入事件结构体  
    12.     DWORD res;      //定义返回记录  
    13.     for (;;)  
    14.     {  
    15.         ReadConsoleInput(handle_in, &keyrec, 1, &res);      //读取输入事件  
    16.         if (keyrec.EventType == KEY_EVENT)      //如果当前事件是键盘事件  
    17.         {  
    18.             if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) //当前事件的虚拟键为Esc键  
    19.             {  
    20.                 printf("Esc ");  
    21.             }  
    22.         }  
    23.     }  
    24.     return 0;  
    25. }  

           在上面的样例程序中,当你按下Esc键后又马上释放,程序会输出两次Esc,因为有两次事件的虚拟键代码都是Esc键的代码,一次是按下,一次是释放。如果要实现按下键后出现反应,释放不出现反应,那么将上例程序中第18行代码的条件改成

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE  
    2.     && keyrec.Event.KeyEvent.bKeyDown == true)     //表示当前为键按下而不是键释放  
    就行了。


           根据控制键的状态我们可以实现不同的状态输出不同的值以及组合键的实现,下面的样例程序在大写锁定打开时输入A键则输出大写字母A,否则输出小写字母a。而在Shift键被按下的状态是则输出Shift+A以及Shift+a。样例程序如下

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <stdlib.h>  
    3. #include <windows.h>  
    4. #include <conio.h>  
    5. #define true 1  
    6. #define false 0  
    7.   
    8. int main()  
    9. {  
    10.     HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);      //获得标准输入设备句柄  
    11.     INPUT_RECORD keyrec;        //定义输入事件结构体  
    12.     DWORD res;      //定义返回记录  
    13.     for (;;)  
    14.     {  
    15.         ReadConsoleInput(handle_in, &keyrec, 1, &res);      //读取输入事件  
    16.         if (keyrec.EventType == KEY_EVENT)      //如果当前事件是键盘事件  
    17.         {  
    18.             if (keyrec.Event.KeyEvent.wVirtualKeyCode == 'A'  
    19.                 && keyrec.Event.KeyEvent.bKeyDown == true)   //当按下字母A键时  
    20.             {  
    21.                 if (keyrec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)    //Shift键为按下状态  
    22.                 {  
    23.                     printf("Shift+");  
    24.                 }  
    25.                 if (keyrec.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON)  //大写锁定为打开状态  
    26.                 {  
    27.                     printf("A ");  
    28.                 }  
    29.                 else        //大写锁定关闭状态  
    30.                 {  
    31.                     printf("a ");  
    32.                 }  
    33.             }  
    34.         }  
    35.     }  
    36.     return 0;  
    37. }  

           由上例需要了解到的是,各个控制键状态的的确定并不是使用等于符号==而是按位与&运算符,因为在同一时刻可能有多种控制键状态值,比如各种锁定都被打开且各种控制键也被同时按下。使用&运算符则显得尤其高明,方便查询各个控制键的状态而不使之出现冲突。呵呵,不服不行啊,感慨一下,还是要多学习一下别人高明的地方,比如灵活运用位运算符实现各种功能等等······



    八:鼠标事件


    上次讲的是键盘事件,这次我们介绍鼠标事件。下面先介绍下鼠标事件的结构体以及相关信息。

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. typedef struct _MOUSE_EVENT_RECORD      //鼠标事件结构体  
    2. {  
    3.     COORD dwMousePosition;      //当前鼠标在控制台窗口缓冲区的位置  
    4.     DWORD dwButtonState;        //鼠标按键的状态  
    5.     DWORD dwControlKeyState;    //控制键状态  
    6.     DWORD dwEventFlags;         //鼠标事件类型  
    7. } MOUSE_EVENT_RECORD;  
    8. /* 
    9. 其中鼠标按键状态dwButtonState可能的值有 
    10. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    11. FROM_LEFT_1ST_BUTTON_PRESSED        最左边的鼠标键被按下      一般来说就是鼠标左键 
    12. FROM_LEFT_2ND_BUTTON_PRESSED        左起第二个鼠标键被按下    一般来说是鼠标中键,就是滚轮键 
    13. FROM_LEFT_3RD_BUTTON_PRESSED        左起第三个鼠标键被按下 
    14. FROM_LEFT_4TH_BUTTON_PRESSED        左起第四个鼠标键被按下 
    15. RIGHTMOST_BUTTON_PRESSED            最右边的鼠标键被按下      一般来说是鼠标右键 
    16. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    17. 控制键状态dwControlKeyState与键盘事件的一样 
    18. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    19. ENHANCED_KEY        扩展键被按下  
    20. LEFT_ALT_PRESSED    左Alt键被按下  
    21. LEFT_CTRL_PRESSED   左Ctrl键被按下  
    22. RIGHT_ALT_PRESSED   右Alt键被按下  
    23. RIGHT_CTRL_PRESSED  右Ctrl键被按下  
    24. NUMLOCK_ON          数字锁定被打开  
    25. SCROLLLOCK_ON       滚动锁定被打开  
    26. CAPSLOCK_ON         大写锁定被打开  
    27. SHIFT_PRESSED       Shift键被按下 
    28. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    29. 鼠标事件类型dwEventFlags有以下几种 
    30. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    31. DOUBLE_CLICK            双击,第一击只作为普通按键事件,第二击才作为双击事件 
    32. MOUSE_HWHEELED          水平鼠标滚轮移动 
    33. MOUSE_MOVED             鼠标移动 
    34. MOUSE_WHEELED           垂直鼠标滚轮移动 
    35. 0                       当鼠标有键被按下或者释放 
    36. */  


           下面给一个样例程序,实现在控制台窗口缓冲区的最下面一行显示当前鼠标在缓冲区的坐标,单击左键在当前鼠标位置输出字母A,单击右键则输出字母B,双击任何鼠标键退出的功能。程序如下:

    [cpp] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. #include <stdio.h>  
    2. #include <windows.h>  
    3. #include <conio.h>  
    4.   
    5. HANDLE handle_in;  
    6. HANDLE handle_out;  
    7. CONSOLE_SCREEN_BUFFER_INFO csbi;        //定义窗口缓冲区信息结构体  
    8.   
    9. void DisplayMousePosition(COORD pos);   //显示鼠标所在位置  
    10.   
    11. void gotoxy(int x, int y);  //将光标移到坐标为(x,y)的位置  
    12.   
    13. int main()  
    14. {  
    15.     handle_in = GetStdHandle(STD_INPUT_HANDLE);      //获得标准输入设备句柄  
    16.     handle_out = GetStdHandle(STD_OUTPUT_HANDLE);    //获得标准输出设备句柄  
    17.     INPUT_RECORD mouserec;      //定义输入事件结构体  
    18.     DWORD res;      //用于存储读取记录  
    19.     COORD pos;      //用于存储鼠标当前位置  
    20.     COORD size = {80, 25};  //窗口缓冲区大小  
    21.     GetConsoleScreenBufferInfo(handle_out, &csbi);  //获得窗口缓冲区信息  
    22.     SetConsoleScreenBufferSize(handle_out, size);   //设置窗口缓冲区大小  
    23.     for (;;)  
    24.     {  
    25.         ReadConsoleInput(handle_in, &mouserec, 1, &res);      //读取输入事件  
    26.         pos = mouserec.Event.MouseEvent.dwMousePosition;    //获得当前鼠标位置  
    27.         gotoxy(0, 24);  //在第25行显示鼠标位置  
    28.         DisplayMousePosition(pos);      //显示鼠标位置  
    29.         if (mouserec.EventType == MOUSE_EVENT)    //如果当前为鼠标事件  
    30.         {  
    31.             gotoxy(pos.X, pos.Y);  
    32.             //单击鼠标左键,输出字符A  
    33.             if (mouserec.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)  
    34.             {  
    35.                 putchar('A');  
    36.             }  
    37.             //单击鼠标右键,输出字符B  
    38.             if (mouserec.Event.MouseEvent.dwButtonState == RIGHTMOST_BUTTON_PRESSED)  
    39.             {  
    40.                 putchar('B');  
    41.             }  
    42.             //双击退出  
    43.             if (mouserec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)  
    44.             {  
    45.                 break;  
    46.             }  
    47.         }  
    48.     }  
    49.     CloseHandle(handle_out);  
    50.     CloseHandle(handle_in);  
    51.     return 0;  
    52. }  
    53.   
    54. void DisplayMousePosition(COORD pos)  
    55. {  
    56.     COORD dis = {0, 24};        //在第24行显示鼠标位置  
    57.     WORD att = FOREGROUND_GREEN | FOREGROUND_INTENSITY; //文本属性  
    58.     GetConsoleScreenBufferInfo(handle_out, &csbi);  //获得窗口缓冲区信息  
    59.     printf("X = %3d, Y = %3d", (int)pos.X, (int)pos.Y);  
    60.     FillConsoleOutputAttribute(handle_out, att, 16, dis, NULL);  //填充文本属性  
    61.     return;  
    62. }  
    63.   
    64. void gotoxy(int x, int y)  
    65. {  
    66.     COORD pos = {x, y};  
    67.     SetConsoleCursorPosition(handle_out, pos);  
    68. }  


           附上用本程序写的Hello world!的图:


           注意:当使用system函数后鼠标事件无法正常发生。


    展开全文
  • 地方
  • Java图形界面编程

    万次阅读 多人点赞 2019-02-23 22:23:45
    一、Java图形用户界面(gui)介绍 1、Java GUI简介   图形用户界面(Graphics User Interface,GUI)是用户与程序交互的窗口,比命令行的界面更加直观并且更好操作。   Sun已经提供了一个跨平台GUI开发工具包AWT...
  • Go语言——图形界面编程

    千次阅读 2019-11-24 11:01:15
    作用:图形界面编程,方便非专业用户使用 使用的框架:GTK框架 1.1 GTK框架     GTK是一套在GIMP的基础上发展而来的高级的、可伸缩的现代化、跨平台的图形工具包,提供一整套完备的图形构件,适用于大大小小各种...
  • 刚学完了C++基础,想学Qt下的图形界面编程,看了《C++GUI Qt4编程》的前面一点,发现有一大堆的函数,宏,信号见都没见过,看起书来很吃力,我是应该怎么看,或者有没有更适合入门的书,有没有大神能帮帮忙推荐一下...
  • 图形界面编程成就了C++

    万次阅读 热门讨论 2007-11-05 17:23:00
    图形界面编程成就了C++听有人说C#、VB比C++好是因为做界面方便还算傻得可爱,听有人说用C++做数值计算而不屑于做界面可就对不起咱C++的恩人了。这我可要说道说道。想当年C++刚出江湖,名门出身,自立门派,想抢...
  • Ubuntu 17.10图形界面编程,需要工具(gcc编译器,make命令,glade开发界面,GTK+的安装,一个编辑程序的ide) Gcc,make,gtk一般系统都是自带的,如果没有可以安装,##sudo apt-get install (你需要安装的工具); ...
  • MFC图形界面编程

    千次阅读 2016-02-25 13:13:30
    转载地址http://www.cnblogs.com/lidabo/category/434801.html
  • linux图形界面编程基本知识

    千次阅读 2015-10-21 22:22:06
    一直对linux的一些图形界面的基本概念很迷茫,直到看到这些曾经或了解或不了解的东西,收益颇深,特粘在此处作为笔记(阅读过程中也修改了一些原作者描述的不恰当的地方,以防误人子弟,然而一定仍不全面,欢迎指正...
  • Java图形界面编程模拟ATM自助取款系统

    万次阅读 多人点赞 2018-08-04 10:44:14
    本人学习Java时一直想做一个图形界面的程序,所以利用闲暇时间,做了一个Java图形界面编程模拟ATM自助取款系统,界面简洁漂亮,基本功能ATM功能实现。 程序初始时 考号:123456 密码:123456 金额:10000 ...
  • qt图形界面编程入门 目录说明 experiment 实验一 程序1.编写一个C++风格的程序,用动态分配空间的方法计算Fibonacci数列的前20项并存储到动态分配的空间中。 程序2.定义一个时间类Time,能提供和设置由时、分、...
  • Python图形界面编程---Tkinter模块基础

    万次阅读 2016-07-20 14:32:45
    Python本身语法优美, 但一旦到了...最近还有一种解决办法,使用html5搭建界面,加上一个web框架 结构形如:Python+HTML5+JS+Python 商业上PyQt用的多,这里只介绍Tkinter。TkinterTkinter是TK在python里的一个接口。TK是
  • QT图形界面编程入门第一章习题5

    千次阅读 2019-04-17 21:44:22
    因为接手的项目是用C++写的,里面图形化界面涉及到QT所以专门从图书馆借了本《QT图形界面编程入门》,第一二章都没正式设计QT,复习了下对象,类,继承和多态之类的C++面对对象编程的重要内容,之前没有学过C++,但是...
  • 死磕Java系列之GUI图形界面编程简介

    千次阅读 2018-12-08 14:21:17
    GUI图形界面设计是用户和程序交互的工具,用户通过图形界面控制程序事件的发生。 首先介绍Swing的基本体系结构,这是底层的东西,swing的MVC设计模式,其次,讲述Swing中常用的窗体,窗体用来放按钮,标签。接下来...
  • 《Qt图形界面编程入门》实验

    千次阅读 2019-12-04 19:37:17
    experiment 实验一 程序1.编写一个C++风格的程序,用动态分配空间的方法计算Fibonacci数列的前20项并存储到动态分配的空间中。 程序2....输出该对象提供的时间。...程序3.用new建立一个动态一维数组,并初始化int[10...
  • BorderLayout边框布局:将容器分为东、西、南、北、中5个部分 ex: /** * BorderLayout边框布局:将容器分为东、西、南、北、中5个部分 * @author Administrator ...public class BorderLayoutDemo { ...
  • 本系列文章是笔者

空空如也

1 2 3 4 5 ... 20
收藏数 140,535
精华内容 56,214
关键字:

图形界面编程