windows channel

BlockQueue.h

#pragma once

#include "Common.h"
#include "Condition.h"

template<typename T>
class BlockQueue
{
public:
	BlockQueue()
		: mutex_()
		, notEmpty_(mutex_)
	{
	}

	void put(const T &t)
	{
		MutexLockGuard lock(mutex_);
		queue_.push_back(t);
		notEmpty_.notify();
	}

	T take()
	{
		MutexLockGuard lock(mutex_);
		while (queue_.empty()) {
			notEmpty_.wait();
		}
		assert(!queue_.empty());
		T front(queue_.front());
		queue_.pop_front();
		return front;
	}

	size_t size() const
	{
		MutexLockGuard lock(mutex_);
		return queue_.size(); 
	}

private:
	BlockQueue(const BlockQueue&);
	BlockQueue& operator=(const BlockQueue&);

private:
	mutable MutexLock mutex_;
	Condition notEmpty_;
	std::deque<T> queue_;
};

Buffer.h

#pragma once

#include "Common.h"

class Buffer;
typedef std::tr1::shared_ptr<Buffer> BufferPtr;

class Buffer
{
public:
  static const size_t kCheapPrepend = 8;
  static const size_t kInitialSize = 2048;

  Buffer()
	 : buffer_(kCheapPrepend + kInitialSize)
	 , readerIndex_(kCheapPrepend)
	 , writerIndex_(kCheapPrepend)
	{
		assert(readableBytes() == 0);
		assert(writableBytes() == kInitialSize);
		assert(prependableBytes() == kCheapPrepend);
	}

	void swap(Buffer &rhs)
	{
		buffer_.swap(rhs.buffer_);
		std::swap(readerIndex_, rhs.readerIndex_);
		std::swap(writerIndex_, rhs.writerIndex_);
	}

	size_t readableBytes() const
	{
		return writerIndex_ - readerIndex_; 
	}

	size_t writableBytes() const
	{
		return buffer_.size() - writerIndex_;
	}

	size_t prependableBytes() const
	{
		return readerIndex_;
	}

	const char *peek() const
	{
		return begin() + readerIndex_;
	}

	void retrieve(size_t len)
	{
		assert(len <= readableBytes());
		if ( len < readableBytes())
		{
			readerIndex_ += len;
		}
		else
		{
			retrieveAll();
		}
	}

	void retrieveUntil(const char *end)
	{
		assert(peek() <= end);
		assert(end <= beginWrite());
		retrieve(end - peek());
	}

	void retrieveInt32()
	{
		retrieve(sizeof(__int32));
	}

	void retrieveInt16()
	{
		retrieve(sizeof(__int16));
	}

	void retrieveInt8()
	{
		retrieve(sizeof(__int8));
	}

	void retrieveAll()
	{
		readerIndex_ = kCheapPrepend;
		writerIndex_ = kCheapPrepend;
	}

	std::string retrieveAllAsString()
	{
		return retrieveAsString(readableBytes());
	}

	std::string retrieveAsString(size_t len)
	{
		assert(len <= readableBytes());
		std::string result(peek(), len);
		retrieve(len);
		return result;
	}

	void append(const std::string &str)
	{
		append(str.c_str(), str.size());
	}

	void append(const char* data, size_t len)
	{
		ensureWritableBytes(len);
		std::copy(data, data+len, stdext::checked_array_iterator<char*>(beginWrite(), len));
		hasWritten(len);
	}

	void append(const void* data, size_t len)
	{
		append(static_cast<const char*>(data), len);
	}

	void ensureWritableBytes(size_t len)
	{
		if (writableBytes() < len)
		{
			makeSpace(len);
		}
		assert(writableBytes() >= len);
	}

	char* beginWrite()
	{
		return begin() + writerIndex_;
	}

	const char* beginWrite() const
	{
		return begin() + writerIndex_;
	}

	void hasWritten(size_t len)
	{
		writerIndex_ += len;
	}

	void appendInt32(__int32 x);
	void appendInt16(__int16 x);
	void appendInt8(__int8 x);
	__int32 readInt32();
	__int16 readInt16();
	__int8 readInt8();
	__int32 peekInt32() const;
	__int16 peekInt16() const;
	__int8 peekInt8() const;
	void prependInt32(__int32 x);
	void prependInt16(__int16 x);
	void prependInt8(__int8 x);

