Qt图形视图框架(一)_qlist<qgraphicsitem *> items;-程序员宅基地

http://blog.csdn.net/goodlixueyong/article/details/5935370


优点:处理多个图元,单击,拖动,选择图元

架构:一个场景,多个图元位于其中,通过视图显示
主要应用: 绘图软件,显示地图软件

当使用没有变换的视图观察场景时,场景中的一个单元对应屏幕上的一个像素

图元坐标通常以图元中心为原点,X轴正方向为向右,Y轴正方向为向下
场景坐标的原点在场景中心,X轴正方向为向右,Y轴正方向为向下
视图坐标以左上角为原点,X轴正方向为向右,Y轴正方向为向下
所有的鼠标事件最开始都是使用视图坐标



场景:图元的容器
1.提供管理很多图元的接口
2.传播事件到图元中
3.管理图元状态,例如选择和焦点处理
4.提供非转换的绘制功能,主要用于打印

QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100)); // 添加图元

QGraphicsItem *item = scene.itemAt(50, 50); // 查询图元
// item == rect;

通过QGraphicsScene::setSelectionArea()可以选择场景的任一个图元,
QGraphicsScene::setSelectedItems()返回被选择的图元
设置焦点图元QGraphicsScene::setFocusItem(), setFocus(),
QGraphicsScene::focusItem(), 返回焦点图元

视图:
一个可视的子部件,可视化场景的内容
多个视图可以显示同一个场景
坐标转换:QGraphicsView::mapToScene(), QGraphicsView::mapFromScene()

图元:
支持鼠标事件,滚轮事件,上下文菜单事件
支持键盘输入焦点,按键事件
支持拖放
支持分组
冲突探测
提供坐标转换,图元与场景,图元与图元之间
利用QGraphicsItem::shape()和QGraphicsItem::collidesWith()
实现冲突探测,这2个函数都是虚函数



相关类:QGraphicsScene, QGraphicsItem, QGraphicsView

QGraphicsItem子类:

QGraphicsEllipseItem     provides an ellipse item
QGraphicsLineItem        provides a line item
QGraphicsPathItem        provides an arbitrary path item
QGraphicsPixmapItem      provides a pixmap item
QGraphicsPolygonItem     provides a polygon item
QGraphicsRectItem        provides a rectangular item
QGraphicsSimpleTextItem  provides a simple text label item
QGraphicsTextItem        provides an advanced text browser item
QGraphicsSvgItem         provides a SVG file item

QGraphicsScene:
拥有多个图元,包含三层:背景层,图元层,前景层

背景层和前景层可以使用QBrush绘制,也可以使用drawBackground(),drawForeground()实现
如果使用图片作为背景,可以用texture QBrush(pixmap)实现
前景层brush可以使用半透明的白色实现褪色效果,或者使用交叉模式实现网格重叠

场景可以告诉我们,哪些图元发生冲突,哪些图元被选择,哪些图元位于一个特定的点或者区域

每个图元可以是:1.顶级图元,场景是它的父亲;2.孩子,它的父亲是另一个图元,任何作用于父图元的转换
都将自动应用于它的孩子

2种分组方式:1.一个图元成为另一个图元的孩子; 2.使用QGraphicsItemGroup。使用分组,可以使位于同一个
组的所有图元的操作都相同

QGraphicsView:
是一个Widget,用于显示一个场景,提供滚动条功能和转换功能,可以缩放和旋转场景。
默认使用内建的2D画图引擎,可以使用OpenGL:在构造后,调用setViewport()

坐标系统:
使用3种坐标系统:viewport, scene, item
viewport: 位于QGraphicsView内部
scene: 逻辑坐标用于定位顶级图元
item: 与图元相关,以图元的(0,0)为中心,移动图元时,它的坐标不会改变
实践中,主要关注场景坐标(定位顶级图元)和图元坐标(定位子图元和绘制图元)

在图元自己的坐标系统里面绘图意味着我们不用担心它在场景中的位置和应用于它的坐标转换

Demo:
// 主要特点:
// 上下文菜单, 右键菜单
// copy->paste方法

//diagram.pro
TEMPLATE      = app
HEADERS       = diagramwindow.h /
link.h /
node.h /
propertiesdialog.h
SOURCES       = diagramwindow.cpp /
link.cpp /
main.cpp /
node.cpp /
propertiesdialog.cpp
FORMS         = propertiesdialog.ui
RESOURCES     = resources.qrc

//link.h
#ifndef LINK_H
#define LINK_H

#include <QGraphicsLineItem>

class Node;

class Link : public QGraphicsLineItem   // 如果使用信号和槽,采用多继承public QObject
{
public:
Link(Node *fromNode, Node *toNode);
~Link();

Node *fromNode() const;
Node *toNode() const;

void setColor(const QColor &color);
QColor color() const;

void trackNodes();   // 节点移动时,跟踪节点

private:
Node *myFromNode;   // 连线的2个节点
Node *myToNode;
};

#endif

//link.cpp
#include <QtGui>

#include "link.h"
#include "node.h"

Link::Link(Node *fromNode, Node *toNode)
{
myFromNode = fromNode;
myToNode = toNode;

myFromNode->addLink(this); // 节点增加连线,每个节点有任意多个连线
myToNode->addLink(this);

setFlags(QGraphicsItem::ItemIsSelectable);  // 连线可以被选择,然后删除
setZValue(-1);  // 在场景中显示的前后层次,因为连线是两个节点的中心,-1表示位于最后面,
// 节点覆盖了部分连线

setColor(Qt::darkRed);  // 设置线的颜色
trackNodes();
}

Link::~Link()
{
myFromNode->removeLink(this);  // 删除连线时,将删除它在节点中的记录
myToNode->removeLink(this);
}

Node *Link::fromNode() const
{
return myFromNode;
}

Node *Link::toNode() const
{
return myToNode;
}

void Link::setColor(const QColor &color)
{
setPen(QPen(color, 1.0));
}

QColor Link::color() const
{
return pen().color();
}

void Link::trackNodes()
{
// pos()返回节点在场景中或者父图元中的位置
setLine(QLineF(myFromNode->pos(), myToNode->pos()));
}

//node.h
#ifndef NODE_H
#define NODE_H

#include <QApplication>
#include <QColor>
#include <QGraphicsItem>
#include <QSet>

class Link;

class Node : public QGraphicsItem
{
Q_DECLARE_TR_FUNCTIONS(Node)   // 在此类中增加tr()功能,直接使用,而不需要QObject::tr()了

public:
Node();
~Node();

void setText(const QString &text);
QString text() const;
void setTextColor(const QColor &color);
QColor textColor() const;
void setOutlineColor(const QColor &color);
QColor outlineColor() const;
void setBackgroundColor(const QColor &color);
QColor backgroundColor() const;

void addLink(Link *link);
void removeLink(Link *link);

QRectF boundingRect() const;    // 重新实现,决定一个图元是否需要绘制,必须的
QPainterPath shape() const;     // 重新实现,返回图元的精确形状,
// 决定一个点是否在图元内,或者2个图元是否发生冲突
void paint(QPainter *painter,   // 重新实现,画图, 必须的
const QStyleOptionGraphicsItem *option, QWidget *widget);

protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); // 双击事件,修改节点的文本
QVariant itemChange(GraphicsItemChange change,   // 重新实现,图元变化时,相关的连线发生变化
const QVariant &value);      // 没有使用mouseMoveEvent(), 
// 是因为程序可以改变节点位置

private:
QRectF outlineRect() const;
int roundness(double size) const;

QSet<Link *> myLinks;
QString myText;
QColor myTextColor;
QColor myBackgroundColor;
QColor myOutlineColor;
};

#endif

//link.cpp
#include <QtGui>

#include "link.h"
#include "node.h"

Node::Node()
{
myTextColor = Qt::darkGreen;
myOutlineColor = Qt::darkBlue;
myBackgroundColor = Qt::white;

setFlags(ItemIsMovable | ItemIsSelectable); // 节点可以移动,被选择
}