	bool Buffer::read(void* data, size_t len)
	{
		if (data && readableBytes() >= len)
		{
			memcpy(data, peek(), len);
			retrieve(len);
			return true;
		}
		return false;
	}

	void prepend(const void* data, size_t len)
	{
		assert(len <= prependableBytes());
		readerIndex_ -= len;
		const char *d = static_cast<const char*>(data);
		std::copy(d, d+len, stdext::checked_array_iterator<char*>(begin()+readerIndex_, len));
	}

	int readFd(void* fd, int *saveErrno);

private:
	char *begin()
	{
		return &*buffer_.begin();
	}

	const char* begin() const
	{
		return &*buffer_.begin();
	}

	void makeSpace(size_t len)
	{
		if (writableBytes() + prependableBytes() < len + kCheapPrepend)
		{
			buffer_.resize(writerIndex_ + len);
		}
		else
		{
			assert(kCheapPrepend < readerIndex_);
			size_t readable = readableBytes();
			std::copy(begin() + readerIndex_, begin() + writerIndex_,
				stdext::checked_array_iterator<char*>(begin() + kCheapPrepend, writerIndex_ - readerIndex_));
			readerIndex_ = kCheapPrepend;
			writerIndex_ = readerIndex_ + readable;
			assert(readable == readableBytes());
		}
	}

	Buffer(const Buffer&);
	const Buffer& operator=(const Buffer&);

private:
	std::vector<char> buffer_;
	size_t readerIndex_;
	size_t writerIndex_;
};

Channel.h

#pragma once

#include "Buffer.h"
#include "BlockQueue.h"

#define TCPCONN_SUCCESS							0
#define TCPCONN_ERROR_BASE						0x00100
#define TCPCONN_ERROR_SOCKET					TCPCONN_ERROR_BASE + 1
#define TCPCONN_ERROR_CONNECT					TCPCONN_ERROR_BASE + 2
#define TCPCONN_ERROR_SEND						TCPCONN_ERROR_BASE + 3
#define TCPCONN_ERROR_RECV						TCPCONN_ERROR_BASE + 4
#define TCPCONN_ERROR_WSAEVENTSELECT			TCPCONN_ERROR_BASE + 5
#define TCPCONN_ERROR_WSAENUMNETWORKEVENTS		TCPCONN_ERROR_BASE + 6
#define TCPCONN_ERROR_WSAWAITFORMULTIPLEEVENTS	TCPCONN_ERROR_BASE + 7


typedef std::tr1::function<void(Buffer&)> OnReadCallback;
typedef std::tr1::function<void(int)> OnErrorCallback;
typedef std::tr1::function<void()> OnCloseCallback;

class Connector;
class Thread;
template<typename T> class BlockQueue;
typedef std::tr1::shared_ptr<Connector> ConnectorPtr;

class Channel
{
public:
	Channel(ConnectorPtr connectorPtr);
	~Channel();

	void start();
	void stop();

	void send(const unsigned char *msg, size_t len);
	void send(const BufferPtr &msg);

	void setOnReadCallback(const OnReadCallback &cb) { onReadCallback_ = cb; }
	void setOnErrorCallback(const OnErrorCallback &cb) { onErrorCallback_ = cb; }
	void setOnCloseCallback(const OnCloseCallback &cb) { onCloseCallback_ = cb; }
	
private:
	Channel(const Channel&);
	Channel& operator=(const Channel&);

	void sendBufferThread();
	void recvBufferThread();

private:
	ConnectorPtr connectorPtr_;
	scoped_ptr<Thread> recvBufferThread_;
	scoped_ptr<Thread> sendBufferThread_;
	scoped_ptr<BlockQueue<BufferPtr> > sendQueue_;
	Buffer recvBuffer_;

	OnReadCallback onReadCallback_;
	OnErrorCallback onErrorCallback_;
	OnCloseCallback onCloseCallback_;
};

Common.h

#pragma once

#include <assert.h>
#include <stdlib.h>
#include <cstddef>
#include <string.h>
#include <memory>
#include <functional>
#include <string>
#include <deque>
#include <algorithm>
#include <vector>
#include <map>
#include <iterator>
#include <WinSock2.h>
#include <Windows.h>
#include <iostream> // for test