Node::~Node()
{
foreach (Link *link, myLinks)  // 删除所有的连线,防止边界效应,不使用aDeleteAll()
delete link;
}

void Node::setText(const QString &text)
{
prepareGeometryChange();  // 改变节点内的文本时,矩形可能会发生变化
myText = text;
update();
}

QString Node::text() const
{
return myText;
}

void Node::setTextColor(const QColor &color)
{
myTextColor = color;
update();
}

QColor Node::textColor() const
{
return myTextColor;
}

void Node::setOutlineColor(const QColor &color)
{
myOutlineColor = color;
update();
}

QColor Node::outlineColor() const
{
return myOutlineColor;
}

void Node::setBackgroundColor(const QColor &color)
{
myBackgroundColor = color;
update();
}

QColor Node::backgroundColor() const
{
return myBackgroundColor;
}

void Node::addLink(Link *link)
{
myLinks.insert(link);   // 增加连线时,记录连线
}

void Node::removeLink(Link *link)
{
myLinks.remove(link);
}

QRectF Node::boundingRect() const  // View决定是否绘制矩形
{
const int Margin = 1;
return outlineRect().adjusted(-Margin, -Margin, +Margin, +Margin);
}

QPainterPath Node::shape() const   // View用于冲突探测
{
QRectF rect = outlineRect();

QPainterPath path;
path.addRoundRect(rect, roundness(rect.width()),
roundness(rect.height()));
return path;
}

// 绘制图元
void Node::paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget * /* widget */)
{
QPen pen(myOutlineColor);
if (option->state & QStyle::State_Selected) {  // 图元被选择
pen.setStyle(Qt::DotLine);
pen.setWidth(2);
}
painter->setPen(pen);
painter->setBrush(myBackgroundColor);

QRectF rect = outlineRect();
painter->drawRoundRect(rect, roundness(rect.width()),
roundness(rect.height()));

painter->setPen(myTextColor);
painter->drawText(rect, Qt::AlignCenter, myText);
}

// 双击节点,弹出标准输入对话框
void Node::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
QString text = QInputDialog::getText(event->widget(),
tr("Edit Text"), tr("Enter new text:"),
QLineEdit::Normal, myText);
if (!text.isEmpty())
setText(text);
}

// 拖动节点时,调用此函数
QVariant Node::itemChange(GraphicsItemChange change,
const QVariant &value)
{
if (change == ItemPositionHasChanged) {
foreach (Link *link, myLinks)
link->trackNodes();
}
return QGraphicsItem::itemChange(change, value);
}

QRectF Node::outlineRect() const
{
const int Padding = 8;
QFontMetricsF metrics = qApp->font();
QRectF rect = metrics.boundingRect(myText);
rect.adjust(-Padding, -Padding, +Padding, +Padding);
rect.translate(-rect.center());
return rect;
}

int Node::roundness(double size) const
{
const int Diameter = 12;
return 100 * Diameter / int(size);
}


// diagramwindow.h
#ifndef DIAGRAMWINDOW_H
#define DIAGRAMWINDOW_H

#include <QMainWindow>
#include <QPair>

class QAction;
class QGraphicsItem;
class QGraphicsScene;
class QGraphicsView;
class Link;
class Node;

class DiagramWindow : public QMainWindow
{
Q_OBJECT

public:
DiagramWindow();

private slots:
void addNode();
void addLink();
void del();
void cut();
void copy();
void paste();
void bringToFront();
void sendToBack();
void properties();    // 弹出属性设置对话框
void updateActions(); // 更新菜单栏和工具栏的动作,哪些可用,哪些不可用

private:
typedef QPair<Node *, Node *> NodePair;

void createActions();
void createMenus();
void createToolBars();
void setZValue(int z);
void setupNode(Node *node);
Node *selectedNode() const;
Link *selectedLink() const;
NodePair selectedNodePair() const;

QMenu *fileMenu;
QMenu *editMenu;
QToolBar *editToolBar;
QAction *exitAction;
QAction *addNodeAction;
QAction *addLinkAction;
QAction *deleteAction;
QAction *cutAction;
QAction *copyAction;
QAction *pasteAction;
QAction *bringToFrontAction;
QAction *sendToBackAction;
QAction *propertiesAction;

QGraphicsScene *scene;
QGraphicsView *view;

int minZ; // sendToBack(), bringToFront()使用
int maxZ;
int seqNumber; // 唯一标示一个节点的文本
};

#endif

//digramwindow.cpp
#include <QtGui>

#include "diagramwindow.h"
#include "link.h"
#include "node.h"
#include "propertiesdialog.h"

DiagramWindow::DiagramWindow()
{
scene = new QGraphicsScene(0, 0, 600, 500); // 创建场景,起始点为(0,0), 宽600,高500

view = new QGraphicsView;
view->setScene(scene);    // 显示场景
view->setDragMode(QGraphicsView::RubberBandDrag); // 选择多个节点方式:1.按ctrl;2.设置橡皮筋方式
view->setRenderHints(QPainter::Antialiasing
| QPainter::TextAntialiasing);
view->setContextMenuPolicy(Qt::ActionsContextMenu); // 右键菜单
setCentralWidget(view);

minZ = 0;
maxZ = 0;
seqNumber = 0;

createActions();
createMenus();
createToolBars();

connect(scene, SIGNAL(selectionChanged()),
this, SLOT(updateActions()));

setWindowTitle(tr("Diagram"));
updateActions();
}

// 增加一个节点
void DiagramWindow::addNode()
{
Node *node = new Node;
node->setText(tr("Node %1").arg(seqNumber + 1));
setupNode(node);
}

void DiagramWindow::addLink()
{
NodePair nodes = selectedNodePair();
if (nodes == NodePair())
return;

Link *link = new Link(nodes.first, nodes.second);
scene->addItem(link);
}

// 删除选择的图元:首先删除连线,然后删除节点,以防止多次删除同一个连线
void DiagramWindow::del()
{
QList<QGraphicsItem *> items = scene->selectedItems();
QMutableListIterator<QGraphicsItem *> it(items);
while (it.hasNext()) {
Link *link = dynamic_cast<Link *>(it.next());
if (link) {
delete link;
it.remove();
}
}

qDeleteAll(items);
}

// 剪切操作:先复制,后删除
void DiagramWindow::cut()
{
Node *node = selectedNode();
if (!node)
return;

copy();
delete node;
}

// 拷贝操作:值得研究!!!
void DiagramWindow::copy()
{
Node *node = selectedNode();
if (!node)
return;

QString str = QString("Node %1 %2 %3 %4")
.arg(node->textColor().name())
.arg(node->outlineColor().name())
.arg(node->backgroundColor().name())
.arg(node->text());
QApplication::clipboard()->setText(str);
}

void DiagramWindow::paste()
{
QString str = QApplication::clipboard()->text();
QStringList parts = str.split(" ");

if (parts.count() >= 5 && parts.first() == "Node") {
Node *node = new Node;
node->setText(QStringList(parts.mid(4)).join(" ")); // 连接字符串列表
node->setTextColor(QColor(parts[1]));
node->setOutlineColor(QColor(parts[2]));
node->setBackgroundColor(QColor(parts[3]));
setupNode(node);
}
}

void DiagramWindow::bringToFront()
{
++maxZ;
setZValue(maxZ);  // 改变绘图顺序,首先绘制父图元,然后是子图元,根据子图元Z值的大小,
// 值最小,最先绘制,值最大,最后绘制
}

void DiagramWindow::sendToBack()
{
--minZ;
setZValue(minZ);
}

void DiagramWindow::properties()
{
Node *node = selectedNode();
Link *link = selectedLink();

if (node) {
PropertiesDialog dialog(node, this);
dialog.exec();
} else if (link) {
QColor color = QColorDialog::getColor(link->color(), this);
if (color.isValid())
link->setColor(color);
}
}