namespace internal {

//  This is an implementation designed to match the anticipated future TR2
//  implementation of the scoped_ptr class, and its closely-related brethren,
//  scoped_array, scoped_ptr_malloc, and make_scoped_ptr.

template <class C> class scoped_ptr;
template <class C> class scoped_array;

// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
// automatically deletes the pointer it holds (if any).
// That is, scoped_ptr<T> owns the T object that it points to.
// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
//
// The size of a scoped_ptr is small:
// sizeof(scoped_ptr<C>) == sizeof(C*)
template <class C>
class scoped_ptr {
 public:

  // The element type
  typedef C element_type;

  // Constructor.  Defaults to intializing with NULL.
  // There is no way to create an uninitialized scoped_ptr.
  // The input parameter must be allocated with new.
  explicit scoped_ptr(C* p = NULL) : ptr_(p) { }

  // Destructor.  If there is a C object, delete it.
  // We don't need to test ptr_ == NULL because C++ does that for us.
  ~scoped_ptr() {
    enum { type_must_be_complete = sizeof(C) };
    delete ptr_;
  }

  // Reset.  Deletes the current owned object, if any.
  // Then takes ownership of a new object, if given.
  // this->reset(this->get()) works.
  void reset(C* p = NULL) {
    if (p != ptr_) {
      enum { type_must_be_complete = sizeof(C) };
      delete ptr_;
      ptr_ = p;
    }
  }

  // Accessors to get the owned object.
  // operator* and operator-> will assert() if there is no current object.
  C& operator*() const {
    assert(ptr_ != NULL);
    return *ptr_;
  }
  C* operator->() const  {
    assert(ptr_ != NULL);
    return ptr_;
  }
  C* get() const { return ptr_; }

  // Comparison operators.
  // These return whether two scoped_ptr refer to the same object, not just to
  // two different but equal objects.
  bool operator==(C* p) const { return ptr_ == p; }
  bool operator!=(C* p) const { return ptr_ != p; }

  // Swap two scoped pointers.
  void swap(scoped_ptr& p2) {
    C* tmp = ptr_;
    ptr_ = p2.ptr_;
    p2.ptr_ = tmp;
  }

  // Release a pointer.
  // The return value is the current pointer held by this object.
  // If this object holds a NULL pointer, the return value is NULL.
  // After this operation, this object will hold a NULL pointer,
  // and will not own the object any more.
  C* release() {
    C* retVal = ptr_;
    ptr_ = NULL;
    return retVal;
  }

 private:
  C* ptr_;

  // Forbid comparison of scoped_ptr types.  If C2 != C, it totally doesn't
  // make sense, and if C2 == C, it still doesn't make sense because you should
  // never have the same object owned by two different scoped_ptrs.
  template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
  template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;

  // Disallow evil constructors
  scoped_ptr(const scoped_ptr&);
  void operator=(const scoped_ptr&);
};

// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
// with new [] and the destructor deletes objects with delete [].
//
// As with scoped_ptr<C>, a scoped_array<C> either points to an object
// or is NULL.  A scoped_array<C> owns the object that it points to.
//
// Size: sizeof(scoped_array<C>) == sizeof(C*)
template <class C>
class scoped_array {
 public:

  // The element type
  typedef C element_type;

  // Constructor.  Defaults to intializing with NULL.
  // There is no way to create an uninitialized scoped_array.
  // The input parameter must be allocated with new [].
  explicit scoped_array(C* p = NULL) : array_(p) { }

  // Destructor.  If there is a C object, delete it.
  // We don't need to test ptr_ == NULL because C++ does that for us.
  ~scoped_array() {
    enum { type_must_be_complete = sizeof(C) };
    delete[] array_;
  }

  // Reset.  Deletes the current owned object, if any.
  // Then takes ownership of a new object, if given.
  // this->reset(this->get()) works.
  void reset(C* p = NULL) {
    if (p != array_) {
      enum { type_must_be_complete = sizeof(C) };
      delete[] array_;
      array_ = p;
    }
  }

  // Get one element of the current object.
  // Will assert() if there is no current object, or index i is negative.
  C& operator[](std::ptrdiff_t i) const {
    assert(i >= 0);
    assert(array_ != NULL);
    return array_[i];
  }