// 更新动作使能
void DiagramWindow::updateActions()
{
bool hasSelection = !scene->selectedItems().isEmpty();
bool isNode = (selectedNode() != 0);
bool isNodePair = (selectedNodePair() != NodePair());

cutAction->setEnabled(isNode);
copyAction->setEnabled(isNode);
addLinkAction->setEnabled(isNodePair);
deleteAction->setEnabled(hasSelection);
bringToFrontAction->setEnabled(isNode);
sendToBackAction->setEnabled(isNode);
propertiesAction->setEnabled(isNode);

foreach (QAction *action, view->actions())
view->removeAction(action);  // 删除右键菜单

foreach (QAction *action, editMenu->actions()) {
if (action->isEnabled())
view->addAction(action);  // 增加右键菜单
}
}

void DiagramWindow::createActions()
{
exitAction = new QAction(tr("E&xit"), this);
exitAction->setShortcut(tr("Ctrl+Q"));
connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));

addNodeAction = new QAction(tr("Add &Node"), this);
addNodeAction->setIcon(QIcon(":/images/node.png"));
addNodeAction->setShortcut(tr("Ctrl+N"));
connect(addNodeAction, SIGNAL(triggered()), this, SLOT(addNode()));

addLinkAction = new QAction(tr("Add &Link"), this);
addLinkAction->setIcon(QIcon(":/images/link.png"));
addLinkAction->setShortcut(tr("Ctrl+L"));
connect(addLinkAction, SIGNAL(triggered()), this, SLOT(addLink()));

deleteAction = new QAction(tr("&Delete"), this);
deleteAction->setIcon(QIcon(":/images/delete.png"));
deleteAction->setShortcut(tr("Del"));
connect(deleteAction, SIGNAL(triggered()), this, SLOT(del()));

cutAction = new QAction(tr("Cu&t"), this);
cutAction->setIcon(QIcon(":/images/cut.png"));
cutAction->setShortcut(tr("Ctrl+X"));
connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));

copyAction = new QAction(tr("&Copy"), this);
copyAction->setIcon(QIcon(":/images/copy.png"));
copyAction->setShortcut(tr("Ctrl+C"));
connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));

pasteAction = new QAction(tr("&Paste"), this);
pasteAction->setIcon(QIcon(":/images/paste.png"));
pasteAction->setShortcut(tr("Ctrl+V"));
connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));

bringToFrontAction = new QAction(tr("Bring to &Front"), this);
bringToFrontAction->setIcon(QIcon(":/images/bringtofront.png"));
connect(bringToFrontAction, SIGNAL(triggered()),
this, SLOT(bringToFront()));

sendToBackAction = new QAction(tr("&Send to Back"), this);
sendToBackAction->setIcon(QIcon(":/images/sendtoback.png"));
connect(sendToBackAction, SIGNAL(triggered()),
this, SLOT(sendToBack()));

propertiesAction = new QAction(tr("P&roperties..."), this);
connect(propertiesAction, SIGNAL(triggered()),
this, SLOT(properties()));
}

void DiagramWindow::createMenus()
{
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(exitAction);

editMenu = menuBar()->addMenu(tr("&Edit"));
editMenu->addAction(addNodeAction);
editMenu->addAction(addLinkAction);
editMenu->addAction(deleteAction);
editMenu->addSeparator();
editMenu->addAction(cutAction);
editMenu->addAction(copyAction);
editMenu->addAction(pasteAction);
editMenu->addSeparator();
editMenu->addAction(bringToFrontAction);
editMenu->addAction(sendToBackAction);
editMenu->addSeparator();
editMenu->addAction(propertiesAction);
}

void DiagramWindow::createToolBars()
{
editToolBar = addToolBar(tr("Edit"));
editToolBar->addAction(addNodeAction);
editToolBar->addAction(addLinkAction);
editToolBar->addAction(deleteAction);
editToolBar->addSeparator();
editToolBar->addAction(cutAction);
editToolBar->addAction(copyAction);
editToolBar->addAction(pasteAction);
editToolBar->addSeparator();
editToolBar->addAction(bringToFrontAction);
editToolBar->addAction(sendToBackAction);
}