  // Get a pointer to the zeroth element of the current object.
  // If there is no current object, return NULL.
  C* get() const {
    return array_;
  }

  // Comparison operators.
  // These return whether two scoped_array refer to the same object, not just to
  // two different but equal objects.
  bool operator==(C* p) const { return array_ == p; }
  bool operator!=(C* p) const { return array_ != p; }

  // Swap two scoped arrays.
  void swap(scoped_array& p2) {
    C* tmp = array_;
    array_ = p2.array_;
    p2.array_ = tmp;
  }

  // Release an array.
  // The return value is the current pointer held by this object.
  // If this object holds a NULL pointer, the return value is NULL.
  // After this operation, this object will hold a NULL pointer,
  // and will not own the object any more.
  C* release() {
    C* retVal = array_;
    array_ = NULL;
    return retVal;
  }

 private:
  C* array_;

  // Forbid comparison of different scoped_array types.
  template <class C2> bool operator==(scoped_array<C2> const& p2) const;
  template <class C2> bool operator!=(scoped_array<C2> const& p2) const;

  // Disallow evil constructors
  scoped_array(const scoped_array&);
  void operator=(const scoped_array&);
};

}  // namespace internal

// We made these internal so that they would show up as such in the docs,
// but we don't want to stick "internal::" in front of them everywhere.
using internal::scoped_ptr;
using internal::scoped_array;

Condition.h

#pragma once

#include "MutexLock.h"

class Condition
{
public:
	explicit Condition(MutexLock &mutex)
		: mutex_(mutex)
	{
		InitializeConditionVariable(&cond_);
	}

	void wait()
	{
		SleepConditionVariableCS(&cond_, &mutex_.cs(), INFINITE);
	}

	void notify()
	{
		WakeConditionVariable(&cond_);
	}

private:
	Condition(const Condition&);
	Condition& operator=(const Condition&);

private:
	MutexLock &mutex_;
	CONDITION_VARIABLE cond_;
};

Connector.h

#pragma once

#include "Common.h"

class Connector
{
public:
	typedef std::tr1::function<void(SOCKET fd)> ConnectionCallback;

	Connector(const std::string &ip, unsigned short port);
	virtual ~Connector(void);
	
	virtual void start();
	virtual void stop();

	void setConnectionCallback(const ConnectionCallback &func) { func_ = func; }
	SOCKET socket() const { return sock_; }
	bool connected() const { return connected_; }

private:
	void connect();

private:
	std::string ip_;
	unsigned short port_;
	ConnectionCallback func_;
	SOCKET sock_;
	bool connected_;
};

MutexLock.h

#pragma once

#include <WinBase.h>

class MutexLock
{
public:
	MutexLock()
	{
		InitializeCriticalSection(&criticalSection_);
	}

	~MutexLock()
	{
		DeleteCriticalSection(&criticalSection_);
	}

	void lock()
	{
		EnterCriticalSection(&criticalSection_);
	}

	void unlock()
	{
		LeaveCriticalSection(&criticalSection_);
	}

	CRITICAL_SECTION& cs()
	{
		return criticalSection_;
	}

private:
	MutexLock(const MutexLock&);
	MutexLock& operator=(const MutexLock&);

	CRITICAL_SECTION criticalSection_;
};

class MutexLockGuard
{
public:
	explicit MutexLockGuard(MutexLock &mutex)
		: mutex_(mutex)
	{
		mutex_.lock();
	}

	~MutexLockGuard()
	{
		mutex_.unlock();
	}

private:
	MutexLockGuard(const MutexLockGuard&);
	MutexLockGuard operator=(const MutexLockGuard&);

	MutexLock& mutex_;
};

Thread.h

#pragma once

#include "Common.h"

class Thread
{
public:
	typedef std::tr1::function<void()> ThreadFunc;

	explicit Thread(const ThreadFunc&, const std::string &name=std::string());
	~Thread();

	void start();
	int join();
	bool started() const { return started_; }
	HANDLE handle() const { return handle_; }
	unsigned int tid() const { return tid_; }
	const std::string& name() const { return name_; }

private:
	static unsigned int WINAPI startThread(void *data);
	void runInThread();

private:
	HANDLE handle_;
	unsigned int tid_;
	bool started_;
	ThreadFunc func_;
	std::string name_;
};