void DiagramWindow::setZValue(int z)
{
Node *node = selectedNode();
if (node)
node->setZValue(z);
}

void DiagramWindow::setupNode(Node *node)
{
node->setPos(QPoint(80 + (100 * (seqNumber % 5)),
80 + (50 * ((seqNumber / 5) % 7))));
scene->addItem(node);
++seqNumber;

scene->clearSelection();
node->setSelected(true);
bringToFront();
}

// 返回一个选择的节点
Node *DiagramWindow::selectedNode() const
{
QList<QGraphicsItem *> items = scene->selectedItems(); // 全部选择的节点
if (items.count() == 1) {
return dynamic_cast<Node *>(items.first());
} else {
return 0;
}
}

Link *DiagramWindow::selectedLink() const
{
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 1) {
return dynamic_cast<Link *>(items.first());
} else {
return 0;
}
}

// 返回选择的节点对
DiagramWindow::NodePair DiagramWindow::selectedNodePair() const
{
QList<QGraphicsItem *> items = scene->selectedItems();
if (items.count() == 2) {
Node *first = dynamic_cast<Node *>(items.first());
Node *second = dynamic_cast<Node *>(items.last());
if (first && second)
return NodePair(first, second);
}
return NodePair();
}

// propertiesdialog.h
#ifndef PROPERTIESDIALOG_H
#define PROPERTIESDIALOG_H

#include "ui_propertiesdialog.h"

class Node;

class PropertiesDialog : public QDialog, private Ui::PropertiesDialog
{
Q_OBJECT

public:
PropertiesDialog(Node *node, QWidget *parent = 0);

private slots:
void on_buttonBox_accepted();    // 快速信号连接
void on_textColorButton_clicked();
void on_outlineColorButton_clicked();
void on_backgroundColorButton_clicked();

private:
void updateColorLabel(QLabel *label, const QColor &color);
void chooseColor(QLabel *label, QColor *color);

Node *node;
QColor textColor;
QColor outlineColor;
QColor backgroundColor;
};

#endif

//propertiesdialog.cpp
#include <QtGui>

#include "node.h"
#include "propertiesdialog.h"

PropertiesDialog::PropertiesDialog(Node *node, QWidget *parent)
: QDialog(parent)
{
setupUi(this);

this->node = node;
xSpinBox->setValue(int(node->x()));
ySpinBox->setValue(int(node->y()));
textLineEdit->setText(node->text());
textColor = node->textColor();
outlineColor = node->outlineColor();
backgroundColor = node->backgroundColor();

updateColorLabel(outlineColorLabel, outlineColor);
updateColorLabel(backgroundColorLabel, backgroundColor);
updateColorLabel(textColorLabel, textColor);
}

void PropertiesDialog::on_buttonBox_accepted()
{
node->setPos(xSpinBox->value(), ySpinBox->value());
node->setText(textLineEdit->text());
node->setOutlineColor(outlineColor);
node->setBackgroundColor(backgroundColor);
node->setTextColor(textColor);
node->update();
QDialog::accept();
}

void PropertiesDialog::on_textColorButton_clicked()
{
chooseColor(textColorLabel, &textColor);
}

void PropertiesDialog::on_outlineColorButton_clicked()
{
chooseColor(outlineColorLabel, &outlineColor);
}

void PropertiesDialog::on_backgroundColorButton_clicked()
{
chooseColor(backgroundColorLabel, &backgroundColor);
}

void PropertiesDialog::updateColorLabel(QLabel *label,
const QColor &color)
{
QPixmap pixmap(16, 16);
pixmap.fill(color);
label->setPixmap(pixmap);
}

void PropertiesDialog::chooseColor(QLabel *label, QColor *color)
{
QColor newColor = QColorDialog::getColor(*color, this);
if (newColor.isValid()) {
*color = newColor;
updateColorLabel(label, *color);
}
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fanyun_01/article/details/52846939

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签