WSAStartup.h

#pragma once

class WsaStartup
{
public:
	WsaStartup(unsigned char majorVer, unsigned char minorVer);
	virtual ~WsaStartup(void);
};
Buffer.cpp

#include "Buffer.h"
#include <WinSock2.h>

void Buffer::appendInt32(__int32 x)
{
	__int32 be32 = htonl(x);
	append(&be32, sizeof(be32));
}

void Buffer::appendInt16(__int16 x)
{
	__int16 be16 = htons(x);
	append(&be16, sizeof(be16));
}

void Buffer::appendInt8(__int8 x)
{
	append(&x, sizeof(x));
}

__int32 Buffer::readInt32()
{
	__int32 result = peekInt32();
	retrieveInt32();
	return result;
}

__int16 Buffer::readInt16()
{
	__int16 result = peekInt16();
	retrieveInt16();
	return result;
}

__int8 Buffer::readInt8()
{
	__int8 result = peekInt8();
	retrieveInt8();
	return result;
}

__int32 Buffer::peekInt32() const
{
	assert(readableBytes() >= sizeof(__int32));
	__int32 be32 = 0;
	::memcpy(&be32, peek(), sizeof(be32));
	return ntohl(be32);
}

__int16 Buffer::peekInt16() const
{
	assert(readableBytes() >= sizeof(__int16));
	__int16 be16 = 0;
	::memcpy(&be16, peek(), sizeof(be16));
	return htons(be16);
}

__int8 Buffer::peekInt8() const
{
	assert(readableBytes() >= sizeof(__int8));
	__int8 x = *peek();
	return x;
}

void Buffer::prependInt32(__int32 x)
{
	__int32 be32 = htonl(x);
	prepend(&be32, sizeof(be32));
}

void Buffer::prependInt16(__int16 x)
{
	__int16 be16 = htons(x);
	prepend(&be16, sizeof(be16));
}

void Buffer::prependInt8(__int8 x)
{
	prepend(&x, sizeof(x));
}

int Buffer::readFd(void* fd, int *saveErrno)
{
	SOCKET sock = (SOCKET)fd;
	int writable = writableBytes();
	int n = ::recv(sock, begin() + writerIndex_, writable, 0);
	if (SOCKET_ERROR == n)
	{
		*saveErrno = WSAGetLastError();
	}
	else if (n <= writable)
	{
		writerIndex_ += n;
	}
	
	return n;
}


Channel.cpp

#include "Channel.h"
#include "Thread.h"
#include "Connector.h"

Channel::Channel(ConnectorPtr connectorPtr)
	: connectorPtr_(connectorPtr)
	, sendQueue_(new BlockQueue<BufferPtr>())
{
	recvBufferThread_.reset(new Thread(std::tr1::bind(&Channel::recvBufferThread, this), "eventHandleThread"));
	sendBufferThread_.reset(new Thread(std::tr1::bind(&Channel::sendBufferThread, this), "sendBufferThread"));
}

Channel::~Channel(void)
{
}

void Channel::start()
{
	connectorPtr_->start();
	recvBufferThread_->start();
	sendBufferThread_->start();
}

void Channel::stop()
{
	connectorPtr_->stop();
	BufferPtr lastBufferPtr(new Buffer());
	sendQueue_->put(lastBufferPtr);

	recvBufferThread_->join();
	sendBufferThread_->join();

	sendQueue_.reset(new BlockQueue<BufferPtr>());
}

void Channel::send(const unsigned char *msg, size_t len)
{
	BufferPtr bufferPtr(new Buffer());
	bufferPtr->append(msg, len);
	sendQueue_->put(bufferPtr);
}

void Channel::send(const BufferPtr &msg)
{
	sendQueue_->put(msg);
}

void Channel::sendBufferThread()
{
	std::cout << "recvBufferThread" << std::endl;
	while (connectorPtr_->connected()) {
		BufferPtr bufferPtr = sendQueue_->take();
		if (!connectorPtr_->connected()) {
			break;
		}

		unsigned int left = bufferPtr->readableBytes();
		while (left > 0) {
			int sendNum = ::send(connectorPtr_->socket(), bufferPtr->peek(), left, 0);
			if (SOCKET_ERROR == sendNum) {
				onErrorCallback_(TCPCONN_ERROR_SEND);
				break;
			}
			left -= sendNum;
			bufferPtr->retrieve(sendNum);
		}
	}

	std::cout << "recvBufferThread exit" << std::endl;
}

void Channel::recvBufferThread()
{
	std::cout << "sendBufferThread" << std::endl;
	while (connectorPtr_->connected()) {
		static const int bufferLen = 65535;;
		char buffer[bufferLen];
		int recvNum = ::recv(connectorPtr_->socket(), buffer, bufferLen, 0);
		if (recvNum > 0) {
			recvBuffer_.append(buffer, recvNum);
			onReadCallback_(recvBuffer_);
		} else if (0 == recvNum) {
			onCloseCallback_();
			break;
		} else {
			onErrorCallback_(TCPCONN_ERROR_RECV);
			break;
		}
	}

	std::cout << "sendBufferThread exit" << std::endl;
}

Connector.cpp

#include "Connector.h"
#include <Windows.h>

Connector::Connector(const std::string &ip, unsigned short port)
	: ip_(ip)
	, port_(port)
	, func_(NULL)
	, sock_(INVALID_SOCKET)
	, connected_(false)
{
}

Connector::~Connector(void)
{
	stop();
}

void Connector::start()
{
	connect();
}

void Connector::stop()
{
	connected_ = false;
	if (INVALID_SOCKET != sock_) {
		::shutdown(sock_, 2); // FIXME
		::closesocket(sock_);
		sock_ = INVALID_SOCKET;
	}
}

void Connector::connect()
{
	sockaddr_in sin;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr(ip_.c_str());
	sin.sin_port = htons(port_);
	sock_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET != sock_) {
		if (0 == ::connect(sock_, (SOCKADDR*)&sin, sizeof(sin))) {
			connected_ = true;
		} else {
			::closesocket(sock_);
			sock_ = INVALID_SOCKET;
		}
	}
}

Thread.cpp

#include "Thread.h"
#include <process.h>
#include <assert.h>

Thread::Thread(const ThreadFunc &func, const std::string &name)
	: handle_(NULL)
	, tid_(0)
	, started_(false)
	, func_(func)
	, name_(name)
{
}

Thread::~Thread()
{
	CloseHandle(handle_);
}

void Thread::start()
{
	started_ = true;
	handle_ = (HANDLE)_beginthreadex(NULL, 0, startThread, this, 0, &tid_);
	assert(handle_ > 0);
}

int Thread::join()
{
	if (GetCurrentThreadId() == tid_) {
		return 0;
	}
	return WaitForSingleObject(handle_, INFINITE);
}

unsigned int Thread::startThread(void *data)
{
	Thread *thread = static_cast<Thread*>(data);
	thread->runInThread();
	return 0;
}

void Thread::runInThread()
{
	assert(GetCurrentThreadId() == tid_);
	if (func_) {
		func_();
	}
	started_ = false;
}

WsaStartup.cpp

#include "WsaStartup.h"
#include <Windows.h>
#include <crtdbg.h>

WsaStartup::WsaStartup(unsigned char majorVer, unsigned char minorVer)
{
	WSAData wsaData;
	_ASSERT(0 == WSAStartup(MAKEWORD(majorVer, minorVer), &wsaData));
}


WsaStartup::~WsaStartup(void)
{
	WSACleanup();
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
10.1 何为Qt样式表 由于Qt样式表的引入,定制Qt部件的外观样式变得非常简单。 Qt样式表的思想很大程度上是来自于HTML的层叠式样式表(CSS), 通过调用QWidget::setStyleSheet()或QApplication::setStyleSheet(), 你可以为一个独立的子部件、整个窗口,甚至是整个个应用程序指定一个样式表。 样式表是通过QStyle的一个叫做QStyleSheetStyle的特殊子类来实现的。 这个特殊的子类实际上是其他的系统特定风格类的包裹类,它会把通过样式表指定的自定义外观风格应用在底层的系统特定风格之上。 10.2 样式表语法基础 Qt样式表与CSS的语法规则几乎完全相同,如果你已经了解了CSS,完全可以跳过本节。 一个样式表由一系列的样式规则构成。每个样式规则都有着下面的形式: selector { attribute: value } 选择器(selector)部分通常是一个类名(例如QComboBox),当然也还有其他的语法形式。 属性(attribute)部分是一个样式表属性的名字, 值(value)部分是赋给该属性的值。 为了使用方便,我们还可以使用一种简化形式: selector1, selector2, ..., selectorM { attribute1: value1; attribute2: value2; ... attributeN: valueN; } 这种简化形式可以同时为与M个选择器相匹配的部件设置N种属性。 例如: QCheckBox, QComboBox, QSpinBox { color: red; //字体颜色:红 font: bold; } 这个规则设置了所有的QCheckBox、QComboBox和QSpinBox的前景色、背景色和字体。 10.3 方箱模型 在样式表中,每个部件都被看作是一个由四个同心相似的矩形组成的箱体: 空白(margin)、边框(border)、填充(padding)和内容(content)。 对于一个平面部件——例如一个空白、边框和填充都是0像素的部件——而言,这四个矩形是完全重合的。 空白区域位于边框外,并且总是透明的。 边框为部件提供了四周的框架,其border-style属性可以设置为一些内置的框架风格,如inset、outset、solid和ridge。 填充在边框和内容区域之间提供了空白间隔。 10.4 前景与背景 部件的前景色用于绘制上面的文本,可以通过color属性指定。 背景色用于绘制部件的填充矩形,可以通过background-color属性指定。 背景图片使用background-image属性定义,它用于绘制由background-origin指定的矩形区域(空白、边框、填充或内容)。背景图片在矩形区域内的对齐和平铺方式可以通过background-position和background-repeat属性指定。 QFrame { margin: 10px; border: 2px solid green; padding: 20px; background-image: url(qt.png); background-position: top right; background-origin: content; background-repeat: none; } 在这个例子中,QFrame四周的空白、边框和填充值都是一样的。 实际上margin属性可以在上下左右四个方向分别指定我们需要的不同值,例如: QFrame { margin: 14px 18px 20px 18px; } 同时,我们也可以分别指定margin-top、margin-right、margin-bottom、margin-left四个属性。 QFrame { margin-top: 14px; margin-right: 18px; margin-bottom: 20px; margin-left: 18px; } 虽然目前我们仅仅使用了QFrame作为例子,但是我们也可以同样的将这些属性应用于任何一个支持方箱模型的Qt部件,例如:QCheckBox、 QLabel、QLineEdit、QListView、QMenu、QPushButton、QTextEdit、和QToolTip。 10.5 可缩放样式 在默认情况下,通过background-image指定的背景图片会自动重复平铺,以覆盖部件的整个填充矩形(即边框里面的那个区域)。 ///注意区别: 如果我们想创建能够随着部件大小自动缩放而不是平铺的背景,我们需要设置一种称之为“边框图片”的东东。 注意 “边框图片”可以通过border-image属性指定,它同时提供了部件的背景和边框。 一个“边框图片”被分为九个部分(九宫格),有点向tic-tac-toe游戏的棋盘。 当一个部件的边框被填充时,四角的格子通常不会发生变化,而其余的五个格子则可能被拉伸或平铺以填充可用空间。 当指定一个“边框图片”时,除了图片本身,我们还必须指定用来分割九宫格的四条分割线。同时我们还必须指定非边角的格子是应该平铺还是拉伸,以及边框的宽度(用来确定边角格子的大小,防止边角被缩放变形)。 例如,下面的样式表定义了上图中的button: QPushButton { border-width: 4px; border-image: url(button.png) 4 4 4 4 stretch stretch; } 另外,“边框图片”还应该含有alpha通道,以使背景能够在边角处露出来。 10.6控制大小 min-width和min-height两个属性可以用来指定一个部件的内容区域的最小大小。 这两个值将影响部件的minimumSizeHint(),并在布局时被考虑。 例如: QPushButton { min-width: 68px; min-height: 28px; } 如果该属性没有被指定,最小大小将从部件的内容区域和当前样式中继承。 10.7 伪状态 部件的外观可以按照用户界面元素状态的不同来分别定义,这在样式表中被称为“伪状态”。例如,如果我们想在一个push button在被按下的时候具有sunken的外观,我们可以指定一个叫做 :pressed 的伪状态。 QPushButton { border: 2px outset green; } QPushButton:pressed { background: gray; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值